brintos

brintos / linux-shallow public Read only

0
0
Text · 13.5 KiB · 30028e5 Raw
525 lines · c
1/*2 *  linux/drivers/scsi/esas2r/esas2r_vda.c3 *      esas2r driver VDA firmware interface functions4 *5 *  Copyright (c) 2001-2013 ATTO Technology, Inc.6 *  (mailto:linuxdrivers@attotech.com)7 */8/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/9/*10 *  This program is free software; you can redistribute it and/or modify11 *  it under the terms of the GNU General Public License as published by12 *  the Free Software Foundation; version 2 of the License.13 *14 *  This program is distributed in the hope that it will be useful,15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the17 *  GNU General Public License for more details.18 *19 *  NO WARRANTY20 *  THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR21 *  CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT22 *  LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,23 *  MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is24 *  solely responsible for determining the appropriateness of using and25 *  distributing the Program and assumes all risks associated with its26 *  exercise of rights under this Agreement, including but not limited to27 *  the risks and costs of program errors, damage to or loss of data,28 *  programs or equipment, and unavailability or interruption of operations.29 *30 *  DISCLAIMER OF LIABILITY31 *  NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY32 *  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL33 *  DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND34 *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR35 *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE36 *  USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED37 *  HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES38 *39 *  You should have received a copy of the GNU General Public License40 *  along with this program; if not, write to the Free Software41 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA42 */43/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/44 45#include "esas2r.h"46 47static u8 esas2r_vdaioctl_versions[] = {48	ATTO_VDA_VER_UNSUPPORTED,49	ATTO_VDA_FLASH_VER,50	ATTO_VDA_VER_UNSUPPORTED,51	ATTO_VDA_VER_UNSUPPORTED,52	ATTO_VDA_CLI_VER,53	ATTO_VDA_VER_UNSUPPORTED,54	ATTO_VDA_CFG_VER,55	ATTO_VDA_MGT_VER,56	ATTO_VDA_GSV_VER57};58 59static void clear_vda_request(struct esas2r_request *rq);60 61static void esas2r_complete_vda_ioctl(struct esas2r_adapter *a,62				      struct esas2r_request *rq);63 64/* Prepare a VDA IOCTL request to be sent to the firmware. */65bool esas2r_process_vda_ioctl(struct esas2r_adapter *a,66			      struct atto_ioctl_vda *vi,67			      struct esas2r_request *rq,68			      struct esas2r_sg_context *sgc)69{70	u32 datalen = 0;71	struct atto_vda_sge *firstsg = NULL;72	u8 vercnt = (u8)ARRAY_SIZE(esas2r_vdaioctl_versions);73 74	vi->status = ATTO_STS_SUCCESS;75	vi->vda_status = RS_PENDING;76 77	if (vi->function >= vercnt) {78		vi->status = ATTO_STS_INV_FUNC;79		return false;80	}81 82	if (vi->version > esas2r_vdaioctl_versions[vi->function]) {83		vi->status = ATTO_STS_INV_VERSION;84		return false;85	}86 87	if (test_bit(AF_DEGRADED_MODE, &a->flags)) {88		vi->status = ATTO_STS_DEGRADED;89		return false;90	}91 92	if (vi->function != VDA_FUNC_SCSI)93		clear_vda_request(rq);94 95	rq->vrq->scsi.function = vi->function;96	rq->interrupt_cb = esas2r_complete_vda_ioctl;97	rq->interrupt_cx = vi;98 99	switch (vi->function) {100	case VDA_FUNC_FLASH:101 102		if (vi->cmd.flash.sub_func != VDA_FLASH_FREAD103		    && vi->cmd.flash.sub_func != VDA_FLASH_FWRITE104		    && vi->cmd.flash.sub_func != VDA_FLASH_FINFO) {105			vi->status = ATTO_STS_INV_FUNC;106			return false;107		}108 109		if (vi->cmd.flash.sub_func != VDA_FLASH_FINFO)110			datalen = vi->data_length;111 112		rq->vrq->flash.length = cpu_to_le32(datalen);113		rq->vrq->flash.sub_func = vi->cmd.flash.sub_func;114 115		memcpy(rq->vrq->flash.data.file.file_name,116		       vi->cmd.flash.data.file.file_name,117		       sizeof(vi->cmd.flash.data.file.file_name));118 119		firstsg = rq->vrq->flash.data.file.sge;120		break;121 122	case VDA_FUNC_CLI:123 124		datalen = vi->data_length;125 126		rq->vrq->cli.cmd_rsp_len =127			cpu_to_le32(vi->cmd.cli.cmd_rsp_len);128		rq->vrq->cli.length = cpu_to_le32(datalen);129 130		firstsg = rq->vrq->cli.sge;131		break;132 133	case VDA_FUNC_MGT:134	{135		u8 *cmdcurr_offset = sgc->cur_offset136				     - offsetof(struct atto_ioctl_vda, data)137				     + offsetof(struct atto_ioctl_vda, cmd)138				     + offsetof(struct atto_ioctl_vda_mgt_cmd,139						data);140		/*141		 * build the data payload SGL here first since142		 * esas2r_sgc_init() will modify the S/G list offset for the143		 * management SGL (which is built below where the data SGL is144		 * usually built).145		 */146 147		if (vi->data_length) {148			u32 payldlen = 0;149 150			if (vi->cmd.mgt.mgt_func == VDAMGT_DEV_HEALTH_REQ151			    || vi->cmd.mgt.mgt_func == VDAMGT_DEV_METRICS) {152				rq->vrq->mgt.payld_sglst_offset =153					(u8)offsetof(struct atto_vda_mgmt_req,154						     payld_sge);155 156				payldlen = vi->data_length;157				datalen = vi->cmd.mgt.data_length;158			} else if (vi->cmd.mgt.mgt_func == VDAMGT_DEV_INFO2159				   || vi->cmd.mgt.mgt_func ==160				   VDAMGT_DEV_INFO2_BYADDR) {161				datalen = vi->data_length;162				cmdcurr_offset = sgc->cur_offset;163			} else {164				vi->status = ATTO_STS_INV_PARAM;165				return false;166			}167 168			/* Setup the length so building the payload SGL works */169			rq->vrq->mgt.length = cpu_to_le32(datalen);170 171			if (payldlen) {172				rq->vrq->mgt.payld_length =173					cpu_to_le32(payldlen);174 175				esas2r_sgc_init(sgc, a, rq,176						rq->vrq->mgt.payld_sge);177				sgc->length = payldlen;178 179				if (!esas2r_build_sg_list(a, rq, sgc)) {180					vi->status = ATTO_STS_OUT_OF_RSRC;181					return false;182				}183			}184		} else {185			datalen = vi->cmd.mgt.data_length;186 187			rq->vrq->mgt.length = cpu_to_le32(datalen);188		}189 190		/*191		 * Now that the payload SGL is built, if any, setup to build192		 * the management SGL.193		 */194		firstsg = rq->vrq->mgt.sge;195		sgc->cur_offset = cmdcurr_offset;196 197		/* Finish initializing the management request. */198		rq->vrq->mgt.mgt_func = vi->cmd.mgt.mgt_func;199		rq->vrq->mgt.scan_generation = vi->cmd.mgt.scan_generation;200		rq->vrq->mgt.dev_index =201			cpu_to_le32(vi->cmd.mgt.dev_index);202 203		esas2r_nuxi_mgt_data(rq->vrq->mgt.mgt_func, &vi->cmd.mgt.data);204		break;205	}206 207	case VDA_FUNC_CFG:208 209		if (vi->data_length210		    || vi->cmd.cfg.data_length == 0) {211			vi->status = ATTO_STS_INV_PARAM;212			return false;213		}214 215		if (vi->cmd.cfg.cfg_func == VDA_CFG_INIT) {216			vi->status = ATTO_STS_INV_FUNC;217			return false;218		}219 220		rq->vrq->cfg.sub_func = vi->cmd.cfg.cfg_func;221		rq->vrq->cfg.length = cpu_to_le32(vi->cmd.cfg.data_length);222 223		if (vi->cmd.cfg.cfg_func == VDA_CFG_GET_INIT) {224			memcpy(&rq->vrq->cfg.data,225			       &vi->cmd.cfg.data,226			       vi->cmd.cfg.data_length);227 228			esas2r_nuxi_cfg_data(rq->vrq->cfg.sub_func,229					     &rq->vrq->cfg.data);230		} else {231			vi->status = ATTO_STS_INV_FUNC;232 233			return false;234		}235 236		break;237 238	case VDA_FUNC_GSV:239 240		vi->cmd.gsv.rsp_len = vercnt;241 242		memcpy(vi->cmd.gsv.version_info, esas2r_vdaioctl_versions,243		       vercnt);244 245		vi->vda_status = RS_SUCCESS;246		break;247 248	default:249 250		vi->status = ATTO_STS_INV_FUNC;251		return false;252	}253 254	if (datalen) {255		esas2r_sgc_init(sgc, a, rq, firstsg);256		sgc->length = datalen;257 258		if (!esas2r_build_sg_list(a, rq, sgc)) {259			vi->status = ATTO_STS_OUT_OF_RSRC;260			return false;261		}262	}263 264	esas2r_start_request(a, rq);265 266	return true;267}268 269static void esas2r_complete_vda_ioctl(struct esas2r_adapter *a,270				      struct esas2r_request *rq)271{272	struct atto_ioctl_vda *vi = (struct atto_ioctl_vda *)rq->interrupt_cx;273 274	vi->vda_status = rq->req_stat;275 276	switch (vi->function) {277	case VDA_FUNC_FLASH:278 279		if (vi->cmd.flash.sub_func == VDA_FLASH_FINFO280		    || vi->cmd.flash.sub_func == VDA_FLASH_FREAD)281			vi->cmd.flash.data.file.file_size =282				le32_to_cpu(rq->func_rsp.flash_rsp.file_size);283 284		break;285 286	case VDA_FUNC_MGT:287 288		vi->cmd.mgt.scan_generation =289			rq->func_rsp.mgt_rsp.scan_generation;290		vi->cmd.mgt.dev_index = le16_to_cpu(291			rq->func_rsp.mgt_rsp.dev_index);292 293		if (vi->data_length == 0)294			vi->cmd.mgt.data_length =295				le32_to_cpu(rq->func_rsp.mgt_rsp.length);296 297		esas2r_nuxi_mgt_data(rq->vrq->mgt.mgt_func, &vi->cmd.mgt.data);298		break;299 300	case VDA_FUNC_CFG:301 302		if (vi->cmd.cfg.cfg_func == VDA_CFG_GET_INIT) {303			struct atto_ioctl_vda_cfg_cmd *cfg = &vi->cmd.cfg;304			struct atto_vda_cfg_rsp *rsp = &rq->func_rsp.cfg_rsp;305			char buf[sizeof(cfg->data.init.fw_release) + 1];306 307			cfg->data_length =308				cpu_to_le32(sizeof(struct atto_vda_cfg_init));309			cfg->data.init.vda_version =310				le32_to_cpu(rsp->vda_version);311			cfg->data.init.fw_build = rsp->fw_build;312 313			snprintf(buf, sizeof(buf), "%1.1u.%2.2u",314				 (int)LOBYTE(le16_to_cpu(rsp->fw_release)),315				 (int)HIBYTE(le16_to_cpu(rsp->fw_release)));316 317			memcpy(&cfg->data.init.fw_release, buf,318			       sizeof(cfg->data.init.fw_release));319 320			if (LOWORD(LOBYTE(cfg->data.init.fw_build)) == 'A')321				cfg->data.init.fw_version =322					cfg->data.init.fw_build;323			else324				cfg->data.init.fw_version =325					cfg->data.init.fw_release;326		} else {327			esas2r_nuxi_cfg_data(rq->vrq->cfg.sub_func,328					     &vi->cmd.cfg.data);329		}330 331		break;332 333	case VDA_FUNC_CLI:334 335		vi->cmd.cli.cmd_rsp_len =336			le32_to_cpu(rq->func_rsp.cli_rsp.cmd_rsp_len);337		break;338 339	default:340 341		break;342	}343}344 345/* Build a flash VDA request. */346void esas2r_build_flash_req(struct esas2r_adapter *a,347			    struct esas2r_request *rq,348			    u8 sub_func,349			    u8 cksum,350			    u32 addr,351			    u32 length)352{353	struct atto_vda_flash_req *vrq = &rq->vrq->flash;354 355	clear_vda_request(rq);356 357	rq->vrq->scsi.function = VDA_FUNC_FLASH;358 359	if (sub_func == VDA_FLASH_BEGINW360	    || sub_func == VDA_FLASH_WRITE361	    || sub_func == VDA_FLASH_READ)362		vrq->sg_list_offset = (u8)offsetof(struct atto_vda_flash_req,363						   data.sge);364 365	vrq->length = cpu_to_le32(length);366	vrq->flash_addr = cpu_to_le32(addr);367	vrq->checksum = cksum;368	vrq->sub_func = sub_func;369}370 371/* Build a VDA management request. */372void esas2r_build_mgt_req(struct esas2r_adapter *a,373			  struct esas2r_request *rq,374			  u8 sub_func,375			  u8 scan_gen,376			  u16 dev_index,377			  u32 length,378			  void *data)379{380	struct atto_vda_mgmt_req *vrq = &rq->vrq->mgt;381 382	clear_vda_request(rq);383 384	rq->vrq->scsi.function = VDA_FUNC_MGT;385 386	vrq->mgt_func = sub_func;387	vrq->scan_generation = scan_gen;388	vrq->dev_index = cpu_to_le16(dev_index);389	vrq->length = cpu_to_le32(length);390 391	if (vrq->length) {392		if (test_bit(AF_LEGACY_SGE_MODE, &a->flags)) {393			vrq->sg_list_offset = (u8)offsetof(394				struct atto_vda_mgmt_req, sge);395 396			vrq->sge[0].length = cpu_to_le32(SGE_LAST | length);397			vrq->sge[0].address = cpu_to_le64(398				rq->vrq_md->phys_addr +399				sizeof(union atto_vda_req));400		} else {401			vrq->sg_list_offset = (u8)offsetof(402				struct atto_vda_mgmt_req, prde);403 404			vrq->prde[0].ctl_len = cpu_to_le32(length);405			vrq->prde[0].address = cpu_to_le64(406				rq->vrq_md->phys_addr +407				sizeof(union atto_vda_req));408		}409	}410 411	if (data) {412		esas2r_nuxi_mgt_data(sub_func, data);413 414		memcpy(&rq->vda_rsp_data->mgt_data.data.bytes[0], data,415		       length);416	}417}418 419/* Build a VDA asyncronous event (AE) request. */420void esas2r_build_ae_req(struct esas2r_adapter *a, struct esas2r_request *rq)421{422	struct atto_vda_ae_req *vrq = &rq->vrq->ae;423 424	clear_vda_request(rq);425 426	rq->vrq->scsi.function = VDA_FUNC_AE;427 428	vrq->length = cpu_to_le32(sizeof(struct atto_vda_ae_data));429 430	if (test_bit(AF_LEGACY_SGE_MODE, &a->flags)) {431		vrq->sg_list_offset =432			(u8)offsetof(struct atto_vda_ae_req, sge);433		vrq->sge[0].length = cpu_to_le32(SGE_LAST | vrq->length);434		vrq->sge[0].address = cpu_to_le64(435			rq->vrq_md->phys_addr +436			sizeof(union atto_vda_req));437	} else {438		vrq->sg_list_offset = (u8)offsetof(struct atto_vda_ae_req,439						   prde);440		vrq->prde[0].ctl_len = cpu_to_le32(vrq->length);441		vrq->prde[0].address = cpu_to_le64(442			rq->vrq_md->phys_addr +443			sizeof(union atto_vda_req));444	}445}446 447/* Build a VDA CLI request. */448void esas2r_build_cli_req(struct esas2r_adapter *a,449			  struct esas2r_request *rq,450			  u32 length,451			  u32 cmd_rsp_len)452{453	struct atto_vda_cli_req *vrq = &rq->vrq->cli;454 455	clear_vda_request(rq);456 457	rq->vrq->scsi.function = VDA_FUNC_CLI;458 459	vrq->length = cpu_to_le32(length);460	vrq->cmd_rsp_len = cpu_to_le32(cmd_rsp_len);461	vrq->sg_list_offset = (u8)offsetof(struct atto_vda_cli_req, sge);462}463 464/* Build a VDA IOCTL request. */465void esas2r_build_ioctl_req(struct esas2r_adapter *a,466			    struct esas2r_request *rq,467			    u32 length,468			    u8 sub_func)469{470	struct atto_vda_ioctl_req *vrq = &rq->vrq->ioctl;471 472	clear_vda_request(rq);473 474	rq->vrq->scsi.function = VDA_FUNC_IOCTL;475 476	vrq->length = cpu_to_le32(length);477	vrq->sub_func = sub_func;478	vrq->sg_list_offset = (u8)offsetof(struct atto_vda_ioctl_req, sge);479}480 481/* Build a VDA configuration request. */482void esas2r_build_cfg_req(struct esas2r_adapter *a,483			  struct esas2r_request *rq,484			  u8 sub_func,485			  u32 length,486			  void *data)487{488	struct atto_vda_cfg_req *vrq = &rq->vrq->cfg;489 490	clear_vda_request(rq);491 492	rq->vrq->scsi.function = VDA_FUNC_CFG;493 494	vrq->sub_func = sub_func;495	vrq->length = cpu_to_le32(length);496 497	if (data) {498		esas2r_nuxi_cfg_data(sub_func, data);499 500		memcpy(&vrq->data, data, length);501	}502}503 504static void clear_vda_request(struct esas2r_request *rq)505{506	u32 handle = rq->vrq->scsi.handle;507 508	memset(rq->vrq, 0, sizeof(*rq->vrq));509 510	rq->vrq->scsi.handle = handle;511 512	rq->req_stat = RS_PENDING;513 514	/* since the data buffer is separate clear that too */515 516	memset(rq->data_buf, 0, ESAS2R_DATA_BUF_LEN);517 518	/*519	 * Setup next and prev pointer in case the request is not going through520	 * esas2r_start_request().521	 */522 523	INIT_LIST_HEAD(&rq->req_list);524}525