162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci *  linux/drivers/scsi/esas2r/esas2r_vda.c
362306a36Sopenharmony_ci *      esas2r driver VDA firmware interface functions
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (c) 2001-2013 ATTO Technology, Inc.
662306a36Sopenharmony_ci *  (mailto:linuxdrivers@attotech.com)
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
962306a36Sopenharmony_ci/*
1062306a36Sopenharmony_ci *  This program is free software; you can redistribute it and/or modify
1162306a36Sopenharmony_ci *  it under the terms of the GNU General Public License as published by
1262306a36Sopenharmony_ci *  the Free Software Foundation; version 2 of the License.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci *  This program is distributed in the hope that it will be useful,
1562306a36Sopenharmony_ci *  but WITHOUT ANY WARRANTY; without even the implied warranty of
1662306a36Sopenharmony_ci *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1762306a36Sopenharmony_ci *  GNU General Public License for more details.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci *  NO WARRANTY
2062306a36Sopenharmony_ci *  THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
2162306a36Sopenharmony_ci *  CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
2262306a36Sopenharmony_ci *  LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
2362306a36Sopenharmony_ci *  MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
2462306a36Sopenharmony_ci *  solely responsible for determining the appropriateness of using and
2562306a36Sopenharmony_ci *  distributing the Program and assumes all risks associated with its
2662306a36Sopenharmony_ci *  exercise of rights under this Agreement, including but not limited to
2762306a36Sopenharmony_ci *  the risks and costs of program errors, damage to or loss of data,
2862306a36Sopenharmony_ci *  programs or equipment, and unavailability or interruption of operations.
2962306a36Sopenharmony_ci *
3062306a36Sopenharmony_ci *  DISCLAIMER OF LIABILITY
3162306a36Sopenharmony_ci *  NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
3262306a36Sopenharmony_ci *  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3362306a36Sopenharmony_ci *  DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
3462306a36Sopenharmony_ci *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
3562306a36Sopenharmony_ci *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
3662306a36Sopenharmony_ci *  USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
3762306a36Sopenharmony_ci *  HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
3862306a36Sopenharmony_ci *
3962306a36Sopenharmony_ci *  You should have received a copy of the GNU General Public License
4062306a36Sopenharmony_ci *  along with this program; if not, write to the Free Software
4162306a36Sopenharmony_ci *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
4262306a36Sopenharmony_ci */
4362306a36Sopenharmony_ci/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#include "esas2r.h"
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic u8 esas2r_vdaioctl_versions[] = {
4862306a36Sopenharmony_ci	ATTO_VDA_VER_UNSUPPORTED,
4962306a36Sopenharmony_ci	ATTO_VDA_FLASH_VER,
5062306a36Sopenharmony_ci	ATTO_VDA_VER_UNSUPPORTED,
5162306a36Sopenharmony_ci	ATTO_VDA_VER_UNSUPPORTED,
5262306a36Sopenharmony_ci	ATTO_VDA_CLI_VER,
5362306a36Sopenharmony_ci	ATTO_VDA_VER_UNSUPPORTED,
5462306a36Sopenharmony_ci	ATTO_VDA_CFG_VER,
5562306a36Sopenharmony_ci	ATTO_VDA_MGT_VER,
5662306a36Sopenharmony_ci	ATTO_VDA_GSV_VER
5762306a36Sopenharmony_ci};
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic void clear_vda_request(struct esas2r_request *rq);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic void esas2r_complete_vda_ioctl(struct esas2r_adapter *a,
6262306a36Sopenharmony_ci				      struct esas2r_request *rq);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/* Prepare a VDA IOCTL request to be sent to the firmware. */
6562306a36Sopenharmony_cibool esas2r_process_vda_ioctl(struct esas2r_adapter *a,
6662306a36Sopenharmony_ci			      struct atto_ioctl_vda *vi,
6762306a36Sopenharmony_ci			      struct esas2r_request *rq,
6862306a36Sopenharmony_ci			      struct esas2r_sg_context *sgc)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	u32 datalen = 0;
7162306a36Sopenharmony_ci	struct atto_vda_sge *firstsg = NULL;
7262306a36Sopenharmony_ci	u8 vercnt = (u8)ARRAY_SIZE(esas2r_vdaioctl_versions);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	vi->status = ATTO_STS_SUCCESS;
7562306a36Sopenharmony_ci	vi->vda_status = RS_PENDING;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (vi->function >= vercnt) {
7862306a36Sopenharmony_ci		vi->status = ATTO_STS_INV_FUNC;
7962306a36Sopenharmony_ci		return false;
8062306a36Sopenharmony_ci	}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	if (vi->version > esas2r_vdaioctl_versions[vi->function]) {
8362306a36Sopenharmony_ci		vi->status = ATTO_STS_INV_VERSION;
8462306a36Sopenharmony_ci		return false;
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	if (test_bit(AF_DEGRADED_MODE, &a->flags)) {
8862306a36Sopenharmony_ci		vi->status = ATTO_STS_DEGRADED;
8962306a36Sopenharmony_ci		return false;
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (vi->function != VDA_FUNC_SCSI)
9362306a36Sopenharmony_ci		clear_vda_request(rq);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	rq->vrq->scsi.function = vi->function;
9662306a36Sopenharmony_ci	rq->interrupt_cb = esas2r_complete_vda_ioctl;
9762306a36Sopenharmony_ci	rq->interrupt_cx = vi;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	switch (vi->function) {
10062306a36Sopenharmony_ci	case VDA_FUNC_FLASH:
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci		if (vi->cmd.flash.sub_func != VDA_FLASH_FREAD
10362306a36Sopenharmony_ci		    && vi->cmd.flash.sub_func != VDA_FLASH_FWRITE
10462306a36Sopenharmony_ci		    && vi->cmd.flash.sub_func != VDA_FLASH_FINFO) {
10562306a36Sopenharmony_ci			vi->status = ATTO_STS_INV_FUNC;
10662306a36Sopenharmony_ci			return false;
10762306a36Sopenharmony_ci		}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci		if (vi->cmd.flash.sub_func != VDA_FLASH_FINFO)
11062306a36Sopenharmony_ci			datalen = vi->data_length;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci		rq->vrq->flash.length = cpu_to_le32(datalen);
11362306a36Sopenharmony_ci		rq->vrq->flash.sub_func = vi->cmd.flash.sub_func;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci		memcpy(rq->vrq->flash.data.file.file_name,
11662306a36Sopenharmony_ci		       vi->cmd.flash.data.file.file_name,
11762306a36Sopenharmony_ci		       sizeof(vi->cmd.flash.data.file.file_name));
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci		firstsg = rq->vrq->flash.data.file.sge;
12062306a36Sopenharmony_ci		break;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	case VDA_FUNC_CLI:
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci		datalen = vi->data_length;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci		rq->vrq->cli.cmd_rsp_len =
12762306a36Sopenharmony_ci			cpu_to_le32(vi->cmd.cli.cmd_rsp_len);
12862306a36Sopenharmony_ci		rq->vrq->cli.length = cpu_to_le32(datalen);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci		firstsg = rq->vrq->cli.sge;
13162306a36Sopenharmony_ci		break;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	case VDA_FUNC_MGT:
13462306a36Sopenharmony_ci	{
13562306a36Sopenharmony_ci		u8 *cmdcurr_offset = sgc->cur_offset
13662306a36Sopenharmony_ci				     - offsetof(struct atto_ioctl_vda, data)
13762306a36Sopenharmony_ci				     + offsetof(struct atto_ioctl_vda, cmd)
13862306a36Sopenharmony_ci				     + offsetof(struct atto_ioctl_vda_mgt_cmd,
13962306a36Sopenharmony_ci						data);
14062306a36Sopenharmony_ci		/*
14162306a36Sopenharmony_ci		 * build the data payload SGL here first since
14262306a36Sopenharmony_ci		 * esas2r_sgc_init() will modify the S/G list offset for the
14362306a36Sopenharmony_ci		 * management SGL (which is built below where the data SGL is
14462306a36Sopenharmony_ci		 * usually built).
14562306a36Sopenharmony_ci		 */
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci		if (vi->data_length) {
14862306a36Sopenharmony_ci			u32 payldlen = 0;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci			if (vi->cmd.mgt.mgt_func == VDAMGT_DEV_HEALTH_REQ
15162306a36Sopenharmony_ci			    || vi->cmd.mgt.mgt_func == VDAMGT_DEV_METRICS) {
15262306a36Sopenharmony_ci				rq->vrq->mgt.payld_sglst_offset =
15362306a36Sopenharmony_ci					(u8)offsetof(struct atto_vda_mgmt_req,
15462306a36Sopenharmony_ci						     payld_sge);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci				payldlen = vi->data_length;
15762306a36Sopenharmony_ci				datalen = vi->cmd.mgt.data_length;
15862306a36Sopenharmony_ci			} else if (vi->cmd.mgt.mgt_func == VDAMGT_DEV_INFO2
15962306a36Sopenharmony_ci				   || vi->cmd.mgt.mgt_func ==
16062306a36Sopenharmony_ci				   VDAMGT_DEV_INFO2_BYADDR) {
16162306a36Sopenharmony_ci				datalen = vi->data_length;
16262306a36Sopenharmony_ci				cmdcurr_offset = sgc->cur_offset;
16362306a36Sopenharmony_ci			} else {
16462306a36Sopenharmony_ci				vi->status = ATTO_STS_INV_PARAM;
16562306a36Sopenharmony_ci				return false;
16662306a36Sopenharmony_ci			}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci			/* Setup the length so building the payload SGL works */
16962306a36Sopenharmony_ci			rq->vrq->mgt.length = cpu_to_le32(datalen);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci			if (payldlen) {
17262306a36Sopenharmony_ci				rq->vrq->mgt.payld_length =
17362306a36Sopenharmony_ci					cpu_to_le32(payldlen);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci				esas2r_sgc_init(sgc, a, rq,
17662306a36Sopenharmony_ci						rq->vrq->mgt.payld_sge);
17762306a36Sopenharmony_ci				sgc->length = payldlen;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci				if (!esas2r_build_sg_list(a, rq, sgc)) {
18062306a36Sopenharmony_ci					vi->status = ATTO_STS_OUT_OF_RSRC;
18162306a36Sopenharmony_ci					return false;
18262306a36Sopenharmony_ci				}
18362306a36Sopenharmony_ci			}
18462306a36Sopenharmony_ci		} else {
18562306a36Sopenharmony_ci			datalen = vi->cmd.mgt.data_length;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci			rq->vrq->mgt.length = cpu_to_le32(datalen);
18862306a36Sopenharmony_ci		}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci		/*
19162306a36Sopenharmony_ci		 * Now that the payload SGL is built, if any, setup to build
19262306a36Sopenharmony_ci		 * the management SGL.
19362306a36Sopenharmony_ci		 */
19462306a36Sopenharmony_ci		firstsg = rq->vrq->mgt.sge;
19562306a36Sopenharmony_ci		sgc->cur_offset = cmdcurr_offset;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci		/* Finish initializing the management request. */
19862306a36Sopenharmony_ci		rq->vrq->mgt.mgt_func = vi->cmd.mgt.mgt_func;
19962306a36Sopenharmony_ci		rq->vrq->mgt.scan_generation = vi->cmd.mgt.scan_generation;
20062306a36Sopenharmony_ci		rq->vrq->mgt.dev_index =
20162306a36Sopenharmony_ci			cpu_to_le32(vi->cmd.mgt.dev_index);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci		esas2r_nuxi_mgt_data(rq->vrq->mgt.mgt_func, &vi->cmd.mgt.data);
20462306a36Sopenharmony_ci		break;
20562306a36Sopenharmony_ci	}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	case VDA_FUNC_CFG:
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci		if (vi->data_length
21062306a36Sopenharmony_ci		    || vi->cmd.cfg.data_length == 0) {
21162306a36Sopenharmony_ci			vi->status = ATTO_STS_INV_PARAM;
21262306a36Sopenharmony_ci			return false;
21362306a36Sopenharmony_ci		}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci		if (vi->cmd.cfg.cfg_func == VDA_CFG_INIT) {
21662306a36Sopenharmony_ci			vi->status = ATTO_STS_INV_FUNC;
21762306a36Sopenharmony_ci			return false;
21862306a36Sopenharmony_ci		}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci		rq->vrq->cfg.sub_func = vi->cmd.cfg.cfg_func;
22162306a36Sopenharmony_ci		rq->vrq->cfg.length = cpu_to_le32(vi->cmd.cfg.data_length);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci		if (vi->cmd.cfg.cfg_func == VDA_CFG_GET_INIT) {
22462306a36Sopenharmony_ci			memcpy(&rq->vrq->cfg.data,
22562306a36Sopenharmony_ci			       &vi->cmd.cfg.data,
22662306a36Sopenharmony_ci			       vi->cmd.cfg.data_length);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci			esas2r_nuxi_cfg_data(rq->vrq->cfg.sub_func,
22962306a36Sopenharmony_ci					     &rq->vrq->cfg.data);
23062306a36Sopenharmony_ci		} else {
23162306a36Sopenharmony_ci			vi->status = ATTO_STS_INV_FUNC;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci			return false;
23462306a36Sopenharmony_ci		}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci		break;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	case VDA_FUNC_GSV:
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci		vi->cmd.gsv.rsp_len = vercnt;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci		memcpy(vi->cmd.gsv.version_info, esas2r_vdaioctl_versions,
24362306a36Sopenharmony_ci		       vercnt);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci		vi->vda_status = RS_SUCCESS;
24662306a36Sopenharmony_ci		break;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	default:
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci		vi->status = ATTO_STS_INV_FUNC;
25162306a36Sopenharmony_ci		return false;
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	if (datalen) {
25562306a36Sopenharmony_ci		esas2r_sgc_init(sgc, a, rq, firstsg);
25662306a36Sopenharmony_ci		sgc->length = datalen;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci		if (!esas2r_build_sg_list(a, rq, sgc)) {
25962306a36Sopenharmony_ci			vi->status = ATTO_STS_OUT_OF_RSRC;
26062306a36Sopenharmony_ci			return false;
26162306a36Sopenharmony_ci		}
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	esas2r_start_request(a, rq);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	return true;
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic void esas2r_complete_vda_ioctl(struct esas2r_adapter *a,
27062306a36Sopenharmony_ci				      struct esas2r_request *rq)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	struct atto_ioctl_vda *vi = (struct atto_ioctl_vda *)rq->interrupt_cx;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	vi->vda_status = rq->req_stat;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	switch (vi->function) {
27762306a36Sopenharmony_ci	case VDA_FUNC_FLASH:
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci		if (vi->cmd.flash.sub_func == VDA_FLASH_FINFO
28062306a36Sopenharmony_ci		    || vi->cmd.flash.sub_func == VDA_FLASH_FREAD)
28162306a36Sopenharmony_ci			vi->cmd.flash.data.file.file_size =
28262306a36Sopenharmony_ci				le32_to_cpu(rq->func_rsp.flash_rsp.file_size);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci		break;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	case VDA_FUNC_MGT:
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci		vi->cmd.mgt.scan_generation =
28962306a36Sopenharmony_ci			rq->func_rsp.mgt_rsp.scan_generation;
29062306a36Sopenharmony_ci		vi->cmd.mgt.dev_index = le16_to_cpu(
29162306a36Sopenharmony_ci			rq->func_rsp.mgt_rsp.dev_index);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci		if (vi->data_length == 0)
29462306a36Sopenharmony_ci			vi->cmd.mgt.data_length =
29562306a36Sopenharmony_ci				le32_to_cpu(rq->func_rsp.mgt_rsp.length);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci		esas2r_nuxi_mgt_data(rq->vrq->mgt.mgt_func, &vi->cmd.mgt.data);
29862306a36Sopenharmony_ci		break;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	case VDA_FUNC_CFG:
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci		if (vi->cmd.cfg.cfg_func == VDA_CFG_GET_INIT) {
30362306a36Sopenharmony_ci			struct atto_ioctl_vda_cfg_cmd *cfg = &vi->cmd.cfg;
30462306a36Sopenharmony_ci			struct atto_vda_cfg_rsp *rsp = &rq->func_rsp.cfg_rsp;
30562306a36Sopenharmony_ci			char buf[sizeof(cfg->data.init.fw_release) + 1];
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci			cfg->data_length =
30862306a36Sopenharmony_ci				cpu_to_le32(sizeof(struct atto_vda_cfg_init));
30962306a36Sopenharmony_ci			cfg->data.init.vda_version =
31062306a36Sopenharmony_ci				le32_to_cpu(rsp->vda_version);
31162306a36Sopenharmony_ci			cfg->data.init.fw_build = rsp->fw_build;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci			snprintf(buf, sizeof(buf), "%1.1u.%2.2u",
31462306a36Sopenharmony_ci				 (int)LOBYTE(le16_to_cpu(rsp->fw_release)),
31562306a36Sopenharmony_ci				 (int)HIBYTE(le16_to_cpu(rsp->fw_release)));
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci			memcpy(&cfg->data.init.fw_release, buf,
31862306a36Sopenharmony_ci			       sizeof(cfg->data.init.fw_release));
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci			if (LOWORD(LOBYTE(cfg->data.init.fw_build)) == 'A')
32162306a36Sopenharmony_ci				cfg->data.init.fw_version =
32262306a36Sopenharmony_ci					cfg->data.init.fw_build;
32362306a36Sopenharmony_ci			else
32462306a36Sopenharmony_ci				cfg->data.init.fw_version =
32562306a36Sopenharmony_ci					cfg->data.init.fw_release;
32662306a36Sopenharmony_ci		} else {
32762306a36Sopenharmony_ci			esas2r_nuxi_cfg_data(rq->vrq->cfg.sub_func,
32862306a36Sopenharmony_ci					     &vi->cmd.cfg.data);
32962306a36Sopenharmony_ci		}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci		break;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	case VDA_FUNC_CLI:
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci		vi->cmd.cli.cmd_rsp_len =
33662306a36Sopenharmony_ci			le32_to_cpu(rq->func_rsp.cli_rsp.cmd_rsp_len);
33762306a36Sopenharmony_ci		break;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	default:
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci		break;
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci/* Build a flash VDA request. */
34662306a36Sopenharmony_civoid esas2r_build_flash_req(struct esas2r_adapter *a,
34762306a36Sopenharmony_ci			    struct esas2r_request *rq,
34862306a36Sopenharmony_ci			    u8 sub_func,
34962306a36Sopenharmony_ci			    u8 cksum,
35062306a36Sopenharmony_ci			    u32 addr,
35162306a36Sopenharmony_ci			    u32 length)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	struct atto_vda_flash_req *vrq = &rq->vrq->flash;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	clear_vda_request(rq);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	rq->vrq->scsi.function = VDA_FUNC_FLASH;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (sub_func == VDA_FLASH_BEGINW
36062306a36Sopenharmony_ci	    || sub_func == VDA_FLASH_WRITE
36162306a36Sopenharmony_ci	    || sub_func == VDA_FLASH_READ)
36262306a36Sopenharmony_ci		vrq->sg_list_offset = (u8)offsetof(struct atto_vda_flash_req,
36362306a36Sopenharmony_ci						   data.sge);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	vrq->length = cpu_to_le32(length);
36662306a36Sopenharmony_ci	vrq->flash_addr = cpu_to_le32(addr);
36762306a36Sopenharmony_ci	vrq->checksum = cksum;
36862306a36Sopenharmony_ci	vrq->sub_func = sub_func;
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci/* Build a VDA management request. */
37262306a36Sopenharmony_civoid esas2r_build_mgt_req(struct esas2r_adapter *a,
37362306a36Sopenharmony_ci			  struct esas2r_request *rq,
37462306a36Sopenharmony_ci			  u8 sub_func,
37562306a36Sopenharmony_ci			  u8 scan_gen,
37662306a36Sopenharmony_ci			  u16 dev_index,
37762306a36Sopenharmony_ci			  u32 length,
37862306a36Sopenharmony_ci			  void *data)
37962306a36Sopenharmony_ci{
38062306a36Sopenharmony_ci	struct atto_vda_mgmt_req *vrq = &rq->vrq->mgt;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	clear_vda_request(rq);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	rq->vrq->scsi.function = VDA_FUNC_MGT;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	vrq->mgt_func = sub_func;
38762306a36Sopenharmony_ci	vrq->scan_generation = scan_gen;
38862306a36Sopenharmony_ci	vrq->dev_index = cpu_to_le16(dev_index);
38962306a36Sopenharmony_ci	vrq->length = cpu_to_le32(length);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	if (vrq->length) {
39262306a36Sopenharmony_ci		if (test_bit(AF_LEGACY_SGE_MODE, &a->flags)) {
39362306a36Sopenharmony_ci			vrq->sg_list_offset = (u8)offsetof(
39462306a36Sopenharmony_ci				struct atto_vda_mgmt_req, sge);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci			vrq->sge[0].length = cpu_to_le32(SGE_LAST | length);
39762306a36Sopenharmony_ci			vrq->sge[0].address = cpu_to_le64(
39862306a36Sopenharmony_ci				rq->vrq_md->phys_addr +
39962306a36Sopenharmony_ci				sizeof(union atto_vda_req));
40062306a36Sopenharmony_ci		} else {
40162306a36Sopenharmony_ci			vrq->sg_list_offset = (u8)offsetof(
40262306a36Sopenharmony_ci				struct atto_vda_mgmt_req, prde);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci			vrq->prde[0].ctl_len = cpu_to_le32(length);
40562306a36Sopenharmony_ci			vrq->prde[0].address = cpu_to_le64(
40662306a36Sopenharmony_ci				rq->vrq_md->phys_addr +
40762306a36Sopenharmony_ci				sizeof(union atto_vda_req));
40862306a36Sopenharmony_ci		}
40962306a36Sopenharmony_ci	}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	if (data) {
41262306a36Sopenharmony_ci		esas2r_nuxi_mgt_data(sub_func, data);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci		memcpy(&rq->vda_rsp_data->mgt_data.data.bytes[0], data,
41562306a36Sopenharmony_ci		       length);
41662306a36Sopenharmony_ci	}
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci/* Build a VDA asyncronous event (AE) request. */
42062306a36Sopenharmony_civoid esas2r_build_ae_req(struct esas2r_adapter *a, struct esas2r_request *rq)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	struct atto_vda_ae_req *vrq = &rq->vrq->ae;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	clear_vda_request(rq);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	rq->vrq->scsi.function = VDA_FUNC_AE;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	vrq->length = cpu_to_le32(sizeof(struct atto_vda_ae_data));
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	if (test_bit(AF_LEGACY_SGE_MODE, &a->flags)) {
43162306a36Sopenharmony_ci		vrq->sg_list_offset =
43262306a36Sopenharmony_ci			(u8)offsetof(struct atto_vda_ae_req, sge);
43362306a36Sopenharmony_ci		vrq->sge[0].length = cpu_to_le32(SGE_LAST | vrq->length);
43462306a36Sopenharmony_ci		vrq->sge[0].address = cpu_to_le64(
43562306a36Sopenharmony_ci			rq->vrq_md->phys_addr +
43662306a36Sopenharmony_ci			sizeof(union atto_vda_req));
43762306a36Sopenharmony_ci	} else {
43862306a36Sopenharmony_ci		vrq->sg_list_offset = (u8)offsetof(struct atto_vda_ae_req,
43962306a36Sopenharmony_ci						   prde);
44062306a36Sopenharmony_ci		vrq->prde[0].ctl_len = cpu_to_le32(vrq->length);
44162306a36Sopenharmony_ci		vrq->prde[0].address = cpu_to_le64(
44262306a36Sopenharmony_ci			rq->vrq_md->phys_addr +
44362306a36Sopenharmony_ci			sizeof(union atto_vda_req));
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci/* Build a VDA CLI request. */
44862306a36Sopenharmony_civoid esas2r_build_cli_req(struct esas2r_adapter *a,
44962306a36Sopenharmony_ci			  struct esas2r_request *rq,
45062306a36Sopenharmony_ci			  u32 length,
45162306a36Sopenharmony_ci			  u32 cmd_rsp_len)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	struct atto_vda_cli_req *vrq = &rq->vrq->cli;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	clear_vda_request(rq);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	rq->vrq->scsi.function = VDA_FUNC_CLI;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	vrq->length = cpu_to_le32(length);
46062306a36Sopenharmony_ci	vrq->cmd_rsp_len = cpu_to_le32(cmd_rsp_len);
46162306a36Sopenharmony_ci	vrq->sg_list_offset = (u8)offsetof(struct atto_vda_cli_req, sge);
46262306a36Sopenharmony_ci}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci/* Build a VDA IOCTL request. */
46562306a36Sopenharmony_civoid esas2r_build_ioctl_req(struct esas2r_adapter *a,
46662306a36Sopenharmony_ci			    struct esas2r_request *rq,
46762306a36Sopenharmony_ci			    u32 length,
46862306a36Sopenharmony_ci			    u8 sub_func)
46962306a36Sopenharmony_ci{
47062306a36Sopenharmony_ci	struct atto_vda_ioctl_req *vrq = &rq->vrq->ioctl;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	clear_vda_request(rq);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	rq->vrq->scsi.function = VDA_FUNC_IOCTL;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	vrq->length = cpu_to_le32(length);
47762306a36Sopenharmony_ci	vrq->sub_func = sub_func;
47862306a36Sopenharmony_ci	vrq->sg_list_offset = (u8)offsetof(struct atto_vda_ioctl_req, sge);
47962306a36Sopenharmony_ci}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci/* Build a VDA configuration request. */
48262306a36Sopenharmony_civoid esas2r_build_cfg_req(struct esas2r_adapter *a,
48362306a36Sopenharmony_ci			  struct esas2r_request *rq,
48462306a36Sopenharmony_ci			  u8 sub_func,
48562306a36Sopenharmony_ci			  u32 length,
48662306a36Sopenharmony_ci			  void *data)
48762306a36Sopenharmony_ci{
48862306a36Sopenharmony_ci	struct atto_vda_cfg_req *vrq = &rq->vrq->cfg;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	clear_vda_request(rq);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	rq->vrq->scsi.function = VDA_FUNC_CFG;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	vrq->sub_func = sub_func;
49562306a36Sopenharmony_ci	vrq->length = cpu_to_le32(length);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	if (data) {
49862306a36Sopenharmony_ci		esas2r_nuxi_cfg_data(sub_func, data);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci		memcpy(&vrq->data, data, length);
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cistatic void clear_vda_request(struct esas2r_request *rq)
50562306a36Sopenharmony_ci{
50662306a36Sopenharmony_ci	u32 handle = rq->vrq->scsi.handle;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	memset(rq->vrq, 0, sizeof(*rq->vrq));
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	rq->vrq->scsi.handle = handle;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	rq->req_stat = RS_PENDING;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	/* since the data buffer is separate clear that too */
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	memset(rq->data_buf, 0, ESAS2R_DATA_BUF_LEN);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	/*
51962306a36Sopenharmony_ci	 * Setup next and prev pointer in case the request is not going through
52062306a36Sopenharmony_ci	 * esas2r_start_request().
52162306a36Sopenharmony_ci	 */
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	INIT_LIST_HEAD(&rq->req_list);
52462306a36Sopenharmony_ci}
525