1255927Snwhitehorn/*-
2255927Snwhitehorn * Copyright 2013 Nathan Whitehorn
3255927Snwhitehorn * All rights reserved.
4255927Snwhitehorn *
5255927Snwhitehorn * Redistribution and use in source and binary forms, with or without
6255927Snwhitehorn * modification, are permitted provided that the following conditions
7255927Snwhitehorn * are met:
8255927Snwhitehorn * 1. Redistributions of source code must retain the above copyright
9255927Snwhitehorn *    notice, this list of conditions and the following disclaimer.
10255927Snwhitehorn * 2. Redistributions in binary form must reproduce the above copyright
11255927Snwhitehorn *    notice, this list of conditions and the following disclaimer in the
12255927Snwhitehorn *    documentation and/or other materials provided with the distribution.
13255927Snwhitehorn *
14255927Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15255927Snwhitehorn * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16255927Snwhitehorn * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17255927Snwhitehorn * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18255927Snwhitehorn * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19255927Snwhitehorn * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20255927Snwhitehorn * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21255927Snwhitehorn * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22255927Snwhitehorn * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23255927Snwhitehorn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24255927Snwhitehorn * SUCH DAMAGE.
25255927Snwhitehorn */
26255927Snwhitehorn
27255927Snwhitehorn#include <sys/cdefs.h>
28255927Snwhitehorn__FBSDID("$FreeBSD: stable/11/sys/powerpc/pseries/phyp_vscsi.c 315812 2017-03-23 06:40:20Z mav $");
29255927Snwhitehorn
30255927Snwhitehorn#include <sys/param.h>
31255927Snwhitehorn#include <sys/systm.h>
32255927Snwhitehorn#include <sys/kernel.h>
33255927Snwhitehorn#include <sys/malloc.h>
34255927Snwhitehorn#include <sys/module.h>
35255927Snwhitehorn#include <sys/selinfo.h>
36255927Snwhitehorn#include <sys/bus.h>
37255927Snwhitehorn#include <sys/conf.h>
38255927Snwhitehorn#include <sys/eventhandler.h>
39255927Snwhitehorn#include <sys/rman.h>
40255927Snwhitehorn#include <sys/bus_dma.h>
41255927Snwhitehorn#include <sys/bio.h>
42255927Snwhitehorn#include <sys/ioccom.h>
43255927Snwhitehorn#include <sys/uio.h>
44255927Snwhitehorn#include <sys/proc.h>
45255927Snwhitehorn#include <sys/signalvar.h>
46255927Snwhitehorn#include <sys/sysctl.h>
47255927Snwhitehorn#include <sys/endian.h>
48255927Snwhitehorn#include <sys/vmem.h>
49255927Snwhitehorn
50255927Snwhitehorn#include <cam/cam.h>
51255927Snwhitehorn#include <cam/cam_ccb.h>
52255927Snwhitehorn#include <cam/cam_debug.h>
53255927Snwhitehorn#include <cam/cam_periph.h>
54255927Snwhitehorn#include <cam/cam_sim.h>
55255927Snwhitehorn#include <cam/cam_xpt_periph.h>
56255927Snwhitehorn#include <cam/cam_xpt_sim.h>
57255927Snwhitehorn#include <cam/scsi/scsi_all.h>
58255927Snwhitehorn#include <cam/scsi/scsi_message.h>
59255927Snwhitehorn
60255927Snwhitehorn#include <dev/ofw/openfirm.h>
61255927Snwhitehorn#include <dev/ofw/ofw_bus.h>
62255927Snwhitehorn#include <dev/ofw/ofw_bus_subr.h>
63255927Snwhitehorn
64255927Snwhitehorn#include <machine/bus.h>
65255927Snwhitehorn#include <machine/resource.h>
66255927Snwhitehorn
67255927Snwhitehorn#include <powerpc/pseries/phyp-hvcall.h>
68255927Snwhitehorn
69255927Snwhitehornstruct vscsi_softc;
70255927Snwhitehorn
71255927Snwhitehorn/* VSCSI CRQ format from table 260 of PAPR spec 2.4 (page 760) */
72255927Snwhitehornstruct vscsi_crq {
73255927Snwhitehorn	uint8_t valid;
74255927Snwhitehorn	uint8_t format;
75255927Snwhitehorn	uint8_t reserved;
76255927Snwhitehorn	uint8_t status;
77255927Snwhitehorn	uint16_t timeout;
78255927Snwhitehorn	uint16_t iu_length;
79255927Snwhitehorn	uint64_t iu_data;
80255927Snwhitehorn};
81255927Snwhitehorn
82255927Snwhitehornstruct vscsi_xfer {
83255927Snwhitehorn        TAILQ_ENTRY(vscsi_xfer) queue;
84255927Snwhitehorn        struct vscsi_softc *sc;
85255927Snwhitehorn        union ccb *ccb;
86255927Snwhitehorn        bus_dmamap_t dmamap;
87255927Snwhitehorn        uint64_t tag;
88255927Snwhitehorn
89255927Snwhitehorn	vmem_addr_t srp_iu_offset;
90255927Snwhitehorn	vmem_size_t srp_iu_size;
91255927Snwhitehorn};
92255927Snwhitehorn
93255927SnwhitehornTAILQ_HEAD(vscsi_xferq, vscsi_xfer);
94255927Snwhitehorn
95255927Snwhitehornstruct vscsi_softc {
96255927Snwhitehorn	device_t	dev;
97255927Snwhitehorn	struct cam_devq *devq;
98255927Snwhitehorn	struct cam_sim	*sim;
99255927Snwhitehorn	struct cam_path	*path;
100255927Snwhitehorn	struct mtx io_lock;
101255927Snwhitehorn
102255927Snwhitehorn	cell_t		unit;
103255927Snwhitehorn	int		bus_initialized;
104255927Snwhitehorn	int		bus_logged_in;
105255927Snwhitehorn	int		max_transactions;
106255927Snwhitehorn
107255927Snwhitehorn	int		irqid;
108255927Snwhitehorn	struct resource	*irq;
109255927Snwhitehorn	void		*irq_cookie;
110255927Snwhitehorn
111255927Snwhitehorn	bus_dma_tag_t	crq_tag;
112255927Snwhitehorn	struct vscsi_crq *crq_queue;
113255927Snwhitehorn	int		n_crqs, cur_crq;
114255927Snwhitehorn	bus_dmamap_t	crq_map;
115255927Snwhitehorn	bus_addr_t	crq_phys;
116255927Snwhitehorn
117255927Snwhitehorn	vmem_t		*srp_iu_arena;
118255927Snwhitehorn	void		*srp_iu_queue;
119255927Snwhitehorn	bus_addr_t	srp_iu_phys;
120255927Snwhitehorn
121255927Snwhitehorn	bus_dma_tag_t	data_tag;
122255927Snwhitehorn
123255927Snwhitehorn	struct vscsi_xfer loginxp;
124255927Snwhitehorn	struct vscsi_xfer *xfer;
125255927Snwhitehorn	struct vscsi_xferq active_xferq;
126255927Snwhitehorn	struct vscsi_xferq free_xferq;
127255927Snwhitehorn};
128255927Snwhitehorn
129255927Snwhitehornstruct srp_login {
130255927Snwhitehorn	uint8_t type;
131255927Snwhitehorn	uint8_t reserved[7];
132255927Snwhitehorn	uint64_t tag;
133255927Snwhitehorn	uint64_t max_cmd_length;
134255927Snwhitehorn	uint32_t reserved2;
135255927Snwhitehorn	uint16_t buffer_formats;
136255927Snwhitehorn	uint8_t flags;
137255927Snwhitehorn	uint8_t reserved3[5];
138255927Snwhitehorn	uint8_t initiator_port_id[16];
139255927Snwhitehorn	uint8_t target_port_id[16];
140255927Snwhitehorn} __packed;
141255927Snwhitehorn
142255927Snwhitehornstruct srp_login_rsp {
143255927Snwhitehorn	uint8_t type;
144255927Snwhitehorn	uint8_t reserved[3];
145255927Snwhitehorn	uint32_t request_limit_delta;
146255927Snwhitehorn	uint8_t tag;
147255927Snwhitehorn	uint32_t max_i_to_t_len;
148255927Snwhitehorn	uint32_t max_t_to_i_len;
149255927Snwhitehorn	uint16_t buffer_formats;
150255927Snwhitehorn	uint8_t flags;
151255927Snwhitehorn	/* Some reserved bits follow */
152255927Snwhitehorn} __packed;
153255927Snwhitehorn
154255927Snwhitehornstruct srp_cmd {
155255927Snwhitehorn	uint8_t type;
156255927Snwhitehorn	uint8_t flags1;
157255927Snwhitehorn	uint8_t reserved[3];
158255927Snwhitehorn	uint8_t formats;
159255927Snwhitehorn	uint8_t out_buffer_count;
160255927Snwhitehorn	uint8_t in_buffer_count;
161255927Snwhitehorn	uint64_t tag;
162255927Snwhitehorn	uint32_t reserved2;
163255927Snwhitehorn	uint64_t lun;
164255927Snwhitehorn	uint8_t reserved3[3];
165255927Snwhitehorn	uint8_t additional_cdb;
166255927Snwhitehorn	uint8_t cdb[16];
167255927Snwhitehorn	uint8_t data_payload[0];
168255927Snwhitehorn} __packed;
169255927Snwhitehorn
170255927Snwhitehornstruct srp_rsp {
171255927Snwhitehorn	uint8_t type;
172255927Snwhitehorn	uint8_t reserved[3];
173255927Snwhitehorn	uint32_t request_limit_delta;
174255927Snwhitehorn	uint64_t tag;
175255927Snwhitehorn	uint16_t reserved2;
176255927Snwhitehorn	uint8_t flags;
177255927Snwhitehorn	uint8_t status;
178255927Snwhitehorn	uint32_t data_out_resid;
179255927Snwhitehorn	uint32_t data_in_resid;
180255927Snwhitehorn	uint32_t sense_data_len;
181255927Snwhitehorn	uint32_t response_data_len;
182255927Snwhitehorn	uint8_t data_payload[0];
183255927Snwhitehorn} __packed;
184255927Snwhitehorn
185255927Snwhitehornstruct srp_tsk_mgmt {
186255927Snwhitehorn	uint8_t type;
187255927Snwhitehorn	uint8_t reserved[7];
188255927Snwhitehorn	uint64_t tag;
189255927Snwhitehorn	uint32_t reserved2;
190255927Snwhitehorn	uint64_t lun;
191255927Snwhitehorn	uint8_t reserved3[2];
192255927Snwhitehorn	uint8_t function;
193255927Snwhitehorn	uint8_t reserved4;
194255927Snwhitehorn	uint64_t manage_tag;
195255927Snwhitehorn	uint64_t reserved5;
196255927Snwhitehorn} __packed;
197255927Snwhitehorn
198255927Snwhitehorn/* Message code type */
199255927Snwhitehorn#define SRP_LOGIN_REQ	0x00
200255927Snwhitehorn#define SRP_TSK_MGMT	0x01
201255927Snwhitehorn#define SRP_CMD		0x02
202255927Snwhitehorn#define SRP_I_LOGOUT	0x03
203255927Snwhitehorn
204255927Snwhitehorn#define SRP_LOGIN_RSP	0xC0
205255927Snwhitehorn#define SRP_RSP		0xC1
206255927Snwhitehorn#define SRP_LOGIN_REJ	0xC2
207255927Snwhitehorn
208255927Snwhitehorn#define SRP_T_LOGOUT	0x80
209255927Snwhitehorn#define SRP_CRED_REQ	0x81
210255927Snwhitehorn#define SRP_AER_REQ	0x82
211255927Snwhitehorn
212255927Snwhitehorn#define SRP_CRED_RSP	0x41
213255927Snwhitehorn#define SRP_AER_RSP	0x41
214255927Snwhitehorn
215255927Snwhitehorn/* Flags for srp_rsp flags field */
216255927Snwhitehorn#define SRP_RSPVALID	0x01
217255927Snwhitehorn#define SRP_SNSVALID	0x02
218255927Snwhitehorn#define SRP_DOOVER	0x04
219255927Snwhitehorn#define SRP_DOUNDER	0x08
220255927Snwhitehorn#define SRP_DIOVER	0x10
221255927Snwhitehorn#define SRP_DIUNDER	0x20
222255927Snwhitehorn
223255927Snwhitehorn#define	MAD_SUCESS			0x00
224255927Snwhitehorn#define	MAD_NOT_SUPPORTED		0xf1
225255927Snwhitehorn#define	MAD_FAILED			0xf7
226255927Snwhitehorn
227255927Snwhitehorn#define	MAD_EMPTY_IU			0x01
228255927Snwhitehorn#define	MAD_ERROR_LOGGING_REQUEST	0x02
229255927Snwhitehorn#define	MAD_ADAPTER_INFO_REQUEST	0x03
230255927Snwhitehorn#define	MAD_CAPABILITIES_EXCHANGE	0x05
231255927Snwhitehorn#define	MAD_PHYS_ADAP_INFO_REQUEST	0x06
232255927Snwhitehorn#define	MAD_TAPE_PASSTHROUGH_REQUEST	0x07
233255927Snwhitehorn#define	MAD_ENABLE_FAST_FAIL		0x08
234255927Snwhitehorn
235255927Snwhitehornstatic int	vscsi_probe(device_t);
236255927Snwhitehornstatic int	vscsi_attach(device_t);
237255927Snwhitehornstatic int	vscsi_detach(device_t);
238255927Snwhitehornstatic void	vscsi_cam_action(struct cam_sim *, union ccb *);
239255927Snwhitehornstatic void	vscsi_cam_poll(struct cam_sim *);
240255927Snwhitehornstatic void	vscsi_intr(void *arg);
241255927Snwhitehornstatic void	vscsi_check_response_queue(struct vscsi_softc *sc);
242255927Snwhitehornstatic void	vscsi_setup_bus(struct vscsi_softc *sc);
243255927Snwhitehorn
244255927Snwhitehornstatic void	vscsi_srp_login(struct vscsi_softc *sc);
245255927Snwhitehornstatic void	vscsi_crq_load_cb(void *, bus_dma_segment_t *, int, int);
246255927Snwhitehornstatic void	vscsi_scsi_command(void *xxp, bus_dma_segment_t *segs,
247255927Snwhitehorn		    int nsegs, int err);
248255927Snwhitehornstatic void	vscsi_task_management(struct vscsi_softc *sc, union ccb *ccb);
249255927Snwhitehornstatic void	vscsi_srp_response(struct vscsi_xfer *, struct vscsi_crq *);
250255927Snwhitehorn
251255927Snwhitehornstatic devclass_t	vscsi_devclass;
252255927Snwhitehornstatic device_method_t	vscsi_methods[] = {
253255927Snwhitehorn	DEVMETHOD(device_probe,		vscsi_probe),
254255927Snwhitehorn	DEVMETHOD(device_attach,	vscsi_attach),
255255927Snwhitehorn	DEVMETHOD(device_detach,	vscsi_detach),
256255927Snwhitehorn
257255927Snwhitehorn	DEVMETHOD_END
258255927Snwhitehorn};
259255927Snwhitehornstatic driver_t vscsi_driver = {
260255927Snwhitehorn	"vscsi",
261255927Snwhitehorn	vscsi_methods,
262255927Snwhitehorn	sizeof(struct vscsi_softc)
263255927Snwhitehorn};
264255927SnwhitehornDRIVER_MODULE(vscsi, vdevice, vscsi_driver, vscsi_devclass, 0, 0);
265255927SnwhitehornMALLOC_DEFINE(M_VSCSI, "vscsi", "CAM device queue for VSCSI");
266255927Snwhitehorn
267255927Snwhitehornstatic int
268255927Snwhitehornvscsi_probe(device_t dev)
269255927Snwhitehorn{
270255927Snwhitehorn
271255927Snwhitehorn	if (!ofw_bus_is_compatible(dev, "IBM,v-scsi"))
272255927Snwhitehorn		return (ENXIO);
273255927Snwhitehorn
274255927Snwhitehorn	device_set_desc(dev, "POWER Hypervisor Virtual SCSI Bus");
275255927Snwhitehorn	return (0);
276255927Snwhitehorn}
277255927Snwhitehorn
278255927Snwhitehornstatic int
279255927Snwhitehornvscsi_attach(device_t dev)
280255927Snwhitehorn{
281255927Snwhitehorn	struct vscsi_softc *sc;
282255927Snwhitehorn	struct vscsi_xfer *xp;
283255927Snwhitehorn	int error, i;
284255927Snwhitehorn
285255927Snwhitehorn	sc = device_get_softc(dev);
286255927Snwhitehorn	if (sc == NULL)
287255927Snwhitehorn		return (EINVAL);
288255927Snwhitehorn
289255927Snwhitehorn	sc->dev = dev;
290255927Snwhitehorn	mtx_init(&sc->io_lock, "vscsi", NULL, MTX_DEF);
291255927Snwhitehorn
292255927Snwhitehorn	/* Get properties */
293290989Snwhitehorn	OF_getencprop(ofw_bus_get_node(dev), "reg", &sc->unit,
294290989Snwhitehorn	    sizeof(sc->unit));
295255927Snwhitehorn
296255927Snwhitehorn	/* Setup interrupt */
297255927Snwhitehorn	sc->irqid = 0;
298255927Snwhitehorn	sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid,
299255927Snwhitehorn	    RF_ACTIVE);
300255927Snwhitehorn
301255927Snwhitehorn	if (!sc->irq) {
302255927Snwhitehorn		device_printf(dev, "Could not allocate IRQ\n");
303255927Snwhitehorn		mtx_destroy(&sc->io_lock);
304255927Snwhitehorn		return (ENXIO);
305255927Snwhitehorn	}
306255927Snwhitehorn
307255927Snwhitehorn	bus_setup_intr(dev, sc->irq, INTR_TYPE_CAM | INTR_MPSAFE |
308255927Snwhitehorn	    INTR_ENTROPY, NULL, vscsi_intr, sc, &sc->irq_cookie);
309255927Snwhitehorn
310255927Snwhitehorn	/* Data DMA */
311255927Snwhitehorn	error = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0,
312255927Snwhitehorn	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE,
313255927Snwhitehorn	    256, BUS_SPACE_MAXSIZE_32BIT, 0, busdma_lock_mutex, &sc->io_lock,
314255927Snwhitehorn	    &sc->data_tag);
315255927Snwhitehorn
316255927Snwhitehorn	TAILQ_INIT(&sc->active_xferq);
317255927Snwhitehorn	TAILQ_INIT(&sc->free_xferq);
318255927Snwhitehorn
319255927Snwhitehorn	/* First XFER for login data */
320255927Snwhitehorn	sc->loginxp.sc = sc;
321255927Snwhitehorn	bus_dmamap_create(sc->data_tag, 0, &sc->loginxp.dmamap);
322255927Snwhitehorn	TAILQ_INSERT_TAIL(&sc->free_xferq, &sc->loginxp, queue);
323255927Snwhitehorn
324255927Snwhitehorn	/* CRQ area */
325255927Snwhitehorn	error = bus_dma_tag_create(bus_get_dma_tag(dev), PAGE_SIZE, 0,
326255927Snwhitehorn	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, 8*PAGE_SIZE,
327255927Snwhitehorn	    1, BUS_SPACE_MAXSIZE, 0, NULL, NULL, &sc->crq_tag);
328255927Snwhitehorn	error = bus_dmamem_alloc(sc->crq_tag, (void **)&sc->crq_queue,
329255927Snwhitehorn	    BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->crq_map);
330255927Snwhitehorn	sc->crq_phys = 0;
331255927Snwhitehorn	sc->n_crqs = 0;
332255927Snwhitehorn	error = bus_dmamap_load(sc->crq_tag, sc->crq_map, sc->crq_queue,
333255927Snwhitehorn	    8*PAGE_SIZE, vscsi_crq_load_cb, sc, 0);
334255927Snwhitehorn
335255927Snwhitehorn	mtx_lock(&sc->io_lock);
336255927Snwhitehorn	vscsi_setup_bus(sc);
337255927Snwhitehorn	sc->xfer = malloc(sizeof(sc->xfer[0])*sc->max_transactions, M_VSCSI,
338255927Snwhitehorn	    M_NOWAIT);
339255927Snwhitehorn	for (i = 0; i < sc->max_transactions; i++) {
340255927Snwhitehorn		xp = &sc->xfer[i];
341255927Snwhitehorn		xp->sc = sc;
342255927Snwhitehorn
343255927Snwhitehorn		error = bus_dmamap_create(sc->data_tag, 0, &xp->dmamap);
344255927Snwhitehorn		if (error) {
345255927Snwhitehorn			device_printf(dev, "Could not create DMA map (%d)\n",
346255927Snwhitehorn			    error);
347255927Snwhitehorn			break;
348255927Snwhitehorn		}
349255927Snwhitehorn
350255927Snwhitehorn		TAILQ_INSERT_TAIL(&sc->free_xferq, xp, queue);
351255927Snwhitehorn	}
352255927Snwhitehorn	mtx_unlock(&sc->io_lock);
353255927Snwhitehorn
354255927Snwhitehorn	/* Allocate CAM bits */
355255927Snwhitehorn	if ((sc->devq = cam_simq_alloc(sc->max_transactions)) == NULL)
356255927Snwhitehorn		return (ENOMEM);
357255927Snwhitehorn
358255927Snwhitehorn	sc->sim = cam_sim_alloc(vscsi_cam_action, vscsi_cam_poll, "vscsi", sc,
359255927Snwhitehorn				device_get_unit(dev), &sc->io_lock,
360255927Snwhitehorn				sc->max_transactions, sc->max_transactions,
361255927Snwhitehorn				sc->devq);
362255927Snwhitehorn	if (sc->sim == NULL) {
363255927Snwhitehorn		cam_simq_free(sc->devq);
364255927Snwhitehorn		sc->devq = NULL;
365255927Snwhitehorn		device_printf(dev, "CAM SIM attach failed\n");
366255927Snwhitehorn		return (EINVAL);
367255927Snwhitehorn	}
368255927Snwhitehorn
369255927Snwhitehorn
370255927Snwhitehorn	mtx_lock(&sc->io_lock);
371255927Snwhitehorn	if (xpt_bus_register(sc->sim, dev, 0) != 0) {
372255927Snwhitehorn		device_printf(dev, "XPT bus registration failed\n");
373255927Snwhitehorn		cam_sim_free(sc->sim, FALSE);
374255927Snwhitehorn		sc->sim = NULL;
375255927Snwhitehorn		cam_simq_free(sc->devq);
376255927Snwhitehorn		sc->devq = NULL;
377255927Snwhitehorn		mtx_unlock(&sc->io_lock);
378255927Snwhitehorn		return (EINVAL);
379255927Snwhitehorn	}
380255927Snwhitehorn	mtx_unlock(&sc->io_lock);
381255927Snwhitehorn
382255927Snwhitehorn	return (0);
383255927Snwhitehorn}
384255927Snwhitehorn
385255927Snwhitehornstatic int
386255927Snwhitehornvscsi_detach(device_t dev)
387255927Snwhitehorn{
388255927Snwhitehorn	struct vscsi_softc *sc;
389255927Snwhitehorn
390255927Snwhitehorn	sc = device_get_softc(dev);
391255927Snwhitehorn	if (sc == NULL)
392255927Snwhitehorn		return (EINVAL);
393255927Snwhitehorn
394255927Snwhitehorn	if (sc->sim != NULL) {
395255927Snwhitehorn		mtx_lock(&sc->io_lock);
396255927Snwhitehorn		xpt_bus_deregister(cam_sim_path(sc->sim));
397255927Snwhitehorn		cam_sim_free(sc->sim, FALSE);
398255927Snwhitehorn		sc->sim = NULL;
399255927Snwhitehorn		mtx_unlock(&sc->io_lock);
400255927Snwhitehorn	}
401255927Snwhitehorn
402255927Snwhitehorn	if (sc->devq != NULL) {
403255927Snwhitehorn		cam_simq_free(sc->devq);
404255927Snwhitehorn		sc->devq = NULL;
405255927Snwhitehorn	}
406255927Snwhitehorn
407255927Snwhitehorn	mtx_destroy(&sc->io_lock);
408255927Snwhitehorn
409255927Snwhitehorn	return (0);
410255927Snwhitehorn}
411255927Snwhitehorn
412255927Snwhitehornstatic void
413255927Snwhitehornvscsi_cam_action(struct cam_sim *sim, union ccb *ccb)
414255927Snwhitehorn{
415255927Snwhitehorn	struct vscsi_softc *sc = cam_sim_softc(sim);
416255927Snwhitehorn
417255927Snwhitehorn	mtx_assert(&sc->io_lock, MA_OWNED);
418255927Snwhitehorn
419255927Snwhitehorn	switch (ccb->ccb_h.func_code) {
420255927Snwhitehorn	case XPT_PATH_INQ:
421255927Snwhitehorn	{
422255927Snwhitehorn		struct ccb_pathinq *cpi = &ccb->cpi;
423255927Snwhitehorn
424255927Snwhitehorn		cpi->version_num = 1;
425255927Snwhitehorn		cpi->hba_inquiry = PI_TAG_ABLE;
426255927Snwhitehorn		cpi->hba_misc = PIM_EXTLUNS;
427255927Snwhitehorn		cpi->target_sprt = 0;
428255927Snwhitehorn		cpi->hba_eng_cnt = 0;
429255927Snwhitehorn		cpi->max_target = 0;
430259418Snwhitehorn		cpi->max_lun = 0;
431255927Snwhitehorn		cpi->initiator_id = ~0;
432315812Smav		strlcpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
433315812Smav		strlcpy(cpi->hba_vid, "IBM", HBA_IDLEN);
434315812Smav		strlcpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
435255927Snwhitehorn		cpi->unit_number = cam_sim_unit(sim);
436255927Snwhitehorn		cpi->bus_id = cam_sim_bus(sim);
437255927Snwhitehorn		cpi->base_transfer_speed = 150000;
438255927Snwhitehorn		cpi->transport = XPORT_SRP;
439255927Snwhitehorn		cpi->transport_version = 0;
440255927Snwhitehorn		cpi->protocol = PROTO_SCSI;
441255927Snwhitehorn		cpi->protocol_version = SCSI_REV_SPC4;
442255927Snwhitehorn		cpi->ccb_h.status = CAM_REQ_CMP;
443255927Snwhitehorn		break;
444255927Snwhitehorn	}
445255927Snwhitehorn	case XPT_RESET_BUS:
446255927Snwhitehorn		ccb->ccb_h.status = CAM_REQ_CMP;
447255927Snwhitehorn		break;
448255927Snwhitehorn	case XPT_RESET_DEV:
449255927Snwhitehorn		ccb->ccb_h.status = CAM_REQ_INPROG;
450255927Snwhitehorn		vscsi_task_management(sc, ccb);
451255927Snwhitehorn		return;
452255927Snwhitehorn	case XPT_GET_TRAN_SETTINGS:
453255927Snwhitehorn		ccb->cts.protocol = PROTO_SCSI;
454255927Snwhitehorn		ccb->cts.protocol_version = SCSI_REV_SPC4;
455255927Snwhitehorn		ccb->cts.transport = XPORT_SRP;
456255927Snwhitehorn		ccb->cts.transport_version = 0;
457255927Snwhitehorn		ccb->cts.proto_specific.valid = 0;
458255927Snwhitehorn		ccb->cts.xport_specific.valid = 0;
459255927Snwhitehorn		ccb->ccb_h.status = CAM_REQ_CMP;
460255927Snwhitehorn		break;
461255927Snwhitehorn	case XPT_SET_TRAN_SETTINGS:
462255927Snwhitehorn		ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
463255927Snwhitehorn		break;
464255927Snwhitehorn	case XPT_SCSI_IO:
465255927Snwhitehorn	{
466255927Snwhitehorn		struct vscsi_xfer *xp;
467255927Snwhitehorn
468255927Snwhitehorn		ccb->ccb_h.status = CAM_REQ_INPROG;
469255927Snwhitehorn
470255927Snwhitehorn		xp = TAILQ_FIRST(&sc->free_xferq);
471255927Snwhitehorn		if (xp == NULL)
472255927Snwhitehorn			panic("SCSI queue flooded");
473255927Snwhitehorn		xp->ccb = ccb;
474255927Snwhitehorn		TAILQ_REMOVE(&sc->free_xferq, xp, queue);
475255927Snwhitehorn		TAILQ_INSERT_TAIL(&sc->active_xferq, xp, queue);
476255927Snwhitehorn		bus_dmamap_load_ccb(sc->data_tag, xp->dmamap,
477255927Snwhitehorn		    ccb, vscsi_scsi_command, xp, 0);
478255927Snwhitehorn
479255927Snwhitehorn		return;
480255927Snwhitehorn	}
481255927Snwhitehorn	default:
482255927Snwhitehorn		ccb->ccb_h.status = CAM_REQ_INVALID;
483255927Snwhitehorn		break;
484255927Snwhitehorn	}
485255927Snwhitehorn
486255927Snwhitehorn	xpt_done(ccb);
487255927Snwhitehorn	return;
488255927Snwhitehorn}
489255927Snwhitehorn
490255927Snwhitehornstatic void
491255927Snwhitehornvscsi_srp_login(struct vscsi_softc *sc)
492255927Snwhitehorn{
493255927Snwhitehorn	struct vscsi_xfer *xp;
494255927Snwhitehorn	struct srp_login *login;
495255927Snwhitehorn	struct vscsi_crq crq;
496255927Snwhitehorn	int err;
497255927Snwhitehorn
498255927Snwhitehorn	mtx_assert(&sc->io_lock, MA_OWNED);
499255927Snwhitehorn
500255927Snwhitehorn	xp = TAILQ_FIRST(&sc->free_xferq);
501255927Snwhitehorn	if (xp == NULL)
502255927Snwhitehorn		panic("SCSI queue flooded");
503255927Snwhitehorn	xp->ccb = NULL;
504255927Snwhitehorn	TAILQ_REMOVE(&sc->free_xferq, xp, queue);
505255927Snwhitehorn	TAILQ_INSERT_TAIL(&sc->active_xferq, xp, queue);
506255927Snwhitehorn
507255927Snwhitehorn	/* Set up command */
508255927Snwhitehorn	xp->srp_iu_size = crq.iu_length = 64;
509255927Snwhitehorn	err = vmem_alloc(xp->sc->srp_iu_arena, xp->srp_iu_size,
510255927Snwhitehorn	    M_BESTFIT | M_NOWAIT, &xp->srp_iu_offset);
511255927Snwhitehorn	if (err)
512255927Snwhitehorn		panic("Error during VMEM allocation (%d)", err);
513255927Snwhitehorn
514255927Snwhitehorn	login = (struct srp_login *)((uint8_t *)xp->sc->srp_iu_queue +
515255927Snwhitehorn	    (uintptr_t)xp->srp_iu_offset);
516255927Snwhitehorn	bzero(login, xp->srp_iu_size);
517255927Snwhitehorn	login->type = SRP_LOGIN_REQ;
518255927Snwhitehorn	login->tag = (uint64_t)(xp);
519255927Snwhitehorn	login->max_cmd_length = htobe64(256);
520255927Snwhitehorn	login->buffer_formats = htobe16(0x1 | 0x2); /* Direct and indirect */
521255927Snwhitehorn	login->flags = 0;
522255927Snwhitehorn
523255927Snwhitehorn	/* Create CRQ entry */
524255927Snwhitehorn	crq.valid = 0x80;
525255927Snwhitehorn	crq.format = 0x01;
526255927Snwhitehorn	crq.iu_data = xp->sc->srp_iu_phys + xp->srp_iu_offset;
527255927Snwhitehorn	bus_dmamap_sync(sc->crq_tag, sc->crq_map, BUS_DMASYNC_PREWRITE);
528255927Snwhitehorn
529255927Snwhitehorn	err = phyp_hcall(H_SEND_CRQ, xp->sc->unit, ((uint64_t *)(&crq))[0],
530255927Snwhitehorn	    ((uint64_t *)(&crq))[1]);
531255927Snwhitehorn	if (err != 0)
532255927Snwhitehorn		panic("CRQ send failure (%d)", err);
533255927Snwhitehorn}
534255927Snwhitehorn
535255927Snwhitehornstatic void
536255927Snwhitehornvscsi_task_management(struct vscsi_softc *sc, union ccb *ccb)
537255927Snwhitehorn{
538255927Snwhitehorn	struct srp_tsk_mgmt *cmd;
539255927Snwhitehorn	struct vscsi_xfer *xp;
540255927Snwhitehorn	struct vscsi_crq crq;
541255927Snwhitehorn	int err;
542255927Snwhitehorn
543255927Snwhitehorn	mtx_assert(&sc->io_lock, MA_OWNED);
544255927Snwhitehorn
545255927Snwhitehorn	xp = TAILQ_FIRST(&sc->free_xferq);
546255927Snwhitehorn	if (xp == NULL)
547255927Snwhitehorn		panic("SCSI queue flooded");
548255927Snwhitehorn	xp->ccb = ccb;
549255927Snwhitehorn	TAILQ_REMOVE(&sc->free_xferq, xp, queue);
550255927Snwhitehorn	TAILQ_INSERT_TAIL(&sc->active_xferq, xp, queue);
551255927Snwhitehorn
552255927Snwhitehorn	xp->srp_iu_size = crq.iu_length = sizeof(*cmd);
553255927Snwhitehorn	err = vmem_alloc(xp->sc->srp_iu_arena, xp->srp_iu_size,
554255927Snwhitehorn	    M_BESTFIT | M_NOWAIT, &xp->srp_iu_offset);
555255927Snwhitehorn	if (err)
556255927Snwhitehorn		panic("Error during VMEM allocation (%d)", err);
557255927Snwhitehorn
558255927Snwhitehorn	cmd = (struct srp_tsk_mgmt *)((uint8_t *)xp->sc->srp_iu_queue +
559255927Snwhitehorn	    (uintptr_t)xp->srp_iu_offset);
560255927Snwhitehorn	bzero(cmd, xp->srp_iu_size);
561255927Snwhitehorn	cmd->type = SRP_TSK_MGMT;
562255927Snwhitehorn	cmd->tag = (uint64_t)xp;
563257345Snwhitehorn	cmd->lun = htobe64(CAM_EXTLUN_BYTE_SWIZZLE(ccb->ccb_h.target_lun));
564255927Snwhitehorn
565255927Snwhitehorn	switch (ccb->ccb_h.func_code) {
566255927Snwhitehorn	case XPT_RESET_DEV:
567255927Snwhitehorn		cmd->function = 0x08;
568255927Snwhitehorn		break;
569255927Snwhitehorn	default:
570255927Snwhitehorn		panic("Unimplemented code %d", ccb->ccb_h.func_code);
571255927Snwhitehorn		break;
572255927Snwhitehorn	}
573255927Snwhitehorn
574255927Snwhitehorn	bus_dmamap_sync(xp->sc->crq_tag, xp->sc->crq_map, BUS_DMASYNC_PREWRITE);
575255927Snwhitehorn
576255927Snwhitehorn	/* Create CRQ entry */
577255927Snwhitehorn	crq.valid = 0x80;
578255927Snwhitehorn	crq.format = 0x01;
579255927Snwhitehorn	crq.iu_data = xp->sc->srp_iu_phys + xp->srp_iu_offset;
580255927Snwhitehorn
581255927Snwhitehorn	err = phyp_hcall(H_SEND_CRQ, xp->sc->unit, ((uint64_t *)(&crq))[0],
582255927Snwhitehorn	    ((uint64_t *)(&crq))[1]);
583255927Snwhitehorn	if (err != 0)
584255927Snwhitehorn		panic("CRQ send failure (%d)", err);
585255927Snwhitehorn}
586255927Snwhitehorn
587255927Snwhitehornstatic void
588255927Snwhitehornvscsi_scsi_command(void *xxp, bus_dma_segment_t *segs, int nsegs, int err)
589255927Snwhitehorn{
590255927Snwhitehorn	struct vscsi_xfer *xp = xxp;
591255927Snwhitehorn	uint8_t *cdb;
592255927Snwhitehorn	union ccb *ccb = xp->ccb;
593255927Snwhitehorn	struct srp_cmd *cmd;
594255927Snwhitehorn	uint64_t chunk_addr;
595255927Snwhitehorn	uint32_t chunk_size;
596255927Snwhitehorn	int desc_start, i;
597255927Snwhitehorn	struct vscsi_crq crq;
598255927Snwhitehorn
599255927Snwhitehorn	KASSERT(err == 0, ("DMA error %d\n", err));
600255927Snwhitehorn
601255927Snwhitehorn	mtx_assert(&xp->sc->io_lock, MA_OWNED);
602255927Snwhitehorn
603255927Snwhitehorn	cdb = (ccb->ccb_h.flags & CAM_CDB_POINTER) ?
604255927Snwhitehorn	    ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes;
605255927Snwhitehorn
606255927Snwhitehorn	/* Command format from Table 20, page 37 of SRP spec */
607255927Snwhitehorn	crq.iu_length = 48 + ((nsegs > 1) ? 20 : 16) +
608255927Snwhitehorn	    ((ccb->csio.cdb_len > 16) ? (ccb->csio.cdb_len - 16) : 0);
609255927Snwhitehorn	xp->srp_iu_size = crq.iu_length;
610255927Snwhitehorn	if (nsegs > 1)
611255927Snwhitehorn		xp->srp_iu_size += nsegs*16;
612255927Snwhitehorn	xp->srp_iu_size = roundup(xp->srp_iu_size, 16);
613255927Snwhitehorn	err = vmem_alloc(xp->sc->srp_iu_arena, xp->srp_iu_size,
614255927Snwhitehorn	    M_BESTFIT | M_NOWAIT, &xp->srp_iu_offset);
615255927Snwhitehorn	if (err)
616255927Snwhitehorn		panic("Error during VMEM allocation (%d)", err);
617255927Snwhitehorn
618255927Snwhitehorn	cmd = (struct srp_cmd *)((uint8_t *)xp->sc->srp_iu_queue +
619255927Snwhitehorn	    (uintptr_t)xp->srp_iu_offset);
620255927Snwhitehorn	bzero(cmd, xp->srp_iu_size);
621255927Snwhitehorn	cmd->type = SRP_CMD;
622255927Snwhitehorn	if (ccb->csio.cdb_len > 16)
623255927Snwhitehorn		cmd->additional_cdb = (ccb->csio.cdb_len - 16) << 2;
624255927Snwhitehorn	memcpy(cmd->cdb, cdb, ccb->csio.cdb_len);
625255927Snwhitehorn
626255927Snwhitehorn	cmd->tag = (uint64_t)(xp); /* Let the responder find this again */
627257345Snwhitehorn	cmd->lun = htobe64(CAM_EXTLUN_BYTE_SWIZZLE(ccb->ccb_h.target_lun));
628255927Snwhitehorn
629255927Snwhitehorn	if (nsegs > 1) {
630255927Snwhitehorn		/* Use indirect descriptors */
631255927Snwhitehorn		switch (ccb->ccb_h.flags & CAM_DIR_MASK) {
632255927Snwhitehorn		case CAM_DIR_OUT:
633255927Snwhitehorn			cmd->formats = (2 << 4);
634255927Snwhitehorn			break;
635255927Snwhitehorn		case CAM_DIR_IN:
636255927Snwhitehorn			cmd->formats = 2;
637255927Snwhitehorn			break;
638255927Snwhitehorn		default:
639255927Snwhitehorn			panic("Does not support bidirectional commands (%d)",
640255927Snwhitehorn			    ccb->ccb_h.flags & CAM_DIR_MASK);
641255927Snwhitehorn			break;
642255927Snwhitehorn		}
643255927Snwhitehorn
644255927Snwhitehorn		desc_start = ((ccb->csio.cdb_len > 16) ?
645255927Snwhitehorn		    ccb->csio.cdb_len - 16 : 0);
646255927Snwhitehorn		chunk_addr = xp->sc->srp_iu_phys + xp->srp_iu_offset + 20 +
647255927Snwhitehorn		    desc_start + sizeof(*cmd);
648255927Snwhitehorn		chunk_size = 16*nsegs;
649255927Snwhitehorn		memcpy(&cmd->data_payload[desc_start], &chunk_addr, 8);
650255927Snwhitehorn		memcpy(&cmd->data_payload[desc_start+12], &chunk_size, 4);
651255927Snwhitehorn		chunk_size = 0;
652255927Snwhitehorn		for (i = 0; i < nsegs; i++)
653255927Snwhitehorn			chunk_size += segs[i].ds_len;
654255927Snwhitehorn		memcpy(&cmd->data_payload[desc_start+16], &chunk_size, 4);
655255927Snwhitehorn		desc_start += 20;
656255927Snwhitehorn		for (i = 0; i < nsegs; i++) {
657255927Snwhitehorn			chunk_addr = segs[i].ds_addr;
658255927Snwhitehorn			chunk_size = segs[i].ds_len;
659255927Snwhitehorn
660255927Snwhitehorn			memcpy(&cmd->data_payload[desc_start + 16*i],
661255927Snwhitehorn			    &chunk_addr, 8);
662255927Snwhitehorn			/* Set handle tag to 0 */
663255927Snwhitehorn			memcpy(&cmd->data_payload[desc_start + 16*i + 12],
664255927Snwhitehorn			    &chunk_size, 4);
665255927Snwhitehorn		}
666255927Snwhitehorn	} else if (nsegs == 1) {
667255927Snwhitehorn		switch (ccb->ccb_h.flags & CAM_DIR_MASK) {
668255927Snwhitehorn		case CAM_DIR_OUT:
669255927Snwhitehorn			cmd->formats = (1 << 4);
670255927Snwhitehorn			break;
671255927Snwhitehorn		case CAM_DIR_IN:
672255927Snwhitehorn			cmd->formats = 1;
673255927Snwhitehorn			break;
674255927Snwhitehorn		default:
675255927Snwhitehorn			panic("Does not support bidirectional commands (%d)",
676255927Snwhitehorn			    ccb->ccb_h.flags & CAM_DIR_MASK);
677255927Snwhitehorn			break;
678255927Snwhitehorn		}
679255927Snwhitehorn
680255927Snwhitehorn		/*
681255927Snwhitehorn		 * Memory descriptor:
682255927Snwhitehorn		 * 8 byte address
683255927Snwhitehorn		 * 4 byte handle
684255927Snwhitehorn		 * 4 byte length
685255927Snwhitehorn		 */
686255927Snwhitehorn
687255927Snwhitehorn		chunk_addr = segs[0].ds_addr;
688255927Snwhitehorn		chunk_size = segs[0].ds_len;
689255927Snwhitehorn		desc_start = ((ccb->csio.cdb_len > 16) ?
690255927Snwhitehorn		    ccb->csio.cdb_len - 16 : 0);
691255927Snwhitehorn
692255927Snwhitehorn		memcpy(&cmd->data_payload[desc_start], &chunk_addr, 8);
693255927Snwhitehorn		/* Set handle tag to 0 */
694255927Snwhitehorn		memcpy(&cmd->data_payload[desc_start+12], &chunk_size, 4);
695255927Snwhitehorn		KASSERT(xp->srp_iu_size >= 48 + ((ccb->csio.cdb_len > 16) ?
696255927Snwhitehorn		    ccb->csio.cdb_len : 16), ("SRP IU command length"));
697255927Snwhitehorn	} else {
698255927Snwhitehorn		cmd->formats = 0;
699255927Snwhitehorn	}
700255927Snwhitehorn	bus_dmamap_sync(xp->sc->crq_tag, xp->sc->crq_map, BUS_DMASYNC_PREWRITE);
701255927Snwhitehorn
702255927Snwhitehorn	/* Create CRQ entry */
703255927Snwhitehorn	crq.valid = 0x80;
704255927Snwhitehorn	crq.format = 0x01;
705255927Snwhitehorn	crq.iu_data = xp->sc->srp_iu_phys + xp->srp_iu_offset;
706255927Snwhitehorn
707255927Snwhitehorn	err = phyp_hcall(H_SEND_CRQ, xp->sc->unit, ((uint64_t *)(&crq))[0],
708255927Snwhitehorn	    ((uint64_t *)(&crq))[1]);
709255927Snwhitehorn	if (err != 0)
710255927Snwhitehorn		panic("CRQ send failure (%d)", err);
711255927Snwhitehorn}
712255927Snwhitehorn
713255927Snwhitehornstatic void
714255927Snwhitehornvscsi_crq_load_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int err)
715255927Snwhitehorn{
716255927Snwhitehorn	struct vscsi_softc *sc = xsc;
717255927Snwhitehorn
718255927Snwhitehorn	sc->crq_phys = segs[0].ds_addr;
719255927Snwhitehorn	sc->n_crqs = PAGE_SIZE/sizeof(struct vscsi_crq);
720255927Snwhitehorn
721255927Snwhitehorn	sc->srp_iu_queue = (uint8_t *)(sc->crq_queue);
722255927Snwhitehorn	sc->srp_iu_phys = segs[0].ds_addr;
723255927Snwhitehorn	sc->srp_iu_arena = vmem_create("VSCSI SRP IU", PAGE_SIZE,
724255927Snwhitehorn	    segs[0].ds_len - PAGE_SIZE, 16, 0, M_BESTFIT | M_NOWAIT);
725255927Snwhitehorn}
726255927Snwhitehorn
727255927Snwhitehornstatic void
728255927Snwhitehornvscsi_setup_bus(struct vscsi_softc *sc)
729255927Snwhitehorn{
730255927Snwhitehorn	struct vscsi_crq crq;
731255927Snwhitehorn	struct vscsi_xfer *xp;
732255927Snwhitehorn	int error;
733255927Snwhitehorn
734255927Snwhitehorn	struct {
735255927Snwhitehorn		uint32_t type;
736255927Snwhitehorn		uint16_t status;
737255927Snwhitehorn		uint16_t length;
738255927Snwhitehorn		uint64_t tag;
739255927Snwhitehorn		uint64_t buffer;
740255927Snwhitehorn		struct {
741255927Snwhitehorn			char srp_version[8];
742255927Snwhitehorn			char partition_name[96];
743255927Snwhitehorn			uint32_t partition_number;
744255927Snwhitehorn			uint32_t mad_version;
745255927Snwhitehorn			uint32_t os_type;
746255927Snwhitehorn			uint32_t port_max_txu[8];
747255927Snwhitehorn		} payload;
748255927Snwhitehorn	} mad_adapter_info;
749255927Snwhitehorn
750255927Snwhitehorn	bzero(&crq, sizeof(crq));
751255927Snwhitehorn
752255927Snwhitehorn	/* Init message */
753255927Snwhitehorn	crq.valid = 0xc0;
754255927Snwhitehorn	crq.format = 0x01;
755255927Snwhitehorn
756255927Snwhitehorn	do {
757255927Snwhitehorn		error = phyp_hcall(H_FREE_CRQ, sc->unit);
758255927Snwhitehorn	} while (error == H_BUSY);
759255927Snwhitehorn
760255927Snwhitehorn	/* See initialization sequence page 757 */
761255927Snwhitehorn	bzero(sc->crq_queue, sc->n_crqs*sizeof(sc->crq_queue[0]));
762255927Snwhitehorn	sc->cur_crq = 0;
763255927Snwhitehorn	sc->bus_initialized = 0;
764255927Snwhitehorn	sc->bus_logged_in = 0;
765255927Snwhitehorn	bus_dmamap_sync(sc->crq_tag, sc->crq_map, BUS_DMASYNC_PREWRITE);
766255927Snwhitehorn	error = phyp_hcall(H_REG_CRQ, sc->unit, sc->crq_phys,
767255927Snwhitehorn	    sc->n_crqs*sizeof(sc->crq_queue[0]));
768255927Snwhitehorn	KASSERT(error == 0, ("CRQ registration success"));
769255927Snwhitehorn
770255927Snwhitehorn	error = phyp_hcall(H_SEND_CRQ, sc->unit, ((uint64_t *)(&crq))[0],
771255927Snwhitehorn	    ((uint64_t *)(&crq))[1]);
772255927Snwhitehorn	if (error != 0)
773255927Snwhitehorn		panic("CRQ setup failure (%d)", error);
774255927Snwhitehorn
775255927Snwhitehorn	while (sc->bus_initialized == 0)
776255927Snwhitehorn		vscsi_check_response_queue(sc);
777255927Snwhitehorn
778255927Snwhitehorn	/* Send MAD adapter info */
779255927Snwhitehorn	mad_adapter_info.type = MAD_ADAPTER_INFO_REQUEST;
780255927Snwhitehorn	mad_adapter_info.status = 0;
781255927Snwhitehorn	mad_adapter_info.length = sizeof(mad_adapter_info.payload);
782255927Snwhitehorn
783255927Snwhitehorn	strcpy(mad_adapter_info.payload.srp_version, "16.a");
784255927Snwhitehorn	strcpy(mad_adapter_info.payload.partition_name, "UNKNOWN");
785255927Snwhitehorn	mad_adapter_info.payload.partition_number = -1;
786255927Snwhitehorn	mad_adapter_info.payload.mad_version = 1;
787255927Snwhitehorn	mad_adapter_info.payload.os_type = 2; /* Claim we are Linux */
788255927Snwhitehorn	mad_adapter_info.payload.port_max_txu[0] = 0;
789255927Snwhitehorn	/* If this fails, we get the defaults above */
790255927Snwhitehorn	OF_getprop(OF_finddevice("/"), "ibm,partition-name",
791255927Snwhitehorn	    mad_adapter_info.payload.partition_name,
792255927Snwhitehorn	    sizeof(mad_adapter_info.payload.partition_name));
793255927Snwhitehorn	OF_getprop(OF_finddevice("/"), "ibm,partition-no",
794255927Snwhitehorn	    &mad_adapter_info.payload.partition_number,
795255927Snwhitehorn	    sizeof(mad_adapter_info.payload.partition_number));
796255927Snwhitehorn
797255927Snwhitehorn	xp = TAILQ_FIRST(&sc->free_xferq);
798255927Snwhitehorn	xp->ccb = NULL;
799255927Snwhitehorn	TAILQ_REMOVE(&sc->free_xferq, xp, queue);
800255927Snwhitehorn	TAILQ_INSERT_TAIL(&sc->active_xferq, xp, queue);
801255927Snwhitehorn	xp->srp_iu_size = crq.iu_length = sizeof(mad_adapter_info);
802255927Snwhitehorn	vmem_alloc(xp->sc->srp_iu_arena, xp->srp_iu_size,
803255927Snwhitehorn	    M_BESTFIT | M_NOWAIT, &xp->srp_iu_offset);
804255927Snwhitehorn	mad_adapter_info.buffer = xp->sc->srp_iu_phys + xp->srp_iu_offset + 24;
805255927Snwhitehorn	mad_adapter_info.tag = (uint64_t)xp;
806255927Snwhitehorn	memcpy((uint8_t *)xp->sc->srp_iu_queue + (uintptr_t)xp->srp_iu_offset,
807255927Snwhitehorn		&mad_adapter_info, sizeof(mad_adapter_info));
808255927Snwhitehorn	crq.valid = 0x80;
809255927Snwhitehorn	crq.format = 0x02;
810255927Snwhitehorn	crq.iu_data = xp->sc->srp_iu_phys + xp->srp_iu_offset;
811255927Snwhitehorn	bus_dmamap_sync(sc->crq_tag, sc->crq_map, BUS_DMASYNC_PREWRITE);
812255927Snwhitehorn	phyp_hcall(H_SEND_CRQ, xp->sc->unit, ((uint64_t *)(&crq))[0],
813255927Snwhitehorn	    ((uint64_t *)(&crq))[1]);
814255927Snwhitehorn
815255927Snwhitehorn	while (TAILQ_EMPTY(&sc->free_xferq))
816255927Snwhitehorn		vscsi_check_response_queue(sc);
817255927Snwhitehorn
818255927Snwhitehorn	/* Send SRP login */
819255927Snwhitehorn	vscsi_srp_login(sc);
820255927Snwhitehorn	while (sc->bus_logged_in == 0)
821255927Snwhitehorn		vscsi_check_response_queue(sc);
822255927Snwhitehorn
823255927Snwhitehorn	error = phyp_hcall(H_VIO_SIGNAL, sc->unit, 1); /* Enable interrupts */
824255927Snwhitehorn}
825255927Snwhitehorn
826255927Snwhitehorn
827255927Snwhitehornstatic void
828255927Snwhitehornvscsi_intr(void *xsc)
829255927Snwhitehorn{
830255927Snwhitehorn	struct vscsi_softc *sc = xsc;
831255927Snwhitehorn
832255927Snwhitehorn	mtx_lock(&sc->io_lock);
833255927Snwhitehorn	vscsi_check_response_queue(sc);
834255927Snwhitehorn	mtx_unlock(&sc->io_lock);
835255927Snwhitehorn}
836255927Snwhitehorn
837255927Snwhitehornstatic void
838255927Snwhitehornvscsi_srp_response(struct vscsi_xfer *xp, struct vscsi_crq *crq)
839255927Snwhitehorn{
840255927Snwhitehorn	union ccb *ccb = xp->ccb;
841255927Snwhitehorn	struct vscsi_softc *sc = xp->sc;
842255927Snwhitehorn	struct srp_rsp *rsp;
843255927Snwhitehorn	uint32_t sense_len;
844255927Snwhitehorn
845255927Snwhitehorn	/* SRP response packet in original request */
846255927Snwhitehorn	rsp = (struct srp_rsp *)((uint8_t *)sc->srp_iu_queue +
847255927Snwhitehorn	    (uintptr_t)xp->srp_iu_offset);
848255927Snwhitehorn	ccb->csio.scsi_status = rsp->status;
849255927Snwhitehorn	if (ccb->csio.scsi_status == SCSI_STATUS_OK)
850255927Snwhitehorn		ccb->ccb_h.status = CAM_REQ_CMP;
851255927Snwhitehorn	else
852255927Snwhitehorn		ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR;
853255927Snwhitehorn#ifdef NOTYET
854255927Snwhitehorn	/* Collect fast fail codes */
855255927Snwhitehorn	if (crq->status != 0)
856255927Snwhitehorn		ccb->ccb_h.status = CAM_REQ_CMP_ERR;
857255927Snwhitehorn#endif
858255927Snwhitehorn
859255927Snwhitehorn	if (ccb->ccb_h.status != CAM_REQ_CMP) {
860255927Snwhitehorn		ccb->ccb_h.status |= CAM_DEV_QFRZN;
861255927Snwhitehorn		xpt_freeze_devq(ccb->ccb_h.path, /*count*/ 1);
862255927Snwhitehorn	}
863255927Snwhitehorn
864255927Snwhitehorn	if (!(rsp->flags & SRP_RSPVALID))
865255927Snwhitehorn		rsp->response_data_len = 0;
866255927Snwhitehorn	if (!(rsp->flags & SRP_SNSVALID))
867255927Snwhitehorn		rsp->sense_data_len = 0;
868255927Snwhitehorn	if (!(rsp->flags & (SRP_DOOVER | SRP_DOUNDER)))
869255927Snwhitehorn		rsp->data_out_resid = 0;
870255927Snwhitehorn	if (!(rsp->flags & (SRP_DIOVER | SRP_DIUNDER)))
871255927Snwhitehorn		rsp->data_in_resid = 0;
872255927Snwhitehorn
873255927Snwhitehorn	if (rsp->flags & SRP_SNSVALID) {
874255927Snwhitehorn		bzero(&ccb->csio.sense_data, sizeof(struct scsi_sense_data));
875255927Snwhitehorn		ccb->ccb_h.status |= CAM_AUTOSNS_VALID;
876255927Snwhitehorn		sense_len = min(be32toh(rsp->sense_data_len),
877255927Snwhitehorn		    ccb->csio.sense_len);
878255927Snwhitehorn		memcpy(&ccb->csio.sense_data,
879255927Snwhitehorn		    &rsp->data_payload[be32toh(rsp->response_data_len)],
880255927Snwhitehorn		    sense_len);
881255927Snwhitehorn		ccb->csio.sense_resid = ccb->csio.sense_len -
882255927Snwhitehorn		    be32toh(rsp->sense_data_len);
883255927Snwhitehorn	}
884255927Snwhitehorn
885255927Snwhitehorn	switch (ccb->ccb_h.flags & CAM_DIR_MASK) {
886255927Snwhitehorn	case CAM_DIR_OUT:
887255927Snwhitehorn		ccb->csio.resid = rsp->data_out_resid;
888255927Snwhitehorn		break;
889255927Snwhitehorn	case CAM_DIR_IN:
890255927Snwhitehorn		ccb->csio.resid = rsp->data_in_resid;
891255927Snwhitehorn		break;
892255927Snwhitehorn	}
893255927Snwhitehorn
894255927Snwhitehorn	bus_dmamap_sync(sc->data_tag, xp->dmamap, BUS_DMASYNC_POSTREAD);
895255927Snwhitehorn	bus_dmamap_unload(sc->data_tag, xp->dmamap);
896255927Snwhitehorn	xpt_done(ccb);
897255927Snwhitehorn	xp->ccb = NULL;
898255927Snwhitehorn}
899255927Snwhitehorn
900255927Snwhitehornstatic void
901255927Snwhitehornvscsi_login_response(struct vscsi_xfer *xp, struct vscsi_crq *crq)
902255927Snwhitehorn{
903255927Snwhitehorn	struct vscsi_softc *sc = xp->sc;
904255927Snwhitehorn	struct srp_login_rsp *rsp;
905255927Snwhitehorn
906255927Snwhitehorn	/* SRP response packet in original request */
907255927Snwhitehorn	rsp = (struct srp_login_rsp *)((uint8_t *)sc->srp_iu_queue +
908255927Snwhitehorn	    (uintptr_t)xp->srp_iu_offset);
909255927Snwhitehorn	KASSERT(be16toh(rsp->buffer_formats) & 0x3, ("Both direct and indirect "
910255927Snwhitehorn	    "buffers supported"));
911255927Snwhitehorn
912255927Snwhitehorn	sc->max_transactions = be32toh(rsp->request_limit_delta);
913255927Snwhitehorn	device_printf(sc->dev, "Queue depth %d commands\n",
914255927Snwhitehorn	    sc->max_transactions);
915255927Snwhitehorn	sc->bus_logged_in = 1;
916255927Snwhitehorn}
917255927Snwhitehorn
918255927Snwhitehornstatic void
919255927Snwhitehornvscsi_cam_poll(struct cam_sim *sim)
920255927Snwhitehorn{
921255927Snwhitehorn	struct vscsi_softc *sc = cam_sim_softc(sim);
922255927Snwhitehorn
923255927Snwhitehorn	vscsi_check_response_queue(sc);
924255927Snwhitehorn}
925255927Snwhitehorn
926255927Snwhitehornstatic void
927255927Snwhitehornvscsi_check_response_queue(struct vscsi_softc *sc)
928255927Snwhitehorn{
929255927Snwhitehorn	struct vscsi_crq *crq;
930255927Snwhitehorn	struct vscsi_xfer *xp;
931255927Snwhitehorn	int code;
932255927Snwhitehorn
933255927Snwhitehorn	mtx_assert(&sc->io_lock, MA_OWNED);
934255927Snwhitehorn
935279217Snwhitehorn	while (sc->crq_queue[sc->cur_crq].valid != 0) {
936279217Snwhitehorn		/* The hypercalls at both ends of this are not optimal */
937279217Snwhitehorn		phyp_hcall(H_VIO_SIGNAL, sc->unit, 0);
938279217Snwhitehorn		bus_dmamap_sync(sc->crq_tag, sc->crq_map, BUS_DMASYNC_POSTREAD);
939255927Snwhitehorn
940255927Snwhitehorn		crq = &sc->crq_queue[sc->cur_crq];
941255927Snwhitehorn
942255927Snwhitehorn		switch (crq->valid) {
943255927Snwhitehorn		case 0xc0:
944255927Snwhitehorn			if (crq->format == 0x02)
945255927Snwhitehorn				sc->bus_initialized = 1;
946255927Snwhitehorn			break;
947255927Snwhitehorn		case 0x80:
948255927Snwhitehorn			/* IU data is set to tag pointer (the XP) */
949255927Snwhitehorn			xp = (struct vscsi_xfer *)crq->iu_data;
950255927Snwhitehorn
951255927Snwhitehorn			switch (crq->format) {
952255927Snwhitehorn			case 0x01:
953255927Snwhitehorn				code = *((uint8_t *)sc->srp_iu_queue +
954255927Snwhitehorn	    			    (uintptr_t)xp->srp_iu_offset);
955255927Snwhitehorn				switch (code) {
956255927Snwhitehorn				case SRP_RSP:
957255927Snwhitehorn					vscsi_srp_response(xp, crq);
958255927Snwhitehorn					break;
959255927Snwhitehorn				case SRP_LOGIN_RSP:
960255927Snwhitehorn					vscsi_login_response(xp, crq);
961255927Snwhitehorn					break;
962255927Snwhitehorn				default:
963255927Snwhitehorn					device_printf(sc->dev, "Unknown SRP "
964255927Snwhitehorn					    "response code %d\n", code);
965255927Snwhitehorn					break;
966255927Snwhitehorn				}
967255927Snwhitehorn				break;
968255927Snwhitehorn			case 0x02:
969255927Snwhitehorn				/* Ignore management datagrams */
970255927Snwhitehorn				break;
971255927Snwhitehorn			default:
972255927Snwhitehorn				panic("Unknown CRQ format %d\n", crq->format);
973255927Snwhitehorn				break;
974255927Snwhitehorn			}
975255927Snwhitehorn			vmem_free(sc->srp_iu_arena, xp->srp_iu_offset,
976255927Snwhitehorn			    xp->srp_iu_size);
977255927Snwhitehorn			TAILQ_REMOVE(&sc->active_xferq, xp, queue);
978255927Snwhitehorn			TAILQ_INSERT_TAIL(&sc->free_xferq, xp, queue);
979255927Snwhitehorn			break;
980255927Snwhitehorn		default:
981255927Snwhitehorn			device_printf(sc->dev,
982255927Snwhitehorn			    "Unknown CRQ message type %d\n", crq->valid);
983255927Snwhitehorn			break;
984255927Snwhitehorn		}
985255927Snwhitehorn
986255927Snwhitehorn		crq->valid = 0;
987255927Snwhitehorn		sc->cur_crq = (sc->cur_crq + 1) % sc->n_crqs;
988255927Snwhitehorn
989279217Snwhitehorn		bus_dmamap_sync(sc->crq_tag, sc->crq_map, BUS_DMASYNC_PREWRITE);
990279217Snwhitehorn		phyp_hcall(H_VIO_SIGNAL, sc->unit, 1);
991279217Snwhitehorn	}
992255927Snwhitehorn}
993255927Snwhitehorn
994