xref: /linux/drivers/scsi/esas2r/esas2r_ioctl.c (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
126780d9eSBradley Grove /*
226780d9eSBradley Grove  *  linux/drivers/scsi/esas2r/esas2r_ioctl.c
326780d9eSBradley Grove  *      For use with ATTO ExpressSAS R6xx SAS/SATA RAID controllers
426780d9eSBradley Grove  *
526780d9eSBradley Grove  *  Copyright (c) 2001-2013 ATTO Technology, Inc.
626780d9eSBradley Grove  *  (mailto:linuxdrivers@attotech.com)
726780d9eSBradley Grove  *
826780d9eSBradley Grove  * This program is free software; you can redistribute it and/or
926780d9eSBradley Grove  * modify it under the terms of the GNU General Public License
1026780d9eSBradley Grove  * as published by the Free Software Foundation; either version 2
1126780d9eSBradley Grove  * of the License, or (at your option) any later version.
1226780d9eSBradley Grove  *
1326780d9eSBradley Grove  * This program is distributed in the hope that it will be useful,
1426780d9eSBradley Grove  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1526780d9eSBradley Grove  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1626780d9eSBradley Grove  * GNU General Public License for more details.
1726780d9eSBradley Grove  *
1826780d9eSBradley Grove  * NO WARRANTY
1926780d9eSBradley Grove  * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
2026780d9eSBradley Grove  * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
2126780d9eSBradley Grove  * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
2226780d9eSBradley Grove  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
2326780d9eSBradley Grove  * solely responsible for determining the appropriateness of using and
2426780d9eSBradley Grove  * distributing the Program and assumes all risks associated with its
2526780d9eSBradley Grove  * exercise of rights under this Agreement, including but not limited to
2626780d9eSBradley Grove  * the risks and costs of program errors, damage to or loss of data,
2726780d9eSBradley Grove  * programs or equipment, and unavailability or interruption of operations.
2826780d9eSBradley Grove  *
2926780d9eSBradley Grove  * DISCLAIMER OF LIABILITY
3026780d9eSBradley Grove  * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
3126780d9eSBradley Grove  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3226780d9eSBradley Grove  * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
3326780d9eSBradley Grove  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
3426780d9eSBradley Grove  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
3526780d9eSBradley Grove  * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
3626780d9eSBradley Grove  * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
3726780d9eSBradley Grove  *
3826780d9eSBradley Grove  * You should have received a copy of the GNU General Public License
3926780d9eSBradley Grove  * along with this program; if not, write to the Free Software
4026780d9eSBradley Grove  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
4126780d9eSBradley Grove  * USA.
4226780d9eSBradley Grove  */
4326780d9eSBradley Grove 
44*5532f249SIlpo Järvinen #include <linux/bitfield.h>
45*5532f249SIlpo Järvinen 
4626780d9eSBradley Grove #include "esas2r.h"
4726780d9eSBradley Grove 
4826780d9eSBradley Grove /*
4926780d9eSBradley Grove  * Buffered ioctl handlers.  A buffered ioctl is one which requires that we
5026780d9eSBradley Grove  * allocate a DMA-able memory area to communicate with the firmware.  In
5126780d9eSBradley Grove  * order to prevent continually allocating and freeing consistent memory,
5226780d9eSBradley Grove  * we will allocate a global buffer the first time we need it and re-use
5326780d9eSBradley Grove  * it for subsequent ioctl calls that require it.
5426780d9eSBradley Grove  */
5526780d9eSBradley Grove 
5626780d9eSBradley Grove u8 *esas2r_buffered_ioctl;
5726780d9eSBradley Grove dma_addr_t esas2r_buffered_ioctl_addr;
5826780d9eSBradley Grove u32 esas2r_buffered_ioctl_size;
5926780d9eSBradley Grove struct pci_dev *esas2r_buffered_ioctl_pcid;
6026780d9eSBradley Grove 
6148380368SPeter Zijlstra static DEFINE_SEMAPHORE(buffered_ioctl_semaphore, 1);
6226780d9eSBradley Grove typedef int (*BUFFERED_IOCTL_CALLBACK)(struct esas2r_adapter *,
6326780d9eSBradley Grove 				       struct esas2r_request *,
6426780d9eSBradley Grove 				       struct esas2r_sg_context *,
6526780d9eSBradley Grove 				       void *);
6626780d9eSBradley Grove typedef void (*BUFFERED_IOCTL_DONE_CALLBACK)(struct esas2r_adapter *,
6726780d9eSBradley Grove 					     struct esas2r_request *, void *);
6826780d9eSBradley Grove 
6926780d9eSBradley Grove struct esas2r_buffered_ioctl {
7026780d9eSBradley Grove 	struct esas2r_adapter *a;
7126780d9eSBradley Grove 	void *ioctl;
7226780d9eSBradley Grove 	u32 length;
7326780d9eSBradley Grove 	u32 control_code;
7426780d9eSBradley Grove 	u32 offset;
7526780d9eSBradley Grove 	BUFFERED_IOCTL_CALLBACK
7626780d9eSBradley Grove 		callback;
7726780d9eSBradley Grove 	void *context;
7826780d9eSBradley Grove 	BUFFERED_IOCTL_DONE_CALLBACK
7926780d9eSBradley Grove 		done_callback;
8026780d9eSBradley Grove 	void *done_context;
8126780d9eSBradley Grove 
8226780d9eSBradley Grove };
8326780d9eSBradley Grove 
complete_fm_api_req(struct esas2r_adapter * a,struct esas2r_request * rq)8426780d9eSBradley Grove static void complete_fm_api_req(struct esas2r_adapter *a,
8526780d9eSBradley Grove 				struct esas2r_request *rq)
8626780d9eSBradley Grove {
8726780d9eSBradley Grove 	a->fm_api_command_done = 1;
8826780d9eSBradley Grove 	wake_up_interruptible(&a->fm_api_waiter);
8926780d9eSBradley Grove }
9026780d9eSBradley Grove 
9126780d9eSBradley Grove /* Callbacks for building scatter/gather lists for FM API requests */
get_physaddr_fm_api(struct esas2r_sg_context * sgc,u64 * addr)9226780d9eSBradley Grove static u32 get_physaddr_fm_api(struct esas2r_sg_context *sgc, u64 *addr)
9326780d9eSBradley Grove {
9426780d9eSBradley Grove 	struct esas2r_adapter *a = (struct esas2r_adapter *)sgc->adapter;
9526780d9eSBradley Grove 	int offset = sgc->cur_offset - a->save_offset;
9626780d9eSBradley Grove 
9726780d9eSBradley Grove 	(*addr) = a->firmware.phys + offset;
9826780d9eSBradley Grove 	return a->firmware.orig_len - offset;
9926780d9eSBradley Grove }
10026780d9eSBradley Grove 
get_physaddr_fm_api_header(struct esas2r_sg_context * sgc,u64 * addr)10126780d9eSBradley Grove static u32 get_physaddr_fm_api_header(struct esas2r_sg_context *sgc, u64 *addr)
10226780d9eSBradley Grove {
10326780d9eSBradley Grove 	struct esas2r_adapter *a = (struct esas2r_adapter *)sgc->adapter;
10426780d9eSBradley Grove 	int offset = sgc->cur_offset - a->save_offset;
10526780d9eSBradley Grove 
10626780d9eSBradley Grove 	(*addr) = a->firmware.header_buff_phys + offset;
10726780d9eSBradley Grove 	return sizeof(struct esas2r_flash_img) - offset;
10826780d9eSBradley Grove }
10926780d9eSBradley Grove 
11026780d9eSBradley Grove /* Handle EXPRESS_IOCTL_RW_FIRMWARE ioctl with img_type = FW_IMG_FM_API. */
do_fm_api(struct esas2r_adapter * a,struct esas2r_flash_img * fi)11126780d9eSBradley Grove static void do_fm_api(struct esas2r_adapter *a, struct esas2r_flash_img *fi)
11226780d9eSBradley Grove {
11326780d9eSBradley Grove 	struct esas2r_request *rq;
11426780d9eSBradley Grove 
11574d2fd48SBinoy Jayan 	if (mutex_lock_interruptible(&a->fm_api_mutex)) {
11626780d9eSBradley Grove 		fi->status = FI_STAT_BUSY;
11726780d9eSBradley Grove 		return;
11826780d9eSBradley Grove 	}
11926780d9eSBradley Grove 
12026780d9eSBradley Grove 	rq = esas2r_alloc_request(a);
12126780d9eSBradley Grove 	if (rq == NULL) {
12226780d9eSBradley Grove 		fi->status = FI_STAT_BUSY;
123ddcae017STomas Henzl 		goto free_sem;
12426780d9eSBradley Grove 	}
12526780d9eSBradley Grove 
12626780d9eSBradley Grove 	if (fi == &a->firmware.header) {
12726780d9eSBradley Grove 		a->firmware.header_buff = dma_alloc_coherent(&a->pcid->dev,
12826780d9eSBradley Grove 							     (size_t)sizeof(
12926780d9eSBradley Grove 								     struct
13026780d9eSBradley Grove 								     esas2r_flash_img),
13126780d9eSBradley Grove 							     (dma_addr_t *)&a->
13226780d9eSBradley Grove 							     firmware.
13326780d9eSBradley Grove 							     header_buff_phys,
13426780d9eSBradley Grove 							     GFP_KERNEL);
13526780d9eSBradley Grove 
13626780d9eSBradley Grove 		if (a->firmware.header_buff == NULL) {
13726780d9eSBradley Grove 			esas2r_debug("failed to allocate header buffer!");
13826780d9eSBradley Grove 			fi->status = FI_STAT_BUSY;
139ddcae017STomas Henzl 			goto free_req;
14026780d9eSBradley Grove 		}
14126780d9eSBradley Grove 
14226780d9eSBradley Grove 		memcpy(a->firmware.header_buff, fi,
14326780d9eSBradley Grove 		       sizeof(struct esas2r_flash_img));
14426780d9eSBradley Grove 		a->save_offset = a->firmware.header_buff;
14526780d9eSBradley Grove 		a->fm_api_sgc.get_phys_addr =
14626780d9eSBradley Grove 			(PGETPHYSADDR)get_physaddr_fm_api_header;
14726780d9eSBradley Grove 	} else {
14826780d9eSBradley Grove 		a->save_offset = (u8 *)fi;
14926780d9eSBradley Grove 		a->fm_api_sgc.get_phys_addr =
15026780d9eSBradley Grove 			(PGETPHYSADDR)get_physaddr_fm_api;
15126780d9eSBradley Grove 	}
15226780d9eSBradley Grove 
15326780d9eSBradley Grove 	rq->comp_cb = complete_fm_api_req;
15426780d9eSBradley Grove 	a->fm_api_command_done = 0;
15526780d9eSBradley Grove 	a->fm_api_sgc.cur_offset = a->save_offset;
15626780d9eSBradley Grove 
15726780d9eSBradley Grove 	if (!esas2r_fm_api(a, (struct esas2r_flash_img *)a->save_offset, rq,
15826780d9eSBradley Grove 			   &a->fm_api_sgc))
15926780d9eSBradley Grove 		goto all_done;
16026780d9eSBradley Grove 
16126780d9eSBradley Grove 	/* Now wait around for it to complete. */
16226780d9eSBradley Grove 	while (!a->fm_api_command_done)
16326780d9eSBradley Grove 		wait_event_interruptible(a->fm_api_waiter,
16426780d9eSBradley Grove 					 a->fm_api_command_done);
16526780d9eSBradley Grove all_done:
16626780d9eSBradley Grove 	if (fi == &a->firmware.header) {
16726780d9eSBradley Grove 		memcpy(fi, a->firmware.header_buff,
16826780d9eSBradley Grove 		       sizeof(struct esas2r_flash_img));
16926780d9eSBradley Grove 
17026780d9eSBradley Grove 		dma_free_coherent(&a->pcid->dev,
17126780d9eSBradley Grove 				  (size_t)sizeof(struct esas2r_flash_img),
17226780d9eSBradley Grove 				  a->firmware.header_buff,
17326780d9eSBradley Grove 				  (dma_addr_t)a->firmware.header_buff_phys);
17426780d9eSBradley Grove 	}
175ddcae017STomas Henzl free_req:
17626780d9eSBradley Grove 	esas2r_free_request(a, (struct esas2r_request *)rq);
177ddcae017STomas Henzl free_sem:
17874d2fd48SBinoy Jayan 	mutex_unlock(&a->fm_api_mutex);
17926780d9eSBradley Grove 	return;
18026780d9eSBradley Grove 
18126780d9eSBradley Grove }
18226780d9eSBradley Grove 
complete_nvr_req(struct esas2r_adapter * a,struct esas2r_request * rq)18326780d9eSBradley Grove static void complete_nvr_req(struct esas2r_adapter *a,
18426780d9eSBradley Grove 			     struct esas2r_request *rq)
18526780d9eSBradley Grove {
18626780d9eSBradley Grove 	a->nvram_command_done = 1;
18726780d9eSBradley Grove 	wake_up_interruptible(&a->nvram_waiter);
18826780d9eSBradley Grove }
18926780d9eSBradley Grove 
19026780d9eSBradley Grove /* Callback for building scatter/gather lists for buffered ioctls */
get_physaddr_buffered_ioctl(struct esas2r_sg_context * sgc,u64 * addr)19126780d9eSBradley Grove static u32 get_physaddr_buffered_ioctl(struct esas2r_sg_context *sgc,
19226780d9eSBradley Grove 				       u64 *addr)
19326780d9eSBradley Grove {
19426780d9eSBradley Grove 	int offset = (u8 *)sgc->cur_offset - esas2r_buffered_ioctl;
19526780d9eSBradley Grove 
19626780d9eSBradley Grove 	(*addr) = esas2r_buffered_ioctl_addr + offset;
19726780d9eSBradley Grove 	return esas2r_buffered_ioctl_size - offset;
19826780d9eSBradley Grove }
19926780d9eSBradley Grove 
complete_buffered_ioctl_req(struct esas2r_adapter * a,struct esas2r_request * rq)20026780d9eSBradley Grove static void complete_buffered_ioctl_req(struct esas2r_adapter *a,
20126780d9eSBradley Grove 					struct esas2r_request *rq)
20226780d9eSBradley Grove {
20326780d9eSBradley Grove 	a->buffered_ioctl_done = 1;
20426780d9eSBradley Grove 	wake_up_interruptible(&a->buffered_ioctl_waiter);
20526780d9eSBradley Grove }
20626780d9eSBradley Grove 
handle_buffered_ioctl(struct esas2r_buffered_ioctl * bi)20726780d9eSBradley Grove static u8 handle_buffered_ioctl(struct esas2r_buffered_ioctl *bi)
20826780d9eSBradley Grove {
20926780d9eSBradley Grove 	struct esas2r_adapter *a = bi->a;
21026780d9eSBradley Grove 	struct esas2r_request *rq;
21126780d9eSBradley Grove 	struct esas2r_sg_context sgc;
21226780d9eSBradley Grove 	u8 result = IOCTL_SUCCESS;
21326780d9eSBradley Grove 
21426780d9eSBradley Grove 	if (down_interruptible(&buffered_ioctl_semaphore))
21526780d9eSBradley Grove 		return IOCTL_OUT_OF_RESOURCES;
21626780d9eSBradley Grove 
21726780d9eSBradley Grove 	/* allocate a buffer or use the existing buffer. */
21826780d9eSBradley Grove 	if (esas2r_buffered_ioctl) {
21926780d9eSBradley Grove 		if (esas2r_buffered_ioctl_size < bi->length) {
22026780d9eSBradley Grove 			/* free the too-small buffer and get a new one */
22126780d9eSBradley Grove 			dma_free_coherent(&a->pcid->dev,
22226780d9eSBradley Grove 					  (size_t)esas2r_buffered_ioctl_size,
22326780d9eSBradley Grove 					  esas2r_buffered_ioctl,
22426780d9eSBradley Grove 					  esas2r_buffered_ioctl_addr);
22526780d9eSBradley Grove 
22626780d9eSBradley Grove 			goto allocate_buffer;
22726780d9eSBradley Grove 		}
22826780d9eSBradley Grove 	} else {
22926780d9eSBradley Grove allocate_buffer:
23026780d9eSBradley Grove 		esas2r_buffered_ioctl_size = bi->length;
23126780d9eSBradley Grove 		esas2r_buffered_ioctl_pcid = a->pcid;
23226780d9eSBradley Grove 		esas2r_buffered_ioctl = dma_alloc_coherent(&a->pcid->dev,
23326780d9eSBradley Grove 							   (size_t)
23426780d9eSBradley Grove 							   esas2r_buffered_ioctl_size,
23526780d9eSBradley Grove 							   &
23626780d9eSBradley Grove 							   esas2r_buffered_ioctl_addr,
23726780d9eSBradley Grove 							   GFP_KERNEL);
23826780d9eSBradley Grove 	}
23926780d9eSBradley Grove 
24026780d9eSBradley Grove 	if (!esas2r_buffered_ioctl) {
24126780d9eSBradley Grove 		esas2r_log(ESAS2R_LOG_CRIT,
24226780d9eSBradley Grove 			   "could not allocate %d bytes of consistent memory "
24326780d9eSBradley Grove 			   "for a buffered ioctl!",
24426780d9eSBradley Grove 			   bi->length);
24526780d9eSBradley Grove 
24626780d9eSBradley Grove 		esas2r_debug("buffered ioctl alloc failure");
24726780d9eSBradley Grove 		result = IOCTL_OUT_OF_RESOURCES;
24826780d9eSBradley Grove 		goto exit_cleanly;
24926780d9eSBradley Grove 	}
25026780d9eSBradley Grove 
25126780d9eSBradley Grove 	memcpy(esas2r_buffered_ioctl, bi->ioctl, bi->length);
25226780d9eSBradley Grove 
25326780d9eSBradley Grove 	rq = esas2r_alloc_request(a);
25426780d9eSBradley Grove 	if (rq == NULL) {
25526780d9eSBradley Grove 		esas2r_log(ESAS2R_LOG_CRIT,
25626780d9eSBradley Grove 			   "could not allocate an internal request");
25726780d9eSBradley Grove 
25826780d9eSBradley Grove 		result = IOCTL_OUT_OF_RESOURCES;
25926780d9eSBradley Grove 		esas2r_debug("buffered ioctl - no requests");
26026780d9eSBradley Grove 		goto exit_cleanly;
26126780d9eSBradley Grove 	}
26226780d9eSBradley Grove 
26326780d9eSBradley Grove 	a->buffered_ioctl_done = 0;
26426780d9eSBradley Grove 	rq->comp_cb = complete_buffered_ioctl_req;
26526780d9eSBradley Grove 	sgc.cur_offset = esas2r_buffered_ioctl + bi->offset;
26626780d9eSBradley Grove 	sgc.get_phys_addr = (PGETPHYSADDR)get_physaddr_buffered_ioctl;
26726780d9eSBradley Grove 	sgc.length = esas2r_buffered_ioctl_size;
26826780d9eSBradley Grove 
26926780d9eSBradley Grove 	if (!(*bi->callback)(a, rq, &sgc, bi->context)) {
27026780d9eSBradley Grove 		/* completed immediately, no need to wait */
27126780d9eSBradley Grove 		a->buffered_ioctl_done = 0;
27226780d9eSBradley Grove 		goto free_andexit_cleanly;
27326780d9eSBradley Grove 	}
27426780d9eSBradley Grove 
27526780d9eSBradley Grove 	/* now wait around for it to complete. */
27626780d9eSBradley Grove 	while (!a->buffered_ioctl_done)
27726780d9eSBradley Grove 		wait_event_interruptible(a->buffered_ioctl_waiter,
27826780d9eSBradley Grove 					 a->buffered_ioctl_done);
27926780d9eSBradley Grove 
28026780d9eSBradley Grove free_andexit_cleanly:
28126780d9eSBradley Grove 	if (result == IOCTL_SUCCESS && bi->done_callback)
28226780d9eSBradley Grove 		(*bi->done_callback)(a, rq, bi->done_context);
28326780d9eSBradley Grove 
28426780d9eSBradley Grove 	esas2r_free_request(a, rq);
28526780d9eSBradley Grove 
28626780d9eSBradley Grove exit_cleanly:
28726780d9eSBradley Grove 	if (result == IOCTL_SUCCESS)
28826780d9eSBradley Grove 		memcpy(bi->ioctl, esas2r_buffered_ioctl, bi->length);
28926780d9eSBradley Grove 
29026780d9eSBradley Grove 	up(&buffered_ioctl_semaphore);
29126780d9eSBradley Grove 	return result;
29226780d9eSBradley Grove }
29326780d9eSBradley Grove 
29426780d9eSBradley Grove /* SMP ioctl support */
smp_ioctl_callback(struct esas2r_adapter * a,struct esas2r_request * rq,struct esas2r_sg_context * sgc,void * context)29526780d9eSBradley Grove static int smp_ioctl_callback(struct esas2r_adapter *a,
29626780d9eSBradley Grove 			      struct esas2r_request *rq,
29726780d9eSBradley Grove 			      struct esas2r_sg_context *sgc, void *context)
29826780d9eSBradley Grove {
29926780d9eSBradley Grove 	struct atto_ioctl_smp *si =
30026780d9eSBradley Grove 		(struct atto_ioctl_smp *)esas2r_buffered_ioctl;
30126780d9eSBradley Grove 
30226780d9eSBradley Grove 	esas2r_sgc_init(sgc, a, rq, rq->vrq->ioctl.sge);
30326780d9eSBradley Grove 	esas2r_build_ioctl_req(a, rq, sgc->length, VDA_IOCTL_SMP);
30426780d9eSBradley Grove 
30526780d9eSBradley Grove 	if (!esas2r_build_sg_list(a, rq, sgc)) {
30626780d9eSBradley Grove 		si->status = ATTO_STS_OUT_OF_RSRC;
30726780d9eSBradley Grove 		return false;
30826780d9eSBradley Grove 	}
30926780d9eSBradley Grove 
31026780d9eSBradley Grove 	esas2r_start_request(a, rq);
31126780d9eSBradley Grove 	return true;
31226780d9eSBradley Grove }
31326780d9eSBradley Grove 
handle_smp_ioctl(struct esas2r_adapter * a,struct atto_ioctl_smp * si)31426780d9eSBradley Grove static u8 handle_smp_ioctl(struct esas2r_adapter *a, struct atto_ioctl_smp *si)
31526780d9eSBradley Grove {
31626780d9eSBradley Grove 	struct esas2r_buffered_ioctl bi;
31726780d9eSBradley Grove 
31826780d9eSBradley Grove 	memset(&bi, 0, sizeof(bi));
31926780d9eSBradley Grove 
32026780d9eSBradley Grove 	bi.a = a;
32126780d9eSBradley Grove 	bi.ioctl = si;
32226780d9eSBradley Grove 	bi.length = sizeof(struct atto_ioctl_smp)
32326780d9eSBradley Grove 		    + le32_to_cpu(si->req_length)
32426780d9eSBradley Grove 		    + le32_to_cpu(si->rsp_length);
32526780d9eSBradley Grove 	bi.offset = 0;
32626780d9eSBradley Grove 	bi.callback = smp_ioctl_callback;
32726780d9eSBradley Grove 	return handle_buffered_ioctl(&bi);
32826780d9eSBradley Grove }
32926780d9eSBradley Grove 
33026780d9eSBradley Grove 
33126780d9eSBradley Grove /* CSMI ioctl support */
esas2r_csmi_ioctl_tunnel_comp_cb(struct esas2r_adapter * a,struct esas2r_request * rq)33226780d9eSBradley Grove static void esas2r_csmi_ioctl_tunnel_comp_cb(struct esas2r_adapter *a,
33326780d9eSBradley Grove 					     struct esas2r_request *rq)
33426780d9eSBradley Grove {
33526780d9eSBradley Grove 	rq->target_id = le16_to_cpu(rq->func_rsp.ioctl_rsp.csmi.target_id);
33626780d9eSBradley Grove 	rq->vrq->scsi.flags |= cpu_to_le32(rq->func_rsp.ioctl_rsp.csmi.lun);
33726780d9eSBradley Grove 
33826780d9eSBradley Grove 	/* Now call the original completion callback. */
33926780d9eSBradley Grove 	(*rq->aux_req_cb)(a, rq);
34026780d9eSBradley Grove }
34126780d9eSBradley Grove 
34226780d9eSBradley Grove /* Tunnel a CSMI IOCTL to the back end driver for processing. */
csmi_ioctl_tunnel(struct esas2r_adapter * a,union atto_ioctl_csmi * ci,struct esas2r_request * rq,struct esas2r_sg_context * sgc,u32 ctrl_code,u16 target_id)34326780d9eSBradley Grove static bool csmi_ioctl_tunnel(struct esas2r_adapter *a,
34426780d9eSBradley Grove 			      union atto_ioctl_csmi *ci,
34526780d9eSBradley Grove 			      struct esas2r_request *rq,
34626780d9eSBradley Grove 			      struct esas2r_sg_context *sgc,
34726780d9eSBradley Grove 			      u32 ctrl_code,
34826780d9eSBradley Grove 			      u16 target_id)
34926780d9eSBradley Grove {
35026780d9eSBradley Grove 	struct atto_vda_ioctl_req *ioctl = &rq->vrq->ioctl;
35126780d9eSBradley Grove 
3529588d24eSBradley Grove 	if (test_bit(AF_DEGRADED_MODE, &a->flags))
35326780d9eSBradley Grove 		return false;
35426780d9eSBradley Grove 
35526780d9eSBradley Grove 	esas2r_sgc_init(sgc, a, rq, rq->vrq->ioctl.sge);
35626780d9eSBradley Grove 	esas2r_build_ioctl_req(a, rq, sgc->length, VDA_IOCTL_CSMI);
35726780d9eSBradley Grove 	ioctl->csmi.ctrl_code = cpu_to_le32(ctrl_code);
35826780d9eSBradley Grove 	ioctl->csmi.target_id = cpu_to_le16(target_id);
35926780d9eSBradley Grove 	ioctl->csmi.lun = (u8)le32_to_cpu(rq->vrq->scsi.flags);
36026780d9eSBradley Grove 
36126780d9eSBradley Grove 	/*
36226780d9eSBradley Grove 	 * Always usurp the completion callback since the interrupt callback
36326780d9eSBradley Grove 	 * mechanism may be used.
36426780d9eSBradley Grove 	 */
36526780d9eSBradley Grove 	rq->aux_req_cx = ci;
36626780d9eSBradley Grove 	rq->aux_req_cb = rq->comp_cb;
36726780d9eSBradley Grove 	rq->comp_cb = esas2r_csmi_ioctl_tunnel_comp_cb;
36826780d9eSBradley Grove 
36926780d9eSBradley Grove 	if (!esas2r_build_sg_list(a, rq, sgc))
37026780d9eSBradley Grove 		return false;
37126780d9eSBradley Grove 
37226780d9eSBradley Grove 	esas2r_start_request(a, rq);
37326780d9eSBradley Grove 	return true;
37426780d9eSBradley Grove }
37526780d9eSBradley Grove 
check_lun(struct scsi_lun lun)37626780d9eSBradley Grove static bool check_lun(struct scsi_lun lun)
37726780d9eSBradley Grove {
37826780d9eSBradley Grove 	bool result;
37926780d9eSBradley Grove 
38026780d9eSBradley Grove 	result = ((lun.scsi_lun[7] == 0) &&
38126780d9eSBradley Grove 		  (lun.scsi_lun[6] == 0) &&
38226780d9eSBradley Grove 		  (lun.scsi_lun[5] == 0) &&
38326780d9eSBradley Grove 		  (lun.scsi_lun[4] == 0) &&
38426780d9eSBradley Grove 		  (lun.scsi_lun[3] == 0) &&
38526780d9eSBradley Grove 		  (lun.scsi_lun[2] == 0) &&
38626780d9eSBradley Grove /* Byte 1 is intentionally skipped */
38726780d9eSBradley Grove 		  (lun.scsi_lun[0] == 0));
38826780d9eSBradley Grove 
38926780d9eSBradley Grove 	return result;
39026780d9eSBradley Grove }
39126780d9eSBradley Grove 
csmi_ioctl_callback(struct esas2r_adapter * a,struct esas2r_request * rq,struct esas2r_sg_context * sgc,void * context)39226780d9eSBradley Grove static int csmi_ioctl_callback(struct esas2r_adapter *a,
39326780d9eSBradley Grove 			       struct esas2r_request *rq,
39426780d9eSBradley Grove 			       struct esas2r_sg_context *sgc, void *context)
39526780d9eSBradley Grove {
39626780d9eSBradley Grove 	struct atto_csmi *ci = (struct atto_csmi *)context;
39726780d9eSBradley Grove 	union atto_ioctl_csmi *ioctl_csmi =
39826780d9eSBradley Grove 		(union atto_ioctl_csmi *)esas2r_buffered_ioctl;
39926780d9eSBradley Grove 	u8 path = 0;
40026780d9eSBradley Grove 	u8 tid = 0;
40126780d9eSBradley Grove 	u8 lun = 0;
40226780d9eSBradley Grove 	u32 sts = CSMI_STS_SUCCESS;
40326780d9eSBradley Grove 	struct esas2r_target *t;
40426780d9eSBradley Grove 	unsigned long flags;
40526780d9eSBradley Grove 
40626780d9eSBradley Grove 	if (ci->control_code == CSMI_CC_GET_DEV_ADDR) {
40726780d9eSBradley Grove 		struct atto_csmi_get_dev_addr *gda = &ci->data.dev_addr;
40826780d9eSBradley Grove 
40926780d9eSBradley Grove 		path = gda->path_id;
41026780d9eSBradley Grove 		tid = gda->target_id;
41126780d9eSBradley Grove 		lun = gda->lun;
41226780d9eSBradley Grove 	} else if (ci->control_code == CSMI_CC_TASK_MGT) {
41326780d9eSBradley Grove 		struct atto_csmi_task_mgmt *tm = &ci->data.tsk_mgt;
41426780d9eSBradley Grove 
41526780d9eSBradley Grove 		path = tm->path_id;
41626780d9eSBradley Grove 		tid = tm->target_id;
41726780d9eSBradley Grove 		lun = tm->lun;
41826780d9eSBradley Grove 	}
41926780d9eSBradley Grove 
4208d3ac484SBradley Grove 	if (path > 0) {
42126780d9eSBradley Grove 		rq->func_rsp.ioctl_rsp.csmi.csmi_status = cpu_to_le32(
42226780d9eSBradley Grove 			CSMI_STS_INV_PARAM);
42326780d9eSBradley Grove 		return false;
42426780d9eSBradley Grove 	}
42526780d9eSBradley Grove 
42626780d9eSBradley Grove 	rq->target_id = tid;
42726780d9eSBradley Grove 	rq->vrq->scsi.flags |= cpu_to_le32(lun);
42826780d9eSBradley Grove 
42926780d9eSBradley Grove 	switch (ci->control_code) {
43026780d9eSBradley Grove 	case CSMI_CC_GET_DRVR_INFO:
43126780d9eSBradley Grove 	{
43226780d9eSBradley Grove 		struct atto_csmi_get_driver_info *gdi = &ioctl_csmi->drvr_info;
43326780d9eSBradley Grove 
43426780d9eSBradley Grove 		strcpy(gdi->description, esas2r_get_model_name(a));
43526780d9eSBradley Grove 		gdi->csmi_major_rev = CSMI_MAJOR_REV;
43626780d9eSBradley Grove 		gdi->csmi_minor_rev = CSMI_MINOR_REV;
43726780d9eSBradley Grove 		break;
43826780d9eSBradley Grove 	}
43926780d9eSBradley Grove 
44026780d9eSBradley Grove 	case CSMI_CC_GET_CNTLR_CFG:
44126780d9eSBradley Grove 	{
44226780d9eSBradley Grove 		struct atto_csmi_get_cntlr_cfg *gcc = &ioctl_csmi->cntlr_cfg;
44326780d9eSBradley Grove 
44426780d9eSBradley Grove 		gcc->base_io_addr = 0;
44526780d9eSBradley Grove 		pci_read_config_dword(a->pcid, PCI_BASE_ADDRESS_2,
44626780d9eSBradley Grove 				      &gcc->base_memaddr_lo);
44726780d9eSBradley Grove 		pci_read_config_dword(a->pcid, PCI_BASE_ADDRESS_3,
44826780d9eSBradley Grove 				      &gcc->base_memaddr_hi);
44926780d9eSBradley Grove 		gcc->board_id = MAKEDWORD(a->pcid->subsystem_device,
45026780d9eSBradley Grove 					  a->pcid->subsystem_vendor);
45126780d9eSBradley Grove 		gcc->slot_num = CSMI_SLOT_NUM_UNKNOWN;
45226780d9eSBradley Grove 		gcc->cntlr_class = CSMI_CNTLR_CLASS_HBA;
45326780d9eSBradley Grove 		gcc->io_bus_type = CSMI_BUS_TYPE_PCI;
45426780d9eSBradley Grove 		gcc->pci_addr.bus_num = a->pcid->bus->number;
45526780d9eSBradley Grove 		gcc->pci_addr.device_num = PCI_SLOT(a->pcid->devfn);
45626780d9eSBradley Grove 		gcc->pci_addr.function_num = PCI_FUNC(a->pcid->devfn);
45726780d9eSBradley Grove 
45826780d9eSBradley Grove 		memset(gcc->serial_num, 0, sizeof(gcc->serial_num));
45926780d9eSBradley Grove 
46026780d9eSBradley Grove 		gcc->major_rev = LOBYTE(LOWORD(a->fw_version));
46126780d9eSBradley Grove 		gcc->minor_rev = HIBYTE(LOWORD(a->fw_version));
46226780d9eSBradley Grove 		gcc->build_rev = LOBYTE(HIWORD(a->fw_version));
46326780d9eSBradley Grove 		gcc->release_rev = HIBYTE(HIWORD(a->fw_version));
46426780d9eSBradley Grove 		gcc->bios_major_rev = HIBYTE(HIWORD(a->flash_ver));
46526780d9eSBradley Grove 		gcc->bios_minor_rev = LOBYTE(HIWORD(a->flash_ver));
46626780d9eSBradley Grove 		gcc->bios_build_rev = LOWORD(a->flash_ver);
46726780d9eSBradley Grove 
4689588d24eSBradley Grove 		if (test_bit(AF2_THUNDERLINK, &a->flags2))
46926780d9eSBradley Grove 			gcc->cntlr_flags = CSMI_CNTLRF_SAS_HBA
47026780d9eSBradley Grove 					   | CSMI_CNTLRF_SATA_HBA;
47126780d9eSBradley Grove 		else
47226780d9eSBradley Grove 			gcc->cntlr_flags = CSMI_CNTLRF_SAS_RAID
47326780d9eSBradley Grove 					   | CSMI_CNTLRF_SATA_RAID;
47426780d9eSBradley Grove 
47526780d9eSBradley Grove 		gcc->rrom_major_rev = 0;
47626780d9eSBradley Grove 		gcc->rrom_minor_rev = 0;
47726780d9eSBradley Grove 		gcc->rrom_build_rev = 0;
47826780d9eSBradley Grove 		gcc->rrom_release_rev = 0;
47926780d9eSBradley Grove 		gcc->rrom_biosmajor_rev = 0;
48026780d9eSBradley Grove 		gcc->rrom_biosminor_rev = 0;
48126780d9eSBradley Grove 		gcc->rrom_biosbuild_rev = 0;
48226780d9eSBradley Grove 		gcc->rrom_biosrelease_rev = 0;
48326780d9eSBradley Grove 		break;
48426780d9eSBradley Grove 	}
48526780d9eSBradley Grove 
48626780d9eSBradley Grove 	case CSMI_CC_GET_CNTLR_STS:
48726780d9eSBradley Grove 	{
48826780d9eSBradley Grove 		struct atto_csmi_get_cntlr_sts *gcs = &ioctl_csmi->cntlr_sts;
48926780d9eSBradley Grove 
4909588d24eSBradley Grove 		if (test_bit(AF_DEGRADED_MODE, &a->flags))
49126780d9eSBradley Grove 			gcs->status = CSMI_CNTLR_STS_FAILED;
49226780d9eSBradley Grove 		else
49326780d9eSBradley Grove 			gcs->status = CSMI_CNTLR_STS_GOOD;
49426780d9eSBradley Grove 
49526780d9eSBradley Grove 		gcs->offline_reason = CSMI_OFFLINE_NO_REASON;
49626780d9eSBradley Grove 		break;
49726780d9eSBradley Grove 	}
49826780d9eSBradley Grove 
49926780d9eSBradley Grove 	case CSMI_CC_FW_DOWNLOAD:
50026780d9eSBradley Grove 	case CSMI_CC_GET_RAID_INFO:
50126780d9eSBradley Grove 	case CSMI_CC_GET_RAID_CFG:
50226780d9eSBradley Grove 
50326780d9eSBradley Grove 		sts = CSMI_STS_BAD_CTRL_CODE;
50426780d9eSBradley Grove 		break;
50526780d9eSBradley Grove 
50626780d9eSBradley Grove 	case CSMI_CC_SMP_PASSTHRU:
50726780d9eSBradley Grove 	case CSMI_CC_SSP_PASSTHRU:
50826780d9eSBradley Grove 	case CSMI_CC_STP_PASSTHRU:
50926780d9eSBradley Grove 	case CSMI_CC_GET_PHY_INFO:
51026780d9eSBradley Grove 	case CSMI_CC_SET_PHY_INFO:
51126780d9eSBradley Grove 	case CSMI_CC_GET_LINK_ERRORS:
51226780d9eSBradley Grove 	case CSMI_CC_GET_SATA_SIG:
51326780d9eSBradley Grove 	case CSMI_CC_GET_CONN_INFO:
51426780d9eSBradley Grove 	case CSMI_CC_PHY_CTRL:
51526780d9eSBradley Grove 
51626780d9eSBradley Grove 		if (!csmi_ioctl_tunnel(a, ioctl_csmi, rq, sgc,
51726780d9eSBradley Grove 				       ci->control_code,
51826780d9eSBradley Grove 				       ESAS2R_TARG_ID_INV)) {
51926780d9eSBradley Grove 			sts = CSMI_STS_FAILED;
52026780d9eSBradley Grove 			break;
52126780d9eSBradley Grove 		}
52226780d9eSBradley Grove 
52326780d9eSBradley Grove 		return true;
52426780d9eSBradley Grove 
52526780d9eSBradley Grove 	case CSMI_CC_GET_SCSI_ADDR:
52626780d9eSBradley Grove 	{
52726780d9eSBradley Grove 		struct atto_csmi_get_scsi_addr *gsa = &ioctl_csmi->scsi_addr;
52826780d9eSBradley Grove 
52926780d9eSBradley Grove 		struct scsi_lun lun;
53026780d9eSBradley Grove 
53126780d9eSBradley Grove 		memcpy(&lun, gsa->sas_lun, sizeof(struct scsi_lun));
53226780d9eSBradley Grove 
53326780d9eSBradley Grove 		if (!check_lun(lun)) {
53426780d9eSBradley Grove 			sts = CSMI_STS_NO_SCSI_ADDR;
53526780d9eSBradley Grove 			break;
53626780d9eSBradley Grove 		}
53726780d9eSBradley Grove 
53826780d9eSBradley Grove 		/* make sure the device is present */
53926780d9eSBradley Grove 		spin_lock_irqsave(&a->mem_lock, flags);
54026780d9eSBradley Grove 		t = esas2r_targ_db_find_by_sas_addr(a, (u64 *)gsa->sas_addr);
54126780d9eSBradley Grove 		spin_unlock_irqrestore(&a->mem_lock, flags);
54226780d9eSBradley Grove 
54326780d9eSBradley Grove 		if (t == NULL) {
54426780d9eSBradley Grove 			sts = CSMI_STS_NO_SCSI_ADDR;
54526780d9eSBradley Grove 			break;
54626780d9eSBradley Grove 		}
54726780d9eSBradley Grove 
54826780d9eSBradley Grove 		gsa->host_index = 0xFF;
54926780d9eSBradley Grove 		gsa->lun = gsa->sas_lun[1];
55026780d9eSBradley Grove 		rq->target_id = esas2r_targ_get_id(t, a);
55126780d9eSBradley Grove 		break;
55226780d9eSBradley Grove 	}
55326780d9eSBradley Grove 
55426780d9eSBradley Grove 	case CSMI_CC_GET_DEV_ADDR:
55526780d9eSBradley Grove 	{
55626780d9eSBradley Grove 		struct atto_csmi_get_dev_addr *gda = &ioctl_csmi->dev_addr;
55726780d9eSBradley Grove 
55826780d9eSBradley Grove 		/* make sure the target is present */
55926780d9eSBradley Grove 		t = a->targetdb + rq->target_id;
56026780d9eSBradley Grove 
56126780d9eSBradley Grove 		if (t >= a->targetdb_end
56226780d9eSBradley Grove 		    || t->target_state != TS_PRESENT
56326780d9eSBradley Grove 		    || t->sas_addr == 0) {
56426780d9eSBradley Grove 			sts = CSMI_STS_NO_DEV_ADDR;
56526780d9eSBradley Grove 			break;
56626780d9eSBradley Grove 		}
56726780d9eSBradley Grove 
56826780d9eSBradley Grove 		/* fill in the result */
56926780d9eSBradley Grove 		*(u64 *)gda->sas_addr = t->sas_addr;
57026780d9eSBradley Grove 		memset(gda->sas_lun, 0, sizeof(gda->sas_lun));
57126780d9eSBradley Grove 		gda->sas_lun[1] = (u8)le32_to_cpu(rq->vrq->scsi.flags);
57226780d9eSBradley Grove 		break;
57326780d9eSBradley Grove 	}
57426780d9eSBradley Grove 
57526780d9eSBradley Grove 	case CSMI_CC_TASK_MGT:
57626780d9eSBradley Grove 
57726780d9eSBradley Grove 		/* make sure the target is present */
57826780d9eSBradley Grove 		t = a->targetdb + rq->target_id;
57926780d9eSBradley Grove 
58026780d9eSBradley Grove 		if (t >= a->targetdb_end
58126780d9eSBradley Grove 		    || t->target_state != TS_PRESENT
58226780d9eSBradley Grove 		    || !(t->flags & TF_PASS_THRU)) {
58326780d9eSBradley Grove 			sts = CSMI_STS_NO_DEV_ADDR;
58426780d9eSBradley Grove 			break;
58526780d9eSBradley Grove 		}
58626780d9eSBradley Grove 
58726780d9eSBradley Grove 		if (!csmi_ioctl_tunnel(a, ioctl_csmi, rq, sgc,
58826780d9eSBradley Grove 				       ci->control_code,
58926780d9eSBradley Grove 				       t->phys_targ_id)) {
59026780d9eSBradley Grove 			sts = CSMI_STS_FAILED;
59126780d9eSBradley Grove 			break;
59226780d9eSBradley Grove 		}
59326780d9eSBradley Grove 
59426780d9eSBradley Grove 		return true;
59526780d9eSBradley Grove 
59626780d9eSBradley Grove 	default:
59726780d9eSBradley Grove 
59826780d9eSBradley Grove 		sts = CSMI_STS_BAD_CTRL_CODE;
59926780d9eSBradley Grove 		break;
60026780d9eSBradley Grove 	}
60126780d9eSBradley Grove 
60226780d9eSBradley Grove 	rq->func_rsp.ioctl_rsp.csmi.csmi_status = cpu_to_le32(sts);
60326780d9eSBradley Grove 
60426780d9eSBradley Grove 	return false;
60526780d9eSBradley Grove }
60626780d9eSBradley Grove 
60726780d9eSBradley Grove 
csmi_ioctl_done_callback(struct esas2r_adapter * a,struct esas2r_request * rq,void * context)60826780d9eSBradley Grove static void csmi_ioctl_done_callback(struct esas2r_adapter *a,
60926780d9eSBradley Grove 				     struct esas2r_request *rq, void *context)
61026780d9eSBradley Grove {
61126780d9eSBradley Grove 	struct atto_csmi *ci = (struct atto_csmi *)context;
61226780d9eSBradley Grove 	union atto_ioctl_csmi *ioctl_csmi =
61326780d9eSBradley Grove 		(union atto_ioctl_csmi *)esas2r_buffered_ioctl;
61426780d9eSBradley Grove 
61526780d9eSBradley Grove 	switch (ci->control_code) {
61626780d9eSBradley Grove 	case CSMI_CC_GET_DRVR_INFO:
61726780d9eSBradley Grove 	{
61826780d9eSBradley Grove 		struct atto_csmi_get_driver_info *gdi =
61926780d9eSBradley Grove 			&ioctl_csmi->drvr_info;
62026780d9eSBradley Grove 
62126780d9eSBradley Grove 		strcpy(gdi->name, ESAS2R_VERSION_STR);
62226780d9eSBradley Grove 
62326780d9eSBradley Grove 		gdi->major_rev = ESAS2R_MAJOR_REV;
62426780d9eSBradley Grove 		gdi->minor_rev = ESAS2R_MINOR_REV;
62526780d9eSBradley Grove 		gdi->build_rev = 0;
62626780d9eSBradley Grove 		gdi->release_rev = 0;
62726780d9eSBradley Grove 		break;
62826780d9eSBradley Grove 	}
62926780d9eSBradley Grove 
63026780d9eSBradley Grove 	case CSMI_CC_GET_SCSI_ADDR:
63126780d9eSBradley Grove 	{
63226780d9eSBradley Grove 		struct atto_csmi_get_scsi_addr *gsa = &ioctl_csmi->scsi_addr;
63326780d9eSBradley Grove 
63426780d9eSBradley Grove 		if (le32_to_cpu(rq->func_rsp.ioctl_rsp.csmi.csmi_status) ==
63526780d9eSBradley Grove 		    CSMI_STS_SUCCESS) {
63626780d9eSBradley Grove 			gsa->target_id = rq->target_id;
63726780d9eSBradley Grove 			gsa->path_id = 0;
63826780d9eSBradley Grove 		}
63926780d9eSBradley Grove 
64026780d9eSBradley Grove 		break;
64126780d9eSBradley Grove 	}
64226780d9eSBradley Grove 	}
64326780d9eSBradley Grove 
64426780d9eSBradley Grove 	ci->status = le32_to_cpu(rq->func_rsp.ioctl_rsp.csmi.csmi_status);
64526780d9eSBradley Grove }
64626780d9eSBradley Grove 
64726780d9eSBradley Grove 
handle_csmi_ioctl(struct esas2r_adapter * a,struct atto_csmi * ci)64826780d9eSBradley Grove static u8 handle_csmi_ioctl(struct esas2r_adapter *a, struct atto_csmi *ci)
64926780d9eSBradley Grove {
65026780d9eSBradley Grove 	struct esas2r_buffered_ioctl bi;
65126780d9eSBradley Grove 
65226780d9eSBradley Grove 	memset(&bi, 0, sizeof(bi));
65326780d9eSBradley Grove 
65426780d9eSBradley Grove 	bi.a = a;
65526780d9eSBradley Grove 	bi.ioctl = &ci->data;
65626780d9eSBradley Grove 	bi.length = sizeof(union atto_ioctl_csmi);
65726780d9eSBradley Grove 	bi.offset = 0;
65826780d9eSBradley Grove 	bi.callback = csmi_ioctl_callback;
65926780d9eSBradley Grove 	bi.context = ci;
66026780d9eSBradley Grove 	bi.done_callback = csmi_ioctl_done_callback;
66126780d9eSBradley Grove 	bi.done_context = ci;
66226780d9eSBradley Grove 
66326780d9eSBradley Grove 	return handle_buffered_ioctl(&bi);
66426780d9eSBradley Grove }
66526780d9eSBradley Grove 
66626780d9eSBradley Grove /* ATTO HBA ioctl support */
66726780d9eSBradley Grove 
66826780d9eSBradley Grove /* Tunnel an ATTO HBA IOCTL to the back end driver for processing. */
hba_ioctl_tunnel(struct esas2r_adapter * a,struct atto_ioctl * hi,struct esas2r_request * rq,struct esas2r_sg_context * sgc)66926780d9eSBradley Grove static bool hba_ioctl_tunnel(struct esas2r_adapter *a,
67026780d9eSBradley Grove 			     struct atto_ioctl *hi,
67126780d9eSBradley Grove 			     struct esas2r_request *rq,
67226780d9eSBradley Grove 			     struct esas2r_sg_context *sgc)
67326780d9eSBradley Grove {
67426780d9eSBradley Grove 	esas2r_sgc_init(sgc, a, rq, rq->vrq->ioctl.sge);
67526780d9eSBradley Grove 
67626780d9eSBradley Grove 	esas2r_build_ioctl_req(a, rq, sgc->length, VDA_IOCTL_HBA);
67726780d9eSBradley Grove 
67826780d9eSBradley Grove 	if (!esas2r_build_sg_list(a, rq, sgc)) {
67926780d9eSBradley Grove 		hi->status = ATTO_STS_OUT_OF_RSRC;
68026780d9eSBradley Grove 
68126780d9eSBradley Grove 		return false;
68226780d9eSBradley Grove 	}
68326780d9eSBradley Grove 
68426780d9eSBradley Grove 	esas2r_start_request(a, rq);
68526780d9eSBradley Grove 
68626780d9eSBradley Grove 	return true;
68726780d9eSBradley Grove }
68826780d9eSBradley Grove 
scsi_passthru_comp_cb(struct esas2r_adapter * a,struct esas2r_request * rq)68926780d9eSBradley Grove static void scsi_passthru_comp_cb(struct esas2r_adapter *a,
69026780d9eSBradley Grove 				  struct esas2r_request *rq)
69126780d9eSBradley Grove {
69226780d9eSBradley Grove 	struct atto_ioctl *hi = (struct atto_ioctl *)rq->aux_req_cx;
69326780d9eSBradley Grove 	struct atto_hba_scsi_pass_thru *spt = &hi->data.scsi_pass_thru;
69426780d9eSBradley Grove 	u8 sts = ATTO_SPT_RS_FAILED;
69526780d9eSBradley Grove 
69626780d9eSBradley Grove 	spt->scsi_status = rq->func_rsp.scsi_rsp.scsi_stat;
69726780d9eSBradley Grove 	spt->sense_length = rq->sense_len;
69826780d9eSBradley Grove 	spt->residual_length =
69926780d9eSBradley Grove 		le32_to_cpu(rq->func_rsp.scsi_rsp.residual_length);
70026780d9eSBradley Grove 
70126780d9eSBradley Grove 	switch (rq->req_stat) {
70226780d9eSBradley Grove 	case RS_SUCCESS:
70326780d9eSBradley Grove 	case RS_SCSI_ERROR:
70426780d9eSBradley Grove 		sts = ATTO_SPT_RS_SUCCESS;
70526780d9eSBradley Grove 		break;
70626780d9eSBradley Grove 	case RS_UNDERRUN:
70726780d9eSBradley Grove 		sts = ATTO_SPT_RS_UNDERRUN;
70826780d9eSBradley Grove 		break;
70926780d9eSBradley Grove 	case RS_OVERRUN:
71026780d9eSBradley Grove 		sts = ATTO_SPT_RS_OVERRUN;
71126780d9eSBradley Grove 		break;
71226780d9eSBradley Grove 	case RS_SEL:
71326780d9eSBradley Grove 	case RS_SEL2:
71426780d9eSBradley Grove 		sts = ATTO_SPT_RS_NO_DEVICE;
71526780d9eSBradley Grove 		break;
71626780d9eSBradley Grove 	case RS_NO_LUN:
71726780d9eSBradley Grove 		sts = ATTO_SPT_RS_NO_LUN;
71826780d9eSBradley Grove 		break;
71926780d9eSBradley Grove 	case RS_TIMEOUT:
72026780d9eSBradley Grove 		sts = ATTO_SPT_RS_TIMEOUT;
72126780d9eSBradley Grove 		break;
72226780d9eSBradley Grove 	case RS_DEGRADED:
72326780d9eSBradley Grove 		sts = ATTO_SPT_RS_DEGRADED;
72426780d9eSBradley Grove 		break;
72526780d9eSBradley Grove 	case RS_BUSY:
72626780d9eSBradley Grove 		sts = ATTO_SPT_RS_BUSY;
72726780d9eSBradley Grove 		break;
72826780d9eSBradley Grove 	case RS_ABORTED:
72926780d9eSBradley Grove 		sts = ATTO_SPT_RS_ABORTED;
73026780d9eSBradley Grove 		break;
73126780d9eSBradley Grove 	case RS_RESET:
73226780d9eSBradley Grove 		sts = ATTO_SPT_RS_BUS_RESET;
73326780d9eSBradley Grove 		break;
73426780d9eSBradley Grove 	}
73526780d9eSBradley Grove 
73626780d9eSBradley Grove 	spt->req_status = sts;
73726780d9eSBradley Grove 
73826780d9eSBradley Grove 	/* Update the target ID to the next one present. */
73926780d9eSBradley Grove 	spt->target_id =
74026780d9eSBradley Grove 		esas2r_targ_db_find_next_present(a, (u16)spt->target_id);
74126780d9eSBradley Grove 
74226780d9eSBradley Grove 	/* Done, call the completion callback. */
74326780d9eSBradley Grove 	(*rq->aux_req_cb)(a, rq);
74426780d9eSBradley Grove }
74526780d9eSBradley Grove 
hba_ioctl_callback(struct esas2r_adapter * a,struct esas2r_request * rq,struct esas2r_sg_context * sgc,void * context)74626780d9eSBradley Grove static int hba_ioctl_callback(struct esas2r_adapter *a,
74726780d9eSBradley Grove 			      struct esas2r_request *rq,
74826780d9eSBradley Grove 			      struct esas2r_sg_context *sgc,
74926780d9eSBradley Grove 			      void *context)
75026780d9eSBradley Grove {
75126780d9eSBradley Grove 	struct atto_ioctl *hi = (struct atto_ioctl *)esas2r_buffered_ioctl;
75226780d9eSBradley Grove 
75326780d9eSBradley Grove 	hi->status = ATTO_STS_SUCCESS;
75426780d9eSBradley Grove 
75526780d9eSBradley Grove 	switch (hi->function) {
75626780d9eSBradley Grove 	case ATTO_FUNC_GET_ADAP_INFO:
75726780d9eSBradley Grove 	{
75826780d9eSBradley Grove 		u8 *class_code = (u8 *)&a->pcid->class;
75926780d9eSBradley Grove 
76026780d9eSBradley Grove 		struct atto_hba_get_adapter_info *gai =
76126780d9eSBradley Grove 			&hi->data.get_adap_info;
76226780d9eSBradley Grove 
76326780d9eSBradley Grove 		if (hi->flags & HBAF_TUNNEL) {
76426780d9eSBradley Grove 			hi->status = ATTO_STS_UNSUPPORTED;
76526780d9eSBradley Grove 			break;
76626780d9eSBradley Grove 		}
76726780d9eSBradley Grove 
76826780d9eSBradley Grove 		if (hi->version > ATTO_VER_GET_ADAP_INFO0) {
76926780d9eSBradley Grove 			hi->status = ATTO_STS_INV_VERSION;
77026780d9eSBradley Grove 			hi->version = ATTO_VER_GET_ADAP_INFO0;
77126780d9eSBradley Grove 			break;
77226780d9eSBradley Grove 		}
77326780d9eSBradley Grove 
77426780d9eSBradley Grove 		memset(gai, 0, sizeof(*gai));
77526780d9eSBradley Grove 
77626780d9eSBradley Grove 		gai->pci.vendor_id = a->pcid->vendor;
77726780d9eSBradley Grove 		gai->pci.device_id = a->pcid->device;
77826780d9eSBradley Grove 		gai->pci.ss_vendor_id = a->pcid->subsystem_vendor;
77926780d9eSBradley Grove 		gai->pci.ss_device_id = a->pcid->subsystem_device;
78026780d9eSBradley Grove 		gai->pci.class_code[0] = class_code[0];
78126780d9eSBradley Grove 		gai->pci.class_code[1] = class_code[1];
78226780d9eSBradley Grove 		gai->pci.class_code[2] = class_code[2];
78326780d9eSBradley Grove 		gai->pci.rev_id = a->pcid->revision;
78426780d9eSBradley Grove 		gai->pci.bus_num = a->pcid->bus->number;
78526780d9eSBradley Grove 		gai->pci.dev_num = PCI_SLOT(a->pcid->devfn);
78626780d9eSBradley Grove 		gai->pci.func_num = PCI_FUNC(a->pcid->devfn);
78726780d9eSBradley Grove 
7882b4f4cb9SFrederick Lawler 		if (pci_is_pcie(a->pcid)) {
78926780d9eSBradley Grove 			u16 stat;
79026780d9eSBradley Grove 			u32 caps;
79126780d9eSBradley Grove 
7922b4f4cb9SFrederick Lawler 			pcie_capability_read_word(a->pcid, PCI_EXP_LNKSTA,
79326780d9eSBradley Grove 						  &stat);
7942b4f4cb9SFrederick Lawler 			pcie_capability_read_dword(a->pcid, PCI_EXP_LNKCAP,
79526780d9eSBradley Grove 						   &caps);
79626780d9eSBradley Grove 
797*5532f249SIlpo Järvinen 			gai->pci.link_speed_curr = FIELD_GET(PCI_EXP_LNKSTA_CLS, stat);
798*5532f249SIlpo Järvinen 			gai->pci.link_speed_max = FIELD_GET(PCI_EXP_LNKCAP_SLS, caps);
799*5532f249SIlpo Järvinen 			gai->pci.link_width_curr = FIELD_GET(PCI_EXP_LNKSTA_NLW, stat);
800*5532f249SIlpo Järvinen 			gai->pci.link_width_max = FIELD_GET(PCI_EXP_LNKCAP_MLW, caps);
80126780d9eSBradley Grove 		}
80226780d9eSBradley Grove 
80326780d9eSBradley Grove 		gai->pci.msi_vector_cnt = 1;
80426780d9eSBradley Grove 
80526780d9eSBradley Grove 		if (a->pcid->msix_enabled)
80626780d9eSBradley Grove 			gai->pci.interrupt_mode = ATTO_GAI_PCIIM_MSIX;
80726780d9eSBradley Grove 		else if (a->pcid->msi_enabled)
80826780d9eSBradley Grove 			gai->pci.interrupt_mode = ATTO_GAI_PCIIM_MSI;
80926780d9eSBradley Grove 		else
81026780d9eSBradley Grove 			gai->pci.interrupt_mode = ATTO_GAI_PCIIM_LEGACY;
81126780d9eSBradley Grove 
81226780d9eSBradley Grove 		gai->adap_type = ATTO_GAI_AT_ESASRAID2;
81326780d9eSBradley Grove 
8149588d24eSBradley Grove 		if (test_bit(AF2_THUNDERLINK, &a->flags2))
81526780d9eSBradley Grove 			gai->adap_type = ATTO_GAI_AT_TLSASHBA;
81626780d9eSBradley Grove 
8179588d24eSBradley Grove 		if (test_bit(AF_DEGRADED_MODE, &a->flags))
81826780d9eSBradley Grove 			gai->adap_flags |= ATTO_GAI_AF_DEGRADED;
81926780d9eSBradley Grove 
82026780d9eSBradley Grove 		gai->adap_flags |= ATTO_GAI_AF_SPT_SUPP |
82126780d9eSBradley Grove 				   ATTO_GAI_AF_DEVADDR_SUPP;
82226780d9eSBradley Grove 
82326780d9eSBradley Grove 		if (a->pcid->subsystem_device == ATTO_ESAS_R60F
82426780d9eSBradley Grove 		    || a->pcid->subsystem_device == ATTO_ESAS_R608
82526780d9eSBradley Grove 		    || a->pcid->subsystem_device == ATTO_ESAS_R644
82626780d9eSBradley Grove 		    || a->pcid->subsystem_device == ATTO_TSSC_3808E)
82726780d9eSBradley Grove 			gai->adap_flags |= ATTO_GAI_AF_VIRT_SES;
82826780d9eSBradley Grove 
82926780d9eSBradley Grove 		gai->num_ports = ESAS2R_NUM_PHYS;
83026780d9eSBradley Grove 		gai->num_phys = ESAS2R_NUM_PHYS;
83126780d9eSBradley Grove 
83226780d9eSBradley Grove 		strcpy(gai->firmware_rev, a->fw_rev);
83326780d9eSBradley Grove 		strcpy(gai->flash_rev, a->flash_rev);
83426780d9eSBradley Grove 		strcpy(gai->model_name_short, esas2r_get_model_name_short(a));
83526780d9eSBradley Grove 		strcpy(gai->model_name, esas2r_get_model_name(a));
83626780d9eSBradley Grove 
83726780d9eSBradley Grove 		gai->num_targets = ESAS2R_MAX_TARGETS;
83826780d9eSBradley Grove 
83926780d9eSBradley Grove 		gai->num_busses = 1;
84026780d9eSBradley Grove 		gai->num_targsper_bus = gai->num_targets;
84126780d9eSBradley Grove 		gai->num_lunsper_targ = 256;
84226780d9eSBradley Grove 
84326780d9eSBradley Grove 		if (a->pcid->subsystem_device == ATTO_ESAS_R6F0
84426780d9eSBradley Grove 		    || a->pcid->subsystem_device == ATTO_ESAS_R60F)
84526780d9eSBradley Grove 			gai->num_connectors = 4;
84626780d9eSBradley Grove 		else
84726780d9eSBradley Grove 			gai->num_connectors = 2;
84826780d9eSBradley Grove 
84926780d9eSBradley Grove 		gai->adap_flags2 |= ATTO_GAI_AF2_ADAP_CTRL_SUPP;
85026780d9eSBradley Grove 
85126780d9eSBradley Grove 		gai->num_targets_backend = a->num_targets_backend;
85226780d9eSBradley Grove 
85326780d9eSBradley Grove 		gai->tunnel_flags = a->ioctl_tunnel
85426780d9eSBradley Grove 				    & (ATTO_GAI_TF_MEM_RW
85526780d9eSBradley Grove 				       | ATTO_GAI_TF_TRACE
85626780d9eSBradley Grove 				       | ATTO_GAI_TF_SCSI_PASS_THRU
85726780d9eSBradley Grove 				       | ATTO_GAI_TF_GET_DEV_ADDR
85826780d9eSBradley Grove 				       | ATTO_GAI_TF_PHY_CTRL
85926780d9eSBradley Grove 				       | ATTO_GAI_TF_CONN_CTRL
86026780d9eSBradley Grove 				       | ATTO_GAI_TF_GET_DEV_INFO);
86126780d9eSBradley Grove 		break;
86226780d9eSBradley Grove 	}
86326780d9eSBradley Grove 
86426780d9eSBradley Grove 	case ATTO_FUNC_GET_ADAP_ADDR:
86526780d9eSBradley Grove 	{
86626780d9eSBradley Grove 		struct atto_hba_get_adapter_address *gaa =
86726780d9eSBradley Grove 			&hi->data.get_adap_addr;
86826780d9eSBradley Grove 
86926780d9eSBradley Grove 		if (hi->flags & HBAF_TUNNEL) {
87026780d9eSBradley Grove 			hi->status = ATTO_STS_UNSUPPORTED;
87126780d9eSBradley Grove 			break;
87226780d9eSBradley Grove 		}
87326780d9eSBradley Grove 
87426780d9eSBradley Grove 		if (hi->version > ATTO_VER_GET_ADAP_ADDR0) {
87526780d9eSBradley Grove 			hi->status = ATTO_STS_INV_VERSION;
87626780d9eSBradley Grove 			hi->version = ATTO_VER_GET_ADAP_ADDR0;
87726780d9eSBradley Grove 		} else if (gaa->addr_type == ATTO_GAA_AT_PORT
87826780d9eSBradley Grove 			   || gaa->addr_type == ATTO_GAA_AT_NODE) {
87926780d9eSBradley Grove 			if (gaa->addr_type == ATTO_GAA_AT_PORT
88026780d9eSBradley Grove 			    && gaa->port_id >= ESAS2R_NUM_PHYS) {
88126780d9eSBradley Grove 				hi->status = ATTO_STS_NOT_APPL;
88226780d9eSBradley Grove 			} else {
88326780d9eSBradley Grove 				memcpy((u64 *)gaa->address,
88426780d9eSBradley Grove 				       &a->nvram->sas_addr[0], sizeof(u64));
88526780d9eSBradley Grove 				gaa->addr_len = sizeof(u64);
88626780d9eSBradley Grove 			}
88726780d9eSBradley Grove 		} else {
88826780d9eSBradley Grove 			hi->status = ATTO_STS_INV_PARAM;
88926780d9eSBradley Grove 		}
89026780d9eSBradley Grove 
89126780d9eSBradley Grove 		break;
89226780d9eSBradley Grove 	}
89326780d9eSBradley Grove 
89426780d9eSBradley Grove 	case ATTO_FUNC_MEM_RW:
89526780d9eSBradley Grove 	{
89626780d9eSBradley Grove 		if (hi->flags & HBAF_TUNNEL) {
89726780d9eSBradley Grove 			if (hba_ioctl_tunnel(a, hi, rq, sgc))
89826780d9eSBradley Grove 				return true;
89926780d9eSBradley Grove 
90026780d9eSBradley Grove 			break;
90126780d9eSBradley Grove 		}
90226780d9eSBradley Grove 
90326780d9eSBradley Grove 		hi->status = ATTO_STS_UNSUPPORTED;
90426780d9eSBradley Grove 
90526780d9eSBradley Grove 		break;
90626780d9eSBradley Grove 	}
90726780d9eSBradley Grove 
90826780d9eSBradley Grove 	case ATTO_FUNC_TRACE:
90926780d9eSBradley Grove 	{
91026780d9eSBradley Grove 		struct atto_hba_trace *trc = &hi->data.trace;
91126780d9eSBradley Grove 
91226780d9eSBradley Grove 		if (hi->flags & HBAF_TUNNEL) {
91326780d9eSBradley Grove 			if (hba_ioctl_tunnel(a, hi, rq, sgc))
91426780d9eSBradley Grove 				return true;
91526780d9eSBradley Grove 
91626780d9eSBradley Grove 			break;
91726780d9eSBradley Grove 		}
91826780d9eSBradley Grove 
91926780d9eSBradley Grove 		if (hi->version > ATTO_VER_TRACE1) {
92026780d9eSBradley Grove 			hi->status = ATTO_STS_INV_VERSION;
92126780d9eSBradley Grove 			hi->version = ATTO_VER_TRACE1;
92226780d9eSBradley Grove 			break;
92326780d9eSBradley Grove 		}
92426780d9eSBradley Grove 
92526780d9eSBradley Grove 		if (trc->trace_type == ATTO_TRC_TT_FWCOREDUMP
92626780d9eSBradley Grove 		    && hi->version >= ATTO_VER_TRACE1) {
92726780d9eSBradley Grove 			if (trc->trace_func == ATTO_TRC_TF_UPLOAD) {
92826780d9eSBradley Grove 				u32 len = hi->data_length;
92926780d9eSBradley Grove 				u32 offset = trc->current_offset;
93026780d9eSBradley Grove 				u32 total_len = ESAS2R_FWCOREDUMP_SZ;
93126780d9eSBradley Grove 
93226780d9eSBradley Grove 				/* Size is zero if a core dump isn't present */
9339588d24eSBradley Grove 				if (!test_bit(AF2_COREDUMP_SAVED, &a->flags2))
93426780d9eSBradley Grove 					total_len = 0;
93526780d9eSBradley Grove 
93626780d9eSBradley Grove 				if (len > total_len)
93726780d9eSBradley Grove 					len = total_len;
93826780d9eSBradley Grove 
93926780d9eSBradley Grove 				if (offset >= total_len
94026780d9eSBradley Grove 				    || offset + len > total_len
94126780d9eSBradley Grove 				    || len == 0) {
94226780d9eSBradley Grove 					hi->status = ATTO_STS_INV_PARAM;
94326780d9eSBradley Grove 					break;
94426780d9eSBradley Grove 				}
94526780d9eSBradley Grove 
9461ce871deSKees Cook 				memcpy(trc->contents,
94726780d9eSBradley Grove 				       a->fw_coredump_buff + offset,
94826780d9eSBradley Grove 				       len);
94926780d9eSBradley Grove 				hi->data_length = len;
95026780d9eSBradley Grove 			} else if (trc->trace_func == ATTO_TRC_TF_RESET) {
95126780d9eSBradley Grove 				memset(a->fw_coredump_buff, 0,
95226780d9eSBradley Grove 				       ESAS2R_FWCOREDUMP_SZ);
95326780d9eSBradley Grove 
9549588d24eSBradley Grove 				clear_bit(AF2_COREDUMP_SAVED, &a->flags2);
95526780d9eSBradley Grove 			} else if (trc->trace_func != ATTO_TRC_TF_GET_INFO) {
95626780d9eSBradley Grove 				hi->status = ATTO_STS_UNSUPPORTED;
95726780d9eSBradley Grove 				break;
95826780d9eSBradley Grove 			}
95926780d9eSBradley Grove 
96026780d9eSBradley Grove 			/* Always return all the info we can. */
96126780d9eSBradley Grove 			trc->trace_mask = 0;
96226780d9eSBradley Grove 			trc->current_offset = 0;
96326780d9eSBradley Grove 			trc->total_length = ESAS2R_FWCOREDUMP_SZ;
96426780d9eSBradley Grove 
96526780d9eSBradley Grove 			/* Return zero length buffer if core dump not present */
9669588d24eSBradley Grove 			if (!test_bit(AF2_COREDUMP_SAVED, &a->flags2))
96726780d9eSBradley Grove 				trc->total_length = 0;
96826780d9eSBradley Grove 		} else {
96926780d9eSBradley Grove 			hi->status = ATTO_STS_UNSUPPORTED;
97026780d9eSBradley Grove 		}
97126780d9eSBradley Grove 
97226780d9eSBradley Grove 		break;
97326780d9eSBradley Grove 	}
97426780d9eSBradley Grove 
97526780d9eSBradley Grove 	case ATTO_FUNC_SCSI_PASS_THRU:
97626780d9eSBradley Grove 	{
97726780d9eSBradley Grove 		struct atto_hba_scsi_pass_thru *spt = &hi->data.scsi_pass_thru;
97826780d9eSBradley Grove 		struct scsi_lun lun;
97926780d9eSBradley Grove 
98026780d9eSBradley Grove 		memcpy(&lun, spt->lun, sizeof(struct scsi_lun));
98126780d9eSBradley Grove 
98226780d9eSBradley Grove 		if (hi->flags & HBAF_TUNNEL) {
98326780d9eSBradley Grove 			if (hba_ioctl_tunnel(a, hi, rq, sgc))
98426780d9eSBradley Grove 				return true;
98526780d9eSBradley Grove 
98626780d9eSBradley Grove 			break;
98726780d9eSBradley Grove 		}
98826780d9eSBradley Grove 
98926780d9eSBradley Grove 		if (hi->version > ATTO_VER_SCSI_PASS_THRU0) {
99026780d9eSBradley Grove 			hi->status = ATTO_STS_INV_VERSION;
99126780d9eSBradley Grove 			hi->version = ATTO_VER_SCSI_PASS_THRU0;
99226780d9eSBradley Grove 			break;
99326780d9eSBradley Grove 		}
99426780d9eSBradley Grove 
99526780d9eSBradley Grove 		if (spt->target_id >= ESAS2R_MAX_TARGETS || !check_lun(lun)) {
99626780d9eSBradley Grove 			hi->status = ATTO_STS_INV_PARAM;
99726780d9eSBradley Grove 			break;
99826780d9eSBradley Grove 		}
99926780d9eSBradley Grove 
100026780d9eSBradley Grove 		esas2r_sgc_init(sgc, a, rq, NULL);
100126780d9eSBradley Grove 
100226780d9eSBradley Grove 		sgc->length = hi->data_length;
100326780d9eSBradley Grove 		sgc->cur_offset += offsetof(struct atto_ioctl, data.byte)
100426780d9eSBradley Grove 				   + sizeof(struct atto_hba_scsi_pass_thru);
100526780d9eSBradley Grove 
100626780d9eSBradley Grove 		/* Finish request initialization */
100726780d9eSBradley Grove 		rq->target_id = (u16)spt->target_id;
100826780d9eSBradley Grove 		rq->vrq->scsi.flags |= cpu_to_le32(spt->lun[1]);
100926780d9eSBradley Grove 		memcpy(rq->vrq->scsi.cdb, spt->cdb, 16);
101026780d9eSBradley Grove 		rq->vrq->scsi.length = cpu_to_le32(hi->data_length);
101126780d9eSBradley Grove 		rq->sense_len = spt->sense_length;
101226780d9eSBradley Grove 		rq->sense_buf = (u8 *)spt->sense_data;
101326780d9eSBradley Grove 		/* NOTE: we ignore spt->timeout */
101426780d9eSBradley Grove 
101526780d9eSBradley Grove 		/*
101626780d9eSBradley Grove 		 * always usurp the completion callback since the interrupt
101726780d9eSBradley Grove 		 * callback mechanism may be used.
101826780d9eSBradley Grove 		 */
101926780d9eSBradley Grove 
102026780d9eSBradley Grove 		rq->aux_req_cx = hi;
102126780d9eSBradley Grove 		rq->aux_req_cb = rq->comp_cb;
102226780d9eSBradley Grove 		rq->comp_cb = scsi_passthru_comp_cb;
102326780d9eSBradley Grove 
102426780d9eSBradley Grove 		if (spt->flags & ATTO_SPTF_DATA_IN) {
102526780d9eSBradley Grove 			rq->vrq->scsi.flags |= cpu_to_le32(FCP_CMND_RDD);
102626780d9eSBradley Grove 		} else if (spt->flags & ATTO_SPTF_DATA_OUT) {
102726780d9eSBradley Grove 			rq->vrq->scsi.flags |= cpu_to_le32(FCP_CMND_WRD);
102826780d9eSBradley Grove 		} else {
102926780d9eSBradley Grove 			if (sgc->length) {
103026780d9eSBradley Grove 				hi->status = ATTO_STS_INV_PARAM;
103126780d9eSBradley Grove 				break;
103226780d9eSBradley Grove 			}
103326780d9eSBradley Grove 		}
103426780d9eSBradley Grove 
103526780d9eSBradley Grove 		if (spt->flags & ATTO_SPTF_ORDERED_Q)
103626780d9eSBradley Grove 			rq->vrq->scsi.flags |=
103726780d9eSBradley Grove 				cpu_to_le32(FCP_CMND_TA_ORDRD_Q);
103826780d9eSBradley Grove 		else if (spt->flags & ATTO_SPTF_HEAD_OF_Q)
103926780d9eSBradley Grove 			rq->vrq->scsi.flags |= cpu_to_le32(FCP_CMND_TA_HEAD_Q);
104026780d9eSBradley Grove 
10419588d24eSBradley Grove 
104226780d9eSBradley Grove 		if (!esas2r_build_sg_list(a, rq, sgc)) {
104326780d9eSBradley Grove 			hi->status = ATTO_STS_OUT_OF_RSRC;
104426780d9eSBradley Grove 			break;
104526780d9eSBradley Grove 		}
104626780d9eSBradley Grove 
104726780d9eSBradley Grove 		esas2r_start_request(a, rq);
104826780d9eSBradley Grove 
104926780d9eSBradley Grove 		return true;
105026780d9eSBradley Grove 	}
105126780d9eSBradley Grove 
105226780d9eSBradley Grove 	case ATTO_FUNC_GET_DEV_ADDR:
105326780d9eSBradley Grove 	{
105426780d9eSBradley Grove 		struct atto_hba_get_device_address *gda =
105526780d9eSBradley Grove 			&hi->data.get_dev_addr;
105626780d9eSBradley Grove 		struct esas2r_target *t;
105726780d9eSBradley Grove 
105826780d9eSBradley Grove 		if (hi->flags & HBAF_TUNNEL) {
105926780d9eSBradley Grove 			if (hba_ioctl_tunnel(a, hi, rq, sgc))
106026780d9eSBradley Grove 				return true;
106126780d9eSBradley Grove 
106226780d9eSBradley Grove 			break;
106326780d9eSBradley Grove 		}
106426780d9eSBradley Grove 
106526780d9eSBradley Grove 		if (hi->version > ATTO_VER_GET_DEV_ADDR0) {
106626780d9eSBradley Grove 			hi->status = ATTO_STS_INV_VERSION;
106726780d9eSBradley Grove 			hi->version = ATTO_VER_GET_DEV_ADDR0;
106826780d9eSBradley Grove 			break;
106926780d9eSBradley Grove 		}
107026780d9eSBradley Grove 
107126780d9eSBradley Grove 		if (gda->target_id >= ESAS2R_MAX_TARGETS) {
107226780d9eSBradley Grove 			hi->status = ATTO_STS_INV_PARAM;
107326780d9eSBradley Grove 			break;
107426780d9eSBradley Grove 		}
107526780d9eSBradley Grove 
107626780d9eSBradley Grove 		t = a->targetdb + (u16)gda->target_id;
107726780d9eSBradley Grove 
107826780d9eSBradley Grove 		if (t->target_state != TS_PRESENT) {
107926780d9eSBradley Grove 			hi->status = ATTO_STS_FAILED;
108026780d9eSBradley Grove 		} else if (gda->addr_type == ATTO_GDA_AT_PORT) {
108126780d9eSBradley Grove 			if (t->sas_addr == 0) {
108226780d9eSBradley Grove 				hi->status = ATTO_STS_UNSUPPORTED;
108326780d9eSBradley Grove 			} else {
108426780d9eSBradley Grove 				*(u64 *)gda->address = t->sas_addr;
108526780d9eSBradley Grove 
108626780d9eSBradley Grove 				gda->addr_len = sizeof(u64);
108726780d9eSBradley Grove 			}
108826780d9eSBradley Grove 		} else if (gda->addr_type == ATTO_GDA_AT_NODE) {
108926780d9eSBradley Grove 			hi->status = ATTO_STS_NOT_APPL;
109026780d9eSBradley Grove 		} else {
109126780d9eSBradley Grove 			hi->status = ATTO_STS_INV_PARAM;
109226780d9eSBradley Grove 		}
109326780d9eSBradley Grove 
109426780d9eSBradley Grove 		/* update the target ID to the next one present. */
109526780d9eSBradley Grove 
109626780d9eSBradley Grove 		gda->target_id =
109726780d9eSBradley Grove 			esas2r_targ_db_find_next_present(a,
109826780d9eSBradley Grove 							 (u16)gda->target_id);
109926780d9eSBradley Grove 		break;
110026780d9eSBradley Grove 	}
110126780d9eSBradley Grove 
110226780d9eSBradley Grove 	case ATTO_FUNC_PHY_CTRL:
110326780d9eSBradley Grove 	case ATTO_FUNC_CONN_CTRL:
110426780d9eSBradley Grove 	{
110526780d9eSBradley Grove 		if (hba_ioctl_tunnel(a, hi, rq, sgc))
110626780d9eSBradley Grove 			return true;
110726780d9eSBradley Grove 
110826780d9eSBradley Grove 		break;
110926780d9eSBradley Grove 	}
111026780d9eSBradley Grove 
111126780d9eSBradley Grove 	case ATTO_FUNC_ADAP_CTRL:
111226780d9eSBradley Grove 	{
111326780d9eSBradley Grove 		struct atto_hba_adap_ctrl *ac = &hi->data.adap_ctrl;
111426780d9eSBradley Grove 
111526780d9eSBradley Grove 		if (hi->flags & HBAF_TUNNEL) {
111626780d9eSBradley Grove 			hi->status = ATTO_STS_UNSUPPORTED;
111726780d9eSBradley Grove 			break;
111826780d9eSBradley Grove 		}
111926780d9eSBradley Grove 
112026780d9eSBradley Grove 		if (hi->version > ATTO_VER_ADAP_CTRL0) {
112126780d9eSBradley Grove 			hi->status = ATTO_STS_INV_VERSION;
112226780d9eSBradley Grove 			hi->version = ATTO_VER_ADAP_CTRL0;
112326780d9eSBradley Grove 			break;
112426780d9eSBradley Grove 		}
112526780d9eSBradley Grove 
112626780d9eSBradley Grove 		if (ac->adap_func == ATTO_AC_AF_HARD_RST) {
112726780d9eSBradley Grove 			esas2r_reset_adapter(a);
112826780d9eSBradley Grove 		} else if (ac->adap_func != ATTO_AC_AF_GET_STATE) {
112926780d9eSBradley Grove 			hi->status = ATTO_STS_UNSUPPORTED;
113026780d9eSBradley Grove 			break;
113126780d9eSBradley Grove 		}
113226780d9eSBradley Grove 
11339588d24eSBradley Grove 		if (test_bit(AF_CHPRST_NEEDED, &a->flags))
113426780d9eSBradley Grove 			ac->adap_state = ATTO_AC_AS_RST_SCHED;
11359588d24eSBradley Grove 		else if (test_bit(AF_CHPRST_PENDING, &a->flags))
113626780d9eSBradley Grove 			ac->adap_state = ATTO_AC_AS_RST_IN_PROG;
11379588d24eSBradley Grove 		else if (test_bit(AF_DISC_PENDING, &a->flags))
113826780d9eSBradley Grove 			ac->adap_state = ATTO_AC_AS_RST_DISC;
11399588d24eSBradley Grove 		else if (test_bit(AF_DISABLED, &a->flags))
114026780d9eSBradley Grove 			ac->adap_state = ATTO_AC_AS_DISABLED;
11419588d24eSBradley Grove 		else if (test_bit(AF_DEGRADED_MODE, &a->flags))
114226780d9eSBradley Grove 			ac->adap_state = ATTO_AC_AS_DEGRADED;
114326780d9eSBradley Grove 		else
114426780d9eSBradley Grove 			ac->adap_state = ATTO_AC_AS_OK;
114526780d9eSBradley Grove 
114626780d9eSBradley Grove 		break;
114726780d9eSBradley Grove 	}
114826780d9eSBradley Grove 
114926780d9eSBradley Grove 	case ATTO_FUNC_GET_DEV_INFO:
115026780d9eSBradley Grove 	{
115126780d9eSBradley Grove 		struct atto_hba_get_device_info *gdi = &hi->data.get_dev_info;
115226780d9eSBradley Grove 		struct esas2r_target *t;
115326780d9eSBradley Grove 
115426780d9eSBradley Grove 		if (hi->flags & HBAF_TUNNEL) {
115526780d9eSBradley Grove 			if (hba_ioctl_tunnel(a, hi, rq, sgc))
115626780d9eSBradley Grove 				return true;
115726780d9eSBradley Grove 
115826780d9eSBradley Grove 			break;
115926780d9eSBradley Grove 		}
116026780d9eSBradley Grove 
116126780d9eSBradley Grove 		if (hi->version > ATTO_VER_GET_DEV_INFO0) {
116226780d9eSBradley Grove 			hi->status = ATTO_STS_INV_VERSION;
116326780d9eSBradley Grove 			hi->version = ATTO_VER_GET_DEV_INFO0;
116426780d9eSBradley Grove 			break;
116526780d9eSBradley Grove 		}
116626780d9eSBradley Grove 
116726780d9eSBradley Grove 		if (gdi->target_id >= ESAS2R_MAX_TARGETS) {
116826780d9eSBradley Grove 			hi->status = ATTO_STS_INV_PARAM;
116926780d9eSBradley Grove 			break;
117026780d9eSBradley Grove 		}
117126780d9eSBradley Grove 
117226780d9eSBradley Grove 		t = a->targetdb + (u16)gdi->target_id;
117326780d9eSBradley Grove 
117426780d9eSBradley Grove 		/* update the target ID to the next one present. */
117526780d9eSBradley Grove 
117626780d9eSBradley Grove 		gdi->target_id =
117726780d9eSBradley Grove 			esas2r_targ_db_find_next_present(a,
117826780d9eSBradley Grove 							 (u16)gdi->target_id);
117926780d9eSBradley Grove 
118026780d9eSBradley Grove 		if (t->target_state != TS_PRESENT) {
118126780d9eSBradley Grove 			hi->status = ATTO_STS_FAILED;
118226780d9eSBradley Grove 			break;
118326780d9eSBradley Grove 		}
118426780d9eSBradley Grove 
118526780d9eSBradley Grove 		hi->status = ATTO_STS_UNSUPPORTED;
118626780d9eSBradley Grove 		break;
118726780d9eSBradley Grove 	}
118826780d9eSBradley Grove 
118926780d9eSBradley Grove 	default:
119026780d9eSBradley Grove 
119126780d9eSBradley Grove 		hi->status = ATTO_STS_INV_FUNC;
119226780d9eSBradley Grove 		break;
119326780d9eSBradley Grove 	}
119426780d9eSBradley Grove 
119526780d9eSBradley Grove 	return false;
119626780d9eSBradley Grove }
119726780d9eSBradley Grove 
hba_ioctl_done_callback(struct esas2r_adapter * a,struct esas2r_request * rq,void * context)119826780d9eSBradley Grove static void hba_ioctl_done_callback(struct esas2r_adapter *a,
119926780d9eSBradley Grove 				    struct esas2r_request *rq, void *context)
120026780d9eSBradley Grove {
120126780d9eSBradley Grove 	struct atto_ioctl *ioctl_hba =
120226780d9eSBradley Grove 		(struct atto_ioctl *)esas2r_buffered_ioctl;
120326780d9eSBradley Grove 
120426780d9eSBradley Grove 	esas2r_debug("hba_ioctl_done_callback %d", a->index);
120526780d9eSBradley Grove 
120626780d9eSBradley Grove 	if (ioctl_hba->function == ATTO_FUNC_GET_ADAP_INFO) {
120726780d9eSBradley Grove 		struct atto_hba_get_adapter_info *gai =
120826780d9eSBradley Grove 			&ioctl_hba->data.get_adap_info;
120926780d9eSBradley Grove 
121026780d9eSBradley Grove 		esas2r_debug("ATTO_FUNC_GET_ADAP_INFO");
121126780d9eSBradley Grove 
121226780d9eSBradley Grove 		gai->drvr_rev_major = ESAS2R_MAJOR_REV;
121326780d9eSBradley Grove 		gai->drvr_rev_minor = ESAS2R_MINOR_REV;
121426780d9eSBradley Grove 
121526780d9eSBradley Grove 		strcpy(gai->drvr_rev_ascii, ESAS2R_VERSION_STR);
121626780d9eSBradley Grove 		strcpy(gai->drvr_name, ESAS2R_DRVR_NAME);
121726780d9eSBradley Grove 
121826780d9eSBradley Grove 		gai->num_busses = 1;
121926780d9eSBradley Grove 		gai->num_targsper_bus = ESAS2R_MAX_ID + 1;
122026780d9eSBradley Grove 		gai->num_lunsper_targ = 1;
122126780d9eSBradley Grove 	}
122226780d9eSBradley Grove }
122326780d9eSBradley Grove 
handle_hba_ioctl(struct esas2r_adapter * a,struct atto_ioctl * ioctl_hba)122426780d9eSBradley Grove u8 handle_hba_ioctl(struct esas2r_adapter *a,
122526780d9eSBradley Grove 		    struct atto_ioctl *ioctl_hba)
122626780d9eSBradley Grove {
122726780d9eSBradley Grove 	struct esas2r_buffered_ioctl bi;
122826780d9eSBradley Grove 
122926780d9eSBradley Grove 	memset(&bi, 0, sizeof(bi));
123026780d9eSBradley Grove 
123126780d9eSBradley Grove 	bi.a = a;
123226780d9eSBradley Grove 	bi.ioctl = ioctl_hba;
123326780d9eSBradley Grove 	bi.length = sizeof(struct atto_ioctl) + ioctl_hba->data_length;
123426780d9eSBradley Grove 	bi.callback = hba_ioctl_callback;
123526780d9eSBradley Grove 	bi.context = NULL;
123626780d9eSBradley Grove 	bi.done_callback = hba_ioctl_done_callback;
123726780d9eSBradley Grove 	bi.done_context = NULL;
123826780d9eSBradley Grove 	bi.offset = 0;
123926780d9eSBradley Grove 
124026780d9eSBradley Grove 	return handle_buffered_ioctl(&bi);
124126780d9eSBradley Grove }
124226780d9eSBradley Grove 
124326780d9eSBradley Grove 
esas2r_write_params(struct esas2r_adapter * a,struct esas2r_request * rq,struct esas2r_sas_nvram * data)124426780d9eSBradley Grove int esas2r_write_params(struct esas2r_adapter *a, struct esas2r_request *rq,
124526780d9eSBradley Grove 			struct esas2r_sas_nvram *data)
124626780d9eSBradley Grove {
124726780d9eSBradley Grove 	int result = 0;
124826780d9eSBradley Grove 
124926780d9eSBradley Grove 	a->nvram_command_done = 0;
125026780d9eSBradley Grove 	rq->comp_cb = complete_nvr_req;
125126780d9eSBradley Grove 
125226780d9eSBradley Grove 	if (esas2r_nvram_write(a, rq, data)) {
125326780d9eSBradley Grove 		/* now wait around for it to complete. */
125426780d9eSBradley Grove 		while (!a->nvram_command_done)
125526780d9eSBradley Grove 			wait_event_interruptible(a->nvram_waiter,
125626780d9eSBradley Grove 						 a->nvram_command_done);
125726780d9eSBradley Grove 		;
125826780d9eSBradley Grove 
125926780d9eSBradley Grove 		/* done, check the status. */
126026780d9eSBradley Grove 		if (rq->req_stat == RS_SUCCESS)
126126780d9eSBradley Grove 			result = 1;
126226780d9eSBradley Grove 	}
126326780d9eSBradley Grove 	return result;
126426780d9eSBradley Grove }
126526780d9eSBradley Grove 
126626780d9eSBradley Grove 
126726780d9eSBradley Grove /* This function only cares about ATTO-specific ioctls (atto_express_ioctl) */
esas2r_ioctl_handler(void * hostdata,unsigned int cmd,void __user * arg)12686f4e626fSNathan Chancellor int esas2r_ioctl_handler(void *hostdata, unsigned int cmd, void __user *arg)
126926780d9eSBradley Grove {
127026780d9eSBradley Grove 	struct atto_express_ioctl *ioctl = NULL;
127126780d9eSBradley Grove 	struct esas2r_adapter *a;
127226780d9eSBradley Grove 	struct esas2r_request *rq;
127326780d9eSBradley Grove 	u16 code;
127426780d9eSBradley Grove 	int err;
127526780d9eSBradley Grove 
127626780d9eSBradley Grove 	esas2r_log(ESAS2R_LOG_DEBG, "ioctl (%p, %x, %p)", hostdata, cmd, arg);
127726780d9eSBradley Grove 
127826780d9eSBradley Grove 	if ((arg == NULL)
127926780d9eSBradley Grove 	    || (cmd < EXPRESS_IOCTL_MIN)
128026780d9eSBradley Grove 	    || (cmd > EXPRESS_IOCTL_MAX))
128126780d9eSBradley Grove 		return -ENOTSUPP;
128226780d9eSBradley Grove 
1283bf7af0ceSAl Viro 	ioctl = memdup_user(arg, sizeof(struct atto_express_ioctl));
1284bf7af0ceSAl Viro 	if (IS_ERR(ioctl)) {
128526780d9eSBradley Grove 		esas2r_log(ESAS2R_LOG_WARN,
12866f4e626fSNathan Chancellor 			   "ioctl_handler access_ok failed for cmd %u, address %p",
12876f4e626fSNathan Chancellor 			   cmd, arg);
1288bf7af0ceSAl Viro 		return PTR_ERR(ioctl);
128926780d9eSBradley Grove 	}
129026780d9eSBradley Grove 
129126780d9eSBradley Grove 	/* verify the signature */
129226780d9eSBradley Grove 
129326780d9eSBradley Grove 	if (memcmp(ioctl->header.signature,
129426780d9eSBradley Grove 		   EXPRESS_IOCTL_SIGNATURE,
129526780d9eSBradley Grove 		   EXPRESS_IOCTL_SIGNATURE_SIZE) != 0) {
129626780d9eSBradley Grove 		esas2r_log(ESAS2R_LOG_WARN, "invalid signature");
129726780d9eSBradley Grove 		kfree(ioctl);
129826780d9eSBradley Grove 
129926780d9eSBradley Grove 		return -ENOTSUPP;
130026780d9eSBradley Grove 	}
130126780d9eSBradley Grove 
130226780d9eSBradley Grove 	/* assume success */
130326780d9eSBradley Grove 
130426780d9eSBradley Grove 	ioctl->header.return_code = IOCTL_SUCCESS;
130526780d9eSBradley Grove 	err = 0;
130626780d9eSBradley Grove 
130726780d9eSBradley Grove 	/*
130826780d9eSBradley Grove 	 * handle EXPRESS_IOCTL_GET_CHANNELS
130926780d9eSBradley Grove 	 * without paying attention to channel
131026780d9eSBradley Grove 	 */
131126780d9eSBradley Grove 
131226780d9eSBradley Grove 	if (cmd == EXPRESS_IOCTL_GET_CHANNELS) {
131326780d9eSBradley Grove 		int i = 0, k = 0;
131426780d9eSBradley Grove 
131526780d9eSBradley Grove 		ioctl->data.chanlist.num_channels = 0;
131626780d9eSBradley Grove 
131726780d9eSBradley Grove 		while (i < MAX_ADAPTERS) {
131826780d9eSBradley Grove 			if (esas2r_adapters[i]) {
131926780d9eSBradley Grove 				ioctl->data.chanlist.num_channels++;
132026780d9eSBradley Grove 				ioctl->data.chanlist.channel[k] = i;
132126780d9eSBradley Grove 				k++;
132226780d9eSBradley Grove 			}
132326780d9eSBradley Grove 			i++;
132426780d9eSBradley Grove 		}
132526780d9eSBradley Grove 
132626780d9eSBradley Grove 		goto ioctl_done;
132726780d9eSBradley Grove 	}
132826780d9eSBradley Grove 
132926780d9eSBradley Grove 	/* get the channel */
133026780d9eSBradley Grove 
133126780d9eSBradley Grove 	if (ioctl->header.channel == 0xFF) {
133226780d9eSBradley Grove 		a = (struct esas2r_adapter *)hostdata;
133326780d9eSBradley Grove 	} else {
13345b2e0c1bSAlan 		if (ioctl->header.channel >= MAX_ADAPTERS ||
13355b2e0c1bSAlan 			esas2r_adapters[ioctl->header.channel] == NULL) {
133626780d9eSBradley Grove 			ioctl->header.return_code = IOCTL_BAD_CHANNEL;
133726780d9eSBradley Grove 			esas2r_log(ESAS2R_LOG_WARN, "bad channel value");
133826780d9eSBradley Grove 			kfree(ioctl);
133926780d9eSBradley Grove 
134026780d9eSBradley Grove 			return -ENOTSUPP;
134126780d9eSBradley Grove 		}
13425b2e0c1bSAlan 		a = esas2r_adapters[ioctl->header.channel];
134326780d9eSBradley Grove 	}
134426780d9eSBradley Grove 
134526780d9eSBradley Grove 	switch (cmd) {
134626780d9eSBradley Grove 	case EXPRESS_IOCTL_RW_FIRMWARE:
134726780d9eSBradley Grove 
134826780d9eSBradley Grove 		if (ioctl->data.fwrw.img_type == FW_IMG_FM_API) {
134926780d9eSBradley Grove 			err = esas2r_write_fw(a,
135026780d9eSBradley Grove 					      (char *)ioctl->data.fwrw.image,
135126780d9eSBradley Grove 					      0,
135226780d9eSBradley Grove 					      sizeof(struct
135326780d9eSBradley Grove 						     atto_express_ioctl));
135426780d9eSBradley Grove 
135526780d9eSBradley Grove 			if (err >= 0) {
135626780d9eSBradley Grove 				err = esas2r_read_fw(a,
135726780d9eSBradley Grove 						     (char *)ioctl->data.fwrw.
135826780d9eSBradley Grove 						     image,
135926780d9eSBradley Grove 						     0,
136026780d9eSBradley Grove 						     sizeof(struct
136126780d9eSBradley Grove 							    atto_express_ioctl));
136226780d9eSBradley Grove 			}
136326780d9eSBradley Grove 		} else if (ioctl->data.fwrw.img_type == FW_IMG_FS_API) {
136426780d9eSBradley Grove 			err = esas2r_write_fs(a,
136526780d9eSBradley Grove 					      (char *)ioctl->data.fwrw.image,
136626780d9eSBradley Grove 					      0,
136726780d9eSBradley Grove 					      sizeof(struct
136826780d9eSBradley Grove 						     atto_express_ioctl));
136926780d9eSBradley Grove 
137026780d9eSBradley Grove 			if (err >= 0) {
137126780d9eSBradley Grove 				err = esas2r_read_fs(a,
137226780d9eSBradley Grove 						     (char *)ioctl->data.fwrw.
137326780d9eSBradley Grove 						     image,
137426780d9eSBradley Grove 						     0,
137526780d9eSBradley Grove 						     sizeof(struct
137626780d9eSBradley Grove 							    atto_express_ioctl));
137726780d9eSBradley Grove 			}
137826780d9eSBradley Grove 		} else {
137926780d9eSBradley Grove 			ioctl->header.return_code = IOCTL_BAD_FLASH_IMGTYPE;
138026780d9eSBradley Grove 		}
138126780d9eSBradley Grove 
138226780d9eSBradley Grove 		break;
138326780d9eSBradley Grove 
138426780d9eSBradley Grove 	case EXPRESS_IOCTL_READ_PARAMS:
138526780d9eSBradley Grove 
138626780d9eSBradley Grove 		memcpy(ioctl->data.prw.data_buffer, a->nvram,
138726780d9eSBradley Grove 		       sizeof(struct esas2r_sas_nvram));
138826780d9eSBradley Grove 		ioctl->data.prw.code = 1;
138926780d9eSBradley Grove 		break;
139026780d9eSBradley Grove 
139126780d9eSBradley Grove 	case EXPRESS_IOCTL_WRITE_PARAMS:
139226780d9eSBradley Grove 
139326780d9eSBradley Grove 		rq = esas2r_alloc_request(a);
139426780d9eSBradley Grove 		if (rq == NULL) {
1395ba9e5874STomas Henzl 			kfree(ioctl);
1396ba9e5874STomas Henzl 			esas2r_log(ESAS2R_LOG_WARN,
1397ba9e5874STomas Henzl 			   "could not allocate an internal request");
1398ba9e5874STomas Henzl 			return -ENOMEM;
139926780d9eSBradley Grove 		}
140026780d9eSBradley Grove 
140126780d9eSBradley Grove 		code = esas2r_write_params(a, rq,
140226780d9eSBradley Grove 					   (struct esas2r_sas_nvram *)ioctl->data.prw.data_buffer);
140326780d9eSBradley Grove 		ioctl->data.prw.code = code;
140426780d9eSBradley Grove 
140526780d9eSBradley Grove 		esas2r_free_request(a, rq);
140626780d9eSBradley Grove 
140726780d9eSBradley Grove 		break;
140826780d9eSBradley Grove 
140926780d9eSBradley Grove 	case EXPRESS_IOCTL_DEFAULT_PARAMS:
141026780d9eSBradley Grove 
141126780d9eSBradley Grove 		esas2r_nvram_get_defaults(a,
141226780d9eSBradley Grove 					  (struct esas2r_sas_nvram *)ioctl->data.prw.data_buffer);
141326780d9eSBradley Grove 		ioctl->data.prw.code = 1;
141426780d9eSBradley Grove 		break;
141526780d9eSBradley Grove 
141626780d9eSBradley Grove 	case EXPRESS_IOCTL_CHAN_INFO:
141726780d9eSBradley Grove 
141826780d9eSBradley Grove 		ioctl->data.chaninfo.major_rev = ESAS2R_MAJOR_REV;
141926780d9eSBradley Grove 		ioctl->data.chaninfo.minor_rev = ESAS2R_MINOR_REV;
142026780d9eSBradley Grove 		ioctl->data.chaninfo.IRQ = a->pcid->irq;
142126780d9eSBradley Grove 		ioctl->data.chaninfo.device_id = a->pcid->device;
142226780d9eSBradley Grove 		ioctl->data.chaninfo.vendor_id = a->pcid->vendor;
142326780d9eSBradley Grove 		ioctl->data.chaninfo.ven_dev_id = a->pcid->subsystem_device;
142426780d9eSBradley Grove 		ioctl->data.chaninfo.revision_id = a->pcid->revision;
142526780d9eSBradley Grove 		ioctl->data.chaninfo.pci_bus = a->pcid->bus->number;
142626780d9eSBradley Grove 		ioctl->data.chaninfo.pci_dev_func = a->pcid->devfn;
142726780d9eSBradley Grove 		ioctl->data.chaninfo.core_rev = 0;
142826780d9eSBradley Grove 		ioctl->data.chaninfo.host_no = a->host->host_no;
142926780d9eSBradley Grove 		ioctl->data.chaninfo.hbaapi_rev = 0;
143026780d9eSBradley Grove 		break;
143126780d9eSBradley Grove 
143226780d9eSBradley Grove 	case EXPRESS_IOCTL_SMP:
143326780d9eSBradley Grove 		ioctl->header.return_code = handle_smp_ioctl(a,
143426780d9eSBradley Grove 							     &ioctl->data.
143526780d9eSBradley Grove 							     ioctl_smp);
143626780d9eSBradley Grove 		break;
143726780d9eSBradley Grove 
143826780d9eSBradley Grove 	case EXPRESS_CSMI:
143926780d9eSBradley Grove 		ioctl->header.return_code =
144026780d9eSBradley Grove 			handle_csmi_ioctl(a, &ioctl->data.csmi);
144126780d9eSBradley Grove 		break;
144226780d9eSBradley Grove 
144326780d9eSBradley Grove 	case EXPRESS_IOCTL_HBA:
144426780d9eSBradley Grove 		ioctl->header.return_code = handle_hba_ioctl(a,
144526780d9eSBradley Grove 							     &ioctl->data.
144626780d9eSBradley Grove 							     ioctl_hba);
144726780d9eSBradley Grove 		break;
144826780d9eSBradley Grove 
144926780d9eSBradley Grove 	case EXPRESS_IOCTL_VDA:
145026780d9eSBradley Grove 		err = esas2r_write_vda(a,
145126780d9eSBradley Grove 				       (char *)&ioctl->data.ioctl_vda,
145226780d9eSBradley Grove 				       0,
145326780d9eSBradley Grove 				       sizeof(struct atto_ioctl_vda) +
145426780d9eSBradley Grove 				       ioctl->data.ioctl_vda.data_length);
145526780d9eSBradley Grove 
145626780d9eSBradley Grove 		if (err >= 0) {
145726780d9eSBradley Grove 			err = esas2r_read_vda(a,
145826780d9eSBradley Grove 					      (char *)&ioctl->data.ioctl_vda,
145926780d9eSBradley Grove 					      0,
146026780d9eSBradley Grove 					      sizeof(struct atto_ioctl_vda) +
146126780d9eSBradley Grove 					      ioctl->data.ioctl_vda.data_length);
146226780d9eSBradley Grove 		}
146326780d9eSBradley Grove 
146426780d9eSBradley Grove 
146526780d9eSBradley Grove 
146626780d9eSBradley Grove 
146726780d9eSBradley Grove 		break;
146826780d9eSBradley Grove 
146926780d9eSBradley Grove 	case EXPRESS_IOCTL_GET_MOD_INFO:
147026780d9eSBradley Grove 
147126780d9eSBradley Grove 		ioctl->data.modinfo.adapter = a;
147226780d9eSBradley Grove 		ioctl->data.modinfo.pci_dev = a->pcid;
147326780d9eSBradley Grove 		ioctl->data.modinfo.scsi_host = a->host;
147426780d9eSBradley Grove 		ioctl->data.modinfo.host_no = a->host->host_no;
147526780d9eSBradley Grove 
147626780d9eSBradley Grove 		break;
147726780d9eSBradley Grove 
147826780d9eSBradley Grove 	default:
147926780d9eSBradley Grove 		esas2r_debug("esas2r_ioctl invalid cmd %p!", cmd);
148026780d9eSBradley Grove 		ioctl->header.return_code = IOCTL_ERR_INVCMD;
148126780d9eSBradley Grove 	}
148226780d9eSBradley Grove 
148326780d9eSBradley Grove ioctl_done:
148426780d9eSBradley Grove 
148526780d9eSBradley Grove 	if (err < 0) {
14866f4e626fSNathan Chancellor 		esas2r_log(ESAS2R_LOG_WARN, "err %d on ioctl cmd %u", err,
148726780d9eSBradley Grove 			   cmd);
148826780d9eSBradley Grove 
148926780d9eSBradley Grove 		switch (err) {
149026780d9eSBradley Grove 		case -ENOMEM:
149126780d9eSBradley Grove 		case -EBUSY:
149226780d9eSBradley Grove 			ioctl->header.return_code = IOCTL_OUT_OF_RESOURCES;
149326780d9eSBradley Grove 			break;
149426780d9eSBradley Grove 
149526780d9eSBradley Grove 		case -ENOSYS:
149626780d9eSBradley Grove 		case -EINVAL:
149726780d9eSBradley Grove 			ioctl->header.return_code = IOCTL_INVALID_PARAM;
149826780d9eSBradley Grove 			break;
149968b14b8fSTomas Henzl 
150068b14b8fSTomas Henzl 		default:
150168b14b8fSTomas Henzl 			ioctl->header.return_code = IOCTL_GENERAL_ERROR;
150268b14b8fSTomas Henzl 			break;
150326780d9eSBradley Grove 		}
150426780d9eSBradley Grove 
150526780d9eSBradley Grove 	}
150626780d9eSBradley Grove 
150726780d9eSBradley Grove 	/* Always copy the buffer back, if only to pick up the status */
15083d3185aeSAl Viro 	err = copy_to_user(arg, ioctl, sizeof(struct atto_express_ioctl));
150926780d9eSBradley Grove 	if (err != 0) {
151026780d9eSBradley Grove 		esas2r_log(ESAS2R_LOG_WARN,
15116f4e626fSNathan Chancellor 			   "ioctl_handler copy_to_user didn't copy everything (err %d, cmd %u)",
15126f4e626fSNathan Chancellor 			   err, cmd);
151326780d9eSBradley Grove 		kfree(ioctl);
151426780d9eSBradley Grove 
151526780d9eSBradley Grove 		return -EFAULT;
151626780d9eSBradley Grove 	}
151726780d9eSBradley Grove 
151826780d9eSBradley Grove 	kfree(ioctl);
151926780d9eSBradley Grove 
152026780d9eSBradley Grove 	return 0;
152126780d9eSBradley Grove }
152226780d9eSBradley Grove 
esas2r_ioctl(struct scsi_device * sd,unsigned int cmd,void __user * arg)15236f4e626fSNathan Chancellor int esas2r_ioctl(struct scsi_device *sd, unsigned int cmd, void __user *arg)
152426780d9eSBradley Grove {
152526780d9eSBradley Grove 	return esas2r_ioctl_handler(sd->host->hostdata, cmd, arg);
152626780d9eSBradley Grove }
152726780d9eSBradley Grove 
free_fw_buffers(struct esas2r_adapter * a)152826780d9eSBradley Grove static void free_fw_buffers(struct esas2r_adapter *a)
152926780d9eSBradley Grove {
153026780d9eSBradley Grove 	if (a->firmware.data) {
153126780d9eSBradley Grove 		dma_free_coherent(&a->pcid->dev,
153226780d9eSBradley Grove 				  (size_t)a->firmware.orig_len,
153326780d9eSBradley Grove 				  a->firmware.data,
153426780d9eSBradley Grove 				  (dma_addr_t)a->firmware.phys);
153526780d9eSBradley Grove 
153626780d9eSBradley Grove 		a->firmware.data = NULL;
153726780d9eSBradley Grove 	}
153826780d9eSBradley Grove }
153926780d9eSBradley Grove 
allocate_fw_buffers(struct esas2r_adapter * a,u32 length)154026780d9eSBradley Grove static int allocate_fw_buffers(struct esas2r_adapter *a, u32 length)
154126780d9eSBradley Grove {
154226780d9eSBradley Grove 	free_fw_buffers(a);
154326780d9eSBradley Grove 
154426780d9eSBradley Grove 	a->firmware.orig_len = length;
154526780d9eSBradley Grove 
154632417d78SAlex Dewar 	a->firmware.data = dma_alloc_coherent(&a->pcid->dev,
154726780d9eSBradley Grove 					      (size_t)length,
154832417d78SAlex Dewar 					      (dma_addr_t *)&a->firmware.phys,
154926780d9eSBradley Grove 					      GFP_KERNEL);
155026780d9eSBradley Grove 
155126780d9eSBradley Grove 	if (!a->firmware.data) {
155226780d9eSBradley Grove 		esas2r_debug("buffer alloc failed!");
155326780d9eSBradley Grove 		return 0;
155426780d9eSBradley Grove 	}
155526780d9eSBradley Grove 
155626780d9eSBradley Grove 	return 1;
155726780d9eSBradley Grove }
155826780d9eSBradley Grove 
155926780d9eSBradley Grove /* Handle a call to read firmware. */
esas2r_read_fw(struct esas2r_adapter * a,char * buf,long off,int count)156026780d9eSBradley Grove int esas2r_read_fw(struct esas2r_adapter *a, char *buf, long off, int count)
156126780d9eSBradley Grove {
156226780d9eSBradley Grove 	esas2r_trace_enter();
156326780d9eSBradley Grove 	/* if the cached header is a status, simply copy it over and return. */
156426780d9eSBradley Grove 	if (a->firmware.state == FW_STATUS_ST) {
156526780d9eSBradley Grove 		int size = min_t(int, count, sizeof(a->firmware.header));
156626780d9eSBradley Grove 		esas2r_trace_exit();
156726780d9eSBradley Grove 		memcpy(buf, &a->firmware.header, size);
156826780d9eSBradley Grove 		esas2r_debug("esas2r_read_fw: STATUS size %d", size);
156926780d9eSBradley Grove 		return size;
157026780d9eSBradley Grove 	}
157126780d9eSBradley Grove 
157226780d9eSBradley Grove 	/*
157326780d9eSBradley Grove 	 * if the cached header is a command, do it if at
157426780d9eSBradley Grove 	 * offset 0, otherwise copy the pieces.
157526780d9eSBradley Grove 	 */
157626780d9eSBradley Grove 
157726780d9eSBradley Grove 	if (a->firmware.state == FW_COMMAND_ST) {
157826780d9eSBradley Grove 		u32 length = a->firmware.header.length;
157926780d9eSBradley Grove 		esas2r_trace_exit();
158026780d9eSBradley Grove 
158126780d9eSBradley Grove 		esas2r_debug("esas2r_read_fw: COMMAND length %d off %d",
158226780d9eSBradley Grove 			     length,
158326780d9eSBradley Grove 			     off);
158426780d9eSBradley Grove 
158526780d9eSBradley Grove 		if (off == 0) {
158626780d9eSBradley Grove 			if (a->firmware.header.action == FI_ACT_UP) {
158726780d9eSBradley Grove 				if (!allocate_fw_buffers(a, length))
158826780d9eSBradley Grove 					return -ENOMEM;
158926780d9eSBradley Grove 
159026780d9eSBradley Grove 
159126780d9eSBradley Grove 				/* copy header over */
159226780d9eSBradley Grove 
159326780d9eSBradley Grove 				memcpy(a->firmware.data,
159426780d9eSBradley Grove 				       &a->firmware.header,
159526780d9eSBradley Grove 				       sizeof(a->firmware.header));
159626780d9eSBradley Grove 
159726780d9eSBradley Grove 				do_fm_api(a,
159826780d9eSBradley Grove 					  (struct esas2r_flash_img *)a->firmware.data);
159926780d9eSBradley Grove 			} else if (a->firmware.header.action == FI_ACT_UPSZ) {
160026780d9eSBradley Grove 				int size =
160126780d9eSBradley Grove 					min((int)count,
160226780d9eSBradley Grove 					    (int)sizeof(a->firmware.header));
160326780d9eSBradley Grove 				do_fm_api(a, &a->firmware.header);
160426780d9eSBradley Grove 				memcpy(buf, &a->firmware.header, size);
160526780d9eSBradley Grove 				esas2r_debug("FI_ACT_UPSZ size %d", size);
160626780d9eSBradley Grove 				return size;
160726780d9eSBradley Grove 			} else {
160826780d9eSBradley Grove 				esas2r_debug("invalid action %d",
160926780d9eSBradley Grove 					     a->firmware.header.action);
161026780d9eSBradley Grove 				return -ENOSYS;
161126780d9eSBradley Grove 			}
161226780d9eSBradley Grove 		}
161326780d9eSBradley Grove 
161426780d9eSBradley Grove 		if (count + off > length)
161526780d9eSBradley Grove 			count = length - off;
161626780d9eSBradley Grove 
161726780d9eSBradley Grove 		if (count < 0)
161826780d9eSBradley Grove 			return 0;
161926780d9eSBradley Grove 
162026780d9eSBradley Grove 		if (!a->firmware.data) {
162126780d9eSBradley Grove 			esas2r_debug(
162226780d9eSBradley Grove 				"read: nonzero offset but no buffer available!");
162326780d9eSBradley Grove 			return -ENOMEM;
162426780d9eSBradley Grove 		}
162526780d9eSBradley Grove 
162626780d9eSBradley Grove 		esas2r_debug("esas2r_read_fw: off %d count %d length %d ", off,
162726780d9eSBradley Grove 			     count,
162826780d9eSBradley Grove 			     length);
162926780d9eSBradley Grove 
163026780d9eSBradley Grove 		memcpy(buf, &a->firmware.data[off], count);
163126780d9eSBradley Grove 
163226780d9eSBradley Grove 		/* when done, release the buffer */
163326780d9eSBradley Grove 
163426780d9eSBradley Grove 		if (length <= off + count) {
163526780d9eSBradley Grove 			esas2r_debug("esas2r_read_fw: freeing buffer!");
163626780d9eSBradley Grove 
163726780d9eSBradley Grove 			free_fw_buffers(a);
163826780d9eSBradley Grove 		}
163926780d9eSBradley Grove 
164026780d9eSBradley Grove 		return count;
164126780d9eSBradley Grove 	}
164226780d9eSBradley Grove 
164326780d9eSBradley Grove 	esas2r_trace_exit();
164426780d9eSBradley Grove 	esas2r_debug("esas2r_read_fw: invalid firmware state %d",
164526780d9eSBradley Grove 		     a->firmware.state);
164626780d9eSBradley Grove 
164726780d9eSBradley Grove 	return -EINVAL;
164826780d9eSBradley Grove }
164926780d9eSBradley Grove 
165026780d9eSBradley Grove /* Handle a call to write firmware. */
esas2r_write_fw(struct esas2r_adapter * a,const char * buf,long off,int count)165126780d9eSBradley Grove int esas2r_write_fw(struct esas2r_adapter *a, const char *buf, long off,
165226780d9eSBradley Grove 		    int count)
165326780d9eSBradley Grove {
165426780d9eSBradley Grove 	u32 length;
165526780d9eSBradley Grove 
165626780d9eSBradley Grove 	if (off == 0) {
165726780d9eSBradley Grove 		struct esas2r_flash_img *header =
165826780d9eSBradley Grove 			(struct esas2r_flash_img *)buf;
165926780d9eSBradley Grove 
166026780d9eSBradley Grove 		/* assume version 0 flash image */
166126780d9eSBradley Grove 
166226780d9eSBradley Grove 		int min_size = sizeof(struct esas2r_flash_img_v0);
166326780d9eSBradley Grove 
166426780d9eSBradley Grove 		a->firmware.state = FW_INVALID_ST;
166526780d9eSBradley Grove 
166626780d9eSBradley Grove 		/* validate the version field first */
166726780d9eSBradley Grove 
166826780d9eSBradley Grove 		if (count < 4
166926780d9eSBradley Grove 		    ||  header->fi_version > FI_VERSION_1) {
167026780d9eSBradley Grove 			esas2r_debug(
167126780d9eSBradley Grove 				"esas2r_write_fw: short header or invalid version");
167226780d9eSBradley Grove 			return -EINVAL;
167326780d9eSBradley Grove 		}
167426780d9eSBradley Grove 
167526780d9eSBradley Grove 		/* See if its a version 1 flash image */
167626780d9eSBradley Grove 
167726780d9eSBradley Grove 		if (header->fi_version == FI_VERSION_1)
167826780d9eSBradley Grove 			min_size = sizeof(struct esas2r_flash_img);
167926780d9eSBradley Grove 
168026780d9eSBradley Grove 		/* If this is the start, the header must be full and valid. */
168126780d9eSBradley Grove 		if (count < min_size) {
168226780d9eSBradley Grove 			esas2r_debug("esas2r_write_fw: short header, aborting");
168326780d9eSBradley Grove 			return -EINVAL;
168426780d9eSBradley Grove 		}
168526780d9eSBradley Grove 
168626780d9eSBradley Grove 		/* Make sure the size is reasonable. */
168726780d9eSBradley Grove 		length = header->length;
168826780d9eSBradley Grove 
168926780d9eSBradley Grove 		if (length > 1024 * 1024) {
169026780d9eSBradley Grove 			esas2r_debug(
169126780d9eSBradley Grove 				"esas2r_write_fw: hosed, length %d  fi_version %d",
169226780d9eSBradley Grove 				length, header->fi_version);
169326780d9eSBradley Grove 			return -EINVAL;
169426780d9eSBradley Grove 		}
169526780d9eSBradley Grove 
169626780d9eSBradley Grove 		/*
169726780d9eSBradley Grove 		 * If this is a write command, allocate memory because
169826780d9eSBradley Grove 		 * we have to cache everything. otherwise, just cache
169926780d9eSBradley Grove 		 * the header, because the read op will do the command.
170026780d9eSBradley Grove 		 */
170126780d9eSBradley Grove 
170226780d9eSBradley Grove 		if (header->action == FI_ACT_DOWN) {
170326780d9eSBradley Grove 			if (!allocate_fw_buffers(a, length))
170426780d9eSBradley Grove 				return -ENOMEM;
170526780d9eSBradley Grove 
170626780d9eSBradley Grove 			/*
170726780d9eSBradley Grove 			 * Store the command, so there is context on subsequent
170826780d9eSBradley Grove 			 * calls.
170926780d9eSBradley Grove 			 */
171026780d9eSBradley Grove 			memcpy(&a->firmware.header,
171126780d9eSBradley Grove 			       buf,
171226780d9eSBradley Grove 			       sizeof(*header));
171326780d9eSBradley Grove 		} else if (header->action == FI_ACT_UP
171426780d9eSBradley Grove 			   ||  header->action == FI_ACT_UPSZ) {
171526780d9eSBradley Grove 			/* Save the command, result will be picked up on read */
171626780d9eSBradley Grove 			memcpy(&a->firmware.header,
171726780d9eSBradley Grove 			       buf,
171826780d9eSBradley Grove 			       sizeof(*header));
171926780d9eSBradley Grove 
172026780d9eSBradley Grove 			a->firmware.state = FW_COMMAND_ST;
172126780d9eSBradley Grove 
172226780d9eSBradley Grove 			esas2r_debug(
172326780d9eSBradley Grove 				"esas2r_write_fw: COMMAND, count %d, action %d ",
172426780d9eSBradley Grove 				count, header->action);
172526780d9eSBradley Grove 
172626780d9eSBradley Grove 			/*
172726780d9eSBradley Grove 			 * Pretend we took the whole buffer,
172826780d9eSBradley Grove 			 * so we don't get bothered again.
172926780d9eSBradley Grove 			 */
173026780d9eSBradley Grove 
173126780d9eSBradley Grove 			return count;
173226780d9eSBradley Grove 		} else {
173326780d9eSBradley Grove 			esas2r_debug("esas2r_write_fw: invalid action %d ",
173426780d9eSBradley Grove 				     a->firmware.header.action);
173526780d9eSBradley Grove 			return -ENOSYS;
173626780d9eSBradley Grove 		}
173726780d9eSBradley Grove 	} else {
173826780d9eSBradley Grove 		length = a->firmware.header.length;
173926780d9eSBradley Grove 	}
174026780d9eSBradley Grove 
174126780d9eSBradley Grove 	/*
174226780d9eSBradley Grove 	 * We only get here on a download command, regardless of offset.
174326780d9eSBradley Grove 	 * the chunks written by the system need to be cached, and when
174426780d9eSBradley Grove 	 * the final one arrives, issue the fmapi command.
174526780d9eSBradley Grove 	 */
174626780d9eSBradley Grove 
174726780d9eSBradley Grove 	if (off + count > length)
174826780d9eSBradley Grove 		count = length - off;
174926780d9eSBradley Grove 
175026780d9eSBradley Grove 	if (count > 0) {
175126780d9eSBradley Grove 		esas2r_debug("esas2r_write_fw: off %d count %d length %d", off,
175226780d9eSBradley Grove 			     count,
175326780d9eSBradley Grove 			     length);
175426780d9eSBradley Grove 
175526780d9eSBradley Grove 		/*
175626780d9eSBradley Grove 		 * On a full upload, the system tries sending the whole buffer.
175726780d9eSBradley Grove 		 * there's nothing to do with it, so just drop it here, before
175826780d9eSBradley Grove 		 * trying to copy over into unallocated memory!
175926780d9eSBradley Grove 		 */
176026780d9eSBradley Grove 		if (a->firmware.header.action == FI_ACT_UP)
176126780d9eSBradley Grove 			return count;
176226780d9eSBradley Grove 
176326780d9eSBradley Grove 		if (!a->firmware.data) {
176426780d9eSBradley Grove 			esas2r_debug(
176526780d9eSBradley Grove 				"write: nonzero offset but no buffer available!");
176626780d9eSBradley Grove 			return -ENOMEM;
176726780d9eSBradley Grove 		}
176826780d9eSBradley Grove 
176926780d9eSBradley Grove 		memcpy(&a->firmware.data[off], buf, count);
177026780d9eSBradley Grove 
177126780d9eSBradley Grove 		if (length == off + count) {
177226780d9eSBradley Grove 			do_fm_api(a,
177326780d9eSBradley Grove 				  (struct esas2r_flash_img *)a->firmware.data);
177426780d9eSBradley Grove 
177526780d9eSBradley Grove 			/*
177626780d9eSBradley Grove 			 * Now copy the header result to be picked up by the
177726780d9eSBradley Grove 			 * next read
177826780d9eSBradley Grove 			 */
177926780d9eSBradley Grove 			memcpy(&a->firmware.header,
178026780d9eSBradley Grove 			       a->firmware.data,
178126780d9eSBradley Grove 			       sizeof(a->firmware.header));
178226780d9eSBradley Grove 
178326780d9eSBradley Grove 			a->firmware.state = FW_STATUS_ST;
178426780d9eSBradley Grove 
178526780d9eSBradley Grove 			esas2r_debug("write completed");
178626780d9eSBradley Grove 
178726780d9eSBradley Grove 			/*
178826780d9eSBradley Grove 			 * Since the system has the data buffered, the only way
178926780d9eSBradley Grove 			 * this can leak is if a root user writes a program
179026780d9eSBradley Grove 			 * that writes a shorter buffer than it claims, and the
179126780d9eSBradley Grove 			 * copyin fails.
179226780d9eSBradley Grove 			 */
179326780d9eSBradley Grove 			free_fw_buffers(a);
179426780d9eSBradley Grove 		}
179526780d9eSBradley Grove 	}
179626780d9eSBradley Grove 
179726780d9eSBradley Grove 	return count;
179826780d9eSBradley Grove }
179926780d9eSBradley Grove 
180026780d9eSBradley Grove /* Callback for the completion of a VDA request. */
vda_complete_req(struct esas2r_adapter * a,struct esas2r_request * rq)180126780d9eSBradley Grove static void vda_complete_req(struct esas2r_adapter *a,
180226780d9eSBradley Grove 			     struct esas2r_request *rq)
180326780d9eSBradley Grove {
180426780d9eSBradley Grove 	a->vda_command_done = 1;
180526780d9eSBradley Grove 	wake_up_interruptible(&a->vda_waiter);
180626780d9eSBradley Grove }
180726780d9eSBradley Grove 
180826780d9eSBradley Grove /* Scatter/gather callback for VDA requests */
get_physaddr_vda(struct esas2r_sg_context * sgc,u64 * addr)180926780d9eSBradley Grove static u32 get_physaddr_vda(struct esas2r_sg_context *sgc, u64 *addr)
181026780d9eSBradley Grove {
181126780d9eSBradley Grove 	struct esas2r_adapter *a = (struct esas2r_adapter *)sgc->adapter;
181226780d9eSBradley Grove 	int offset = (u8 *)sgc->cur_offset - (u8 *)a->vda_buffer;
181326780d9eSBradley Grove 
181426780d9eSBradley Grove 	(*addr) = a->ppvda_buffer + offset;
181526780d9eSBradley Grove 	return VDA_MAX_BUFFER_SIZE - offset;
181626780d9eSBradley Grove }
181726780d9eSBradley Grove 
181826780d9eSBradley Grove /* Handle a call to read a VDA command. */
esas2r_read_vda(struct esas2r_adapter * a,char * buf,long off,int count)181926780d9eSBradley Grove int esas2r_read_vda(struct esas2r_adapter *a, char *buf, long off, int count)
182026780d9eSBradley Grove {
182126780d9eSBradley Grove 	if (!a->vda_buffer)
182226780d9eSBradley Grove 		return -ENOMEM;
182326780d9eSBradley Grove 
182426780d9eSBradley Grove 	if (off == 0) {
182526780d9eSBradley Grove 		struct esas2r_request *rq;
182626780d9eSBradley Grove 		struct atto_ioctl_vda *vi =
182726780d9eSBradley Grove 			(struct atto_ioctl_vda *)a->vda_buffer;
182826780d9eSBradley Grove 		struct esas2r_sg_context sgc;
182926780d9eSBradley Grove 		bool wait_for_completion;
183026780d9eSBradley Grove 
183126780d9eSBradley Grove 		/*
183226780d9eSBradley Grove 		 * Presumeably, someone has already written to the vda_buffer,
183326780d9eSBradley Grove 		 * and now they are reading the node the response, so now we
183426780d9eSBradley Grove 		 * will actually issue the request to the chip and reply.
183526780d9eSBradley Grove 		 */
183626780d9eSBradley Grove 
183726780d9eSBradley Grove 		/* allocate a request */
183826780d9eSBradley Grove 		rq = esas2r_alloc_request(a);
183926780d9eSBradley Grove 		if (rq == NULL) {
1840eaa6d0dfSColin Ian King 			esas2r_debug("esas2r_read_vda: out of requests");
184126780d9eSBradley Grove 			return -EBUSY;
184226780d9eSBradley Grove 		}
184326780d9eSBradley Grove 
184426780d9eSBradley Grove 		rq->comp_cb = vda_complete_req;
184526780d9eSBradley Grove 
184626780d9eSBradley Grove 		sgc.first_req = rq;
184726780d9eSBradley Grove 		sgc.adapter = a;
184826780d9eSBradley Grove 		sgc.cur_offset = a->vda_buffer + VDA_BUFFER_HEADER_SZ;
184926780d9eSBradley Grove 		sgc.get_phys_addr = (PGETPHYSADDR)get_physaddr_vda;
185026780d9eSBradley Grove 
185126780d9eSBradley Grove 		a->vda_command_done = 0;
185226780d9eSBradley Grove 
185326780d9eSBradley Grove 		wait_for_completion =
185426780d9eSBradley Grove 			esas2r_process_vda_ioctl(a, vi, rq, &sgc);
185526780d9eSBradley Grove 
185626780d9eSBradley Grove 		if (wait_for_completion) {
185726780d9eSBradley Grove 			/* now wait around for it to complete. */
185826780d9eSBradley Grove 
185926780d9eSBradley Grove 			while (!a->vda_command_done)
186026780d9eSBradley Grove 				wait_event_interruptible(a->vda_waiter,
186126780d9eSBradley Grove 							 a->vda_command_done);
186226780d9eSBradley Grove 		}
186326780d9eSBradley Grove 
186426780d9eSBradley Grove 		esas2r_free_request(a, (struct esas2r_request *)rq);
186526780d9eSBradley Grove 	}
186626780d9eSBradley Grove 
186726780d9eSBradley Grove 	if (off > VDA_MAX_BUFFER_SIZE)
186826780d9eSBradley Grove 		return 0;
186926780d9eSBradley Grove 
187026780d9eSBradley Grove 	if (count + off > VDA_MAX_BUFFER_SIZE)
187126780d9eSBradley Grove 		count = VDA_MAX_BUFFER_SIZE - off;
187226780d9eSBradley Grove 
187326780d9eSBradley Grove 	if (count < 0)
187426780d9eSBradley Grove 		return 0;
187526780d9eSBradley Grove 
187626780d9eSBradley Grove 	memcpy(buf, a->vda_buffer + off, count);
187726780d9eSBradley Grove 
187826780d9eSBradley Grove 	return count;
187926780d9eSBradley Grove }
188026780d9eSBradley Grove 
188126780d9eSBradley Grove /* Handle a call to write a VDA command. */
esas2r_write_vda(struct esas2r_adapter * a,const char * buf,long off,int count)188226780d9eSBradley Grove int esas2r_write_vda(struct esas2r_adapter *a, const char *buf, long off,
188326780d9eSBradley Grove 		     int count)
188426780d9eSBradley Grove {
188526780d9eSBradley Grove 	/*
188626780d9eSBradley Grove 	 * allocate memory for it, if not already done.  once allocated,
188726780d9eSBradley Grove 	 * we will keep it around until the driver is unloaded.
188826780d9eSBradley Grove 	 */
188926780d9eSBradley Grove 
189026780d9eSBradley Grove 	if (!a->vda_buffer) {
189126780d9eSBradley Grove 		dma_addr_t dma_addr;
189232417d78SAlex Dewar 		a->vda_buffer = dma_alloc_coherent(&a->pcid->dev,
189326780d9eSBradley Grove 						   (size_t)
189426780d9eSBradley Grove 						   VDA_MAX_BUFFER_SIZE,
189526780d9eSBradley Grove 						   &dma_addr,
189626780d9eSBradley Grove 						   GFP_KERNEL);
189726780d9eSBradley Grove 
189826780d9eSBradley Grove 		a->ppvda_buffer = dma_addr;
189926780d9eSBradley Grove 	}
190026780d9eSBradley Grove 
190126780d9eSBradley Grove 	if (!a->vda_buffer)
190226780d9eSBradley Grove 		return -ENOMEM;
190326780d9eSBradley Grove 
190426780d9eSBradley Grove 	if (off > VDA_MAX_BUFFER_SIZE)
190526780d9eSBradley Grove 		return 0;
190626780d9eSBradley Grove 
190726780d9eSBradley Grove 	if (count + off > VDA_MAX_BUFFER_SIZE)
190826780d9eSBradley Grove 		count = VDA_MAX_BUFFER_SIZE - off;
190926780d9eSBradley Grove 
191026780d9eSBradley Grove 	if (count < 1)
191126780d9eSBradley Grove 		return 0;
191226780d9eSBradley Grove 
191326780d9eSBradley Grove 	memcpy(a->vda_buffer + off, buf, count);
191426780d9eSBradley Grove 
191526780d9eSBradley Grove 	return count;
191626780d9eSBradley Grove }
191726780d9eSBradley Grove 
191826780d9eSBradley Grove /* Callback for the completion of an FS_API request.*/
fs_api_complete_req(struct esas2r_adapter * a,struct esas2r_request * rq)191926780d9eSBradley Grove static void fs_api_complete_req(struct esas2r_adapter *a,
192026780d9eSBradley Grove 				struct esas2r_request *rq)
192126780d9eSBradley Grove {
192226780d9eSBradley Grove 	a->fs_api_command_done = 1;
192326780d9eSBradley Grove 
192426780d9eSBradley Grove 	wake_up_interruptible(&a->fs_api_waiter);
192526780d9eSBradley Grove }
192626780d9eSBradley Grove 
192726780d9eSBradley Grove /* Scatter/gather callback for VDA requests */
get_physaddr_fs_api(struct esas2r_sg_context * sgc,u64 * addr)192826780d9eSBradley Grove static u32 get_physaddr_fs_api(struct esas2r_sg_context *sgc, u64 *addr)
192926780d9eSBradley Grove {
193026780d9eSBradley Grove 	struct esas2r_adapter *a = (struct esas2r_adapter *)sgc->adapter;
193126780d9eSBradley Grove 	struct esas2r_ioctl_fs *fs =
193226780d9eSBradley Grove 		(struct esas2r_ioctl_fs *)a->fs_api_buffer;
193326780d9eSBradley Grove 	u32 offset = (u8 *)sgc->cur_offset - (u8 *)fs;
193426780d9eSBradley Grove 
193526780d9eSBradley Grove 	(*addr) = a->ppfs_api_buffer + offset;
193626780d9eSBradley Grove 
193726780d9eSBradley Grove 	return a->fs_api_buffer_size - offset;
193826780d9eSBradley Grove }
193926780d9eSBradley Grove 
194026780d9eSBradley Grove /* Handle a call to read firmware via FS_API. */
esas2r_read_fs(struct esas2r_adapter * a,char * buf,long off,int count)194126780d9eSBradley Grove int esas2r_read_fs(struct esas2r_adapter *a, char *buf, long off, int count)
194226780d9eSBradley Grove {
194326780d9eSBradley Grove 	if (!a->fs_api_buffer)
194426780d9eSBradley Grove 		return -ENOMEM;
194526780d9eSBradley Grove 
194626780d9eSBradley Grove 	if (off == 0) {
194726780d9eSBradley Grove 		struct esas2r_request *rq;
194826780d9eSBradley Grove 		struct esas2r_sg_context sgc;
194926780d9eSBradley Grove 		struct esas2r_ioctl_fs *fs =
195026780d9eSBradley Grove 			(struct esas2r_ioctl_fs *)a->fs_api_buffer;
195126780d9eSBradley Grove 
195226780d9eSBradley Grove 		/* If another flash request is already in progress, return. */
1953249cf320SBinoy Jayan 		if (mutex_lock_interruptible(&a->fs_api_mutex)) {
195426780d9eSBradley Grove busy:
195526780d9eSBradley Grove 			fs->status = ATTO_STS_OUT_OF_RSRC;
195626780d9eSBradley Grove 			return -EBUSY;
195726780d9eSBradley Grove 		}
195826780d9eSBradley Grove 
195926780d9eSBradley Grove 		/*
196026780d9eSBradley Grove 		 * Presumeably, someone has already written to the
196126780d9eSBradley Grove 		 * fs_api_buffer, and now they are reading the node the
196226780d9eSBradley Grove 		 * response, so now we will actually issue the request to the
196326780d9eSBradley Grove 		 * chip and reply. Allocate a request
196426780d9eSBradley Grove 		 */
196526780d9eSBradley Grove 
196626780d9eSBradley Grove 		rq = esas2r_alloc_request(a);
196726780d9eSBradley Grove 		if (rq == NULL) {
196826780d9eSBradley Grove 			esas2r_debug("esas2r_read_fs: out of requests");
1969249cf320SBinoy Jayan 			mutex_unlock(&a->fs_api_mutex);
197026780d9eSBradley Grove 			goto busy;
197126780d9eSBradley Grove 		}
197226780d9eSBradley Grove 
197326780d9eSBradley Grove 		rq->comp_cb = fs_api_complete_req;
197426780d9eSBradley Grove 
197526780d9eSBradley Grove 		/* Set up the SGCONTEXT for to build the s/g table */
197626780d9eSBradley Grove 
197726780d9eSBradley Grove 		sgc.cur_offset = fs->data;
197826780d9eSBradley Grove 		sgc.get_phys_addr = (PGETPHYSADDR)get_physaddr_fs_api;
197926780d9eSBradley Grove 
198026780d9eSBradley Grove 		a->fs_api_command_done = 0;
198126780d9eSBradley Grove 
198226780d9eSBradley Grove 		if (!esas2r_process_fs_ioctl(a, fs, rq, &sgc)) {
198326780d9eSBradley Grove 			if (fs->status == ATTO_STS_OUT_OF_RSRC)
198426780d9eSBradley Grove 				count = -EBUSY;
198526780d9eSBradley Grove 
198626780d9eSBradley Grove 			goto dont_wait;
198726780d9eSBradley Grove 		}
198826780d9eSBradley Grove 
198926780d9eSBradley Grove 		/* Now wait around for it to complete. */
199026780d9eSBradley Grove 
199126780d9eSBradley Grove 		while (!a->fs_api_command_done)
199226780d9eSBradley Grove 			wait_event_interruptible(a->fs_api_waiter,
199326780d9eSBradley Grove 						 a->fs_api_command_done);
199426780d9eSBradley Grove 		;
199526780d9eSBradley Grove dont_wait:
199626780d9eSBradley Grove 		/* Free the request and keep going */
1997249cf320SBinoy Jayan 		mutex_unlock(&a->fs_api_mutex);
199826780d9eSBradley Grove 		esas2r_free_request(a, (struct esas2r_request *)rq);
199926780d9eSBradley Grove 
200026780d9eSBradley Grove 		/* Pick up possible error code from above */
200126780d9eSBradley Grove 		if (count < 0)
200226780d9eSBradley Grove 			return count;
200326780d9eSBradley Grove 	}
200426780d9eSBradley Grove 
200526780d9eSBradley Grove 	if (off > a->fs_api_buffer_size)
200626780d9eSBradley Grove 		return 0;
200726780d9eSBradley Grove 
200826780d9eSBradley Grove 	if (count + off > a->fs_api_buffer_size)
200926780d9eSBradley Grove 		count = a->fs_api_buffer_size - off;
201026780d9eSBradley Grove 
201126780d9eSBradley Grove 	if (count < 0)
201226780d9eSBradley Grove 		return 0;
201326780d9eSBradley Grove 
201426780d9eSBradley Grove 	memcpy(buf, a->fs_api_buffer + off, count);
201526780d9eSBradley Grove 
201626780d9eSBradley Grove 	return count;
201726780d9eSBradley Grove }
201826780d9eSBradley Grove 
201926780d9eSBradley Grove /* Handle a call to write firmware via FS_API. */
esas2r_write_fs(struct esas2r_adapter * a,const char * buf,long off,int count)202026780d9eSBradley Grove int esas2r_write_fs(struct esas2r_adapter *a, const char *buf, long off,
202126780d9eSBradley Grove 		    int count)
202226780d9eSBradley Grove {
202326780d9eSBradley Grove 	if (off == 0) {
202426780d9eSBradley Grove 		struct esas2r_ioctl_fs *fs = (struct esas2r_ioctl_fs *)buf;
202526780d9eSBradley Grove 		u32 length = fs->command.length + offsetof(
202626780d9eSBradley Grove 			struct esas2r_ioctl_fs,
202726780d9eSBradley Grove 			data);
202826780d9eSBradley Grove 
202926780d9eSBradley Grove 		/*
203026780d9eSBradley Grove 		 * Special case, for BEGIN commands, the length field
203126780d9eSBradley Grove 		 * is lying to us, so just get enough for the header.
203226780d9eSBradley Grove 		 */
203326780d9eSBradley Grove 
203426780d9eSBradley Grove 		if (fs->command.command == ESAS2R_FS_CMD_BEGINW)
203526780d9eSBradley Grove 			length = offsetof(struct esas2r_ioctl_fs, data);
203626780d9eSBradley Grove 
203726780d9eSBradley Grove 		/*
203826780d9eSBradley Grove 		 * Beginning a command.  We assume we'll get at least
203926780d9eSBradley Grove 		 * enough in the first write so we can look at the
204026780d9eSBradley Grove 		 * header and see how much we need to alloc.
204126780d9eSBradley Grove 		 */
204226780d9eSBradley Grove 
204326780d9eSBradley Grove 		if (count < offsetof(struct esas2r_ioctl_fs, data))
204426780d9eSBradley Grove 			return -EINVAL;
204526780d9eSBradley Grove 
204626780d9eSBradley Grove 		/* Allocate a buffer or use the existing buffer. */
204726780d9eSBradley Grove 		if (a->fs_api_buffer) {
204826780d9eSBradley Grove 			if (a->fs_api_buffer_size < length) {
204926780d9eSBradley Grove 				/* Free too-small buffer and get a new one */
205026780d9eSBradley Grove 				dma_free_coherent(&a->pcid->dev,
205126780d9eSBradley Grove 						  (size_t)a->fs_api_buffer_size,
205226780d9eSBradley Grove 						  a->fs_api_buffer,
205326780d9eSBradley Grove 						  (dma_addr_t)a->ppfs_api_buffer);
205426780d9eSBradley Grove 
205526780d9eSBradley Grove 				goto re_allocate_buffer;
205626780d9eSBradley Grove 			}
205726780d9eSBradley Grove 		} else {
205826780d9eSBradley Grove re_allocate_buffer:
205926780d9eSBradley Grove 			a->fs_api_buffer_size = length;
206026780d9eSBradley Grove 
206132417d78SAlex Dewar 			a->fs_api_buffer = dma_alloc_coherent(&a->pcid->dev,
206226780d9eSBradley Grove 							      (size_t)a->fs_api_buffer_size,
206326780d9eSBradley Grove 							      (dma_addr_t *)&a->ppfs_api_buffer,
206426780d9eSBradley Grove 							      GFP_KERNEL);
206526780d9eSBradley Grove 		}
206626780d9eSBradley Grove 	}
206726780d9eSBradley Grove 
206826780d9eSBradley Grove 	if (!a->fs_api_buffer)
206926780d9eSBradley Grove 		return -ENOMEM;
207026780d9eSBradley Grove 
207126780d9eSBradley Grove 	if (off > a->fs_api_buffer_size)
207226780d9eSBradley Grove 		return 0;
207326780d9eSBradley Grove 
207426780d9eSBradley Grove 	if (count + off > a->fs_api_buffer_size)
207526780d9eSBradley Grove 		count = a->fs_api_buffer_size - off;
207626780d9eSBradley Grove 
207726780d9eSBradley Grove 	if (count < 1)
207826780d9eSBradley Grove 		return 0;
207926780d9eSBradley Grove 
208026780d9eSBradley Grove 	memcpy(a->fs_api_buffer + off, buf, count);
208126780d9eSBradley Grove 
208226780d9eSBradley Grove 	return count;
208326780d9eSBradley Grove }
2084