1c0586b87SGarrett D'Amore /*
2c0586b87SGarrett D'Amore  * This file and its contents are supplied under the terms of the
3c0586b87SGarrett D'Amore  * Common Development and Distribution License ("CDDL"), version 1.0.
4c0586b87SGarrett D'Amore  * You may only use this file in accordance with the terms of version
5c0586b87SGarrett D'Amore  * 1.0 of the CDDL.
6c0586b87SGarrett D'Amore  *
7c0586b87SGarrett D'Amore  * A full copy of the text of the CDDL should have accompanied this
8c0586b87SGarrett D'Amore  * source.  A copy of the CDDL is also available via the Internet at
9c0586b87SGarrett D'Amore  * http://www.illumos.org/license/CDDL.
10c0586b87SGarrett D'Amore  */
11c0586b87SGarrett D'Amore 
12c0586b87SGarrett D'Amore /*
13c0586b87SGarrett D'Amore  * Copyright 2016 Nexenta Systems, Inc.
14c0586b87SGarrett D'Amore  * Copyright 2022 RackTop Systems, Inc.
15c0586b87SGarrett D'Amore  */
16c0586b87SGarrett D'Amore 
17c0586b87SGarrett D'Amore #include <sys/atomic.h>
18c0586b87SGarrett D'Amore #include <sys/cmn_err.h>
19c0586b87SGarrett D'Amore #include <sys/cpuvar.h>
20c0586b87SGarrett D'Amore #include <sys/ddi.h>
21c0586b87SGarrett D'Amore #include <sys/id32.h>
22c0586b87SGarrett D'Amore #include <sys/kmem.h>
23c0586b87SGarrett D'Amore #include <sys/list.h>
24c0586b87SGarrett D'Amore #include <sys/modctl.h>
25c0586b87SGarrett D'Amore #include <sys/pci.h>
26c0586b87SGarrett D'Amore #include <sys/scsi/scsi.h>
27c0586b87SGarrett D'Amore #include <sys/sunddi.h>
28c0586b87SGarrett D'Amore #include <sys/sysmacros.h>
29c0586b87SGarrett D'Amore #include <sys/types.h>
30c0586b87SGarrett D'Amore #include <sys/note.h>
31c0586b87SGarrett D'Amore 
32c0586b87SGarrett D'Amore #include "pvscsi.h"
33c0586b87SGarrett D'Amore #include "pvscsi_var.h"
34c0586b87SGarrett D'Amore 
35c0586b87SGarrett D'Amore /* we can support any of the interrupt types */
36c0586b87SGarrett D'Amore int pvscsi_intr_types = \
37c0586b87SGarrett D'Amore 	DDI_INTR_TYPE_MSIX|DDI_INTR_TYPE_MSI|DDI_INTR_TYPE_FIXED;
38c0586b87SGarrett D'Amore int pvscsi_ring_pages = PVSCSI_DEFAULT_NUM_PAGES_PER_RING;
39c0586b87SGarrett D'Amore int pvscsi_msg_ring_pages = PVSCSI_DEFAULT_NUM_PAGES_MSG_RING;
40c0586b87SGarrett D'Amore static int pvscsi_hz;
41c0586b87SGarrett D'Amore 
42c0586b87SGarrett D'Amore static int pvscsi_abort(struct scsi_address *, struct scsi_pkt *);
43c0586b87SGarrett D'Amore static void pvscsi_timeout(void *);
44c0586b87SGarrett D'Amore static void pvscsi_setup_rings(pvscsi_softc_t *);
45c0586b87SGarrett D'Amore static void pvscsi_complete_cmds(pvscsi_softc_t *, pvscsi_cmd_t *);
46c0586b87SGarrett D'Amore static boolean_t pvscsi_cmd_init(pvscsi_softc_t *, pvscsi_cmd_t *, int);
47c0586b87SGarrett D'Amore static void pvscsi_cmd_fini(pvscsi_cmd_t *);
48c0586b87SGarrett D'Amore 
49c0586b87SGarrett D'Amore /* HBA DMA attributes */
50c0586b87SGarrett D'Amore static ddi_dma_attr_t pvscsi_dma_attr = {
51c0586b87SGarrett D'Amore 	.dma_attr_version =	DMA_ATTR_V0,
52c0586b87SGarrett D'Amore 	.dma_attr_addr_lo =	0,
53c0586b87SGarrett D'Amore 	.dma_attr_addr_hi =	0xFFFFFFFFFFFFFFFFull,
54c0586b87SGarrett D'Amore 	.dma_attr_count_max =	0xFFFFFFFFFFFFFFFFull,
55c0586b87SGarrett D'Amore 	.dma_attr_align =	PAGE_SIZE,
56c0586b87SGarrett D'Amore 	.dma_attr_burstsizes =	1,
57c0586b87SGarrett D'Amore 	.dma_attr_minxfer =	1,
58c0586b87SGarrett D'Amore 	.dma_attr_maxxfer =	0xFFFFFFFFFFFFFFFFull,
59c0586b87SGarrett D'Amore 	.dma_attr_seg =		0xFFFFFFFFFFFFFFFFull,
60c0586b87SGarrett D'Amore 	.dma_attr_sgllen =	1,
61c0586b87SGarrett D'Amore 	.dma_attr_granular =	1,
62c0586b87SGarrett D'Amore 	.dma_attr_flags =	0
63c0586b87SGarrett D'Amore };
64c0586b87SGarrett D'Amore 
65c0586b87SGarrett D'Amore /* DMA attributes for buffer I/O */
66c0586b87SGarrett D'Amore static ddi_dma_attr_t pvscsi_io_dma_attr = {
67c0586b87SGarrett D'Amore 	.dma_attr_version =	DMA_ATTR_V0,
68c0586b87SGarrett D'Amore 	.dma_attr_addr_lo =	0,
69c0586b87SGarrett D'Amore 	.dma_attr_addr_hi =	0xFFFFFFFFFFFFFFFFull,
70c0586b87SGarrett D'Amore 	.dma_attr_count_max =	0x7FFFFFFFll,
71c0586b87SGarrett D'Amore 	.dma_attr_align =	1,
72c0586b87SGarrett D'Amore 	.dma_attr_burstsizes =	1,
73c0586b87SGarrett D'Amore 	.dma_attr_minxfer =	1,
74c0586b87SGarrett D'Amore 	.dma_attr_maxxfer =	PAGE_SIZE * PVSCSI_MAX_SG_SIZE,
75c0586b87SGarrett D'Amore 	.dma_attr_seg =		0xFFFFFFFFFFFFFFFFull,
76c0586b87SGarrett D'Amore 	.dma_attr_sgllen =	PVSCSI_MAX_SG_SIZE,
77c0586b87SGarrett D'Amore 	.dma_attr_granular =	1,
78c0586b87SGarrett D'Amore 	.dma_attr_flags =	0
79c0586b87SGarrett D'Amore };
80c0586b87SGarrett D'Amore 
81c0586b87SGarrett D'Amore /*
82c0586b87SGarrett D'Amore  * The structures are always little endian (VMware only runs
83c0586b87SGarrett D'Amore  * on little endian CPUs), but we only run on LE processors,
84c0586b87SGarrett D'Amore  * and NEVERSWAP avoids needing to use DDI accessor functions.
85c0586b87SGarrett D'Amore  * (It would be incredibly bizarre to have a VMware guest running
86c0586b87SGarrett D'Amore  * with a different endianness than the hypervisor.)
87c0586b87SGarrett D'Amore  */
88c0586b87SGarrett D'Amore static ddi_device_acc_attr_t pvscsi_mmio_attr = {
89c0586b87SGarrett D'Amore 	.devacc_attr_version =		DDI_DEVICE_ATTR_V1,
90c0586b87SGarrett D'Amore 	.devacc_attr_endian_flags =	DDI_NEVERSWAP_ACC,
91c0586b87SGarrett D'Amore 	.devacc_attr_dataorder =	DDI_STRICTORDER_ACC,
92c0586b87SGarrett D'Amore 	.devacc_attr_access =		DDI_DEFAULT_ACC
93c0586b87SGarrett D'Amore };
94c0586b87SGarrett D'Amore 
95c0586b87SGarrett D'Amore static ddi_device_acc_attr_t pvscsi_dma_attrs = {
96c0586b87SGarrett D'Amore 	.devacc_attr_version =		DDI_DEVICE_ATTR_V1,
97c0586b87SGarrett D'Amore 	.devacc_attr_endian_flags =	DDI_NEVERSWAP_ACC,
98c0586b87SGarrett D'Amore 	.devacc_attr_dataorder =	DDI_STRICTORDER_ACC,
99c0586b87SGarrett D'Amore 	.devacc_attr_access =		DDI_DEFAULT_ACC,
100c0586b87SGarrett D'Amore };
101c0586b87SGarrett D'Amore 
102c0586b87SGarrett D'Amore static void
pvscsi_add_to_queue(pvscsi_softc_t * pvs,pvscsi_cmd_t * cmd)103c0586b87SGarrett D'Amore pvscsi_add_to_queue(pvscsi_softc_t *pvs, pvscsi_cmd_t *cmd)
104c0586b87SGarrett D'Amore {
105c0586b87SGarrett D'Amore 	pvscsi_cmd_t	*r;
106c0586b87SGarrett D'Amore 	list_t		*l;
107c0586b87SGarrett D'Amore 
108c0586b87SGarrett D'Amore 	/*
109c0586b87SGarrett D'Amore 	 * We insert in order of expiration, with the earliest
110c0586b87SGarrett D'Amore 	 * expirations at the front.  This logic assumes that most
111c0586b87SGarrett D'Amore 	 * commands will have the same timeout, and is optimized
112c0586b87SGarrett D'Amore 	 * to minimize walking the list.  It allows timeouts to
113c0586b87SGarrett D'Amore 	 * run without looking at more than one node that has not
114c0586b87SGarrett D'Amore 	 * yet expired.
115c0586b87SGarrett D'Amore 	 */
116c0586b87SGarrett D'Amore 	ASSERT(mutex_owned(&pvs->lock));
117c0586b87SGarrett D'Amore 
118c0586b87SGarrett D'Amore 	l = &pvs->cmd_queue;
119c0586b87SGarrett D'Amore 	for (r = list_tail(l); r != NULL; r = list_prev(l, r)) {
120c0586b87SGarrett D'Amore 		/* this subtraction is safe if lbolt wraps */
121c0586b87SGarrett D'Amore 		if (((cmd->start + cmd->timeout) -
122c0586b87SGarrett D'Amore 		    (r->start + r->timeout)) >= 0) {
123c0586b87SGarrett D'Amore 			list_insert_after(l, r, cmd);
124c0586b87SGarrett D'Amore 			return;
125c0586b87SGarrett D'Amore 		}
126c0586b87SGarrett D'Amore 	}
127c0586b87SGarrett D'Amore 
128c0586b87SGarrett D'Amore 	list_insert_head(l, cmd);
129c0586b87SGarrett D'Amore }
130c0586b87SGarrett D'Amore 
131c0586b87SGarrett D'Amore static uint32_t
pvscsi_reg_read(pvscsi_softc_t * pvs,uint32_t offset)132c0586b87SGarrett D'Amore pvscsi_reg_read(pvscsi_softc_t *pvs, uint32_t offset)
133c0586b87SGarrett D'Amore {
134c0586b87SGarrett D'Amore 	uint32_t	ret;
135c0586b87SGarrett D'Amore 
136c0586b87SGarrett D'Amore 	ASSERT((offset & (sizeof (uint32_t) - 1)) == 0);
137c0586b87SGarrett D'Amore 
138c0586b87SGarrett D'Amore 	ret = ddi_get32(pvs->mmio_handle,
139c0586b87SGarrett D'Amore 	    (uint32_t *)(pvs->mmio_base + offset));
140c0586b87SGarrett D'Amore 
141c0586b87SGarrett D'Amore 	return (ret);
142c0586b87SGarrett D'Amore }
143c0586b87SGarrett D'Amore 
144c0586b87SGarrett D'Amore static void
pvscsi_reg_write(pvscsi_softc_t * pvs,uint32_t offset,uint32_t value)145c0586b87SGarrett D'Amore pvscsi_reg_write(pvscsi_softc_t *pvs, uint32_t offset, uint32_t value)
146c0586b87SGarrett D'Amore {
147c0586b87SGarrett D'Amore 	ASSERT((offset & (sizeof (uint32_t) - 1)) == 0);
148c0586b87SGarrett D'Amore 
149c0586b87SGarrett D'Amore 	ddi_put32(pvs->mmio_handle, (uint32_t *)(pvs->mmio_base + offset),
150c0586b87SGarrett D'Amore 	    value);
151c0586b87SGarrett D'Amore }
152c0586b87SGarrett D'Amore 
153c0586b87SGarrett D'Amore static void
pvscsi_write_cmd_desc(pvscsi_softc_t * pvs,uint32_t cmd,void * desc,size_t len)154c0586b87SGarrett D'Amore pvscsi_write_cmd_desc(pvscsi_softc_t *pvs, uint32_t cmd, void *desc, size_t len)
155c0586b87SGarrett D'Amore {
156c0586b87SGarrett D'Amore 	len /= sizeof (uint32_t);
157c0586b87SGarrett D'Amore 	pvscsi_reg_write(pvs, PVSCSI_REG_OFFSET_COMMAND, cmd);
158c0586b87SGarrett D'Amore 	ddi_rep_put32(pvs->mmio_handle, (uint32_t *)desc,
159c0586b87SGarrett D'Amore 	    (uint32_t *)(pvs->mmio_base + PVSCSI_REG_OFFSET_COMMAND_DATA),
160c0586b87SGarrett D'Amore 	    len, DDI_DEV_NO_AUTOINCR);
161c0586b87SGarrett D'Amore }
162c0586b87SGarrett D'Amore 
163c0586b87SGarrett D'Amore static uint32_t
pvscsi_read_intr_status(pvscsi_softc_t * pvs)164c0586b87SGarrett D'Amore pvscsi_read_intr_status(pvscsi_softc_t *pvs)
165c0586b87SGarrett D'Amore {
166c0586b87SGarrett D'Amore 	return (pvscsi_reg_read(pvs, PVSCSI_REG_OFFSET_INTR_STATUS));
167c0586b87SGarrett D'Amore }
168c0586b87SGarrett D'Amore 
169c0586b87SGarrett D'Amore static void
pvscsi_write_intr_status(pvscsi_softc_t * pvs,uint32_t val)170c0586b87SGarrett D'Amore pvscsi_write_intr_status(pvscsi_softc_t *pvs, uint32_t val)
171c0586b87SGarrett D'Amore {
172c0586b87SGarrett D'Amore 	pvscsi_reg_write(pvs, PVSCSI_REG_OFFSET_INTR_STATUS, val);
173c0586b87SGarrett D'Amore }
174c0586b87SGarrett D'Amore 
175c0586b87SGarrett D'Amore static pvscsi_cmd_t *
pvscsi_reclaim_cmds(pvscsi_softc_t * pvs)176c0586b87SGarrett D'Amore pvscsi_reclaim_cmds(pvscsi_softc_t *pvs)
177c0586b87SGarrett D'Amore {
178c0586b87SGarrett D'Amore 	pvscsi_cmd_t	*head = NULL;
179c0586b87SGarrett D'Amore 	pvscsi_cmd_t	**tail = &head;
180c0586b87SGarrett D'Amore 	pvscsi_cmd_t	*cmd;
181c0586b87SGarrett D'Amore 
182c0586b87SGarrett D'Amore 	ASSERT(mutex_owned(&pvs->lock));
183c0586b87SGarrett D'Amore 	while ((cmd = list_remove_head(&pvs->cmd_queue)) != NULL) {
184c0586b87SGarrett D'Amore 		list_remove(&pvs->cmd_queue, cmd);
185c0586b87SGarrett D'Amore 		*tail = cmd;
186c0586b87SGarrett D'Amore 		tail = &cmd->next_cmd;
187c0586b87SGarrett D'Amore 		*tail = NULL;
188c0586b87SGarrett D'Amore 		cmd->host_status = BTSTAT_BUSRESET;
189c0586b87SGarrett D'Amore 	}
190c0586b87SGarrett D'Amore 	return (head);
191c0586b87SGarrett D'Amore }
192c0586b87SGarrett D'Amore 
193c0586b87SGarrett D'Amore static void
pvscsi_stop_hba(pvscsi_softc_t * pvs)194c0586b87SGarrett D'Amore pvscsi_stop_hba(pvscsi_softc_t *pvs)
195c0586b87SGarrett D'Amore {
196c0586b87SGarrett D'Amore 	pvscsi_write_cmd_desc(pvs, PVSCSI_CMD_ADAPTER_RESET, NULL, 0);
197c0586b87SGarrett D'Amore 	pvscsi_reg_write(pvs, PVSCSI_REG_OFFSET_INTR_MASK, 0);
198c0586b87SGarrett D'Amore 	/* read interrupt status to flush PCI write buffers */
199c0586b87SGarrett D'Amore 	(void) pvscsi_read_intr_status(pvs);
200c0586b87SGarrett D'Amore }
201c0586b87SGarrett D'Amore 
202c0586b87SGarrett D'Amore static void
pvscsi_start_hba(pvscsi_softc_t * pvs)203c0586b87SGarrett D'Amore pvscsi_start_hba(pvscsi_softc_t *pvs)
204c0586b87SGarrett D'Amore {
205c0586b87SGarrett D'Amore 	pvscsi_setup_rings(pvs);
206c0586b87SGarrett D'Amore 	pvscsi_reg_write(pvs, PVSCSI_REG_OFFSET_INTR_MASK,
207c0586b87SGarrett D'Amore 	    PVSCSI_INTR_CMPL_MASK | PVSCSI_INTR_MSG_MASK);
208c0586b87SGarrett D'Amore }
209c0586b87SGarrett D'Amore 
210c0586b87SGarrett D'Amore static void
pvscsi_reset_bus(pvscsi_softc_t * pvs)211c0586b87SGarrett D'Amore pvscsi_reset_bus(pvscsi_softc_t *pvs)
212c0586b87SGarrett D'Amore {
213c0586b87SGarrett D'Amore 	pvscsi_write_cmd_desc(pvs, PVSCSI_CMD_RESET_BUS, NULL, 0);
214c0586b87SGarrett D'Amore }
215c0586b87SGarrett D'Amore 
216c0586b87SGarrett D'Amore /*
217c0586b87SGarrett D'Amore  * pvscsi_restart_hba resets the HBA, and reconfigures it.  It also
218c0586b87SGarrett D'Amore  * completes all commands that have not been already completed with
219c0586b87SGarrett D'Amore  * a reset.
220c0586b87SGarrett D'Amore  */
221c0586b87SGarrett D'Amore static void
pvscsi_restart_hba(pvscsi_softc_t * pvs)222c0586b87SGarrett D'Amore pvscsi_restart_hba(pvscsi_softc_t *pvs)
223c0586b87SGarrett D'Amore {
224c0586b87SGarrett D'Amore 	pvscsi_cmd_t	*cmd;
225c0586b87SGarrett D'Amore 
226c0586b87SGarrett D'Amore 	mutex_enter(&pvs->lock);
227c0586b87SGarrett D'Amore 	pvscsi_stop_hba(pvs);
228c0586b87SGarrett D'Amore 	cmd = pvscsi_reclaim_cmds(pvs);
229c0586b87SGarrett D'Amore 	pvscsi_start_hba(pvs);
230c0586b87SGarrett D'Amore 	mutex_exit(&pvs->lock);
231c0586b87SGarrett D'Amore 
232c0586b87SGarrett D'Amore 	/* run the completions from the reclaimed commands */
233c0586b87SGarrett D'Amore 	pvscsi_complete_cmds(pvs, cmd);
234c0586b87SGarrett D'Amore }
235c0586b87SGarrett D'Amore 
236c0586b87SGarrett D'Amore static void
pvscsi_submit_nonrw_io(pvscsi_softc_t * pvs)237c0586b87SGarrett D'Amore pvscsi_submit_nonrw_io(pvscsi_softc_t *pvs)
238c0586b87SGarrett D'Amore {
239c0586b87SGarrett D'Amore 	pvscsi_reg_write(pvs, PVSCSI_REG_OFFSET_KICK_NON_RW_IO, 0);
240c0586b87SGarrett D'Amore }
241c0586b87SGarrett D'Amore 
242c0586b87SGarrett D'Amore static void
pvscsi_submit_rw_io(pvscsi_softc_t * pvs)243c0586b87SGarrett D'Amore pvscsi_submit_rw_io(pvscsi_softc_t *pvs)
244c0586b87SGarrett D'Amore {
245c0586b87SGarrett D'Amore 	pvscsi_reg_write(pvs, PVSCSI_REG_OFFSET_KICK_RW_IO, 0);
246c0586b87SGarrett D'Amore }
247c0586b87SGarrett D'Amore 
248c0586b87SGarrett D'Amore static pvscsi_cmd_t *
pvscsi_process_comp_ring(pvscsi_softc_t * pvs)249c0586b87SGarrett D'Amore pvscsi_process_comp_ring(pvscsi_softc_t *pvs)
250c0586b87SGarrett D'Amore {
251c0586b87SGarrett D'Amore 	pvscsi_cmd_t	**pnext_cmd;
252c0586b87SGarrett D'Amore 	pvscsi_cmd_t	*cmd;
253c0586b87SGarrett D'Amore 	pvscsi_cmd_t	*head = NULL;
254c0586b87SGarrett D'Amore 	struct PVSCSIRingsState *sdesc = RINGS_STATE(pvs);
255c0586b87SGarrett D'Amore 	uint32_t	cmp_ne = sdesc->cmpNumEntriesLog2;
256c0586b87SGarrett D'Amore 
257c0586b87SGarrett D'Amore 	ASSERT(mutex_owned(&pvs->lock));
258c0586b87SGarrett D'Amore 
259c0586b87SGarrett D'Amore 	pnext_cmd = &head;
260c0586b87SGarrett D'Amore 
261c0586b87SGarrett D'Amore 	(void) ddi_dma_sync(pvs->state_buf.dmah, 0, 0, DDI_DMA_SYNC_FORKERNEL);
262c0586b87SGarrett D'Amore 
263c0586b87SGarrett D'Amore 	while (sdesc->cmpConsIdx != sdesc->cmpProdIdx) {
264c0586b87SGarrett D'Amore 		struct PVSCSIRingCmpDesc *cdesc;
265c0586b87SGarrett D'Amore 
266c0586b87SGarrett D'Amore 		(void) ddi_dma_sync(pvs->cmp_ring_buf.dmah, 0, 0,
267c0586b87SGarrett D'Amore 		    DDI_DMA_SYNC_FORKERNEL);
268c0586b87SGarrett D'Amore 
269c0586b87SGarrett D'Amore 		cdesc = CMP_RING(pvs) + (sdesc->cmpConsIdx & MASK(cmp_ne));
270c0586b87SGarrett D'Amore 
271c0586b87SGarrett D'Amore 		if ((cmd = id32_lookup((uint32_t)cdesc->context)) != NULL) {
272c0586b87SGarrett D'Amore 			cmd->next_cmd = NULL;
273c0586b87SGarrett D'Amore 
274c0586b87SGarrett D'Amore 			/* Save command status for further processing */
275c0586b87SGarrett D'Amore 			cmd->host_status = cdesc->hostStatus;
276c0586b87SGarrett D'Amore 			cmd->scsi_status = cdesc->scsiStatus;
277c0586b87SGarrett D'Amore 			cmd->transferred = cdesc->dataLen;
278c0586b87SGarrett D'Amore 
279c0586b87SGarrett D'Amore 			*pnext_cmd = cmd;
280c0586b87SGarrett D'Amore 			pnext_cmd = &cmd->next_cmd;
281c0586b87SGarrett D'Amore 
282c0586b87SGarrett D'Amore 			list_remove(&pvs->cmd_queue, cmd);
283c0586b87SGarrett D'Amore 		}
284c0586b87SGarrett D'Amore 
285c0586b87SGarrett D'Amore 		sdesc->cmpConsIdx++;
286c0586b87SGarrett D'Amore 	}
287c0586b87SGarrett D'Amore 	(void) ddi_dma_sync(pvs->state_buf.dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
288c0586b87SGarrett D'Amore 
289c0586b87SGarrett D'Amore 	return (head);
290c0586b87SGarrett D'Amore }
291c0586b87SGarrett D'Amore 
292c0586b87SGarrett D'Amore static pvscsi_msg_t *
pvscsi_process_msg_ring(pvscsi_softc_t * pvs)293c0586b87SGarrett D'Amore pvscsi_process_msg_ring(pvscsi_softc_t *pvs)
294c0586b87SGarrett D'Amore {
295c0586b87SGarrett D'Amore 	pvscsi_msg_t	*msg;
296c0586b87SGarrett D'Amore 	struct PVSCSIRingsState *sdesc = RINGS_STATE(pvs);
297c0586b87SGarrett D'Amore 	struct PVSCSIRingMsgDesc *mdesc;
298c0586b87SGarrett D'Amore 	struct PVSCSIMsgDescDevStatusChanged *desc;
299c0586b87SGarrett D'Amore 	uint32_t	msg_ne = sdesc->msgNumEntriesLog2;
300c0586b87SGarrett D'Amore 
301c0586b87SGarrett D'Amore 	(void) ddi_dma_sync(pvs->state_buf.dmah, 0, 0, DDI_DMA_SYNC_FORKERNEL);
302c0586b87SGarrett D'Amore 
303c0586b87SGarrett D'Amore 	if (sdesc->msgProdIdx == sdesc->msgConsIdx) {
304c0586b87SGarrett D'Amore 		return (NULL);
305c0586b87SGarrett D'Amore 	}
306c0586b87SGarrett D'Amore 
307c0586b87SGarrett D'Amore 	(void) ddi_dma_sync(pvs->msg_ring_buf.dmah, 0, 0,
308c0586b87SGarrett D'Amore 	    DDI_DMA_SYNC_FORKERNEL);
309c0586b87SGarrett D'Amore 
310c0586b87SGarrett D'Amore 	mdesc = MSG_RING(pvs) + (sdesc->msgConsIdx & MASK(msg_ne));
311c0586b87SGarrett D'Amore 
312c0586b87SGarrett D'Amore 	switch (mdesc->type) {
313c0586b87SGarrett D'Amore 	case PVSCSI_MSG_DEV_ADDED:
314c0586b87SGarrett D'Amore 	case PVSCSI_MSG_DEV_REMOVED:
315c0586b87SGarrett D'Amore 		desc = (struct PVSCSIMsgDescDevStatusChanged *)mdesc;
316c0586b87SGarrett D'Amore 		msg = kmem_alloc(sizeof (pvscsi_msg_t), KM_NOSLEEP);
317c0586b87SGarrett D'Amore 		if (msg == NULL)
318c0586b87SGarrett D'Amore 			return (NULL);
319c0586b87SGarrett D'Amore 		msg->pvs = pvs;
320c0586b87SGarrett D'Amore 		msg->type = mdesc->type;
321c0586b87SGarrett D'Amore 		msg->target = desc->target;
322c0586b87SGarrett D'Amore 		msg->lun = desc->lun[1]; /* T10 format */
323c0586b87SGarrett D'Amore 		break;
324c0586b87SGarrett D'Amore 	default:
325c0586b87SGarrett D'Amore 		dev_err(pvs->dip, CE_WARN, "!unknown msg type: %d",
326c0586b87SGarrett D'Amore 		    mdesc->type);
327c0586b87SGarrett D'Amore 		return (NULL);
328c0586b87SGarrett D'Amore 	}
329c0586b87SGarrett D'Amore 
330c0586b87SGarrett D'Amore 	sdesc->msgConsIdx++;
331c0586b87SGarrett D'Amore 	(void) ddi_dma_sync(pvs->state_buf.dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
332c0586b87SGarrett D'Amore 	return (msg);
333c0586b87SGarrett D'Amore }
334c0586b87SGarrett D'Amore 
335c0586b87SGarrett D'Amore static void
pvscsi_handle_msg(void * arg)336c0586b87SGarrett D'Amore pvscsi_handle_msg(void *arg)
337c0586b87SGarrett D'Amore {
338c0586b87SGarrett D'Amore 	pvscsi_msg_t	*msg = arg;
339c0586b87SGarrett D'Amore 	pvscsi_softc_t	*pvs = msg->pvs;
340c0586b87SGarrett D'Amore 	char		addr[8];
341c0586b87SGarrett D'Amore 
342c0586b87SGarrett D'Amore 	(void) snprintf(addr, sizeof (addr), "%x", msg->target);
343c0586b87SGarrett D'Amore 
344c0586b87SGarrett D'Amore 	if (msg->lun == 0) {
345c0586b87SGarrett D'Amore 		switch (msg->type) {
346c0586b87SGarrett D'Amore 		case PVSCSI_MSG_DEV_ADDED:
347c0586b87SGarrett D'Amore 			(void) scsi_hba_tgtmap_tgt_add(pvs->tgtmap,
348c0586b87SGarrett D'Amore 			    SCSI_TGT_SCSI_DEVICE, addr, NULL);
349c0586b87SGarrett D'Amore 			break;
350c0586b87SGarrett D'Amore 		case PVSCSI_MSG_DEV_REMOVED:
351c0586b87SGarrett D'Amore 			(void) scsi_hba_tgtmap_tgt_remove(pvs->tgtmap,
352c0586b87SGarrett D'Amore 			    SCSI_TGT_SCSI_DEVICE, addr);
353c0586b87SGarrett D'Amore 			break;
354c0586b87SGarrett D'Amore 		}
355c0586b87SGarrett D'Amore 	} else {
356c0586b87SGarrett D'Amore 		scsi_hba_tgtmap_scan_luns(pvs->tgtmap, addr);
357c0586b87SGarrett D'Amore 	}
358c0586b87SGarrett D'Amore 	kmem_free(msg, sizeof (pvscsi_msg_t));
359c0586b87SGarrett D'Amore }
360c0586b87SGarrett D'Amore 
361c0586b87SGarrett D'Amore static void
pvscsi_abort_cmd(pvscsi_softc_t * pvs,pvscsi_cmd_t * cmd)362c0586b87SGarrett D'Amore pvscsi_abort_cmd(pvscsi_softc_t *pvs, pvscsi_cmd_t *cmd)
363c0586b87SGarrett D'Amore {
364c0586b87SGarrett D'Amore 	struct PVSCSICmdDescAbortCmd	acmd;
365c0586b87SGarrett D'Amore 
366c0586b87SGarrett D'Amore 	bzero(&acmd, sizeof (acmd));
367c0586b87SGarrett D'Amore 	acmd.target = cmd->target;
368c0586b87SGarrett D'Amore 	acmd.context = cmd->ctx;
369c0586b87SGarrett D'Amore 	pvscsi_write_cmd_desc(pvs, PVSCSI_CMD_ABORT_CMD, &acmd, sizeof (acmd));
370c0586b87SGarrett D'Amore }
371c0586b87SGarrett D'Amore 
372c0586b87SGarrett D'Amore static void
pvscsi_map_buffers(pvscsi_cmd_t * cmd,struct PVSCSIRingReqDesc * rdesc)373c0586b87SGarrett D'Amore pvscsi_map_buffers(pvscsi_cmd_t *cmd, struct PVSCSIRingReqDesc *rdesc)
374c0586b87SGarrett D'Amore {
375c0586b87SGarrett D'Amore 	struct scsi_pkt *pkt = cmd->pkt;
376c0586b87SGarrett D'Amore 
377c0586b87SGarrett D'Amore 	rdesc->dataLen = 0;
378c0586b87SGarrett D'Amore 	rdesc->dataAddr = 0;
379c0586b87SGarrett D'Amore 	if (pkt == NULL || pkt->pkt_numcookies == 0) {
380c0586b87SGarrett D'Amore 		return;
381c0586b87SGarrett D'Amore 	}
382c0586b87SGarrett D'Amore 
383c0586b87SGarrett D'Amore 	pkt->pkt_resid = 0;
384c0586b87SGarrett D'Amore 
385c0586b87SGarrett D'Amore 	if (pkt->pkt_numcookies > 1) {
386c0586b87SGarrett D'Amore 		size_t	len = 0;
387c0586b87SGarrett D'Amore 		struct PVSCSISGElement *sgl = cmd->sgl;
388c0586b87SGarrett D'Amore 
389c0586b87SGarrett D'Amore 		for (uint_t i = 0; i < pkt->pkt_numcookies; i++) {
390c0586b87SGarrett D'Amore 			sgl[i].addr = pkt->pkt_cookies[i].dmac_laddress;
391c0586b87SGarrett D'Amore 			sgl[i].length = pkt->pkt_cookies[i].dmac_size;
392c0586b87SGarrett D'Amore 			sgl[i].flags = 0;
393c0586b87SGarrett D'Amore 			len += pkt->pkt_cookies[i].dmac_size;
394c0586b87SGarrett D'Amore 		}
395c0586b87SGarrett D'Amore 		rdesc->flags |= PVSCSI_FLAG_CMD_WITH_SG_LIST;
396c0586b87SGarrett D'Amore 		rdesc->dataAddr = cmd->sgl_pa;
397c0586b87SGarrett D'Amore 		rdesc->dataLen = len;
398c0586b87SGarrett D'Amore 		(void) ddi_dma_sync(cmd->sgl_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
399c0586b87SGarrett D'Amore 	} else {
400c0586b87SGarrett D'Amore 		rdesc->flags = 0;
401c0586b87SGarrett D'Amore 		rdesc->dataAddr = pkt->pkt_cookies[0].dmac_laddress;
402c0586b87SGarrett D'Amore 		rdesc->dataLen = pkt->pkt_cookies[0].dmac_size;
403c0586b87SGarrett D'Amore 	}
404c0586b87SGarrett D'Amore 	pkt->pkt_resid = rdesc->dataLen;
405c0586b87SGarrett D'Amore }
406c0586b87SGarrett D'Amore 
407c0586b87SGarrett D'Amore static void
pvscsi_comp_cmd(pvscsi_cmd_t * cmd)408c0586b87SGarrett D'Amore pvscsi_comp_cmd(pvscsi_cmd_t *cmd)
409c0586b87SGarrett D'Amore {
410c0586b87SGarrett D'Amore 	struct scsi_pkt	*pkt = cmd->pkt;
411c0586b87SGarrett D'Amore 	uint8_t		status = cmd->scsi_status;
412c0586b87SGarrett D'Amore 
413c0586b87SGarrett D'Amore 	pkt->pkt_state |= (STATE_GOT_BUS | STATE_GOT_TARGET | STATE_SENT_CMD |
414c0586b87SGarrett D'Amore 	    STATE_GOT_STATUS);
415c0586b87SGarrett D'Amore 	if (pkt->pkt_numcookies > 0) {
416c0586b87SGarrett D'Amore 		pkt->pkt_state |= STATE_XFERRED_DATA;
417c0586b87SGarrett D'Amore 	}
418c0586b87SGarrett D'Amore 	pkt->pkt_reason = CMD_CMPLT;
419c0586b87SGarrett D'Amore 	pkt->pkt_resid -= cmd->transferred;
420c0586b87SGarrett D'Amore 	*(pkt->pkt_scbp) = status;
421c0586b87SGarrett D'Amore 
422c0586b87SGarrett D'Amore 	if (status == STATUS_CHECK) {
423c0586b87SGarrett D'Amore 		/*
424c0586b87SGarrett D'Amore 		 * Our virtual HBA *always* does ARQ, and it never
425c0586b87SGarrett D'Amore 		 * is more than 20 bytes, so no need to try to handle
426c0586b87SGarrett D'Amore 		 * extended versions of it.
427c0586b87SGarrett D'Amore 		 */
428c0586b87SGarrett D'Amore 		struct scsi_arq_status *ars = (void *)(pkt->pkt_scbp);
429c0586b87SGarrett D'Amore 		int		len = min(pkt->pkt_scblen, SENSE_LENGTH);
430c0586b87SGarrett D'Amore 
431c0586b87SGarrett D'Amore 		pkt->pkt_state |= STATE_ARQ_DONE;
432c0586b87SGarrett D'Amore 		ars->sts_rqpkt_resid = 0;
433c0586b87SGarrett D'Amore 		bcopy(cmd->arq_sense, &ars->sts_sensedata, len);
434c0586b87SGarrett D'Amore 		ars->sts_rqpkt_reason = CMD_CMPLT;
435c0586b87SGarrett D'Amore 		*(uint8_t *)&ars->sts_rqpkt_status = STATUS_GOOD;
436c0586b87SGarrett D'Amore 		ars->sts_rqpkt_state = STATE_GOT_BUS |
437c0586b87SGarrett D'Amore 		    STATE_GOT_TARGET | STATE_SENT_CMD |
438c0586b87SGarrett D'Amore 		    STATE_XFERRED_DATA | STATE_GOT_STATUS;
439c0586b87SGarrett D'Amore 	}
440c0586b87SGarrett D'Amore }
441c0586b87SGarrett D'Amore 
442c0586b87SGarrett D'Amore static void
pvscsi_set_status(pvscsi_softc_t * pvs,pvscsi_cmd_t * cmd)443c0586b87SGarrett D'Amore pvscsi_set_status(pvscsi_softc_t *pvs, pvscsi_cmd_t *cmd)
444c0586b87SGarrett D'Amore {
445c0586b87SGarrett D'Amore 	struct scsi_pkt	*pkt = cmd->pkt;
446c0586b87SGarrett D'Amore 	uint32_t	host_status = cmd->host_status;
447c0586b87SGarrett D'Amore 
448c0586b87SGarrett D'Amore 	switch (host_status) {
449c0586b87SGarrett D'Amore 	case BTSTAT_SUCCESS:
450c0586b87SGarrett D'Amore 	case BTSTAT_LINKED_COMMAND_COMPLETED:
451c0586b87SGarrett D'Amore 	case BTSTAT_LINKED_COMMAND_COMPLETED_WITH_FLAG:
452c0586b87SGarrett D'Amore 		pvscsi_comp_cmd(cmd);
453c0586b87SGarrett D'Amore 		break;
454c0586b87SGarrett D'Amore 	case BTSTAT_DATARUN:
455c0586b87SGarrett D'Amore 		pkt->pkt_reason = CMD_DATA_OVR;
456c0586b87SGarrett D'Amore 		pkt->pkt_state |= (STATE_GOT_BUS | STATE_GOT_TARGET |
457c0586b87SGarrett D'Amore 		    STATE_SENT_CMD | STATE_GOT_STATUS |
458c0586b87SGarrett D'Amore 		    STATE_XFERRED_DATA);
459c0586b87SGarrett D'Amore 		pkt->pkt_resid -= cmd->transferred;
460c0586b87SGarrett D'Amore 		break;
461c0586b87SGarrett D'Amore 	case BTSTAT_SELTIMEO:
462c0586b87SGarrett D'Amore 		pkt->pkt_reason = CMD_DEV_GONE;
463c0586b87SGarrett D'Amore 		pkt->pkt_state |= STATE_GOT_BUS;
464c0586b87SGarrett D'Amore 		break;
465c0586b87SGarrett D'Amore 	case BTSTAT_TAGREJECT:
466c0586b87SGarrett D'Amore 		pkt->pkt_reason = CMD_TAG_REJECT;
467c0586b87SGarrett D'Amore 		pkt->pkt_state |= (STATE_GOT_BUS | STATE_GOT_TARGET |
468c0586b87SGarrett D'Amore 		    STATE_SENT_CMD | STATE_GOT_STATUS);
469c0586b87SGarrett D'Amore 		break;
470c0586b87SGarrett D'Amore 	case BTSTAT_BADMSG:
471c0586b87SGarrett D'Amore 		pkt->pkt_reason = CMD_BADMSG;
472c0586b87SGarrett D'Amore 		pkt->pkt_state |= (STATE_GOT_BUS | STATE_GOT_TARGET |
473c0586b87SGarrett D'Amore 		    STATE_SENT_CMD | STATE_GOT_STATUS);
474c0586b87SGarrett D'Amore 		break;
475c0586b87SGarrett D'Amore 	case BTSTAT_SENTRST:
476c0586b87SGarrett D'Amore 	case BTSTAT_RECVRST:
477c0586b87SGarrett D'Amore 		pkt->pkt_reason = CMD_RESET;
478c0586b87SGarrett D'Amore 		pkt->pkt_state |= (STATE_GOT_BUS | STATE_GOT_TARGET |
479c0586b87SGarrett D'Amore 		    STATE_SENT_CMD | STATE_GOT_STATUS);
480c0586b87SGarrett D'Amore 		pkt->pkt_statistics |= STAT_DEV_RESET;
481c0586b87SGarrett D'Amore 		pkt->pkt_resid -= cmd->transferred;
482c0586b87SGarrett D'Amore 		break;
483c0586b87SGarrett D'Amore 	case BTSTAT_BUSRESET:
484c0586b87SGarrett D'Amore 		pkt->pkt_reason = CMD_RESET;
485c0586b87SGarrett D'Amore 		pkt->pkt_state |= (STATE_GOT_BUS | STATE_GOT_TARGET |
486c0586b87SGarrett D'Amore 		    STATE_SENT_CMD | STATE_GOT_STATUS);
487c0586b87SGarrett D'Amore 		pkt->pkt_statistics |= STAT_BUS_RESET;
488c0586b87SGarrett D'Amore 		pkt->pkt_resid -= cmd->transferred;
489c0586b87SGarrett D'Amore 		break;
490c0586b87SGarrett D'Amore 	case BTSTAT_ABORTQUEUE:
491c0586b87SGarrett D'Amore 		if (cmd->expired) {
492c0586b87SGarrett D'Amore 			pkt->pkt_reason = CMD_TIMEOUT;
493c0586b87SGarrett D'Amore 			pkt->pkt_statistics |= STAT_TIMEOUT;
494c0586b87SGarrett D'Amore 		} else {
495c0586b87SGarrett D'Amore 			pkt->pkt_reason = CMD_ABORTED;
496c0586b87SGarrett D'Amore 			pkt->pkt_statistics |= STAT_ABORTED;
497c0586b87SGarrett D'Amore 		}
498c0586b87SGarrett D'Amore 		pkt->pkt_state |= (STATE_GOT_BUS | STATE_GOT_TARGET |
499c0586b87SGarrett D'Amore 		    STATE_SENT_CMD | STATE_GOT_STATUS);
500c0586b87SGarrett D'Amore 		pkt->pkt_resid -= cmd->transferred;
501c0586b87SGarrett D'Amore 		break;
502c0586b87SGarrett D'Amore 	case BTSTAT_HAHARDWARE:
503c0586b87SGarrett D'Amore 	case BTSTAT_INVPHASE:
504c0586b87SGarrett D'Amore 	case BTSTAT_HATIMEOUT:
505c0586b87SGarrett D'Amore 	case BTSTAT_NORESPONSE:
506c0586b87SGarrett D'Amore 	case BTSTAT_DISCONNECT:
507c0586b87SGarrett D'Amore 	case BTSTAT_HASOFTWARE:
508c0586b87SGarrett D'Amore 	case BTSTAT_BUSFREE:
509c0586b87SGarrett D'Amore 	case BTSTAT_SENSFAILED:
510c0586b87SGarrett D'Amore 	case BTSTAT_DATA_UNDERRUN:
511c0586b87SGarrett D'Amore 		pkt->pkt_reason = CMD_TRAN_ERR;
512c0586b87SGarrett D'Amore 		pkt->pkt_state |= (STATE_GOT_BUS | STATE_GOT_TARGET |
513c0586b87SGarrett D'Amore 		    STATE_SENT_CMD | STATE_GOT_STATUS);
514c0586b87SGarrett D'Amore 		pkt->pkt_resid -= cmd->transferred;
515c0586b87SGarrett D'Amore 		break;
516c0586b87SGarrett D'Amore 	default:
517c0586b87SGarrett D'Amore 		dev_err(pvs->dip, CE_WARN,
518c0586b87SGarrett D'Amore 		    "!unknown host status code: %d", host_status);
519c0586b87SGarrett D'Amore 		pkt->pkt_reason = CMD_TRAN_ERR;
520c0586b87SGarrett D'Amore 		pkt->pkt_state |= (STATE_GOT_BUS | STATE_GOT_TARGET |
521c0586b87SGarrett D'Amore 		    STATE_SENT_CMD | STATE_GOT_STATUS);
522c0586b87SGarrett D'Amore 		break;
523c0586b87SGarrett D'Amore 	}
524c0586b87SGarrett D'Amore }
525c0586b87SGarrett D'Amore 
526c0586b87SGarrett D'Amore /*
527c0586b87SGarrett D'Amore  * pvscsi_complete_cmds processes a linked list of
528c0586b87SGarrett D'Amore  * commands that have been completed.  This is done
529c0586b87SGarrett D'Amore  * without acquiring any locks.
530c0586b87SGarrett D'Amore  */
531c0586b87SGarrett D'Amore static void
pvscsi_complete_cmds(pvscsi_softc_t * pvs,pvscsi_cmd_t * cmd)532c0586b87SGarrett D'Amore pvscsi_complete_cmds(pvscsi_softc_t *pvs, pvscsi_cmd_t *cmd)
533c0586b87SGarrett D'Amore {
534c0586b87SGarrett D'Amore 	struct scsi_pkt	*pkt;
535c0586b87SGarrett D'Amore 
536c0586b87SGarrett D'Amore 	while (cmd != NULL) {
537c0586b87SGarrett D'Amore 		pvscsi_cmd_t	*next = cmd->next_cmd;
538c0586b87SGarrett D'Amore 
539c0586b87SGarrett D'Amore 		cmd->next_cmd = NULL;
540c0586b87SGarrett D'Amore 
541c0586b87SGarrett D'Amore 		if (((pkt = cmd->pkt) == NULL) || (cmd->poll)) {
542c0586b87SGarrett D'Amore 			atomic_or_8(&cmd->done, 1);
543c0586b87SGarrett D'Amore 		} else {
544c0586b87SGarrett D'Amore 			pvscsi_set_status(pvs, cmd);
545c0586b87SGarrett D'Amore 			scsi_hba_pkt_comp(pkt);
546c0586b87SGarrett D'Amore 		}
547c0586b87SGarrett D'Amore 
548c0586b87SGarrett D'Amore 		cmd = next;
549c0586b87SGarrett D'Amore 	}
550c0586b87SGarrett D'Amore }
551c0586b87SGarrett D'Amore 
552c0586b87SGarrett D'Amore static void
pvscsi_dev_reset(pvscsi_softc_t * pvs,int target,int lun)553c0586b87SGarrett D'Amore pvscsi_dev_reset(pvscsi_softc_t *pvs, int target, int lun)
554c0586b87SGarrett D'Amore {
555c0586b87SGarrett D'Amore 	struct PVSCSICmdDescResetDevice cmd = { 0 };
556c0586b87SGarrett D'Amore 
557c0586b87SGarrett D'Amore 	cmd.target = target;
558c0586b87SGarrett D'Amore 	cmd.lun[1] = lun & 0xff;
559c0586b87SGarrett D'Amore 	pvscsi_write_cmd_desc(pvs, PVSCSI_CMD_RESET_DEVICE, &cmd, sizeof (cmd));
560c0586b87SGarrett D'Amore }
561c0586b87SGarrett D'Amore 
562c0586b87SGarrett D'Amore static boolean_t
pvscsi_poll_cmd_until(pvscsi_softc_t * pvs,pvscsi_cmd_t * cmd,clock_t usec)563c0586b87SGarrett D'Amore pvscsi_poll_cmd_until(pvscsi_softc_t *pvs, pvscsi_cmd_t *cmd, clock_t usec)
564c0586b87SGarrett D'Amore {
565c0586b87SGarrett D'Amore 	while (usec > 0) {
566c0586b87SGarrett D'Amore 		pvscsi_cmd_t	*done;
567c0586b87SGarrett D'Amore 		if (cmd->done) {
568c0586b87SGarrett D'Amore 			return (B_TRUE);
569c0586b87SGarrett D'Amore 		}
570c0586b87SGarrett D'Amore 		mutex_enter(&pvs->lock);
571c0586b87SGarrett D'Amore 		done = pvscsi_process_comp_ring(pvs);
572c0586b87SGarrett D'Amore 		mutex_exit(&pvs->lock);
573c0586b87SGarrett D'Amore 
574c0586b87SGarrett D'Amore 		pvscsi_complete_cmds(pvs, done);
575c0586b87SGarrett D'Amore 		drv_usecwait(10);
576c0586b87SGarrett D'Amore 		usec -= 10;
577c0586b87SGarrett D'Amore 	}
578c0586b87SGarrett D'Amore 
579c0586b87SGarrett D'Amore 	return (B_FALSE);
580c0586b87SGarrett D'Amore }
581c0586b87SGarrett D'Amore 
582c0586b87SGarrett D'Amore static void
pvscsi_poll_cmd(pvscsi_softc_t * pvs,pvscsi_cmd_t * cmd)583c0586b87SGarrett D'Amore pvscsi_poll_cmd(pvscsi_softc_t *pvs, pvscsi_cmd_t *cmd)
584c0586b87SGarrett D'Amore {
585c0586b87SGarrett D'Amore 	if (pvscsi_poll_cmd_until(pvs, cmd, drv_hztousec(cmd->timeout))) {
586c0586b87SGarrett D'Amore 		return;
587c0586b87SGarrett D'Amore 	}
588c0586b87SGarrett D'Amore 
589c0586b87SGarrett D'Amore 	/* now we try an abort first */
590c0586b87SGarrett D'Amore 	pvscsi_abort_cmd(pvs, cmd);
591c0586b87SGarrett D'Amore 	if (pvscsi_poll_cmd_until(pvs, cmd, 2)) {
592c0586b87SGarrett D'Amore 		return;
593c0586b87SGarrett D'Amore 	}
594c0586b87SGarrett D'Amore 	/* well that failed... try reset */
595c0586b87SGarrett D'Amore 	pvscsi_dev_reset(pvs, cmd->target, cmd->lun);
596c0586b87SGarrett D'Amore 	if (pvscsi_poll_cmd_until(pvs, cmd, 2)) {
597c0586b87SGarrett D'Amore 		return;
598c0586b87SGarrett D'Amore 	}
599c0586b87SGarrett D'Amore 	/* still trying... reset the bus */
600c0586b87SGarrett D'Amore 	pvscsi_reset_bus(pvs);
601c0586b87SGarrett D'Amore 	if (pvscsi_poll_cmd_until(pvs, cmd, 2)) {
602c0586b87SGarrett D'Amore 		return;
603c0586b87SGarrett D'Amore 	}
604c0586b87SGarrett D'Amore 	/* full up adapter reset -- be brutal */
605c0586b87SGarrett D'Amore 	pvscsi_restart_hba(pvs);
606c0586b87SGarrett D'Amore }
607c0586b87SGarrett D'Amore 
608c0586b87SGarrett D'Amore static void
pvscsi_abort_all(pvscsi_softc_t * pvs,pvscsi_device_t * pd)609c0586b87SGarrett D'Amore pvscsi_abort_all(pvscsi_softc_t *pvs, pvscsi_device_t *pd)
610c0586b87SGarrett D'Amore {
611c0586b87SGarrett D'Amore 	pvscsi_cmd_t	*cmd;
612c0586b87SGarrett D'Amore 
613c0586b87SGarrett D'Amore 	mutex_enter(&pvs->lock);
614c0586b87SGarrett D'Amore 	list_t *l = &pvs->cmd_queue;
615c0586b87SGarrett D'Amore 	for (cmd = list_head(l); cmd != NULL; cmd = list_next(l, cmd)) {
616c0586b87SGarrett D'Amore 		if ((pd->target == cmd->target) && (pd->lun == cmd->lun)) {
617c0586b87SGarrett D'Amore 			pvscsi_abort_cmd(pvs, cmd);
618c0586b87SGarrett D'Amore 		}
619c0586b87SGarrett D'Amore 	}
620c0586b87SGarrett D'Amore 	mutex_exit(&pvs->lock);
621c0586b87SGarrett D'Amore }
622c0586b87SGarrett D'Amore 
623c0586b87SGarrett D'Amore static int
pvscsi_transport_command(pvscsi_softc_t * pvs,pvscsi_cmd_t * cmd)624c0586b87SGarrett D'Amore pvscsi_transport_command(pvscsi_softc_t *pvs, pvscsi_cmd_t *cmd)
625c0586b87SGarrett D'Amore {
626c0586b87SGarrett D'Amore 	struct PVSCSIRingReqDesc	*rdesc;
627c0586b87SGarrett D'Amore 	struct PVSCSIRingsState		*sdesc = RINGS_STATE(pvs);
628c0586b87SGarrett D'Amore 	uint32_t			req_ne = sdesc->reqNumEntriesLog2;
629c0586b87SGarrett D'Amore 
630c0586b87SGarrett D'Amore 	cmd->done = 0;
631c0586b87SGarrett D'Amore 	cmd->expired = 0;
632c0586b87SGarrett D'Amore 
633c0586b87SGarrett D'Amore 	mutex_enter(&pvs->lock);
634c0586b87SGarrett D'Amore 
635c0586b87SGarrett D'Amore 	if ((sdesc->reqProdIdx - sdesc->cmpConsIdx) >= (1 << req_ne)) {
636c0586b87SGarrett D'Amore 		mutex_exit(&pvs->lock);
637c0586b87SGarrett D'Amore 		return (TRAN_BUSY);
638c0586b87SGarrett D'Amore 	}
639c0586b87SGarrett D'Amore 
640c0586b87SGarrett D'Amore 	rdesc = REQ_RING(pvs) + (sdesc->reqProdIdx & MASK(req_ne));
641c0586b87SGarrett D'Amore 
642c0586b87SGarrett D'Amore 	rdesc->bus = 0;
643c0586b87SGarrett D'Amore 	rdesc->target = cmd->target;
644c0586b87SGarrett D'Amore 	bzero(rdesc->lun, sizeof (rdesc->lun));
645c0586b87SGarrett D'Amore 	/* Matches other implementations; can pvscsi support luns > 255? */
646c0586b87SGarrett D'Amore 	rdesc->lun[1] = cmd->lun & 0xff;
647c0586b87SGarrett D'Amore 
648c0586b87SGarrett D'Amore 	bzero(cmd->arq_sense, sizeof (cmd->arq_sense));
649c0586b87SGarrett D'Amore 	rdesc->context = cmd->ctx;
650c0586b87SGarrett D'Amore 	rdesc->senseLen = sizeof (cmd->arq_sense);
651c0586b87SGarrett D'Amore 	rdesc->senseAddr = cmd->arq_pa;
652c0586b87SGarrett D'Amore 	rdesc->tag = cmd->tag;
653c0586b87SGarrett D'Amore 	rdesc->vcpuHint = CPU->cpu_id;
654c0586b87SGarrett D'Amore 	rdesc->cdbLen = cmd->cdblen;
655c0586b87SGarrett D'Amore 	rdesc->flags = cmd->dma_dir;
656c0586b87SGarrett D'Amore 	bcopy(cmd->cdb, rdesc->cdb, cmd->cdblen);
657c0586b87SGarrett D'Amore 	pvscsi_map_buffers(cmd, rdesc);
658c0586b87SGarrett D'Amore 
659c0586b87SGarrett D'Amore 	(void) ddi_dma_sync(pvs->req_ring_buf.dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
660c0586b87SGarrett D'Amore 
661c0586b87SGarrett D'Amore 	sdesc->reqProdIdx++;
662c0586b87SGarrett D'Amore 	(void) ddi_dma_sync(pvs->state_buf.dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
663c0586b87SGarrett D'Amore 
664c0586b87SGarrett D'Amore 	pvscsi_add_to_queue(pvs, cmd);
665c0586b87SGarrett D'Amore 
666c0586b87SGarrett D'Amore 	switch (cmd->cdb[0]) {
667c0586b87SGarrett D'Amore 	case SCMD_READ:
668c0586b87SGarrett D'Amore 	case SCMD_WRITE:
669c0586b87SGarrett D'Amore 	case SCMD_READ_G1:
670c0586b87SGarrett D'Amore 	case SCMD_WRITE_G1:
671c0586b87SGarrett D'Amore 	case SCMD_READ_G4:
672c0586b87SGarrett D'Amore 	case SCMD_WRITE_G4:
673c0586b87SGarrett D'Amore 	case SCMD_READ_G5:
674c0586b87SGarrett D'Amore 	case SCMD_WRITE_G5:
675c0586b87SGarrett D'Amore 		pvscsi_submit_rw_io(pvs);
676c0586b87SGarrett D'Amore 		break;
677c0586b87SGarrett D'Amore 	default:
678c0586b87SGarrett D'Amore 		pvscsi_submit_nonrw_io(pvs);
679c0586b87SGarrett D'Amore 		break;
680c0586b87SGarrett D'Amore 	}
681c0586b87SGarrett D'Amore 
682c0586b87SGarrett D'Amore 	if (pvs->timeout == 0) {
683c0586b87SGarrett D'Amore 		/* drivers above should supply, but give a default */
684c0586b87SGarrett D'Amore 		pvs->timeout = timeout(pvscsi_timeout, pvs, pvscsi_hz * 8);
685c0586b87SGarrett D'Amore 	}
686c0586b87SGarrett D'Amore 	mutex_exit(&pvs->lock);
687c0586b87SGarrett D'Amore 
688c0586b87SGarrett D'Amore 	return (TRAN_ACCEPT);
689c0586b87SGarrett D'Amore }
690c0586b87SGarrett D'Amore 
691c0586b87SGarrett D'Amore static int
pvscsi_setup_dma_buffer(pvscsi_softc_t * pvs,size_t length,pvscsi_dma_buf_t * buf)692c0586b87SGarrett D'Amore pvscsi_setup_dma_buffer(pvscsi_softc_t *pvs, size_t length,
693c0586b87SGarrett D'Amore     pvscsi_dma_buf_t *buf)
694c0586b87SGarrett D'Amore {
695c0586b87SGarrett D'Amore 	if ((ddi_dma_alloc_handle(pvs->dip, &pvscsi_dma_attr,
696c0586b87SGarrett D'Amore 	    DDI_DMA_SLEEP, NULL, &buf->dmah)) != DDI_SUCCESS) {
697c0586b87SGarrett D'Amore 		dev_err(pvs->dip, CE_WARN, "!failed to alloc DMA handle");
698c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
699c0586b87SGarrett D'Amore 	}
700c0586b87SGarrett D'Amore 
701c0586b87SGarrett D'Amore 	if ((ddi_dma_mem_alloc(buf->dmah, length, &pvscsi_dma_attrs,
702c0586b87SGarrett D'Amore 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &buf->addr,
703c0586b87SGarrett D'Amore 	    &length, &buf->acch)) != DDI_SUCCESS) {
704c0586b87SGarrett D'Amore 		dev_err(pvs->dip, CE_WARN, "!failed to alloc DMA memory");
705c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
706c0586b87SGarrett D'Amore 	}
707c0586b87SGarrett D'Amore 
708c0586b87SGarrett D'Amore 	if ((ddi_dma_addr_bind_handle(buf->dmah, NULL, buf->addr,
709c0586b87SGarrett D'Amore 	    length, DDI_DMA_CONSISTENT | DDI_DMA_RDWR, DDI_DMA_SLEEP,
710c0586b87SGarrett D'Amore 	    NULL, NULL, NULL)) != DDI_SUCCESS) {
711c0586b87SGarrett D'Amore 		dev_err(pvs->dip, CE_WARN, "!failed to bind DMA buffer");
712c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
713c0586b87SGarrett D'Amore 	}
714c0586b87SGarrett D'Amore 
715c0586b87SGarrett D'Amore 	buf->pa = ddi_dma_cookie_one(buf->dmah)->dmac_laddress;
716c0586b87SGarrett D'Amore 
717c0586b87SGarrett D'Amore 	return (DDI_SUCCESS);
718c0586b87SGarrett D'Amore }
719c0586b87SGarrett D'Amore 
720c0586b87SGarrett D'Amore static void
pvscsi_free_dma_buffer(pvscsi_dma_buf_t * buf)721c0586b87SGarrett D'Amore pvscsi_free_dma_buffer(pvscsi_dma_buf_t *buf)
722c0586b87SGarrett D'Amore {
723c0586b87SGarrett D'Amore 	if (buf->pa != 0) {
724c0586b87SGarrett D'Amore 		(void) ddi_dma_unbind_handle(buf->dmah);
725c0586b87SGarrett D'Amore 	}
726c0586b87SGarrett D'Amore 	if (buf->acch != NULL) {
727c0586b87SGarrett D'Amore 		ddi_dma_mem_free(&buf->acch);
728c0586b87SGarrett D'Amore 	}
729c0586b87SGarrett D'Amore 	if (buf->dmah != NULL) {
730c0586b87SGarrett D'Amore 		ddi_dma_free_handle(&buf->dmah);
731c0586b87SGarrett D'Amore 	}
732c0586b87SGarrett D'Amore }
733c0586b87SGarrett D'Amore 
734c0586b87SGarrett D'Amore static int
pvscsi_allocate_rings(pvscsi_softc_t * pvs)735c0586b87SGarrett D'Amore pvscsi_allocate_rings(pvscsi_softc_t *pvs)
736c0586b87SGarrett D'Amore {
737c0586b87SGarrett D'Amore 	/* allocate DMA buffer for rings state */
738c0586b87SGarrett D'Amore 	if (pvscsi_setup_dma_buffer(pvs, PAGE_SIZE, &pvs->state_buf) !=
739c0586b87SGarrett D'Amore 	    DDI_SUCCESS) {
740c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
741c0586b87SGarrett D'Amore 	}
742c0586b87SGarrett D'Amore 
743c0586b87SGarrett D'Amore 	/* allocate DMA buffer for request ring */
744c0586b87SGarrett D'Amore 	pvs->req_pages = MIN(pvscsi_ring_pages, PVSCSI_MAX_NUM_PAGES_REQ_RING);
745c0586b87SGarrett D'Amore 	pvs->req_depth = pvs->req_pages * PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE;
746c0586b87SGarrett D'Amore 	if (pvscsi_setup_dma_buffer(pvs, pvs->req_pages * PAGE_SIZE,
747c0586b87SGarrett D'Amore 	    &pvs->req_ring_buf) != DDI_SUCCESS) {
748c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
749c0586b87SGarrett D'Amore 	}
750c0586b87SGarrett D'Amore 
751c0586b87SGarrett D'Amore 	/* allocate completion ring */
752c0586b87SGarrett D'Amore 	pvs->cmp_pages = MIN(pvscsi_ring_pages, PVSCSI_MAX_NUM_PAGES_CMP_RING);
753c0586b87SGarrett D'Amore 	if (pvscsi_setup_dma_buffer(pvs, pvs->cmp_pages * PAGE_SIZE,
754c0586b87SGarrett D'Amore 	    &pvs->cmp_ring_buf) != DDI_SUCCESS) {
755c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
756c0586b87SGarrett D'Amore 	}
757c0586b87SGarrett D'Amore 
758c0586b87SGarrett D'Amore 	/* allocate message ring */
759c0586b87SGarrett D'Amore 	pvs->msg_pages = MIN(pvscsi_msg_ring_pages,
760c0586b87SGarrett D'Amore 	    PVSCSI_MAX_NUM_PAGES_MSG_RING);
761c0586b87SGarrett D'Amore 	if (pvscsi_setup_dma_buffer(pvs, pvs->msg_pages * PAGE_SIZE,
762c0586b87SGarrett D'Amore 	    &pvs->msg_ring_buf) != DDI_SUCCESS) {
763c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
764c0586b87SGarrett D'Amore 	}
765c0586b87SGarrett D'Amore 
766c0586b87SGarrett D'Amore 	return (DDI_SUCCESS);
767c0586b87SGarrett D'Amore }
768c0586b87SGarrett D'Amore 
769c0586b87SGarrett D'Amore static void
pvscsi_free_rings(pvscsi_softc_t * pvs)770c0586b87SGarrett D'Amore pvscsi_free_rings(pvscsi_softc_t *pvs)
771c0586b87SGarrett D'Amore {
772c0586b87SGarrett D'Amore 	pvscsi_free_dma_buffer(&pvs->msg_ring_buf);
773c0586b87SGarrett D'Amore 	pvscsi_free_dma_buffer(&pvs->cmp_ring_buf);
774c0586b87SGarrett D'Amore 	pvscsi_free_dma_buffer(&pvs->req_ring_buf);
775c0586b87SGarrett D'Amore 	pvscsi_free_dma_buffer(&pvs->state_buf);
776c0586b87SGarrett D'Amore }
777c0586b87SGarrett D'Amore 
778c0586b87SGarrett D'Amore static void
pvscsi_setup_rings(pvscsi_softc_t * pvs)779c0586b87SGarrett D'Amore pvscsi_setup_rings(pvscsi_softc_t *pvs)
780c0586b87SGarrett D'Amore {
781c0586b87SGarrett D'Amore 	int		i;
782c0586b87SGarrett D'Amore 	struct PVSCSICmdDescSetupMsgRing cmd_msg = { 0 };
783c0586b87SGarrett D'Amore 	struct PVSCSICmdDescSetupRings cmd = { 0 };
784c0586b87SGarrett D'Amore 	uint64_t	base;
785c0586b87SGarrett D'Amore 
786c0586b87SGarrett D'Amore 	cmd.ringsStatePPN = pvs->state_buf.pa >> PAGE_SHIFT;
787c0586b87SGarrett D'Amore 	cmd.reqRingNumPages = pvs->req_pages;
788c0586b87SGarrett D'Amore 	cmd.cmpRingNumPages = pvs->cmp_pages;
789c0586b87SGarrett D'Amore 
790c0586b87SGarrett D'Amore 	/* Setup request ring */
791c0586b87SGarrett D'Amore 	base = pvs->req_ring_buf.pa;
792c0586b87SGarrett D'Amore 	for (i = 0; i < pvs->req_pages; i++) {
793c0586b87SGarrett D'Amore 		cmd.reqRingPPNs[i] = base >> PAGE_SHIFT;
794c0586b87SGarrett D'Amore 		base += PAGE_SIZE;
795c0586b87SGarrett D'Amore 	}
796c0586b87SGarrett D'Amore 
797c0586b87SGarrett D'Amore 	/* Setup completion ring */
798c0586b87SGarrett D'Amore 	base = pvs->cmp_ring_buf.pa;
799c0586b87SGarrett D'Amore 	for (i = 0; i < pvs->cmp_pages; i++) {
800c0586b87SGarrett D'Amore 		cmd.cmpRingPPNs[i] = base >> PAGE_SHIFT;
801c0586b87SGarrett D'Amore 		base += PAGE_SIZE;
802c0586b87SGarrett D'Amore 	}
803c0586b87SGarrett D'Amore 
804c0586b87SGarrett D'Amore 	bzero(RINGS_STATE(pvs), PAGE_SIZE);
805c0586b87SGarrett D'Amore 	bzero(REQ_RING(pvs), pvs->req_pages * PAGE_SIZE);
806c0586b87SGarrett D'Amore 	bzero(CMP_RING(pvs), pvs->cmp_pages * PAGE_SIZE);
807c0586b87SGarrett D'Amore 
808c0586b87SGarrett D'Amore 	/* Issue SETUP command */
809c0586b87SGarrett D'Amore 	pvscsi_write_cmd_desc(pvs, PVSCSI_CMD_SETUP_RINGS, &cmd, sizeof (cmd));
810c0586b87SGarrett D'Amore 
811c0586b87SGarrett D'Amore 	/* Setup message ring */
812c0586b87SGarrett D'Amore 	cmd_msg.numPages = pvs->msg_pages;
813c0586b87SGarrett D'Amore 	base = pvs->msg_ring_buf.pa;
814c0586b87SGarrett D'Amore 
815c0586b87SGarrett D'Amore 	for (i = 0; i < pvs->msg_pages; i++) {
816c0586b87SGarrett D'Amore 		cmd_msg.ringPPNs[i] = base >> PAGE_SHIFT;
817c0586b87SGarrett D'Amore 		base += PAGE_SIZE;
818c0586b87SGarrett D'Amore 	}
819c0586b87SGarrett D'Amore 	bzero(MSG_RING(pvs), pvs->msg_pages * PAGE_SIZE);
820c0586b87SGarrett D'Amore 
821c0586b87SGarrett D'Amore 	pvscsi_write_cmd_desc(pvs, PVSCSI_CMD_SETUP_MSG_RING, &cmd_msg,
822c0586b87SGarrett D'Amore 	    sizeof (cmd_msg));
823c0586b87SGarrett D'Amore }
824c0586b87SGarrett D'Amore 
825c0586b87SGarrett D'Amore static int
pvscsi_setup_io(pvscsi_softc_t * pvs)826c0586b87SGarrett D'Amore pvscsi_setup_io(pvscsi_softc_t *pvs)
827c0586b87SGarrett D'Amore {
828c0586b87SGarrett D'Amore 	int		offset, rcount, rn, type;
829c0586b87SGarrett D'Amore 	int		ret = DDI_FAILURE;
830c0586b87SGarrett D'Amore 	off_t		regsize;
831c0586b87SGarrett D'Amore 	pci_regspec_t	*regs;
832c0586b87SGarrett D'Amore 	uint_t		regs_length;
833c0586b87SGarrett D'Amore 
834c0586b87SGarrett D'Amore 	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, pvs->dip,
835c0586b87SGarrett D'Amore 	    DDI_PROP_DONTPASS, "reg", (int **)&regs,
836c0586b87SGarrett D'Amore 	    &regs_length) != DDI_PROP_SUCCESS) {
837c0586b87SGarrett D'Amore 		dev_err(pvs->dip, CE_WARN, "!failed to lookup 'reg' property");
838c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
839c0586b87SGarrett D'Amore 	}
840c0586b87SGarrett D'Amore 
841c0586b87SGarrett D'Amore 	rcount = regs_length * sizeof (int) / sizeof (pci_regspec_t);
842c0586b87SGarrett D'Amore 
843c0586b87SGarrett D'Amore 	for (offset = PCI_CONF_BASE0; offset <= PCI_CONF_BASE5; offset += 4) {
844c0586b87SGarrett D'Amore 		for (rn = 0; rn < rcount; ++rn) {
845c0586b87SGarrett D'Amore 			if (PCI_REG_REG_G(regs[rn].pci_phys_hi) == offset) {
846c0586b87SGarrett D'Amore 				type = regs[rn].pci_phys_hi & PCI_ADDR_MASK;
847c0586b87SGarrett D'Amore 				break;
848c0586b87SGarrett D'Amore 			}
849c0586b87SGarrett D'Amore 		}
850c0586b87SGarrett D'Amore 
851c0586b87SGarrett D'Amore 		if (rn >= rcount)
852c0586b87SGarrett D'Amore 			continue;
853c0586b87SGarrett D'Amore 
854c0586b87SGarrett D'Amore 		if (type != PCI_ADDR_IO) {
855c0586b87SGarrett D'Amore 			if (ddi_dev_regsize(pvs->dip, rn,
856c0586b87SGarrett D'Amore 			    &regsize) != DDI_SUCCESS) {
857c0586b87SGarrett D'Amore 				dev_err(pvs->dip, CE_WARN,
858c0586b87SGarrett D'Amore 				    "!failed to get size of reg %d", rn);
859c0586b87SGarrett D'Amore 				goto out;
860c0586b87SGarrett D'Amore 			}
861c0586b87SGarrett D'Amore 			if (regsize == PVSCSI_MEM_SPACE_SIZE) {
862c0586b87SGarrett D'Amore 				if (ddi_regs_map_setup(pvs->dip, rn,
863c0586b87SGarrett D'Amore 				    &pvs->mmio_base, 0, 0,
864c0586b87SGarrett D'Amore 				    &pvscsi_mmio_attr,
865c0586b87SGarrett D'Amore 				    &pvs->mmio_handle) != DDI_SUCCESS) {
866c0586b87SGarrett D'Amore 					dev_err(pvs->dip, CE_WARN,
867c0586b87SGarrett D'Amore 					    "!failed to map MMIO BAR");
868c0586b87SGarrett D'Amore 					goto out;
869c0586b87SGarrett D'Amore 				}
870c0586b87SGarrett D'Amore 				ret = DDI_SUCCESS;
871c0586b87SGarrett D'Amore 				break;
872c0586b87SGarrett D'Amore 			}
873c0586b87SGarrett D'Amore 		}
874c0586b87SGarrett D'Amore 	}
875c0586b87SGarrett D'Amore 
876c0586b87SGarrett D'Amore out:
877c0586b87SGarrett D'Amore 	ddi_prop_free(regs);
878c0586b87SGarrett D'Amore 
879c0586b87SGarrett D'Amore 	return (ret);
880c0586b87SGarrett D'Amore }
881c0586b87SGarrett D'Amore 
882c0586b87SGarrett D'Amore static int
pvscsi_enable_intrs(pvscsi_softc_t * pvs)883c0586b87SGarrett D'Amore pvscsi_enable_intrs(pvscsi_softc_t *pvs)
884c0586b87SGarrett D'Amore {
885c0586b87SGarrett D'Amore 	int	i, rc, intr_caps;
886c0586b87SGarrett D'Amore 
887c0586b87SGarrett D'Amore 	if ((rc = ddi_intr_get_cap(pvs->intr_handles[0], &intr_caps)) !=
888c0586b87SGarrett D'Amore 	    DDI_SUCCESS) {
889c0586b87SGarrett D'Amore 		dev_err(pvs->dip, CE_WARN, "!failed to get interrupt caps");
890c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
891c0586b87SGarrett D'Amore 	}
892c0586b87SGarrett D'Amore 
893c0586b87SGarrett D'Amore 	if ((intr_caps & DDI_INTR_FLAG_BLOCK) != 0) {
894c0586b87SGarrett D'Amore 		if ((rc = ddi_intr_block_enable(pvs->intr_handles,
895c0586b87SGarrett D'Amore 		    pvs->intr_cnt)) != DDI_SUCCESS) {
896c0586b87SGarrett D'Amore 			dev_err(pvs->dip, CE_WARN,
897c0586b87SGarrett D'Amore 			    "!failed to enable interrupt block");
898c0586b87SGarrett D'Amore 		}
899c0586b87SGarrett D'Amore 	} else {
900c0586b87SGarrett D'Amore 		for (i = 0; i < pvs->intr_cnt; i++) {
901c0586b87SGarrett D'Amore 			if ((rc = ddi_intr_enable(pvs->intr_handles[i])) ==
902c0586b87SGarrett D'Amore 			    DDI_SUCCESS)
903c0586b87SGarrett D'Amore 				continue;
904c0586b87SGarrett D'Amore 			dev_err(pvs->dip, CE_WARN,
905c0586b87SGarrett D'Amore 			    "!failed to enable interrupt");
906c0586b87SGarrett D'Amore 			while (--i >= 0)
907c0586b87SGarrett D'Amore 				(void) ddi_intr_disable(pvs->intr_handles[i]);
908c0586b87SGarrett D'Amore 			break;
909c0586b87SGarrett D'Amore 		}
910c0586b87SGarrett D'Amore 	}
911c0586b87SGarrett D'Amore 
912c0586b87SGarrett D'Amore 	return (rc);
913c0586b87SGarrett D'Amore }
914c0586b87SGarrett D'Amore 
915c0586b87SGarrett D'Amore static uint32_t
pvscsi_intr(caddr_t arg1,caddr_t arg2)916c0586b87SGarrett D'Amore pvscsi_intr(caddr_t arg1, caddr_t arg2)
917c0586b87SGarrett D'Amore {
918c0586b87SGarrett D'Amore 	pvscsi_softc_t	*pvs = (pvscsi_softc_t *)arg1;
919c0586b87SGarrett D'Amore 	uint32_t	status;
920c0586b87SGarrett D'Amore 	pvscsi_cmd_t	*cmd;
921c0586b87SGarrett D'Amore 	pvscsi_msg_t	*msg;
922c0586b87SGarrett D'Amore 	uint32_t	rv = DDI_INTR_CLAIMED;
923c0586b87SGarrett D'Amore 	_NOTE(ARGUNUSED(arg2));
924c0586b87SGarrett D'Amore 
925c0586b87SGarrett D'Amore 	mutex_enter(&pvs->lock);
926c0586b87SGarrett D'Amore 	status = pvscsi_read_intr_status(pvs);
927c0586b87SGarrett D'Amore 	if ((status & PVSCSI_INTR_ALL_SUPPORTED) != 0) {
928c0586b87SGarrett D'Amore 		pvscsi_write_intr_status(pvs, status);
929c0586b87SGarrett D'Amore 	} else if (pvs->intr_type == DDI_INTR_TYPE_FIXED) {
930c0586b87SGarrett D'Amore 		rv = DDI_INTR_UNCLAIMED;
931c0586b87SGarrett D'Amore 	}
932c0586b87SGarrett D'Amore 	if (pvs->detach) {
933c0586b87SGarrett D'Amore 		mutex_exit(&pvs->lock);
934c0586b87SGarrett D'Amore 		return (rv);
935c0586b87SGarrett D'Amore 	}
936c0586b87SGarrett D'Amore 	cmd = pvscsi_process_comp_ring(pvs);
937c0586b87SGarrett D'Amore 	msg = pvscsi_process_msg_ring(pvs);
938c0586b87SGarrett D'Amore 
939c0586b87SGarrett D'Amore 	/*
940c0586b87SGarrett D'Amore 	 * Do this under the lock, so that we won't dispatch
941c0586b87SGarrett D'Amore 	 * if we are detaching
942c0586b87SGarrett D'Amore 	 */
943c0586b87SGarrett D'Amore 	if (msg != NULL) {
944c0586b87SGarrett D'Amore 		if (ddi_taskq_dispatch(pvs->tq, pvscsi_handle_msg, msg,
945c0586b87SGarrett D'Amore 		    DDI_NOSLEEP) != DDI_SUCCESS) {
946c0586b87SGarrett D'Amore 			dev_err(pvs->dip, CE_WARN,
947c0586b87SGarrett D'Amore 			    "!failed to dispatch discovery");
948c0586b87SGarrett D'Amore 		}
949c0586b87SGarrett D'Amore 	}
950c0586b87SGarrett D'Amore 	mutex_exit(&pvs->lock);
951c0586b87SGarrett D'Amore 
952c0586b87SGarrett D'Amore 	pvscsi_complete_cmds(pvs, cmd);
953c0586b87SGarrett D'Amore 
954c0586b87SGarrett D'Amore 	return (rv);
955c0586b87SGarrett D'Amore }
956c0586b87SGarrett D'Amore 
957c0586b87SGarrett D'Amore static void
pvscsi_free_intrs(pvscsi_softc_t * pvs)958c0586b87SGarrett D'Amore pvscsi_free_intrs(pvscsi_softc_t *pvs)
959c0586b87SGarrett D'Amore {
960c0586b87SGarrett D'Amore 	for (int i = 0; i < pvs->intr_cnt; i++) {
961c0586b87SGarrett D'Amore 		(void) ddi_intr_disable(pvs->intr_handles[i]);
962c0586b87SGarrett D'Amore 		(void) ddi_intr_remove_handler(pvs->intr_handles[i]);
963c0586b87SGarrett D'Amore 		(void) ddi_intr_free(pvs->intr_handles[i]);
964c0586b87SGarrett D'Amore 	}
965c0586b87SGarrett D'Amore 	pvs->intr_cnt = 0;
966c0586b87SGarrett D'Amore }
967c0586b87SGarrett D'Amore 
968c0586b87SGarrett D'Amore static int
pvscsi_register_isr(pvscsi_softc_t * pvs,int type)969c0586b87SGarrett D'Amore pvscsi_register_isr(pvscsi_softc_t *pvs, int type)
970c0586b87SGarrett D'Amore {
971c0586b87SGarrett D'Amore 	int	navail, nactual;
972c0586b87SGarrett D'Amore 	int	i;
973c0586b87SGarrett D'Amore 
974c0586b87SGarrett D'Amore 	if (ddi_intr_get_navail(pvs->dip, type, &navail) != DDI_SUCCESS ||
975c0586b87SGarrett D'Amore 	    navail == 0) {
976c0586b87SGarrett D'Amore 		dev_err(pvs->dip, CE_WARN,
977c0586b87SGarrett D'Amore 		    "!failed to get number of available interrupts of type %d",
978c0586b87SGarrett D'Amore 		    type);
979c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
980c0586b87SGarrett D'Amore 	}
981c0586b87SGarrett D'Amore 	navail = MIN(navail, PVSCSI_MAX_INTRS);
982c0586b87SGarrett D'Amore 
983c0586b87SGarrett D'Amore 	if (ddi_intr_alloc(pvs->dip, pvs->intr_handles, type, 0, navail,
984c0586b87SGarrett D'Amore 	    &nactual, DDI_INTR_ALLOC_NORMAL) != DDI_SUCCESS || nactual == 0) {
985c0586b87SGarrett D'Amore 		dev_err(pvs->dip, CE_WARN, "!failed to allocate %d interrupts",
986c0586b87SGarrett D'Amore 		    navail);
987c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
988c0586b87SGarrett D'Amore 	}
989c0586b87SGarrett D'Amore 
990c0586b87SGarrett D'Amore 	pvs->intr_cnt = nactual;
991c0586b87SGarrett D'Amore 
992c0586b87SGarrett D'Amore 	if (ddi_intr_get_pri(pvs->intr_handles[0], (uint_t *)&pvs->intr_pri) !=
993c0586b87SGarrett D'Amore 	    DDI_SUCCESS) {
994c0586b87SGarrett D'Amore 		dev_err(pvs->dip, CE_WARN, "!failed to get interrupt priority");
995c0586b87SGarrett D'Amore 		pvscsi_free_intrs(pvs);
996c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
997c0586b87SGarrett D'Amore 	}
998c0586b87SGarrett D'Amore 
999c0586b87SGarrett D'Amore 	for (i = 0; i < nactual; i++) {
1000c0586b87SGarrett D'Amore 		if (ddi_intr_add_handler(pvs->intr_handles[i], pvscsi_intr,
1001c0586b87SGarrett D'Amore 		    (caddr_t)pvs, NULL) != DDI_SUCCESS) {
1002c0586b87SGarrett D'Amore 			dev_err(pvs->dip, CE_WARN,
1003c0586b87SGarrett D'Amore 			    "!failed to add intr handler");
1004c0586b87SGarrett D'Amore 			pvscsi_free_intrs(pvs);
1005c0586b87SGarrett D'Amore 			return (DDI_FAILURE);
1006c0586b87SGarrett D'Amore 		}
1007c0586b87SGarrett D'Amore 	}
1008c0586b87SGarrett D'Amore 
1009c0586b87SGarrett D'Amore 	pvs->intr_type = type;
1010c0586b87SGarrett D'Amore 	return (DDI_SUCCESS);
1011c0586b87SGarrett D'Amore }
1012c0586b87SGarrett D'Amore 
1013c0586b87SGarrett D'Amore static int
pvscsi_setup_isr(pvscsi_softc_t * pvs)1014c0586b87SGarrett D'Amore pvscsi_setup_isr(pvscsi_softc_t *pvs)
1015c0586b87SGarrett D'Amore {
1016c0586b87SGarrett D'Amore 	int	types;
1017c0586b87SGarrett D'Amore 
1018c0586b87SGarrett D'Amore 	if (ddi_intr_get_supported_types(pvs->dip, &types) != DDI_SUCCESS) {
1019c0586b87SGarrett D'Amore 		dev_err(pvs->dip, CE_WARN, "!failed to get interrupt types");
1020c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
1021c0586b87SGarrett D'Amore 	}
1022c0586b87SGarrett D'Amore 
1023c0586b87SGarrett D'Amore 	types &= pvscsi_intr_types;
1024c0586b87SGarrett D'Amore 	if (types == 0) {
1025c0586b87SGarrett D'Amore 		dev_err(pvs->dip, CE_WARN, "!no supported interrupt types");
1026c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
1027c0586b87SGarrett D'Amore 	}
1028c0586b87SGarrett D'Amore 
1029c0586b87SGarrett D'Amore 
1030c0586b87SGarrett D'Amore 	if (((types & DDI_INTR_TYPE_MSIX) != 0) &&
1031c0586b87SGarrett D'Amore 	    (pvscsi_register_isr(pvs, DDI_INTR_TYPE_MSIX) == DDI_SUCCESS)) {
1032c0586b87SGarrett D'Amore 		return (DDI_SUCCESS);
1033c0586b87SGarrett D'Amore 	}
1034c0586b87SGarrett D'Amore 	if (((types & DDI_INTR_TYPE_MSI) != 0) &&
1035c0586b87SGarrett D'Amore 	    (pvscsi_register_isr(pvs, DDI_INTR_TYPE_MSI) == DDI_SUCCESS)) {
1036c0586b87SGarrett D'Amore 		return (DDI_SUCCESS);
1037c0586b87SGarrett D'Amore 	}
1038c0586b87SGarrett D'Amore 	if (((types & DDI_INTR_TYPE_FIXED) != 0) &&
1039c0586b87SGarrett D'Amore 	    (pvscsi_register_isr(pvs, DDI_INTR_TYPE_FIXED) == DDI_SUCCESS)) {
1040c0586b87SGarrett D'Amore 		return (DDI_SUCCESS);
1041c0586b87SGarrett D'Amore 	}
1042c0586b87SGarrett D'Amore 
1043c0586b87SGarrett D'Amore 	dev_err(pvs->dip, CE_WARN, "!failed installing any interrupt handler");
1044c0586b87SGarrett D'Amore 	return (DDI_FAILURE);
1045c0586b87SGarrett D'Amore }
1046c0586b87SGarrett D'Amore 
1047c0586b87SGarrett D'Amore 
1048c0586b87SGarrett D'Amore static void
pvscsi_timeout(void * arg)1049c0586b87SGarrett D'Amore pvscsi_timeout(void *arg)
1050c0586b87SGarrett D'Amore {
1051c0586b87SGarrett D'Amore 	pvscsi_softc_t	*pvs;
1052c0586b87SGarrett D'Amore 	pvscsi_cmd_t	*cmd;
1053c0586b87SGarrett D'Amore 	pvscsi_cmd_t	*reclaimed = NULL;
1054c0586b87SGarrett D'Amore 	list_t		*l;
1055c0586b87SGarrett D'Amore 	clock_t		now;
1056c0586b87SGarrett D'Amore 
1057c0586b87SGarrett D'Amore 	pvs = arg;
1058c0586b87SGarrett D'Amore 	l = &pvs->cmd_queue;
1059c0586b87SGarrett D'Amore 	now = ddi_get_lbolt();
1060c0586b87SGarrett D'Amore 
1061c0586b87SGarrett D'Amore 	mutex_enter(&pvs->lock);
1062c0586b87SGarrett D'Amore 	if (pvs->timeout == 0) {
1063c0586b87SGarrett D'Amore 		mutex_exit(&pvs->lock);
1064c0586b87SGarrett D'Amore 		return;
1065c0586b87SGarrett D'Amore 	}
1066c0586b87SGarrett D'Amore 
1067c0586b87SGarrett D'Amore 	for (cmd = list_head(l); cmd != NULL; cmd = list_next(l, cmd)) {
1068c0586b87SGarrett D'Amore 		clock_t	overdue;
1069c0586b87SGarrett D'Amore 
1070c0586b87SGarrett D'Amore 		/* polling takes care of it's own timeouts */
1071c0586b87SGarrett D'Amore 		if (cmd->poll) {
1072c0586b87SGarrett D'Amore 			continue;
1073c0586b87SGarrett D'Amore 		}
1074c0586b87SGarrett D'Amore 
1075c0586b87SGarrett D'Amore 		overdue = now - (cmd->start + cmd->timeout);
1076c0586b87SGarrett D'Amore 
1077c0586b87SGarrett D'Amore 		/*
1078c0586b87SGarrett D'Amore 		 * We keep the list of requests sorted by expiration
1079c0586b87SGarrett D'Amore 		 * time, so we hopefully won't need to walk through
1080c0586b87SGarrett D'Amore 		 * many of these.  This check is safe if lbolt wraps.
1081c0586b87SGarrett D'Amore 		 */
1082c0586b87SGarrett D'Amore 		if (overdue <= 0) {
1083c0586b87SGarrett D'Amore 			break;
1084c0586b87SGarrett D'Amore 		}
1085c0586b87SGarrett D'Amore 
1086c0586b87SGarrett D'Amore 		/* first we try aborting */
1087c0586b87SGarrett D'Amore 		if (!cmd->expired) {
1088c0586b87SGarrett D'Amore 			atomic_or_8(&cmd->expired, 1);
1089c0586b87SGarrett D'Amore 			dev_err(pvs->dip, CE_WARN, "!cmd timed out (%lds)",
1090c0586b87SGarrett D'Amore 			    drv_hztousec(cmd->timeout)/1000000);
1091c0586b87SGarrett D'Amore 			continue;
1092c0586b87SGarrett D'Amore 		}
1093c0586b87SGarrett D'Amore 
1094c0586b87SGarrett D'Amore 		/* if we're less than 2 seconds overdue, wait for abort */
1095c0586b87SGarrett D'Amore 		if (overdue <= pvscsi_hz * 2) {
1096c0586b87SGarrett D'Amore 			continue;
1097c0586b87SGarrett D'Amore 		}
1098c0586b87SGarrett D'Amore 
1099c0586b87SGarrett D'Amore 		/* next it's a reset of the device */
1100c0586b87SGarrett D'Amore 		if (overdue <= pvscsi_hz * 8) {
1101c0586b87SGarrett D'Amore 			pvscsi_dev_reset(pvs, cmd->target, cmd->lun);
1102c0586b87SGarrett D'Amore 			break;
1103c0586b87SGarrett D'Amore 		}
1104c0586b87SGarrett D'Amore 
1105c0586b87SGarrett D'Amore 		/* next it's a reset of the bus */
1106c0586b87SGarrett D'Amore 		if (overdue <= pvscsi_hz * 16) {
1107c0586b87SGarrett D'Amore 			pvscsi_reset_bus(pvs);
1108c0586b87SGarrett D'Amore 			break;
1109c0586b87SGarrett D'Amore 		}
1110c0586b87SGarrett D'Amore 
1111c0586b87SGarrett D'Amore 		/* finally it's a reset of the entire adapter */
1112c0586b87SGarrett D'Amore 		dev_err(pvs->dip, CE_WARN, "!adapter hung? restarting...");
1113c0586b87SGarrett D'Amore 		mutex_enter(&pvs->lock);
1114c0586b87SGarrett D'Amore 		pvscsi_stop_hba(pvs);
1115c0586b87SGarrett D'Amore 		reclaimed = pvscsi_reclaim_cmds(pvs);
1116c0586b87SGarrett D'Amore 		pvscsi_start_hba(pvs);
1117c0586b87SGarrett D'Amore 		mutex_exit(&pvs->lock);
1118c0586b87SGarrett D'Amore 		break;
1119c0586b87SGarrett D'Amore 	}
1120c0586b87SGarrett D'Amore 
1121c0586b87SGarrett D'Amore 	/* see if reset or abort completed anything */
1122c0586b87SGarrett D'Amore 	cmd = pvscsi_process_comp_ring(pvs);
1123c0586b87SGarrett D'Amore 
1124c0586b87SGarrett D'Amore 	/* reschedule us if we still have requests pending */
1125c0586b87SGarrett D'Amore 	if (!list_is_empty(l)) {
1126c0586b87SGarrett D'Amore 		pvs->timeout = timeout(pvscsi_timeout, pvs, pvscsi_hz);
1127c0586b87SGarrett D'Amore 	}
1128c0586b87SGarrett D'Amore 
1129c0586b87SGarrett D'Amore 	mutex_exit(&pvs->lock);
1130c0586b87SGarrett D'Amore 
1131c0586b87SGarrett D'Amore 	/* if we had things that got completed, then do the callbacks */
1132c0586b87SGarrett D'Amore 	pvscsi_complete_cmds(pvs, reclaimed);
1133c0586b87SGarrett D'Amore 	pvscsi_complete_cmds(pvs, cmd);
1134c0586b87SGarrett D'Amore }
1135c0586b87SGarrett D'Amore 
1136c0586b87SGarrett D'Amore static int
pvscsi_start(struct scsi_address * ap,struct scsi_pkt * pkt)1137c0586b87SGarrett D'Amore pvscsi_start(struct scsi_address *ap, struct scsi_pkt *pkt)
1138c0586b87SGarrett D'Amore {
1139c0586b87SGarrett D'Amore 	pvscsi_cmd_t		*cmd = pkt->pkt_ha_private;
1140c0586b87SGarrett D'Amore 	struct scsi_device	*sd;
1141c0586b87SGarrett D'Amore 	pvscsi_device_t		*pd;
1142c0586b87SGarrett D'Amore 	pvscsi_softc_t		*pvs;
1143c0586b87SGarrett D'Amore 	int			rc;
1144*7a73cc88SGarrett D'Amore 	boolean_t		poll;
1145c0586b87SGarrett D'Amore 
1146c0586b87SGarrett D'Amore 	/* make sure the packet is sane */
1147c0586b87SGarrett D'Amore 	if ((pkt->pkt_numcookies > PVSCSI_MAX_SG_SIZE) ||
1148c0586b87SGarrett D'Amore 	    ((pkt->pkt_dma_flags & DDI_DMA_RDWR) == DDI_DMA_RDWR) ||
1149c0586b87SGarrett D'Amore 	    (pkt->pkt_cdblen > sizeof (cmd->cdb)) ||
1150c0586b87SGarrett D'Amore 	    ((sd = scsi_address_device(ap)) == NULL) ||
1151c0586b87SGarrett D'Amore 	    ((pd = scsi_device_hba_private_get(sd)) == NULL) ||
1152c0586b87SGarrett D'Amore 	    ((pvs = pd->pvs) == NULL))  {
1153c0586b87SGarrett D'Amore 		return (TRAN_BADPKT);
1154c0586b87SGarrett D'Amore 	}
1155c0586b87SGarrett D'Amore 
1156c0586b87SGarrett D'Amore 	ASSERT(cmd->pkt == pkt);
1157c0586b87SGarrett D'Amore 
1158*7a73cc88SGarrett D'Amore 	poll = cmd->poll = ((pkt->pkt_flags & FLAG_NOINTR) != 0);
1159c0586b87SGarrett D'Amore 
1160c0586b87SGarrett D'Amore 	if (pkt->pkt_flags & (FLAG_HTAG|FLAG_HEAD)) {
1161c0586b87SGarrett D'Amore 		cmd->tag = MSG_HEAD_QTAG;
1162c0586b87SGarrett D'Amore 	} else if (pkt->pkt_flags & FLAG_OTAG) {
1163c0586b87SGarrett D'Amore 		cmd->tag = MSG_ORDERED_QTAG;
1164c0586b87SGarrett D'Amore 	} else { /* also FLAG_STAG */
1165c0586b87SGarrett D'Amore 		cmd->tag = MSG_SIMPLE_QTAG;
1166c0586b87SGarrett D'Amore 	}
1167c0586b87SGarrett D'Amore 
1168c0586b87SGarrett D'Amore 	bcopy(pkt->pkt_cdbp, cmd->cdb, pkt->pkt_cdblen);
1169c0586b87SGarrett D'Amore 	cmd->cdblen = pkt->pkt_cdblen;
1170c0586b87SGarrett D'Amore 	bzero(&cmd->cmd_scb, sizeof (cmd->cmd_scb));
1171c0586b87SGarrett D'Amore 
1172c0586b87SGarrett D'Amore 	/*
1173c0586b87SGarrett D'Amore 	 * Reinitialize some fields because the packet may
1174c0586b87SGarrett D'Amore 	 * have been resubmitted.
1175c0586b87SGarrett D'Amore 	 */
1176c0586b87SGarrett D'Amore 	pkt->pkt_reason = CMD_CMPLT;
1177c0586b87SGarrett D'Amore 	pkt->pkt_state = 0;
1178c0586b87SGarrett D'Amore 	pkt->pkt_statistics = 0;
1179c0586b87SGarrett D'Amore 
1180c0586b87SGarrett D'Amore 	/* Zero status byte - but only if present */
1181c0586b87SGarrett D'Amore 	if (pkt->pkt_scblen > 0) {
1182c0586b87SGarrett D'Amore 		*(pkt->pkt_scbp) = 0;
1183c0586b87SGarrett D'Amore 	}
1184c0586b87SGarrett D'Amore 
1185c0586b87SGarrett D'Amore 	if (pkt->pkt_numcookies > 0) {
1186c0586b87SGarrett D'Amore 		if (pkt->pkt_dma_flags & DDI_DMA_READ) {
1187c0586b87SGarrett D'Amore 			cmd->dma_dir = PVSCSI_FLAG_CMD_DIR_TOHOST;
1188c0586b87SGarrett D'Amore 		} else if (pkt->pkt_dma_flags & DDI_DMA_WRITE) {
1189c0586b87SGarrett D'Amore 			cmd->dma_dir = PVSCSI_FLAG_CMD_DIR_TODEVICE;
1190c0586b87SGarrett D'Amore 		} else {
1191c0586b87SGarrett D'Amore 			cmd->dma_dir = 0;
1192c0586b87SGarrett D'Amore 		}
1193c0586b87SGarrett D'Amore 	}
1194c0586b87SGarrett D'Amore 
1195c0586b87SGarrett D'Amore 	cmd->target = pd->target;
1196c0586b87SGarrett D'Amore 	cmd->lun = pd->lun;
1197c0586b87SGarrett D'Amore 	cmd->start = ddi_get_lbolt();
1198c0586b87SGarrett D'Amore 	cmd->timeout = pkt->pkt_time * pvscsi_hz;
1199c0586b87SGarrett D'Amore 
1200c0586b87SGarrett D'Amore 	rc = pvscsi_transport_command(pvs, cmd);
1201c0586b87SGarrett D'Amore 
1202*7a73cc88SGarrett D'Amore 	if (poll && rc == TRAN_ACCEPT) {
1203c0586b87SGarrett D'Amore 		pvscsi_poll_cmd(pvs, cmd);
1204c0586b87SGarrett D'Amore 		pvscsi_set_status(pvs, cmd);
1205c0586b87SGarrett D'Amore 	}
1206c0586b87SGarrett D'Amore 
1207c0586b87SGarrett D'Amore 	return (rc);
1208c0586b87SGarrett D'Amore }
1209c0586b87SGarrett D'Amore 
1210c0586b87SGarrett D'Amore 
1211c0586b87SGarrett D'Amore static int
pvscsi_parse_ua(const char * ua,int * target,int * lun)1212c0586b87SGarrett D'Amore pvscsi_parse_ua(const char *ua, int *target, int *lun)
1213c0586b87SGarrett D'Amore {
1214c0586b87SGarrett D'Amore 	char *end;
1215c0586b87SGarrett D'Amore 	long num;
1216c0586b87SGarrett D'Amore 	if (((ddi_strtol(ua, &end, 16, &num)) != 0) ||
1217c0586b87SGarrett D'Amore 	    ((*end != ',') && (*end != 0))) {
1218c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
1219c0586b87SGarrett D'Amore 	}
1220c0586b87SGarrett D'Amore 	*target = (int)num;
1221c0586b87SGarrett D'Amore 	if (*end == 0) {
1222c0586b87SGarrett D'Amore 		*lun = 0;
1223c0586b87SGarrett D'Amore 		return (DDI_SUCCESS);
1224c0586b87SGarrett D'Amore 	}
1225c0586b87SGarrett D'Amore 	end++;
1226c0586b87SGarrett D'Amore 	if ((ddi_strtol(end, &end, 16, &num) != 0) || (*end != 0)) {
1227c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
1228c0586b87SGarrett D'Amore 	}
1229c0586b87SGarrett D'Amore 	*lun = (int)num;
1230c0586b87SGarrett D'Amore 	return (DDI_SUCCESS);
1231c0586b87SGarrett D'Amore }
1232c0586b87SGarrett D'Amore 
1233c0586b87SGarrett D'Amore static uint32_t
pvscsi_max_targets(pvscsi_softc_t * pvs)1234c0586b87SGarrett D'Amore pvscsi_max_targets(pvscsi_softc_t *pvs)
1235c0586b87SGarrett D'Amore {
1236c0586b87SGarrett D'Amore 	pvscsi_dma_buf_t			db;
1237c0586b87SGarrett D'Amore 	struct PVSCSIConfigPageController	cpc;
1238c0586b87SGarrett D'Amore 	struct PVSCSICmdDescConfigCmd		cmd;
1239c0586b87SGarrett D'Amore 
1240c0586b87SGarrett D'Amore 	bzero(&db, sizeof (db));
1241c0586b87SGarrett D'Amore 
1242c0586b87SGarrett D'Amore 	/* NB: config pages fit in a single page */
1243c0586b87SGarrett D'Amore 	if (pvscsi_setup_dma_buffer(pvs, PAGE_SIZE, &db) != DDI_SUCCESS) {
1244c0586b87SGarrett D'Amore 		dev_err(pvs->dip, CE_WARN,
1245c0586b87SGarrett D'Amore 		    "!failed to setup config page DMA");
1246c0586b87SGarrett D'Amore 		return (PVSCSI_MAXTGTS);
1247c0586b87SGarrett D'Amore 	}
1248c0586b87SGarrett D'Amore 
1249c0586b87SGarrett D'Amore 	bzero(&cmd, sizeof (cmd));
1250c0586b87SGarrett D'Amore 	cmd.configPageAddress = PVSCSI_CONFIG_CONTROLLER_ADDRESS;
1251c0586b87SGarrett D'Amore 	cmd.configPageAddress <<= 32;
1252c0586b87SGarrett D'Amore 	cmd.configPageNum = PVSCSI_CONFIG_PAGE_CONTROLLER;
1253c0586b87SGarrett D'Amore 	cmd.cmpAddr = db.pa;
1254c0586b87SGarrett D'Amore 
1255c0586b87SGarrett D'Amore 	pvscsi_write_cmd_desc(pvs, PVSCSI_CMD_CONFIG, &cmd, sizeof (cmd));
1256c0586b87SGarrett D'Amore 	(void) ddi_dma_sync(db.dmah, 0, 0, DDI_DMA_SYNC_FORKERNEL);
1257c0586b87SGarrett D'Amore 	bcopy(db.addr, &cpc, sizeof (cpc));
1258c0586b87SGarrett D'Amore 	pvscsi_free_dma_buffer(&db);
1259c0586b87SGarrett D'Amore 
1260c0586b87SGarrett D'Amore 
1261c0586b87SGarrett D'Amore 	if ((cpc.header.scsiStatus == STATUS_GOOD) &&
1262c0586b87SGarrett D'Amore 	    (cpc.header.hostStatus == BTSTAT_SUCCESS) &&
1263c0586b87SGarrett D'Amore 	    (cpc.numPhys > 0)) {
1264c0586b87SGarrett D'Amore 		return (cpc.numPhys);
1265c0586b87SGarrett D'Amore 	}
1266c0586b87SGarrett D'Amore 
1267c0586b87SGarrett D'Amore 	dev_err(pvs->dip, CE_WARN, "!failed to determine max targets");
1268c0586b87SGarrett D'Amore 	return (PVSCSI_MAXTGTS);
1269c0586b87SGarrett D'Amore }
1270c0586b87SGarrett D'Amore 
1271c0586b87SGarrett D'Amore static boolean_t
pvscsi_probe_target(pvscsi_softc_t * pvs,int target)1272c0586b87SGarrett D'Amore pvscsi_probe_target(pvscsi_softc_t *pvs, int target)
1273c0586b87SGarrett D'Amore {
1274c0586b87SGarrett D'Amore 	pvscsi_cmd_t		cmd;
1275c0586b87SGarrett D'Amore 
1276c0586b87SGarrett D'Amore 	if (!pvscsi_cmd_init(pvs, &cmd, KM_SLEEP)) {
1277c0586b87SGarrett D'Amore 		pvscsi_cmd_fini(&cmd);
1278c0586b87SGarrett D'Amore 		return (B_FALSE);
1279c0586b87SGarrett D'Amore 	}
1280c0586b87SGarrett D'Amore 	/* NB: CDB 0 is a TUR which is perfect for our needs */
1281c0586b87SGarrett D'Amore 	bzero(cmd.cdb, sizeof (cmd.cdb));
1282c0586b87SGarrett D'Amore 	cmd.poll = B_TRUE;
1283c0586b87SGarrett D'Amore 	cmd.dma_dir = 0;
1284c0586b87SGarrett D'Amore 	cmd.target = target;
1285c0586b87SGarrett D'Amore 	cmd.lun = 0;
1286c0586b87SGarrett D'Amore 	cmd.start = ddi_get_lbolt();
1287c0586b87SGarrett D'Amore 	cmd.timeout = pvscsi_hz;
1288c0586b87SGarrett D'Amore 
1289c0586b87SGarrett D'Amore 	if (pvscsi_transport_command(pvs, &cmd) != TRAN_ACCEPT) {
1290c0586b87SGarrett D'Amore 		pvscsi_cmd_fini(&cmd);
1291c0586b87SGarrett D'Amore 		return (B_FALSE);
1292c0586b87SGarrett D'Amore 	}
1293c0586b87SGarrett D'Amore 	pvscsi_poll_cmd(pvs, &cmd);
1294c0586b87SGarrett D'Amore 
1295c0586b87SGarrett D'Amore 	switch (cmd.host_status) {
1296c0586b87SGarrett D'Amore 	case BTSTAT_SUCCESS:
1297c0586b87SGarrett D'Amore 	case BTSTAT_LINKED_COMMAND_COMPLETED:
1298c0586b87SGarrett D'Amore 	case BTSTAT_LINKED_COMMAND_COMPLETED_WITH_FLAG:
1299c0586b87SGarrett D'Amore 		/* We don't care about the actual SCSI status */
1300c0586b87SGarrett D'Amore 		pvscsi_cmd_fini(&cmd);
1301c0586b87SGarrett D'Amore 		return (B_TRUE);
1302c0586b87SGarrett D'Amore 	}
1303c0586b87SGarrett D'Amore 
1304c0586b87SGarrett D'Amore 	pvscsi_cmd_fini(&cmd);
1305c0586b87SGarrett D'Amore 	return (B_FALSE);
1306c0586b87SGarrett D'Amore }
1307c0586b87SGarrett D'Amore 
1308c0586b87SGarrett D'Amore static int
pvscsi_tgt_init(dev_info_t * dip,dev_info_t * child,scsi_hba_tran_t * tran,struct scsi_device * sd)1309c0586b87SGarrett D'Amore pvscsi_tgt_init(dev_info_t *dip, dev_info_t *child, scsi_hba_tran_t *tran,
1310c0586b87SGarrett D'Amore     struct scsi_device *sd)
1311c0586b87SGarrett D'Amore {
1312c0586b87SGarrett D'Amore 	/*
1313c0586b87SGarrett D'Amore 	 * Assumption: the HBA framework only asks us to have a single
1314c0586b87SGarrett D'Amore 	 * target initialized per address at any given time.
1315c0586b87SGarrett D'Amore 	 */
1316c0586b87SGarrett D'Amore 	pvscsi_device_t	*pd;
1317c0586b87SGarrett D'Amore 	pvscsi_softc_t	*pvs;
1318c0586b87SGarrett D'Amore 	const char	*ua;
1319c0586b87SGarrett D'Amore 
1320c0586b87SGarrett D'Amore 	if (((scsi_hba_iport_unit_address(dip)) == NULL) ||
1321c0586b87SGarrett D'Amore 	    ((pvs = tran->tran_hba_private) == NULL) ||
1322c0586b87SGarrett D'Amore 	    ((ua = scsi_device_unit_address(sd)) == NULL)) {
1323c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
1324c0586b87SGarrett D'Amore 	}
1325c0586b87SGarrett D'Amore 
1326c0586b87SGarrett D'Amore 	/* parse the unit address */
1327c0586b87SGarrett D'Amore 	pd = kmem_zalloc(sizeof (*pd), KM_SLEEP);
1328c0586b87SGarrett D'Amore 	if (pvscsi_parse_ua(ua, &pd->target, &pd->lun) != DDI_SUCCESS) {
1329c0586b87SGarrett D'Amore 		kmem_free(pd, sizeof (*pd));
1330c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
1331c0586b87SGarrett D'Amore 	}
1332c0586b87SGarrett D'Amore 	pd->pvs = pvs;
1333c0586b87SGarrett D'Amore 	scsi_device_hba_private_set(sd, pd);
1334c0586b87SGarrett D'Amore 
1335c0586b87SGarrett D'Amore 	mutex_enter(&pvs->lock);
1336c0586b87SGarrett D'Amore 	list_insert_tail(&pvs->devices, pd);
1337c0586b87SGarrett D'Amore 	mutex_exit(&pvs->lock);
1338c0586b87SGarrett D'Amore 	return (DDI_SUCCESS);
1339c0586b87SGarrett D'Amore }
1340c0586b87SGarrett D'Amore 
1341c0586b87SGarrett D'Amore static void
pvscsi_tgt_free(dev_info_t * dip,dev_info_t * child,scsi_hba_tran_t * tran,struct scsi_device * sd)1342c0586b87SGarrett D'Amore pvscsi_tgt_free(dev_info_t *dip, dev_info_t *child, scsi_hba_tran_t *tran,
1343c0586b87SGarrett D'Amore     struct scsi_device *sd)
1344c0586b87SGarrett D'Amore {
1345c0586b87SGarrett D'Amore 	pvscsi_device_t	*pd;
1346c0586b87SGarrett D'Amore 	pvscsi_softc_t	*pvs;
1347c0586b87SGarrett D'Amore 
1348c0586b87SGarrett D'Amore 	if (((scsi_hba_iport_unit_address(dip)) == NULL) ||
1349c0586b87SGarrett D'Amore 	    ((pvs = tran->tran_hba_private) == NULL) ||
1350c0586b87SGarrett D'Amore 	    ((pd = scsi_device_hba_private_get(sd)) == NULL)) {
1351c0586b87SGarrett D'Amore 		return;
1352c0586b87SGarrett D'Amore 	}
1353c0586b87SGarrett D'Amore 	scsi_device_hba_private_set(sd, NULL);
1354c0586b87SGarrett D'Amore 	mutex_enter(&pvs->lock);
1355c0586b87SGarrett D'Amore 	list_remove(&pvs->devices, pd);
1356c0586b87SGarrett D'Amore 	mutex_exit(&pvs->lock);
1357c0586b87SGarrett D'Amore 
1358c0586b87SGarrett D'Amore 	kmem_free(pd, sizeof (*pd));
1359c0586b87SGarrett D'Amore }
1360c0586b87SGarrett D'Amore 
1361c0586b87SGarrett D'Amore static int
pvscsi_reset(struct scsi_address * ap,int level)1362c0586b87SGarrett D'Amore pvscsi_reset(struct scsi_address *ap, int level)
1363c0586b87SGarrett D'Amore {
1364c0586b87SGarrett D'Amore 	struct scsi_device	*sd;
1365c0586b87SGarrett D'Amore 	pvscsi_device_t		*pd;
1366c0586b87SGarrett D'Amore 	pvscsi_softc_t		*pvs;
1367c0586b87SGarrett D'Amore 	pvscsi_cmd_t		*cmd;
1368c0586b87SGarrett D'Amore 
1369c0586b87SGarrett D'Amore 	if (((sd = scsi_address_device(ap)) == NULL) ||
1370c0586b87SGarrett D'Amore 	    ((pd = scsi_device_hba_private_get(sd)) == NULL) ||
1371c0586b87SGarrett D'Amore 	    ((pvs = pd->pvs) == NULL))  {
1372c0586b87SGarrett D'Amore 		return (0);
1373c0586b87SGarrett D'Amore 	}
1374c0586b87SGarrett D'Amore 	switch (level) {
1375c0586b87SGarrett D'Amore 	case RESET_ALL:
1376c0586b87SGarrett D'Amore 	case RESET_BUS:
1377c0586b87SGarrett D'Amore 		pvscsi_reset_bus(pvs);
1378c0586b87SGarrett D'Amore 		break;
1379c0586b87SGarrett D'Amore 	case RESET_TARGET:
1380c0586b87SGarrett D'Amore 		/* reset both the lun and lun 0 */
1381c0586b87SGarrett D'Amore 		pvscsi_dev_reset(pvs, pd->target, pd->lun);
1382c0586b87SGarrett D'Amore 		pvscsi_dev_reset(pvs, pd->target, 0);
1383c0586b87SGarrett D'Amore 		break;
1384c0586b87SGarrett D'Amore 	case RESET_LUN:
1385c0586b87SGarrett D'Amore 		pvscsi_dev_reset(pvs, pd->target, pd->lun);
1386c0586b87SGarrett D'Amore 		break;
1387c0586b87SGarrett D'Amore 	default:
1388c0586b87SGarrett D'Amore 		return (0);
1389c0586b87SGarrett D'Amore 	}
1390c0586b87SGarrett D'Amore 
1391c0586b87SGarrett D'Amore 	/* reset may have caused some completions */
1392c0586b87SGarrett D'Amore 	mutex_enter(&pvs->lock);
1393c0586b87SGarrett D'Amore 	cmd = pvscsi_process_comp_ring(pvs);
1394c0586b87SGarrett D'Amore 	mutex_exit(&pvs->lock);
1395c0586b87SGarrett D'Amore 
1396c0586b87SGarrett D'Amore 	pvscsi_complete_cmds(pvs, cmd);
1397c0586b87SGarrett D'Amore 	return (1);
1398c0586b87SGarrett D'Amore }
1399c0586b87SGarrett D'Amore 
1400c0586b87SGarrett D'Amore static int
pvscsi_abort(struct scsi_address * ap,struct scsi_pkt * pkt)1401c0586b87SGarrett D'Amore pvscsi_abort(struct scsi_address *ap, struct scsi_pkt *pkt)
1402c0586b87SGarrett D'Amore {
1403c0586b87SGarrett D'Amore 	struct scsi_device	*sd;
1404c0586b87SGarrett D'Amore 	pvscsi_device_t		*pd;
1405c0586b87SGarrett D'Amore 	pvscsi_softc_t		*pvs;
1406c0586b87SGarrett D'Amore 	pvscsi_cmd_t		*cmd;
1407c0586b87SGarrett D'Amore 
1408c0586b87SGarrett D'Amore 	if (pkt != NULL) {
1409c0586b87SGarrett D'Amore 		/* abort single command */
1410c0586b87SGarrett D'Amore 		cmd = pkt->pkt_ha_private;
1411c0586b87SGarrett D'Amore 		pvs = cmd->pvs;
1412c0586b87SGarrett D'Amore 		pvscsi_abort_cmd(pvs, cmd);
1413c0586b87SGarrett D'Amore 	} else if ((ap != NULL) &&
1414c0586b87SGarrett D'Amore 	    ((sd = scsi_address_device(ap)) != NULL) &&
1415c0586b87SGarrett D'Amore 	    ((pd = scsi_device_hba_private_get(sd)) != NULL) &&
1416c0586b87SGarrett D'Amore 	    ((pvs = pd->pvs) != NULL)) {
1417c0586b87SGarrett D'Amore 		/* abort all commands on the bus */
1418c0586b87SGarrett D'Amore 		pvscsi_abort_all(pvs, pd);
1419c0586b87SGarrett D'Amore 	} else {
1420c0586b87SGarrett D'Amore 		return (0);
1421c0586b87SGarrett D'Amore 	}
1422c0586b87SGarrett D'Amore 
1423c0586b87SGarrett D'Amore 	/* abort may have caused some completions */
1424c0586b87SGarrett D'Amore 	mutex_enter(&pvs->lock);
1425c0586b87SGarrett D'Amore 	cmd = pvscsi_process_comp_ring(pvs);
1426c0586b87SGarrett D'Amore 	mutex_exit(&pvs->lock);
1427c0586b87SGarrett D'Amore 
1428c0586b87SGarrett D'Amore 	pvscsi_complete_cmds(pvs, cmd);
1429c0586b87SGarrett D'Amore 
1430c0586b87SGarrett D'Amore 	return (1);
1431c0586b87SGarrett D'Amore }
1432c0586b87SGarrett D'Amore 
1433c0586b87SGarrett D'Amore static int
pvscsi_getcap(struct scsi_address * ap,char * cap,int whom)1434c0586b87SGarrett D'Amore pvscsi_getcap(struct scsi_address *ap, char *cap, int whom)
1435c0586b87SGarrett D'Amore {
1436c0586b87SGarrett D'Amore 	_NOTE(ARGUNUSED(ap));
1437c0586b87SGarrett D'Amore 	_NOTE(ARGUNUSED(whom));
1438c0586b87SGarrett D'Amore 
1439c0586b87SGarrett D'Amore 	if (cap == NULL) {
1440c0586b87SGarrett D'Amore 		return (-1);
1441c0586b87SGarrett D'Amore 	}
1442c0586b87SGarrett D'Amore 
1443c0586b87SGarrett D'Amore 	switch (scsi_hba_lookup_capstr(cap)) {
1444c0586b87SGarrett D'Amore 	case SCSI_CAP_ARQ:
1445c0586b87SGarrett D'Amore 	case SCSI_CAP_UNTAGGED_QING:
1446c0586b87SGarrett D'Amore 	case SCSI_CAP_TAGGED_QING:
1447c0586b87SGarrett D'Amore 		return (1);
1448c0586b87SGarrett D'Amore 	default:
1449c0586b87SGarrett D'Amore 		return (-1);
1450c0586b87SGarrett D'Amore 	}
1451c0586b87SGarrett D'Amore }
1452c0586b87SGarrett D'Amore 
1453c0586b87SGarrett D'Amore static int
pvscsi_setcap(struct scsi_address * ap,char * cap,int value,int whom)1454c0586b87SGarrett D'Amore pvscsi_setcap(struct scsi_address *ap, char *cap, int value, int whom)
1455c0586b87SGarrett D'Amore {
1456c0586b87SGarrett D'Amore 	_NOTE(ARGUNUSED(ap));
1457c0586b87SGarrett D'Amore 	_NOTE(ARGUNUSED(value));
1458c0586b87SGarrett D'Amore 	_NOTE(ARGUNUSED(whom));
1459c0586b87SGarrett D'Amore 
1460c0586b87SGarrett D'Amore 	if (cap == NULL) {
1461c0586b87SGarrett D'Amore 		return (-1);
1462c0586b87SGarrett D'Amore 	}
1463c0586b87SGarrett D'Amore 
1464c0586b87SGarrett D'Amore 	switch (scsi_hba_lookup_capstr(cap)) {
1465c0586b87SGarrett D'Amore 	case SCSI_CAP_ARQ:
1466c0586b87SGarrett D'Amore 	case SCSI_CAP_UNTAGGED_QING:
1467c0586b87SGarrett D'Amore 	case SCSI_CAP_TAGGED_QING:
1468c0586b87SGarrett D'Amore 		return (0); /* not changeable */
1469c0586b87SGarrett D'Amore 	default:
1470c0586b87SGarrett D'Amore 		return (-1);
1471c0586b87SGarrett D'Amore 	}
1472c0586b87SGarrett D'Amore }
1473c0586b87SGarrett D'Amore 
1474c0586b87SGarrett D'Amore static void
pvscsi_cmd_fini(pvscsi_cmd_t * cmd)1475c0586b87SGarrett D'Amore pvscsi_cmd_fini(pvscsi_cmd_t *cmd)
1476c0586b87SGarrett D'Amore {
1477c0586b87SGarrett D'Amore 	if (cmd->arq_pa != 0) {
1478c0586b87SGarrett D'Amore 		(void) ddi_dma_unbind_handle(cmd->arq_dmah);
1479c0586b87SGarrett D'Amore 		cmd->arq_dmah = NULL;
1480c0586b87SGarrett D'Amore 	}
1481c0586b87SGarrett D'Amore 	if (cmd->arq_dmah != NULL) {
1482c0586b87SGarrett D'Amore 		ddi_dma_free_handle(&cmd->arq_dmah);
1483c0586b87SGarrett D'Amore 		cmd->arq_dmah = NULL;
1484c0586b87SGarrett D'Amore 	}
1485c0586b87SGarrett D'Amore 	if (cmd->sgl_pa != 0) {
1486c0586b87SGarrett D'Amore 		(void) ddi_dma_unbind_handle(cmd->sgl_dmah);
1487c0586b87SGarrett D'Amore 		cmd->sgl_pa = 0;
1488c0586b87SGarrett D'Amore 	}
1489c0586b87SGarrett D'Amore 	if (cmd->sgl_acch != NULL) {
1490c0586b87SGarrett D'Amore 		ddi_dma_mem_free(&cmd->sgl_acch);
1491c0586b87SGarrett D'Amore 		cmd->sgl_acch = NULL;
1492c0586b87SGarrett D'Amore 		cmd->sgl = NULL;
1493c0586b87SGarrett D'Amore 	}
1494c0586b87SGarrett D'Amore 	if (cmd->sgl_dmah != NULL) {
1495c0586b87SGarrett D'Amore 		ddi_dma_free_handle(&cmd->sgl_dmah);
1496c0586b87SGarrett D'Amore 		cmd->sgl_dmah = NULL;
1497c0586b87SGarrett D'Amore 	}
1498c0586b87SGarrett D'Amore 	if (cmd->ctx != 0) {
1499c0586b87SGarrett D'Amore 		id32_free(cmd->ctx);
1500c0586b87SGarrett D'Amore 		cmd->ctx = 0;
1501c0586b87SGarrett D'Amore 	}
1502c0586b87SGarrett D'Amore }
1503c0586b87SGarrett D'Amore 
1504c0586b87SGarrett D'Amore static void
pvscsi_pkt_dtor(struct scsi_pkt * pkt,scsi_hba_tran_t * tran)1505c0586b87SGarrett D'Amore pvscsi_pkt_dtor(struct scsi_pkt *pkt, scsi_hba_tran_t *tran)
1506c0586b87SGarrett D'Amore {
1507c0586b87SGarrett D'Amore 	pvscsi_cmd_t	*cmd = pkt->pkt_ha_private;
1508c0586b87SGarrett D'Amore 	pvscsi_cmd_fini(cmd);
1509c0586b87SGarrett D'Amore }
1510c0586b87SGarrett D'Amore 
1511c0586b87SGarrett D'Amore static boolean_t
pvscsi_cmd_init(pvscsi_softc_t * pvs,pvscsi_cmd_t * cmd,int sleep)1512c0586b87SGarrett D'Amore pvscsi_cmd_init(pvscsi_softc_t *pvs, pvscsi_cmd_t *cmd, int sleep)
1513c0586b87SGarrett D'Amore {
1514c0586b87SGarrett D'Amore 	int		(*cb)(caddr_t);
1515c0586b87SGarrett D'Amore 	size_t		len;
1516c0586b87SGarrett D'Amore 	caddr_t		kaddr;
1517c0586b87SGarrett D'Amore 
1518c0586b87SGarrett D'Amore 	cb = sleep == KM_SLEEP ? DDI_DMA_SLEEP : DDI_DMA_DONTWAIT;
1519c0586b87SGarrett D'Amore 
1520c0586b87SGarrett D'Amore 	bzero(cmd, sizeof (*cmd));
1521c0586b87SGarrett D'Amore 	cmd->ctx = id32_alloc(cmd, sleep);
1522c0586b87SGarrett D'Amore 	if (cmd->ctx == 0) {
1523c0586b87SGarrett D'Amore 		dev_err(pvs->dip, CE_WARN,
1524c0586b87SGarrett D'Amore 		    "!failed to allocate 32-bit context id");
1525c0586b87SGarrett D'Amore 		return (B_FALSE);
1526c0586b87SGarrett D'Amore 	}
1527c0586b87SGarrett D'Amore 
1528c0586b87SGarrett D'Amore 	/* allocate DMA resources for scatter/gather list */
1529c0586b87SGarrett D'Amore 	if (ddi_dma_alloc_handle(pvs->dip, &pvscsi_dma_attr, cb, NULL,
1530c0586b87SGarrett D'Amore 	    &cmd->sgl_dmah) != DDI_SUCCESS) {
1531c0586b87SGarrett D'Amore 		dev_err(pvs->dip, CE_WARN,
1532c0586b87SGarrett D'Amore 		    "!failed to allocate DMA handle for SG list");
1533c0586b87SGarrett D'Amore 		return (B_FALSE);
1534c0586b87SGarrett D'Amore 	}
1535c0586b87SGarrett D'Amore 	if (ddi_dma_mem_alloc(cmd->sgl_dmah, PAGE_SIZE, &pvscsi_dma_attrs,
1536c0586b87SGarrett D'Amore 	    DDI_DMA_CONSISTENT, cb, NULL, &kaddr, &len, &cmd->sgl_acch) !=
1537c0586b87SGarrett D'Amore 	    DDI_SUCCESS) {
1538c0586b87SGarrett D'Amore 		dev_err(pvs->dip, CE_WARN,
1539c0586b87SGarrett D'Amore 		    "!failed to allocate DMA memory for SG list");
1540c0586b87SGarrett D'Amore 		return (B_FALSE);
1541c0586b87SGarrett D'Amore 	}
1542c0586b87SGarrett D'Amore 	cmd->sgl = (void *)kaddr;
1543c0586b87SGarrett D'Amore 	if (ddi_dma_addr_bind_handle(cmd->sgl_dmah, NULL, kaddr,
1544c0586b87SGarrett D'Amore 	    PAGE_SIZE, DDI_DMA_WRITE | DDI_DMA_CONSISTENT, cb, NULL,
1545c0586b87SGarrett D'Amore 	    NULL, NULL) != DDI_DMA_MAPPED) {
1546c0586b87SGarrett D'Amore 		dev_err(pvs->dip, CE_WARN, "!failed to bind SGL list");
1547c0586b87SGarrett D'Amore 		return (B_FALSE);
1548c0586b87SGarrett D'Amore 	}
1549c0586b87SGarrett D'Amore 	cmd->sgl_pa = ddi_dma_cookie_one(cmd->sgl_dmah)->dmac_laddress;
1550c0586b87SGarrett D'Amore 
1551c0586b87SGarrett D'Amore 	/* allocate DMA resource for auto-sense-request */
1552c0586b87SGarrett D'Amore 	if (ddi_dma_alloc_handle(pvs->dip, &pvscsi_dma_attr,
1553c0586b87SGarrett D'Amore 	    cb, NULL, &cmd->arq_dmah) != DDI_SUCCESS) {
1554c0586b87SGarrett D'Amore 		dev_err(pvs->dip, CE_WARN,
1555c0586b87SGarrett D'Amore 		    "!failed to allocate DMA handle for ARQ buffer");
1556c0586b87SGarrett D'Amore 		return (B_FALSE);
1557c0586b87SGarrett D'Amore 	}
1558c0586b87SGarrett D'Amore 
1559c0586b87SGarrett D'Amore 	if (ddi_dma_addr_bind_handle(cmd->arq_dmah, NULL,
1560c0586b87SGarrett D'Amore 	    (void *)cmd->arq_sense, SENSE_LENGTH,
1561c0586b87SGarrett D'Amore 	    DDI_DMA_READ | DDI_DMA_CONSISTENT, cb, NULL,
1562c0586b87SGarrett D'Amore 	    NULL, NULL) != DDI_DMA_MAPPED) {
1563c0586b87SGarrett D'Amore 		dev_err(pvs->dip, CE_WARN, "!failed to bind ARQ buffer");
1564c0586b87SGarrett D'Amore 		return (B_FALSE);
1565c0586b87SGarrett D'Amore 	}
1566c0586b87SGarrett D'Amore 	cmd->arq_pa = ddi_dma_cookie_one(cmd->arq_dmah)->dmac_laddress;
1567c0586b87SGarrett D'Amore 	return (B_TRUE);
1568c0586b87SGarrett D'Amore }
1569c0586b87SGarrett D'Amore 
1570c0586b87SGarrett D'Amore static int
pvscsi_pkt_ctor(struct scsi_pkt * pkt,scsi_hba_tran_t * tran,int sleep)1571c0586b87SGarrett D'Amore pvscsi_pkt_ctor(struct scsi_pkt *pkt, scsi_hba_tran_t *tran, int sleep)
1572c0586b87SGarrett D'Amore {
1573c0586b87SGarrett D'Amore 	pvscsi_cmd_t	*cmd = pkt->pkt_ha_private;
1574c0586b87SGarrett D'Amore 	pvscsi_softc_t	*pvs = tran->tran_hba_private;
1575c0586b87SGarrett D'Amore 
1576c0586b87SGarrett D'Amore 	if (!pvscsi_cmd_init(pvs, cmd, sleep)) {
1577c0586b87SGarrett D'Amore 		pvscsi_pkt_dtor(pkt, tran);
1578c0586b87SGarrett D'Amore 		return (-1);
1579c0586b87SGarrett D'Amore 	}
1580c0586b87SGarrett D'Amore 	cmd->pkt = pkt;
1581c0586b87SGarrett D'Amore 	return (0);
1582c0586b87SGarrett D'Amore }
1583c0586b87SGarrett D'Amore 
1584c0586b87SGarrett D'Amore static void
pvscsi_teardown_pkt(struct scsi_pkt * pkt)1585c0586b87SGarrett D'Amore pvscsi_teardown_pkt(struct scsi_pkt *pkt)
1586c0586b87SGarrett D'Amore {
1587c0586b87SGarrett D'Amore 	_NOTE(ARGUNUSED(pkt));
1588c0586b87SGarrett D'Amore 	/* nothing to do */
1589c0586b87SGarrett D'Amore }
1590c0586b87SGarrett D'Amore 
1591c0586b87SGarrett D'Amore static int
pvscsi_setup_pkt(struct scsi_pkt * pkt,int (* cb)(caddr_t),caddr_t arg)1592c0586b87SGarrett D'Amore pvscsi_setup_pkt(struct scsi_pkt *pkt, int (*cb)(caddr_t), caddr_t arg)
1593c0586b87SGarrett D'Amore {
1594c0586b87SGarrett D'Amore 	/* all work is done in start */
1595c0586b87SGarrett D'Amore 	return (0);
1596c0586b87SGarrett D'Amore }
1597c0586b87SGarrett D'Amore 
1598c0586b87SGarrett D'Amore static int
pvscsi_hba_setup(pvscsi_softc_t * pvs)1599c0586b87SGarrett D'Amore pvscsi_hba_setup(pvscsi_softc_t *pvs)
1600c0586b87SGarrett D'Amore {
1601c0586b87SGarrett D'Amore 	scsi_hba_tran_t	*tran;
1602c0586b87SGarrett D'Amore 
1603c0586b87SGarrett D'Amore 	tran = scsi_hba_tran_alloc(pvs->dip, SCSI_HBA_CANSLEEP);
1604c0586b87SGarrett D'Amore 	ASSERT(tran != NULL);
1605c0586b87SGarrett D'Amore 
1606c0586b87SGarrett D'Amore 	tran->tran_hba_private = pvs;
1607c0586b87SGarrett D'Amore 	tran->tran_start = pvscsi_start;
1608c0586b87SGarrett D'Amore 	tran->tran_reset = pvscsi_reset;
1609c0586b87SGarrett D'Amore 	tran->tran_abort = pvscsi_abort;
1610c0586b87SGarrett D'Amore 	tran->tran_getcap = pvscsi_getcap;
1611c0586b87SGarrett D'Amore 	tran->tran_setcap = pvscsi_setcap;
1612c0586b87SGarrett D'Amore 	tran->tran_pkt_constructor = pvscsi_pkt_ctor;
1613c0586b87SGarrett D'Amore 	tran->tran_pkt_destructor = pvscsi_pkt_dtor;
1614c0586b87SGarrett D'Amore 	tran->tran_setup_pkt = pvscsi_setup_pkt;
1615c0586b87SGarrett D'Amore 	tran->tran_teardown_pkt = pvscsi_teardown_pkt;
1616c0586b87SGarrett D'Amore 	tran->tran_tgt_init = pvscsi_tgt_init;
1617c0586b87SGarrett D'Amore 	tran->tran_tgt_free = pvscsi_tgt_free;
1618c0586b87SGarrett D'Amore 	tran->tran_hba_len = sizeof (pvscsi_cmd_t);
1619c0586b87SGarrett D'Amore 
1620c0586b87SGarrett D'Amore 	tran->tran_interconnect_type = INTERCONNECT_PARALLEL;
1621c0586b87SGarrett D'Amore 
1622c0586b87SGarrett D'Amore 	if (scsi_hba_attach_setup(pvs->dip, &pvscsi_io_dma_attr, tran,
1623c0586b87SGarrett D'Amore 	    SCSI_HBA_HBA | SCSI_HBA_TRAN_CDB | SCSI_HBA_TRAN_SCB |
1624c0586b87SGarrett D'Amore 	    SCSI_HBA_ADDR_COMPLEX) !=
1625c0586b87SGarrett D'Amore 	    DDI_SUCCESS) {
1626c0586b87SGarrett D'Amore 		scsi_hba_tran_free(tran);
1627c0586b87SGarrett D'Amore 		dev_err(pvs->dip, CE_WARN, "!failed to attach HBA");
1628c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
1629c0586b87SGarrett D'Amore 	}
1630c0586b87SGarrett D'Amore 
1631c0586b87SGarrett D'Amore 	pvs->tran = tran;
1632c0586b87SGarrett D'Amore 	return (DDI_SUCCESS);
1633c0586b87SGarrett D'Amore }
1634c0586b87SGarrett D'Amore 
1635c0586b87SGarrett D'Amore static void
pvscsi_teardown(pvscsi_softc_t * pvs)1636c0586b87SGarrett D'Amore pvscsi_teardown(pvscsi_softc_t *pvs)
1637c0586b87SGarrett D'Amore {
1638c0586b87SGarrett D'Amore 	timeout_id_t	tid;
1639c0586b87SGarrett D'Amore 
1640c0586b87SGarrett D'Amore 	pvscsi_stop_hba(pvs);
1641c0586b87SGarrett D'Amore 
1642c0586b87SGarrett D'Amore 	if (pvs->tq != NULL) {
1643c0586b87SGarrett D'Amore 		ddi_taskq_destroy(pvs->tq);
1644c0586b87SGarrett D'Amore 	}
1645c0586b87SGarrett D'Amore 	mutex_enter(&pvs->lock);
1646c0586b87SGarrett D'Amore 	tid = pvs->timeout;
1647c0586b87SGarrett D'Amore 	pvs->timeout = 0;
1648c0586b87SGarrett D'Amore 	mutex_exit(&pvs->lock);
1649c0586b87SGarrett D'Amore 
1650c0586b87SGarrett D'Amore 	if (tid != 0) {
1651c0586b87SGarrett D'Amore 		(void) untimeout(tid);
1652c0586b87SGarrett D'Amore 	}
1653c0586b87SGarrett D'Amore 
1654c0586b87SGarrett D'Amore 	pvscsi_free_intrs(pvs);
1655c0586b87SGarrett D'Amore 	pvscsi_free_rings(pvs);
1656c0586b87SGarrett D'Amore 
1657c0586b87SGarrett D'Amore 	if (pvs->mmio_handle != NULL) {
1658c0586b87SGarrett D'Amore 		ddi_regs_map_free(&pvs->mmio_handle);
1659c0586b87SGarrett D'Amore 	}
1660c0586b87SGarrett D'Amore 
1661c0586b87SGarrett D'Amore 	if (pvs->tran != NULL) {
1662c0586b87SGarrett D'Amore 		scsi_hba_tran_free(pvs->tran);
1663c0586b87SGarrett D'Amore 	}
1664c0586b87SGarrett D'Amore 	mutex_destroy(&pvs->lock);
1665c0586b87SGarrett D'Amore 	list_destroy(&pvs->cmd_queue);
1666c0586b87SGarrett D'Amore 	list_destroy(&pvs->devices);
1667c0586b87SGarrett D'Amore 
1668c0586b87SGarrett D'Amore 	kmem_free(pvs, sizeof (*pvs));
1669c0586b87SGarrett D'Amore }
1670c0586b87SGarrett D'Amore 
1671c0586b87SGarrett D'Amore static int
pvscsi_iport_attach(dev_info_t * dip)1672c0586b87SGarrett D'Amore pvscsi_iport_attach(dev_info_t *dip)
1673c0586b87SGarrett D'Amore {
1674c0586b87SGarrett D'Amore 	scsi_hba_tran_t	*tran;
1675c0586b87SGarrett D'Amore 	dev_info_t	*parent;
1676c0586b87SGarrett D'Amore 	pvscsi_softc_t	*pvs;
1677c0586b87SGarrett D'Amore 	char		*ua;
1678c0586b87SGarrett D'Amore 	uint32_t	max_targets;
1679c0586b87SGarrett D'Amore 
1680c0586b87SGarrett D'Amore 	if (((parent = ddi_get_parent(dip)) == NULL) ||
1681c0586b87SGarrett D'Amore 	    ((tran = ddi_get_driver_private(parent)) == NULL) ||
1682c0586b87SGarrett D'Amore 	    ((pvs = tran->tran_hba_private) == NULL) ||
1683c0586b87SGarrett D'Amore 	    ((ua = scsi_hba_iport_unit_address(dip)) == NULL) ||
1684c0586b87SGarrett D'Amore 	    (strcmp(ua, "iport0") != 0)) {
1685c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
1686c0586b87SGarrett D'Amore 	}
1687c0586b87SGarrett D'Amore 
1688c0586b87SGarrett D'Amore 	/* store our softc on the iport private tran */
1689c0586b87SGarrett D'Amore 	tran = ddi_get_driver_private(dip);
1690c0586b87SGarrett D'Amore 	tran->tran_hba_private = pvs;
1691c0586b87SGarrett D'Amore 
1692c0586b87SGarrett D'Amore 	/* setup the target map - allow 100ms for settle / sync times */
1693c0586b87SGarrett D'Amore 	if (scsi_hba_tgtmap_create(dip, SCSI_TM_PERADDR, 100000,
1694c0586b87SGarrett D'Amore 	    100000, pvs, NULL, NULL, &pvs->tgtmap) != DDI_SUCCESS) {
1695c0586b87SGarrett D'Amore 		dev_err(pvs->dip, CE_WARN, "!failed to create target map");
1696c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
1697c0586b87SGarrett D'Amore 	}
1698c0586b87SGarrett D'Amore 
1699c0586b87SGarrett D'Amore 	/* reset hardware and setup the rings */
1700c0586b87SGarrett D'Amore 	mutex_enter(&pvs->lock);
1701c0586b87SGarrett D'Amore 	pvs->detach = B_FALSE; /* in case of reattach */
1702c0586b87SGarrett D'Amore 	pvscsi_start_hba(pvs);
1703c0586b87SGarrett D'Amore 
1704c0586b87SGarrett D'Amore 	max_targets = pvs->max_targets = pvscsi_max_targets(pvs);
1705c0586b87SGarrett D'Amore 	mutex_exit(&pvs->lock);
1706c0586b87SGarrett D'Amore 
1707c0586b87SGarrett D'Amore 	for (uint32_t i = 0; i < max_targets; i++) {
1708c0586b87SGarrett D'Amore 		char addr[8];
1709c0586b87SGarrett D'Amore 		if (pvscsi_probe_target(pvs, i)) {
1710c0586b87SGarrett D'Amore 			(void) snprintf(addr, sizeof (addr), "%x", i);
1711c0586b87SGarrett D'Amore 			(void) scsi_hba_tgtmap_tgt_add(pvs->tgtmap,
1712c0586b87SGarrett D'Amore 			    SCSI_TGT_SCSI_DEVICE, addr, NULL);
1713c0586b87SGarrett D'Amore 		}
1714c0586b87SGarrett D'Amore 	}
1715c0586b87SGarrett D'Amore 
1716c0586b87SGarrett D'Amore 	return (DDI_SUCCESS);
1717c0586b87SGarrett D'Amore }
1718c0586b87SGarrett D'Amore 
1719c0586b87SGarrett D'Amore static int
pvscsi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)1720c0586b87SGarrett D'Amore pvscsi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1721c0586b87SGarrett D'Amore {
1722c0586b87SGarrett D'Amore 	pvscsi_softc_t	*pvs;
1723c0586b87SGarrett D'Amore 
1724c0586b87SGarrett D'Amore 	if (cmd != DDI_ATTACH) {
1725c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
1726c0586b87SGarrett D'Amore 	}
1727c0586b87SGarrett D'Amore 
1728c0586b87SGarrett D'Amore 	if (scsi_hba_iport_unit_address(dip) != NULL) {
1729c0586b87SGarrett D'Amore 		return (pvscsi_iport_attach(dip));
1730c0586b87SGarrett D'Amore 	}
1731c0586b87SGarrett D'Amore 
1732c0586b87SGarrett D'Amore 	pvs = kmem_zalloc(sizeof (*pvs), KM_SLEEP);
1733c0586b87SGarrett D'Amore 
1734c0586b87SGarrett D'Amore 	/* Setup HBA instance */
1735c0586b87SGarrett D'Amore 	pvs->dip = dip;
1736c0586b87SGarrett D'Amore 
1737c0586b87SGarrett D'Amore 	/*
1738c0586b87SGarrett D'Amore 	 * mutex initialization - note that we always run below
1739c0586b87SGarrett D'Amore 	 * lock level, so we can get by without interrupt priorities
1740c0586b87SGarrett D'Amore 	 */
1741c0586b87SGarrett D'Amore 	mutex_init(&pvs->lock, NULL, MUTEX_DRIVER, NULL);
1742c0586b87SGarrett D'Amore 	list_create(&pvs->cmd_queue, sizeof (pvscsi_cmd_t),
1743c0586b87SGarrett D'Amore 	    offsetof(pvscsi_cmd_t, queue_node));
1744c0586b87SGarrett D'Amore 	list_create(&pvs->devices, sizeof (pvscsi_device_t),
1745c0586b87SGarrett D'Amore 	    offsetof(pvscsi_device_t, node));
1746c0586b87SGarrett D'Amore 
1747c0586b87SGarrett D'Amore 	if ((pvscsi_setup_io(pvs)) != DDI_SUCCESS) {
1748c0586b87SGarrett D'Amore 		dev_err(pvs->dip, CE_WARN, "!failed to setup I/O region");
1749c0586b87SGarrett D'Amore 		pvscsi_teardown(pvs);
1750c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
1751c0586b87SGarrett D'Amore 	}
1752c0586b87SGarrett D'Amore 
1753c0586b87SGarrett D'Amore 	pvscsi_stop_hba(pvs);
1754c0586b87SGarrett D'Amore 
1755c0586b87SGarrett D'Amore 	if ((pvscsi_allocate_rings(pvs)) != DDI_SUCCESS) {
1756c0586b87SGarrett D'Amore 		dev_err(pvs->dip, CE_WARN, "!failed to allocate DMA rings");
1757c0586b87SGarrett D'Amore 		pvscsi_teardown(pvs);
1758c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
1759c0586b87SGarrett D'Amore 	}
1760c0586b87SGarrett D'Amore 
1761c0586b87SGarrett D'Amore 	if (pvscsi_setup_isr(pvs) != DDI_SUCCESS) {
1762c0586b87SGarrett D'Amore 		dev_err(pvs->dip, CE_WARN, "!failed to setup ISR");
1763c0586b87SGarrett D'Amore 		pvscsi_teardown(pvs);
1764c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
1765c0586b87SGarrett D'Amore 	}
1766c0586b87SGarrett D'Amore 
1767c0586b87SGarrett D'Amore 	/* enable interrupts */
1768c0586b87SGarrett D'Amore 	if (pvscsi_enable_intrs(pvs) != DDI_SUCCESS) {
1769c0586b87SGarrett D'Amore 		dev_err(pvs->dip, CE_WARN, "!failed to enable interrupts");
1770c0586b87SGarrett D'Amore 		pvscsi_teardown(pvs);
1771c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
1772c0586b87SGarrett D'Amore 	}
1773c0586b87SGarrett D'Amore 
1774c0586b87SGarrett D'Amore 	pvs->tq = ddi_taskq_create(dip, "iport", 1, TASKQ_DEFAULTPRI, 0);
1775c0586b87SGarrett D'Amore 	if (pvs->tq == NULL) {
1776c0586b87SGarrett D'Amore 		dev_err(pvs->dip, CE_WARN, "!failed creating tq");
1777c0586b87SGarrett D'Amore 		pvscsi_teardown(pvs);
1778c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
1779c0586b87SGarrett D'Amore 	}
1780c0586b87SGarrett D'Amore 	if (pvscsi_hba_setup(pvs) != DDI_SUCCESS) {
1781c0586b87SGarrett D'Amore 		dev_err(pvs->dip, CE_WARN, "!failed to setup HBA");
1782c0586b87SGarrett D'Amore 		pvscsi_teardown(pvs);
1783c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
1784c0586b87SGarrett D'Amore 	}
1785c0586b87SGarrett D'Amore 
1786c0586b87SGarrett D'Amore 	if (scsi_hba_iport_register(dip, "iport0") != 0) {
1787c0586b87SGarrett D'Amore 		dev_err(pvs->dip, CE_WARN, "failed to register iport");
1788c0586b87SGarrett D'Amore 		/* detach cannot fail since we didn't setup the iport */
1789c0586b87SGarrett D'Amore 		(void) scsi_hba_detach(dip);
1790c0586b87SGarrett D'Amore 		pvscsi_teardown(pvs);
1791c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
1792c0586b87SGarrett D'Amore 	}
1793c0586b87SGarrett D'Amore 
1794c0586b87SGarrett D'Amore 	return (DDI_SUCCESS);
1795c0586b87SGarrett D'Amore }
1796c0586b87SGarrett D'Amore 
1797c0586b87SGarrett D'Amore static int
pvscsi_iport_detach(dev_info_t * dip)1798c0586b87SGarrett D'Amore pvscsi_iport_detach(dev_info_t *dip)
1799c0586b87SGarrett D'Amore {
1800c0586b87SGarrett D'Amore 	pvscsi_softc_t	*pvs;
1801c0586b87SGarrett D'Amore 	scsi_hba_tran_t	*tran;
1802c0586b87SGarrett D'Amore 	const char	*ua;
1803c0586b87SGarrett D'Amore 	pvscsi_cmd_t	*reclaimed;
1804c0586b87SGarrett D'Amore 
1805c0586b87SGarrett D'Amore 	if (((ua = scsi_hba_iport_unit_address(dip)) == NULL) ||
1806c0586b87SGarrett D'Amore 	    (strcmp(ua, "iport0") != 0) ||
1807c0586b87SGarrett D'Amore 	    ((tran = ddi_get_driver_private(dip)) == NULL) ||
1808c0586b87SGarrett D'Amore 	    ((pvs = tran->tran_hba_private) == NULL)) {
1809c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
1810c0586b87SGarrett D'Amore 	}
1811c0586b87SGarrett D'Amore 
1812c0586b87SGarrett D'Amore 	/* stop the HBA */
1813c0586b87SGarrett D'Amore 	mutex_enter(&pvs->lock);
1814c0586b87SGarrett D'Amore 	pvs->detach = B_TRUE;
1815c0586b87SGarrett D'Amore 	pvscsi_stop_hba(pvs);
1816c0586b87SGarrett D'Amore 	mutex_exit(&pvs->lock);
1817c0586b87SGarrett D'Amore 
1818c0586b87SGarrett D'Amore 	/* drain the taskq - nothing else will post to it */
1819c0586b87SGarrett D'Amore 	ddi_taskq_wait(pvs->tq);
1820c0586b87SGarrett D'Amore 
1821c0586b87SGarrett D'Amore 	/* reset the HBA */
1822c0586b87SGarrett D'Amore 	mutex_enter(&pvs->lock);
1823c0586b87SGarrett D'Amore 	reclaimed = pvscsi_reclaim_cmds(pvs);
1824c0586b87SGarrett D'Amore 	mutex_exit(&pvs->lock);
1825c0586b87SGarrett D'Amore 
1826c0586b87SGarrett D'Amore 	/*
1827c0586b87SGarrett D'Amore 	 * If we had any commands, complete them so we can
1828c0586b87SGarrett D'Amore 	 * reclaim the resources.  There really should not be any.
1829c0586b87SGarrett D'Amore 	 */
1830c0586b87SGarrett D'Amore 	pvscsi_complete_cmds(pvs, reclaimed);
1831c0586b87SGarrett D'Amore 
1832c0586b87SGarrett D'Amore 	scsi_hba_tgtmap_destroy(pvs->tgtmap);
1833c0586b87SGarrett D'Amore 	pvs->tgtmap = NULL;
1834c0586b87SGarrett D'Amore 
1835c0586b87SGarrett D'Amore 	return (DDI_SUCCESS);
1836c0586b87SGarrett D'Amore }
1837c0586b87SGarrett D'Amore 
1838c0586b87SGarrett D'Amore static int
pvscsi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)1839c0586b87SGarrett D'Amore pvscsi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
1840c0586b87SGarrett D'Amore {
1841c0586b87SGarrett D'Amore 	pvscsi_softc_t	*pvs;
1842c0586b87SGarrett D'Amore 	scsi_hba_tran_t	*tran;
1843c0586b87SGarrett D'Amore 
1844c0586b87SGarrett D'Amore 	if (cmd != DDI_DETACH) {
1845c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
1846c0586b87SGarrett D'Amore 	}
1847c0586b87SGarrett D'Amore 
1848c0586b87SGarrett D'Amore 	if (scsi_hba_iport_unit_address(dip) != NULL) {
1849c0586b87SGarrett D'Amore 		return (pvscsi_iport_detach(dip));
1850c0586b87SGarrett D'Amore 	}
1851c0586b87SGarrett D'Amore 
1852c0586b87SGarrett D'Amore 	if (((tran = ddi_get_driver_private(dip)) == NULL) ||
1853c0586b87SGarrett D'Amore 	    ((pvs = tran->tran_hba_private) == NULL)) {
1854c0586b87SGarrett D'Amore 		/* this can only mean we aren't attached yet */
1855c0586b87SGarrett D'Amore 		return (DDI_SUCCESS);
1856c0586b87SGarrett D'Amore 	}
1857c0586b87SGarrett D'Amore 	if (scsi_hba_detach(dip) != DDI_SUCCESS) {
1858c0586b87SGarrett D'Amore 		return (DDI_FAILURE);
1859c0586b87SGarrett D'Amore 	}
1860c0586b87SGarrett D'Amore 
1861c0586b87SGarrett D'Amore 	pvscsi_teardown(pvs);
1862c0586b87SGarrett D'Amore 
1863c0586b87SGarrett D'Amore 	return (DDI_SUCCESS);
1864c0586b87SGarrett D'Amore }
1865c0586b87SGarrett D'Amore 
1866c0586b87SGarrett D'Amore static int
pvscsi_quiesce(dev_info_t * dip)1867c0586b87SGarrett D'Amore pvscsi_quiesce(dev_info_t *dip)
1868c0586b87SGarrett D'Amore {
1869c0586b87SGarrett D'Amore 	scsi_hba_tran_t	*tran;
1870c0586b87SGarrett D'Amore 	pvscsi_softc_t	*pvs;
1871c0586b87SGarrett D'Amore 
1872c0586b87SGarrett D'Amore 	if (((tran = ddi_get_driver_private(dip)) == NULL) ||
1873c0586b87SGarrett D'Amore 	    ((pvs = tran->tran_hba_private) == NULL)) {
1874c0586b87SGarrett D'Amore 		return (DDI_SUCCESS);
1875c0586b87SGarrett D'Amore 	}
1876c0586b87SGarrett D'Amore 
1877c0586b87SGarrett D'Amore 	pvscsi_stop_hba(pvs);
1878c0586b87SGarrett D'Amore 
1879c0586b87SGarrett D'Amore 	return (DDI_SUCCESS);
1880c0586b87SGarrett D'Amore }
1881c0586b87SGarrett D'Amore 
1882c0586b87SGarrett D'Amore static struct dev_ops pvscsi_ops = {
1883c0586b87SGarrett D'Amore 	.devo_rev =	DEVO_REV,
1884c0586b87SGarrett D'Amore 	.devo_refcnt =	0,
1885c0586b87SGarrett D'Amore 	.devo_getinfo =	nodev,
1886c0586b87SGarrett D'Amore 	.devo_identify = nulldev,
1887c0586b87SGarrett D'Amore 	.devo_probe =	nulldev,
1888c0586b87SGarrett D'Amore 	.devo_attach =	pvscsi_attach,
1889c0586b87SGarrett D'Amore 	.devo_detach =	pvscsi_detach,
1890c0586b87SGarrett D'Amore 	.devo_reset =	nodev,
1891c0586b87SGarrett D'Amore 	.devo_cb_ops =	NULL,
1892c0586b87SGarrett D'Amore 	.devo_bus_ops =	NULL,
1893c0586b87SGarrett D'Amore 	.devo_power =	NULL,
1894c0586b87SGarrett D'Amore 	.devo_quiesce =	pvscsi_quiesce
1895c0586b87SGarrett D'Amore };
1896c0586b87SGarrett D'Amore 
1897c0586b87SGarrett D'Amore #define	PVSCSI_IDENT "VMware PVSCSI"
1898c0586b87SGarrett D'Amore 
1899c0586b87SGarrett D'Amore static struct modldrv modldrv = {
1900c0586b87SGarrett D'Amore 	&mod_driverops,
1901c0586b87SGarrett D'Amore 	PVSCSI_IDENT,
1902c0586b87SGarrett D'Amore 	&pvscsi_ops,
1903c0586b87SGarrett D'Amore };
1904c0586b87SGarrett D'Amore 
1905c0586b87SGarrett D'Amore static struct modlinkage modlinkage = {
1906c0586b87SGarrett D'Amore 	MODREV_1,
1907c0586b87SGarrett D'Amore 	&modldrv,
1908c0586b87SGarrett D'Amore 	NULL
1909c0586b87SGarrett D'Amore };
1910c0586b87SGarrett D'Amore 
1911c0586b87SGarrett D'Amore int
_init(void)1912c0586b87SGarrett D'Amore _init(void)
1913c0586b87SGarrett D'Amore {
1914c0586b87SGarrett D'Amore 	int	ret;
1915c0586b87SGarrett D'Amore 
1916c0586b87SGarrett D'Amore 	/* get HZ - DDI compliant */
1917c0586b87SGarrett D'Amore 	pvscsi_hz = drv_usectohz(1000000);
1918c0586b87SGarrett D'Amore 
1919c0586b87SGarrett D'Amore 	if ((ret = scsi_hba_init(&modlinkage)) != 0) {
1920c0586b87SGarrett D'Amore 		cmn_err(CE_WARN, "!scsi_hba_init() failed");
1921c0586b87SGarrett D'Amore 		return (ret);
1922c0586b87SGarrett D'Amore 	}
1923c0586b87SGarrett D'Amore 
1924c0586b87SGarrett D'Amore 	if ((ret = mod_install(&modlinkage)) != 0) {
1925c0586b87SGarrett D'Amore 		cmn_err(CE_WARN, "!mod_install() failed");
1926c0586b87SGarrett D'Amore 		scsi_hba_fini(&modlinkage);
1927c0586b87SGarrett D'Amore 	}
1928c0586b87SGarrett D'Amore 
1929c0586b87SGarrett D'Amore 	return (ret);
1930c0586b87SGarrett D'Amore }
1931c0586b87SGarrett D'Amore 
1932c0586b87SGarrett D'Amore int
_info(struct modinfo * modinfop)1933c0586b87SGarrett D'Amore _info(struct modinfo *modinfop)
1934c0586b87SGarrett D'Amore {
1935c0586b87SGarrett D'Amore 	return (mod_info(&modlinkage, modinfop));
1936c0586b87SGarrett D'Amore }
1937c0586b87SGarrett D'Amore 
1938c0586b87SGarrett D'Amore int
_fini(void)1939c0586b87SGarrett D'Amore _fini(void)
1940c0586b87SGarrett D'Amore {
1941c0586b87SGarrett D'Amore 	int	ret;
1942c0586b87SGarrett D'Amore 
1943c0586b87SGarrett D'Amore 	if ((ret = mod_remove(&modlinkage)) == 0) {
1944c0586b87SGarrett D'Amore 		scsi_hba_fini(&modlinkage);
1945c0586b87SGarrett D'Amore 	}
1946c0586b87SGarrett D'Amore 
1947c0586b87SGarrett D'Amore 	return (ret);
1948c0586b87SGarrett D'Amore }
1949