xref: /netbsd-src/sys/dev/i2o/iopsp.c (revision 1a2899ff2fb9d41376805c2efbdd84ae77d24072)
1*1a2899ffSandvar /*	$NetBSD: iopsp.c,v 1.41 2022/05/04 07:48:34 andvar Exp $	*/
2d49fd135Sad 
3d49fd135Sad /*-
4e5bca80aSad  * Copyright (c) 2000, 2001, 2007 The NetBSD Foundation, Inc.
5d49fd135Sad  * All rights reserved.
6d49fd135Sad  *
7d49fd135Sad  * This code is derived from software contributed to The NetBSD Foundation
8d49fd135Sad  * by Andrew Doran.
9d49fd135Sad  *
10d49fd135Sad  * Redistribution and use in source and binary forms, with or without
11d49fd135Sad  * modification, are permitted provided that the following conditions
12d49fd135Sad  * are met:
13d49fd135Sad  * 1. Redistributions of source code must retain the above copyright
14d49fd135Sad  *    notice, this list of conditions and the following disclaimer.
15d49fd135Sad  * 2. Redistributions in binary form must reproduce the above copyright
16d49fd135Sad  *    notice, this list of conditions and the following disclaimer in the
17d49fd135Sad  *    documentation and/or other materials provided with the distribution.
18d49fd135Sad  *
19d49fd135Sad  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20d49fd135Sad  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21d49fd135Sad  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22d49fd135Sad  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23d49fd135Sad  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24d49fd135Sad  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25d49fd135Sad  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26d49fd135Sad  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27d49fd135Sad  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28d49fd135Sad  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29d49fd135Sad  * POSSIBILITY OF SUCH DAMAGE.
30d49fd135Sad  */
31d49fd135Sad 
32d49fd135Sad /*
33ebf51109Sad  * Raw SCSI device support for I2O.  IOPs present SCSI devices individually;
34ebf51109Sad  * we group them by controlling port.
35d49fd135Sad  */
36d49fd135Sad 
3705b019baSlukem #include <sys/cdefs.h>
38*1a2899ffSandvar __KERNEL_RCSID(0, "$NetBSD: iopsp.c,v 1.41 2022/05/04 07:48:34 andvar Exp $");
39d49fd135Sad 
40d49fd135Sad #include <sys/param.h>
41d49fd135Sad #include <sys/systm.h>
42d49fd135Sad #include <sys/kernel.h>
43d49fd135Sad #include <sys/device.h>
44d49fd135Sad #include <sys/queue.h>
45d49fd135Sad #include <sys/proc.h>
46d49fd135Sad #include <sys/buf.h>
47d49fd135Sad #include <sys/endian.h>
48d49fd135Sad #include <sys/malloc.h>
49d49fd135Sad #include <sys/scsiio.h>
50d49fd135Sad 
516f0f9f87Sdsl #include <sys/bswap.h>
52a2a38285Sad #include <sys/bus.h>
53d49fd135Sad 
54d49fd135Sad #include <dev/scsipi/scsi_all.h>
55d49fd135Sad #include <dev/scsipi/scsi_disk.h>
56d49fd135Sad #include <dev/scsipi/scsipi_all.h>
57d49fd135Sad #include <dev/scsipi/scsiconf.h>
58937a7a3eSbouyer #include <dev/scsipi/scsi_message.h>
59d49fd135Sad 
60d49fd135Sad #include <dev/i2o/i2o.h>
61ebf51109Sad #include <dev/i2o/iopio.h>
62d49fd135Sad #include <dev/i2o/iopvar.h>
63d49fd135Sad #include <dev/i2o/iopspvar.h>
64d49fd135Sad 
65529e91fcScegger static void	iopsp_adjqparam(device_t, int);
66529e91fcScegger static void	iopsp_attach(device_t, device_t, void *);
67529e91fcScegger static void	iopsp_intr(device_t, struct iop_msg *, void *);
68937a7a3eSbouyer static int	iopsp_ioctl(struct scsipi_channel *, u_long,
6953524e44Schristos 			    void *, int, struct proc *);
70529e91fcScegger static int	iopsp_match(device_t, cfdata_t, void *);
71d49fd135Sad static int	iopsp_rescan(struct iopsp_softc *);
72529e91fcScegger static int	iopsp_reconfig(device_t);
73937a7a3eSbouyer static void	iopsp_scsipi_request(struct scsipi_channel *,
74937a7a3eSbouyer 				     scsipi_adapter_req_t, void *);
75d49fd135Sad 
76cbab9cadSchs CFATTACH_DECL_NEW(iopsp, sizeof(struct iopsp_softc),
77c9b3657cSthorpej     iopsp_match, iopsp_attach, NULL, NULL);
78d49fd135Sad 
79d49fd135Sad /*
80ebf51109Sad  * Match a supported device.
81d49fd135Sad  */
82d49fd135Sad static int
iopsp_match(device_t parent,cfdata_t match,void * aux)83529e91fcScegger iopsp_match(device_t parent, cfdata_t match, void *aux)
84d49fd135Sad {
85d49fd135Sad 	struct iop_attach_args *ia;
8633a7e100Smsaitoh 	struct iop_softc *iop;
87d49fd135Sad 	struct {
88d49fd135Sad 		struct	i2o_param_op_results pr;
89d49fd135Sad 		struct	i2o_param_read_results prr;
90d49fd135Sad 		struct	i2o_param_hba_ctlr_info ci;
910de5da96Sgmcgarry 	} __packed param;
92d49fd135Sad 
93d49fd135Sad 	ia = aux;
9433a7e100Smsaitoh 	iop = device_private(parent);
95d49fd135Sad 
96d49fd135Sad 	if (ia->ia_class != I2O_CLASS_BUS_ADAPTER_PORT)
97d49fd135Sad 		return (0);
98d49fd135Sad 
9933a7e100Smsaitoh 	if (iop_field_get_all(iop, ia->ia_tid, I2O_PARAM_HBA_CTLR_INFO, &param,
10033a7e100Smsaitoh 	    sizeof(param), NULL) != 0)
101d49fd135Sad 		return (0);
102d49fd135Sad 
103d49fd135Sad 	return (param.ci.bustype == I2O_HBA_BUS_SCSI ||
104d49fd135Sad 	    param.ci.bustype == I2O_HBA_BUS_FCA);
105d49fd135Sad }
106d49fd135Sad 
107d49fd135Sad /*
108d49fd135Sad  * Attach a supported device.
109d49fd135Sad  */
110d49fd135Sad static void
iopsp_attach(device_t parent,device_t self,void * aux)111529e91fcScegger iopsp_attach(device_t parent, device_t self, void *aux)
112d49fd135Sad {
113d49fd135Sad 	struct iop_attach_args *ia;
114d49fd135Sad 	struct iopsp_softc *sc;
115d49fd135Sad 	struct iop_softc *iop;
116d49fd135Sad 	struct {
117d49fd135Sad 		struct	i2o_param_op_results pr;
118d49fd135Sad 		struct	i2o_param_read_results prr;
119d49fd135Sad 		union {
120d49fd135Sad 			struct	i2o_param_hba_ctlr_info ci;
121d49fd135Sad 			struct	i2o_param_hba_scsi_ctlr_info sci;
122af9d4c4fSad 			struct	i2o_param_hba_scsi_port_info spi;
123d49fd135Sad 		} p;
1240de5da96Sgmcgarry 	} __packed param;
12503e46dc8Sad 	int fc, rv;
126d49fd135Sad 	int size;
127d49fd135Sad 
128d49fd135Sad 	ia = (struct iop_attach_args *)aux;
1298fc35725Sthorpej 	sc = device_private(self);
1308fc35725Sthorpej 	iop = device_private(parent);
131d49fd135Sad 
132d49fd135Sad 	/* Register us as an initiator. */
133d49fd135Sad 	sc->sc_ii.ii_dv = self;
134d49fd135Sad 	sc->sc_ii.ii_intr = iopsp_intr;
135d49fd135Sad 	sc->sc_ii.ii_flags = 0;
136af9d4c4fSad 	sc->sc_ii.ii_tid = ia->ia_tid;
137af9d4c4fSad 	sc->sc_ii.ii_reconfig = iopsp_reconfig;
138ebf51109Sad 	sc->sc_ii.ii_adjqparam = iopsp_adjqparam;
139ebf51109Sad 	iop_initiator_register(iop, &sc->sc_ii);
140d49fd135Sad 
1411d6329bbSad 	rv = iop_field_get_all(iop, ia->ia_tid, I2O_PARAM_HBA_CTLR_INFO,
1421d6329bbSad 	    &param, sizeof(param), NULL);
1431d6329bbSad 	if (rv != 0)
144d49fd135Sad 		goto bad;
145d49fd135Sad 
14603e46dc8Sad 	fc = (param.p.ci.bustype == I2O_HBA_BUS_FCA);
147d49fd135Sad 
148d49fd135Sad 	/*
149*1a2899ffSandvar 	 * Say what the device is.  If we can find out what the controlling
150d49fd135Sad 	 * device is, say what that is too.
151d49fd135Sad 	 */
1528ef79919Sad 	aprint_normal(": SCSI port");
153ebf51109Sad 	iop_print_ident(iop, ia->ia_tid);
1548ef79919Sad 	aprint_normal("\n");
155d49fd135Sad 
1561d6329bbSad 	rv = iop_field_get_all(iop, ia->ia_tid, I2O_PARAM_HBA_SCSI_CTLR_INFO,
1571d6329bbSad 	    &param, sizeof(param), NULL);
1581d6329bbSad 	if (rv != 0)
159d49fd135Sad 		goto bad;
160d49fd135Sad 
161cbab9cadSchs 	aprint_normal_dev(sc->sc_dev, "");
16203e46dc8Sad 	if (fc)
1638ef79919Sad 		aprint_normal("FC");
16403e46dc8Sad 	else
1658ef79919Sad 		aprint_normal("%d-bit", param.p.sci.maxdatawidth);
1668ef79919Sad 	aprint_normal(", max sync rate %dMHz, initiator ID %d\n",
167d49fd135Sad 	    (u_int32_t)le64toh(param.p.sci.maxsyncrate) / 1000,
168d49fd135Sad 	    le32toh(param.p.sci.initiatorid));
169d49fd135Sad 
170434a4243Sthorpej 	sc->sc_openings = 1;
171434a4243Sthorpej 
172cbab9cadSchs 	sc->sc_adapter.adapt_dev = sc->sc_dev;
173937a7a3eSbouyer 	sc->sc_adapter.adapt_nchannels = 1;
174937a7a3eSbouyer 	sc->sc_adapter.adapt_openings = 1;
175937a7a3eSbouyer 	sc->sc_adapter.adapt_max_periph = 1;
176937a7a3eSbouyer 	sc->sc_adapter.adapt_ioctl = iopsp_ioctl;
177937a7a3eSbouyer 	sc->sc_adapter.adapt_minphys = minphys;
178937a7a3eSbouyer 	sc->sc_adapter.adapt_request = iopsp_scsipi_request;
179d49fd135Sad 
180937a7a3eSbouyer 	memset(&sc->sc_channel, 0, sizeof(sc->sc_channel));
181937a7a3eSbouyer 	sc->sc_channel.chan_adapter = &sc->sc_adapter;
182937a7a3eSbouyer 	sc->sc_channel.chan_bustype = &scsi_bustype;
183937a7a3eSbouyer 	sc->sc_channel.chan_channel = 0;
18403e46dc8Sad 	sc->sc_channel.chan_ntargets = fc ?
18503e46dc8Sad 	    IOPSP_MAX_FC_TARGET : param.p.sci.maxdatawidth;
186937a7a3eSbouyer 	sc->sc_channel.chan_nluns = IOPSP_MAX_LUN;
187937a7a3eSbouyer 	sc->sc_channel.chan_id = le32toh(param.p.sci.initiatorid);
188937a7a3eSbouyer 	sc->sc_channel.chan_flags = SCSIPI_CHAN_NOSETTLE;
189d49fd135Sad 
190d49fd135Sad 	/*
191d49fd135Sad 	 * Allocate the target map.  Currently used for informational
192d49fd135Sad 	 * purposes only.
193d49fd135Sad 	 */
194937a7a3eSbouyer 	size = sc->sc_channel.chan_ntargets * sizeof(struct iopsp_target);
195d47bcd29Schs 	sc->sc_targetmap = malloc(size, M_DEVBUF, M_WAITOK|M_ZERO);
196d49fd135Sad 
197d49fd135Sad  	/* Build the two maps, and attach to scsipi. */
198af9d4c4fSad 	if (iopsp_reconfig(self) != 0) {
199cbab9cadSchs 		aprint_error_dev(sc->sc_dev, "configure failed\n");
200af9d4c4fSad 		goto bad;
201af9d4c4fSad 	}
202c7fb772bSthorpej 	config_found(self, &sc->sc_channel, scsiprint, CFARGS_NONE);
203d49fd135Sad 	return;
204d49fd135Sad 
205d49fd135Sad  bad:
206d49fd135Sad 	iop_initiator_unregister(iop, &sc->sc_ii);
207d49fd135Sad }
208d49fd135Sad 
209d49fd135Sad /*
210af9d4c4fSad  * Scan the LCT to determine which devices we control, and enter them into
211af9d4c4fSad  * the maps.
212d49fd135Sad  */
213af9d4c4fSad static int
iopsp_reconfig(device_t dv)214529e91fcScegger iopsp_reconfig(device_t dv)
215d49fd135Sad {
216af9d4c4fSad 	struct iopsp_softc *sc;
217d49fd135Sad 	struct iop_softc *iop;
218d49fd135Sad 	struct i2o_lct_entry *le;
219937a7a3eSbouyer 	struct scsipi_channel *sc_chan;
220d49fd135Sad 	struct {
221d49fd135Sad 		struct	i2o_param_op_results pr;
222d49fd135Sad 		struct	i2o_param_read_results prr;
223d49fd135Sad 		struct	i2o_param_scsi_device_info sdi;
2240de5da96Sgmcgarry 	} __packed param;
225e5bca80aSad 	u_int tid, nent, i, targ, lun, size, rv, bptid;
226d49fd135Sad 	u_short *tidmap;
227e5bca80aSad 	void *tofree;
228d49fd135Sad 	struct iopsp_target *it;
229d49fd135Sad 	int syncrate;
230d49fd135Sad 
231cbab9cadSchs 	sc = device_private(dv);
232cbab9cadSchs 	iop = device_private(device_parent(sc->sc_dev));
233937a7a3eSbouyer 	sc_chan = &sc->sc_channel;
234d49fd135Sad 
235e5bca80aSad 	KASSERT(mutex_owned(&iop->sc_conflock));
236e5bca80aSad 
237af9d4c4fSad 	/* Anything to do? */
238ebf51109Sad 	if (iop->sc_chgind == sc->sc_chgind)
239af9d4c4fSad 		return (0);
240af9d4c4fSad 
241d49fd135Sad 	/*
242d49fd135Sad 	 * Allocate memory for the target/LUN -> TID map.  Use zero to
243d49fd135Sad 	 * denote absent targets (zero is the TID of the I2O executive,
244d49fd135Sad 	 * and we never address that here).
245d49fd135Sad 	 */
246937a7a3eSbouyer 	size = sc_chan->chan_ntargets * (IOPSP_MAX_LUN) * sizeof(u_short);
247c12b5c18Stsutsui 	if ((tidmap = malloc(size, M_DEVBUF, M_WAITOK|M_ZERO)) == NULL)
248af9d4c4fSad 		return (ENOMEM);
249d49fd135Sad 
250937a7a3eSbouyer 	for (i = 0; i < sc_chan->chan_ntargets; i++)
251d49fd135Sad 		sc->sc_targetmap[i].it_flags &= ~IT_PRESENT;
252d49fd135Sad 
253ebf51109Sad 	/*
254ebf51109Sad 	 * A quick hack to handle Intel's stacked bus port arrangement.
255ebf51109Sad 	 */
256ebf51109Sad 	bptid = sc->sc_ii.ii_tid;
257ebf51109Sad 	nent = iop->sc_nlctent;
258ebf51109Sad 	for (le = iop->sc_lct->entry; nent != 0; nent--, le++)
259ebf51109Sad 		if ((le16toh(le->classid) & 4095) ==
260ebf51109Sad 		    I2O_CLASS_BUS_ADAPTER_PORT &&
261ebf51109Sad 		    (le32toh(le->usertid) & 4095) == bptid) {
26203e46dc8Sad 			bptid = le16toh(le->localtid) & 4095;
263ebf51109Sad 			break;
264ebf51109Sad 		}
265ebf51109Sad 
266d49fd135Sad 	nent = iop->sc_nlctent;
267d49fd135Sad 	for (i = 0, le = iop->sc_lct->entry; i < nent; i++, le++) {
268ebf51109Sad 		if ((le16toh(le->classid) & 4095) != I2O_CLASS_SCSI_PERIPHERAL)
269d49fd135Sad 			continue;
270ebf51109Sad 		if (((le32toh(le->usertid) >> 12) & 4095) != bptid)
271d49fd135Sad 			continue;
27203e46dc8Sad 		tid = le16toh(le->localtid) & 4095;
273d49fd135Sad 
2741d6329bbSad 		rv = iop_field_get_all(iop, tid, I2O_PARAM_SCSI_DEVICE_INFO,
2751d6329bbSad 		    &param, sizeof(param), NULL);
2761d6329bbSad 		if (rv != 0)
277d49fd135Sad 			continue;
278d49fd135Sad 		targ = le32toh(param.sdi.identifier);
279d49fd135Sad 		lun = param.sdi.luninfo[1];
280ebf51109Sad #if defined(DIAGNOSTIC) || defined(I2ODEBUG)
281937a7a3eSbouyer 		if (targ >= sc_chan->chan_ntargets ||
282937a7a3eSbouyer 		    lun >= sc_chan->chan_nluns) {
283cbab9cadSchs 			aprint_error_dev(sc->sc_dev, "target %d,%d (tid %d): "
2840e50a946Scegger 			    "bad target/LUN\n", targ, lun, tid);
285d49fd135Sad 			continue;
286d49fd135Sad 		}
287ebf51109Sad #endif
288d49fd135Sad 
289d49fd135Sad 		/*
290d49fd135Sad 		 * If we've already described this target, and nothing has
291d49fd135Sad 		 * changed, then don't describe it again.
292d49fd135Sad 		 */
293d49fd135Sad 		it = &sc->sc_targetmap[targ];
294d49fd135Sad 		it->it_flags |= IT_PRESENT;
295d49fd135Sad 		syncrate = ((int)le64toh(param.sdi.negsyncrate) + 500) / 1000;
29657d787eeSad 		if (it->it_width != param.sdi.negdatawidth ||
29757d787eeSad 		    it->it_offset != param.sdi.negoffset ||
29857d787eeSad 		    it->it_syncrate != syncrate) {
299d49fd135Sad 			it->it_width = param.sdi.negdatawidth;
300d49fd135Sad 			it->it_offset = param.sdi.negoffset;
301d49fd135Sad 			it->it_syncrate = syncrate;
302d49fd135Sad 
303cbab9cadSchs 			aprint_verbose_dev(sc->sc_dev, "target %d (tid %d): %d-bit, ",
3040e50a946Scegger 			    targ, tid, it->it_width);
305d49fd135Sad 			if (it->it_syncrate == 0)
3068ef79919Sad 				aprint_verbose("asynchronous\n");
307d49fd135Sad 			else
3088ef79919Sad 				aprint_verbose("synchronous at %dMHz, "
3098ef79919Sad 				    "offset 0x%x\n", it->it_syncrate,
3108ef79919Sad 				    it->it_offset);
31157d787eeSad 		}
312ebf51109Sad 
313ebf51109Sad 		/* Ignore the device if it's in use by somebody else. */
314ebf51109Sad 		if ((le32toh(le->usertid) & 4095) != I2O_TID_NONE) {
315ebf51109Sad 			if (sc->sc_tidmap == NULL ||
316ebf51109Sad 			    IOPSP_TIDMAP(sc->sc_tidmap, targ, lun) !=
3178ef79919Sad 			    IOPSP_TID_INUSE) {
318cbab9cadSchs 				aprint_verbose_dev(sc->sc_dev, "target %d,%d (tid %d): "
3190e50a946Scegger 				    "in use by tid %d\n",
320ebf51109Sad 				    targ, lun, tid,
321ebf51109Sad 				    le32toh(le->usertid) & 4095);
3228ef79919Sad 			}
323ebf51109Sad 			IOPSP_TIDMAP(tidmap, targ, lun) = IOPSP_TID_INUSE;
324ebf51109Sad 		} else
325ebf51109Sad 			IOPSP_TIDMAP(tidmap, targ, lun) = (u_short)tid;
326d49fd135Sad 	}
327d49fd135Sad 
328937a7a3eSbouyer 	for (i = 0; i < sc_chan->chan_ntargets; i++)
329d49fd135Sad 		if ((sc->sc_targetmap[i].it_flags & IT_PRESENT) == 0)
330d49fd135Sad 			sc->sc_targetmap[i].it_width = 0;
331d49fd135Sad 
332d49fd135Sad 	/* Swap in the new map and return. */
333e5bca80aSad 	mutex_spin_enter(&iop->sc_intrlock);
334e5bca80aSad 	tofree = sc->sc_tidmap;
335d49fd135Sad 	sc->sc_tidmap = tidmap;
336e5bca80aSad 	mutex_spin_exit(&iop->sc_intrlock);
337e5bca80aSad 
338e5bca80aSad 	if (tofree != NULL)
339e5bca80aSad 		free(tofree, M_DEVBUF);
340ebf51109Sad 	sc->sc_chgind = iop->sc_chgind;
341af9d4c4fSad 	return (0);
342d49fd135Sad }
343d49fd135Sad 
344d49fd135Sad /*
345af9d4c4fSad  * Re-scan the bus; to be called from a higher level (e.g. scsipi).
346d49fd135Sad  */
347d49fd135Sad static int
iopsp_rescan(struct iopsp_softc * sc)348d49fd135Sad iopsp_rescan(struct iopsp_softc *sc)
349d49fd135Sad {
350d49fd135Sad 	struct iop_softc *iop;
351d49fd135Sad 	struct iop_msg *im;
352ebf51109Sad 	struct i2o_hba_bus_scan mf;
353d49fd135Sad 	int rv;
354d49fd135Sad 
355cbab9cadSchs 	iop = device_private(device_parent(sc->sc_dev));
356d49fd135Sad 
3579abeea58Sad 	mutex_enter(&iop->sc_conflock);
35803e46dc8Sad 	im = iop_msg_alloc(iop, IM_WAIT);
359d49fd135Sad 
360ebf51109Sad 	mf.msgflags = I2O_MSGFLAGS(i2o_hba_bus_scan);
361ebf51109Sad 	mf.msgfunc = I2O_MSGFUNC(sc->sc_ii.ii_tid, I2O_HBA_BUS_SCAN);
362ebf51109Sad 	mf.msgictx = sc->sc_ii.ii_ictx;
363ebf51109Sad 	mf.msgtctx = im->im_tctx;
364d49fd135Sad 
365ebf51109Sad 	rv = iop_msg_post(iop, im, &mf, 5*60*1000);
366ebf51109Sad 	iop_msg_free(iop, im);
367d49fd135Sad 	if (rv != 0)
368cbab9cadSchs 		aprint_error_dev(sc->sc_dev, "bus rescan failed (error %d)\n",
3690e50a946Scegger 		    rv);
370af9d4c4fSad 
37199187cddSad 	if ((rv = iop_lct_get(iop)) == 0)
372cbab9cadSchs 		rv = iopsp_reconfig(sc->sc_dev);
37399187cddSad 
3749abeea58Sad 	mutex_exit(&iop->sc_conflock);
375d49fd135Sad 	return (rv);
376d49fd135Sad }
377d49fd135Sad 
378d49fd135Sad /*
379d49fd135Sad  * Start a SCSI command.
380d49fd135Sad  */
381937a7a3eSbouyer static void
iopsp_scsipi_request(struct scsipi_channel * chan,scsipi_adapter_req_t req,void * arg)382937a7a3eSbouyer iopsp_scsipi_request(struct scsipi_channel *chan, scsipi_adapter_req_t req,
383937a7a3eSbouyer 		     void *arg)
384d49fd135Sad {
385937a7a3eSbouyer 	struct scsipi_xfer *xs;
386937a7a3eSbouyer 	struct scsipi_periph *periph;
387d49fd135Sad 	struct iopsp_softc *sc;
388d49fd135Sad 	struct iop_msg *im;
389d49fd135Sad 	struct iop_softc *iop;
390ebf51109Sad 	struct i2o_scsi_scb_exec *mf;
391434a4243Sthorpej 	int error, flags, tid;
392ebf51109Sad 	u_int32_t mb[IOP_MAX_MSG_SIZE / sizeof(u_int32_t)];
393d49fd135Sad 
394cbab9cadSchs 	sc = device_private(chan->chan_adapter->adapt_dev);
395cbab9cadSchs 	iop = device_private(device_parent(sc->sc_dev));
396d49fd135Sad 
397937a7a3eSbouyer 	switch (req) {
398937a7a3eSbouyer 	case ADAPTER_REQ_RUN_XFER:
399937a7a3eSbouyer 		xs = arg;
400937a7a3eSbouyer 		periph = xs->xs_periph;
401937a7a3eSbouyer 		flags = xs->xs_control;
402937a7a3eSbouyer 
40357d787eeSad 		SC_DEBUG(periph, SCSIPI_DB2, ("iopsp_scsi_request run_xfer\n"));
40457d787eeSad 
405937a7a3eSbouyer 		tid = IOPSP_TIDMAP(sc->sc_tidmap, periph->periph_target,
406937a7a3eSbouyer 		    periph->periph_lun);
407d49fd135Sad 		if (tid == IOPSP_TID_ABSENT || tid == IOPSP_TID_INUSE) {
408d49fd135Sad 			xs->error = XS_SELTIMEOUT;
409937a7a3eSbouyer 			scsipi_done(xs);
410937a7a3eSbouyer 			return;
411d49fd135Sad 		}
412d49fd135Sad 
413d49fd135Sad 		/* Need to reset the target? */
414d49fd135Sad 		if ((flags & XS_CTL_RESET) != 0) {
415d49fd135Sad 			if (iop_simple_cmd(iop, tid, I2O_SCSI_DEVICE_RESET,
41699187cddSad 			    sc->sc_ii.ii_ictx, 1, 30*1000) != 0) {
417cbab9cadSchs 				aprint_error_dev(sc->sc_dev, "reset failed\n");
418d49fd135Sad 				xs->error = XS_DRIVER_STUFFUP;
419937a7a3eSbouyer 			} else
420937a7a3eSbouyer 				xs->error = XS_NOERROR;
421937a7a3eSbouyer 
422937a7a3eSbouyer 			scsipi_done(xs);
423937a7a3eSbouyer 			return;
424d49fd135Sad 		}
425d49fd135Sad 
426d49fd135Sad #if defined(I2ODEBUG) || defined(SCSIDEBUG)
427ebf51109Sad 		if (xs->cmdlen > sizeof(mf->cdb))
428cbab9cadSchs 			panic("%s: CDB too large", device_xname(sc->sc_dev));
429d49fd135Sad #endif
430d49fd135Sad 
43103e46dc8Sad 		im = iop_msg_alloc(iop, IM_POLL_INTR |
432937a7a3eSbouyer 		    IM_NOSTATUS | ((flags & XS_CTL_POLL) != 0 ? IM_POLL : 0));
433d49fd135Sad 		im->im_dvcontext = xs;
434d49fd135Sad 
435ebf51109Sad 		mf = (struct i2o_scsi_scb_exec *)mb;
436ebf51109Sad 		mf->msgflags = I2O_MSGFLAGS(i2o_scsi_scb_exec);
437ebf51109Sad 		mf->msgfunc = I2O_MSGFUNC(tid, I2O_SCSI_SCB_EXEC);
438ebf51109Sad 		mf->msgictx = sc->sc_ii.ii_ictx;
439ebf51109Sad 		mf->msgtctx = im->im_tctx;
440ebf51109Sad 		mf->flags = xs->cmdlen | I2O_SCB_FLAG_ENABLE_DISCONNECT |
441d49fd135Sad 		    I2O_SCB_FLAG_SENSE_DATA_IN_MESSAGE;
442ebf51109Sad 		mf->datalen = xs->datalen;
443ebf51109Sad 		memcpy(mf->cdb, xs->cmd, xs->cmdlen);
444d49fd135Sad 
445937a7a3eSbouyer 		switch (xs->xs_tag_type) {
446937a7a3eSbouyer 		case MSG_ORDERED_Q_TAG:
447ebf51109Sad 			mf->flags |= I2O_SCB_FLAG_ORDERED_QUEUE_TAG;
448937a7a3eSbouyer 			break;
449937a7a3eSbouyer 		case MSG_SIMPLE_Q_TAG:
450ebf51109Sad 			mf->flags |= I2O_SCB_FLAG_SIMPLE_QUEUE_TAG;
451937a7a3eSbouyer 			break;
452937a7a3eSbouyer 		case MSG_HEAD_OF_Q_TAG:
453937a7a3eSbouyer 			mf->flags |= I2O_SCB_FLAG_HEAD_QUEUE_TAG;
454937a7a3eSbouyer 			break;
455937a7a3eSbouyer 		default:
456937a7a3eSbouyer 			break;
457d49fd135Sad 		}
458d49fd135Sad 
459d49fd135Sad 		if (xs->datalen != 0) {
460937a7a3eSbouyer 			error = iop_msg_map_bio(iop, im, mb, xs->data,
461937a7a3eSbouyer 			    xs->datalen, (flags & XS_CTL_DATA_OUT) == 0);
462d49fd135Sad 			if (error) {
463d49fd135Sad 				xs->error = XS_DRIVER_STUFFUP;
464ebf51109Sad 				iop_msg_free(iop, im);
465937a7a3eSbouyer 				scsipi_done(xs);
466937a7a3eSbouyer 				return;
467d49fd135Sad 			}
468d49fd135Sad 			if ((flags & XS_CTL_DATA_IN) == 0)
469ebf51109Sad 				mf->flags |= I2O_SCB_FLAG_XFER_TO_DEVICE;
470d49fd135Sad 			else
471ebf51109Sad 				mf->flags |= I2O_SCB_FLAG_XFER_FROM_DEVICE;
472d49fd135Sad 		}
473d49fd135Sad 
47499187cddSad 		if (iop_msg_post(iop, im, mb, xs->timeout)) {
475ebf51109Sad 			if (xs->datalen != 0)
476ebf51109Sad 				iop_msg_unmap(iop, im);
477ebf51109Sad 			iop_msg_free(iop, im);
478ebf51109Sad 			xs->error = XS_DRIVER_STUFFUP;
479937a7a3eSbouyer 			scsipi_done(xs);
480ebf51109Sad 		}
481937a7a3eSbouyer 		break;
48299187cddSad 
483937a7a3eSbouyer 	case ADAPTER_REQ_GROW_RESOURCES:
484937a7a3eSbouyer 		/*
485937a7a3eSbouyer 		 * Not supported.
486937a7a3eSbouyer 		 */
487937a7a3eSbouyer 		break;
488937a7a3eSbouyer 
489937a7a3eSbouyer 	case ADAPTER_REQ_SET_XFER_MODE:
490937a7a3eSbouyer 		/*
491937a7a3eSbouyer 		 * The DDM takes care of this, and we can't modify its
492937a7a3eSbouyer 		 * behaviour.
493937a7a3eSbouyer 		 */
494937a7a3eSbouyer 		break;
495937a7a3eSbouyer 	}
496d49fd135Sad }
497d49fd135Sad 
49899187cddSad #ifdef notyet
499d49fd135Sad /*
500d49fd135Sad  * Abort the specified I2O_SCSI_SCB_EXEC message and its associated SCB.
501d49fd135Sad  */
502d49fd135Sad static int
iopsp_scsi_abort(struct iopsp_softc * sc,int atid,struct iop_msg * aim)503af9d4c4fSad iopsp_scsi_abort(struct iopsp_softc *sc, int atid, struct iop_msg *aim)
504d49fd135Sad {
505d49fd135Sad 	struct iop_msg *im;
506ebf51109Sad 	struct i2o_scsi_scb_abort mf;
507d49fd135Sad 	struct iop_softc *iop;
508ebf51109Sad 	int rv, s;
509d49fd135Sad 
510cbab9cadSchs 	iop = device_private(device_parent(sc->sc_dev));
51103e46dc8Sad 	im = iop_msg_alloc(iop, IM_POLL);
512d49fd135Sad 
513ebf51109Sad 	mf.msgflags = I2O_MSGFLAGS(i2o_scsi_scb_abort);
514ebf51109Sad 	mf.msgfunc = I2O_MSGFUNC(atid, I2O_SCSI_SCB_ABORT);
515ebf51109Sad 	mf.msgictx = sc->sc_ii.ii_ictx;
516ebf51109Sad 	mf.msgtctx = im->im_tctx;
517ebf51109Sad 	mf.tctxabort = aim->im_tctx;
518d49fd135Sad 
519ebf51109Sad 	rv = iop_msg_post(iop, im, &mf, 30000);
520ebf51109Sad 	iop_msg_free(iop, im);
521e5bca80aSad 
522d49fd135Sad 	return (rv);
523d49fd135Sad }
52499187cddSad #endif
525d49fd135Sad 
526d49fd135Sad /*
527d49fd135Sad  * We have a message which has been processed and replied to by the IOP -
528d49fd135Sad  * deal with it.
529d49fd135Sad  */
530d49fd135Sad static void
iopsp_intr(device_t dv,struct iop_msg * im,void * reply)531529e91fcScegger iopsp_intr(device_t dv, struct iop_msg *im, void *reply)
532d49fd135Sad {
533d49fd135Sad 	struct scsipi_xfer *xs;
534d49fd135Sad 	struct iopsp_softc *sc;
535d49fd135Sad 	struct i2o_scsi_reply *rb;
536d49fd135Sad  	struct iop_softc *iop;
537ebf51109Sad 	u_int sl;
538d49fd135Sad 
539cbab9cadSchs 	sc = device_private(dv);
540cbab9cadSchs 	xs = im->im_dvcontext;
541cbab9cadSchs 	iop = device_private(device_parent(dv));
54299187cddSad 	rb = reply;
543d49fd135Sad 
544f47d097cSbouyer 	SC_DEBUG(xs->xs_periph, SCSIPI_DB2, ("iopsp_intr\n"));
545d49fd135Sad 
546ebf51109Sad 	if ((rb->msgflags & I2O_MSGFLAGS_FAIL) != 0) {
547ebf51109Sad 		xs->error = XS_DRIVER_STUFFUP;
548ebf51109Sad 		xs->resid = xs->datalen;
549ebf51109Sad 	} else {
550ebf51109Sad 		if (rb->hbastatus != I2O_SCSI_DSC_SUCCESS) {
551ebf51109Sad 			switch (rb->hbastatus) {
552d49fd135Sad 			case I2O_SCSI_DSC_ADAPTER_BUSY:
553d49fd135Sad 			case I2O_SCSI_DSC_SCSI_BUS_RESET:
554d49fd135Sad 			case I2O_SCSI_DSC_BUS_BUSY:
555d49fd135Sad 				xs->error = XS_BUSY;
556d49fd135Sad 				break;
557d49fd135Sad 			case I2O_SCSI_DSC_SELECTION_TIMEOUT:
558d49fd135Sad 				xs->error = XS_SELTIMEOUT;
559d49fd135Sad 				break;
560d49fd135Sad 			case I2O_SCSI_DSC_COMMAND_TIMEOUT:
561d49fd135Sad 			case I2O_SCSI_DSC_DEVICE_NOT_PRESENT:
562d49fd135Sad 			case I2O_SCSI_DSC_LUN_INVALID:
563d49fd135Sad 			case I2O_SCSI_DSC_SCSI_TID_INVALID:
564d49fd135Sad 				xs->error = XS_TIMEOUT;
565d49fd135Sad 				break;
566d49fd135Sad 			default:
567d49fd135Sad 				xs->error = XS_DRIVER_STUFFUP;
568d49fd135Sad 				break;
569d49fd135Sad 			}
570cbab9cadSchs 			aprint_error_dev(sc->sc_dev, "HBA status 0x%02x\n",
5710e50a946Scegger 			    rb->hbastatus);
572ebf51109Sad 		} else if (rb->scsistatus != SCSI_OK) {
573ebf51109Sad 			switch (rb->scsistatus) {
574d49fd135Sad 			case SCSI_CHECK:
575d49fd135Sad 				xs->error = XS_SENSE;
576d49fd135Sad 				sl = le32toh(rb->senselen);
577d49fd135Sad 				if (sl > sizeof(xs->sense.scsi_sense))
578ebf51109Sad 					sl = sizeof(xs->sense.scsi_sense);
579d49fd135Sad 				memcpy(&xs->sense.scsi_sense, rb->sense, sl);
580d49fd135Sad 				break;
581937a7a3eSbouyer 			case SCSI_QUEUE_FULL:
582d49fd135Sad 			case SCSI_BUSY:
583d49fd135Sad 				xs->error = XS_BUSY;
584d49fd135Sad 				break;
585d49fd135Sad 			default:
586d49fd135Sad 				xs->error = XS_DRIVER_STUFFUP;
587d49fd135Sad 				break;
588d49fd135Sad 			}
589d49fd135Sad 		} else
590d49fd135Sad 			xs->error = XS_NOERROR;
591d49fd135Sad 
592ebf51109Sad 		xs->resid = xs->datalen - le32toh(rb->datalen);
593ebf51109Sad 		xs->status = rb->scsistatus;
594d49fd135Sad 	}
595d49fd135Sad 
596d49fd135Sad 	/* Free the message wrapper and pass the news to scsipi. */
597ebf51109Sad 	if (xs->datalen != 0)
598d49fd135Sad 		iop_msg_unmap(iop, im);
599ebf51109Sad 	iop_msg_free(iop, im);
600937a7a3eSbouyer 
601d49fd135Sad 	scsipi_done(xs);
602d49fd135Sad }
603d49fd135Sad 
604d49fd135Sad /*
605d49fd135Sad  * ioctl hook; used here only to initiate low-level rescans.
606d49fd135Sad  */
607d49fd135Sad static int
iopsp_ioctl(struct scsipi_channel * chan,u_long cmd,void * data,int flag,struct proc * p)60853524e44Schristos iopsp_ioctl(struct scsipi_channel *chan, u_long cmd, void *data,
609168cd830Schristos     int flag, struct proc *p)
610d49fd135Sad {
611d49fd135Sad 	int rv;
612d49fd135Sad 
613d49fd135Sad 	switch (cmd) {
614d49fd135Sad 	case SCBUSIOLLSCAN:
615ebf51109Sad 		/*
616ebf51109Sad 		 * If it's boot time, the bus will have been scanned and the
617ebf51109Sad 		 * maps built.  Locking would stop re-configuration, but we
618ebf51109Sad 		 * want to fake success.
619ebf51109Sad 		 */
620e5bca80aSad 		if (curlwp != &lwp0)
621937a7a3eSbouyer 			rv = iopsp_rescan(
622cbab9cadSchs 			   device_private(chan->chan_adapter->adapt_dev));
623ebf51109Sad 		else
624ebf51109Sad 			rv = 0;
625d49fd135Sad 		break;
626ebf51109Sad 
627d49fd135Sad 	default:
628ebf51109Sad 		rv = ENOTTY;
629d49fd135Sad 		break;
630d49fd135Sad 	}
631d49fd135Sad 
632d49fd135Sad 	return (rv);
633d49fd135Sad }
634ebf51109Sad 
635ebf51109Sad /*
636ebf51109Sad  * The number of openings available to us has changed, so inform scsipi.
637ebf51109Sad  */
638ebf51109Sad static void
iopsp_adjqparam(device_t dv,int mpi)639529e91fcScegger iopsp_adjqparam(device_t dv, int mpi)
640ebf51109Sad {
641937a7a3eSbouyer 	struct iopsp_softc *sc;
642e5bca80aSad 	struct iop_softc *iop;
643ebf51109Sad 
644e5bca80aSad 	sc = device_private(dv);
645e5bca80aSad 	iop = device_private(device_parent(dv));
646937a7a3eSbouyer 
647e5bca80aSad 	mutex_spin_enter(&iop->sc_intrlock);
648434a4243Sthorpej 	sc->sc_adapter.adapt_openings += mpi - sc->sc_openings;
649434a4243Sthorpej 	sc->sc_openings = mpi;
650e5bca80aSad 	mutex_spin_exit(&iop->sc_intrlock);
651ebf51109Sad }
652