1 /* $NetBSD: icpsp.c,v 1.22 2009/05/12 12:15:37 cegger Exp $ */
4 * Copyright (c) 2002 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: icpsp.c,v 1.22 2009/05/12 12:15:37 cegger Exp $");
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/device.h>
39 #include <sys/queue.h>
42 #include <sys/endian.h>
43 #include <sys/malloc.h>
44 #include <sys/scsiio.h>
46 #include <sys/bswap.h>
49 #include <uvm/uvm_extern.h>
51 #include <dev/scsipi/scsi_all.h>
52 #include <dev/scsipi/scsi_disk.h>
53 #include <dev/scsipi/scsipi_all.h>
54 #include <dev/scsipi/scsiconf.h>
55 #include <dev/scsipi/scsi_message.h>
57 #include <dev/ic/icpreg.h>
58 #include <dev/ic/icpvar.h>
62 struct scsipi_adapter sc_adapter
;
63 struct scsipi_channel sc_channel
;
68 void icpsp_attach(device_t
, device_t
, void *);
69 void icpsp_intr(struct icp_ccb
*);
70 int icpsp_match(device_t
, cfdata_t
, void *);
71 void icpsp_scsipi_request(struct scsipi_channel
*, scsipi_adapter_req_t
,
74 void icpsp_adjqparam(device_t
, int);
76 CFATTACH_DECL(icpsp
, sizeof(struct icpsp_softc
),
77 icpsp_match
, icpsp_attach
, NULL
, NULL
);
79 static const struct icp_servicecb icpsp_servicecb
= {
84 icpsp_match(device_t parent
, cfdata_t match
,
87 struct icp_attach_args
*icpa
;
91 return (icpa
->icpa_unit
>= ICPA_UNIT_SCSI
);
95 icpsp_attach(device_t parent
, device_t self
, void *aux
)
97 struct icp_attach_args
*icpa
;
98 struct icpsp_softc
*sc
;
99 struct icp_softc
*icp
;
101 icpa
= (struct icp_attach_args
*)aux
;
102 sc
= (struct icpsp_softc
*)self
;
103 icp
= (struct icp_softc
*)parent
;
105 sc
->sc_busno
= icpa
->icpa_unit
- ICPA_UNIT_SCSI
;
106 sc
->sc_openings
= icp
->icp_openings
;
107 printf(": physical SCSI channel %d\n", sc
->sc_busno
);
109 icp_register_servicecb(icp
, icpa
->icpa_unit
, &icpsp_servicecb
);
111 sc
->sc_adapter
.adapt_dev
= &sc
->sc_dv
;
112 sc
->sc_adapter
.adapt_nchannels
= 1;
113 sc
->sc_adapter
.adapt_openings
= icp
->icp_openings
;
114 sc
->sc_adapter
.adapt_max_periph
= icp
->icp_openings
;
115 sc
->sc_adapter
.adapt_minphys
= minphys
;
116 sc
->sc_adapter
.adapt_request
= icpsp_scsipi_request
;
118 sc
->sc_channel
.chan_adapter
= &sc
->sc_adapter
;
119 sc
->sc_channel
.chan_bustype
= &scsi_bustype
;
120 sc
->sc_channel
.chan_channel
= 0;
121 sc
->sc_channel
.chan_ntargets
= ((icp
->icp_class
& ICP_FC
) != 0 ?
122 127 : 16); /* XXX bogus check */
123 sc
->sc_channel
.chan_nluns
= 8;
124 sc
->sc_channel
.chan_id
= icp
->icp_bus_id
[sc
->sc_busno
];
125 sc
->sc_channel
.chan_flags
= SCSIPI_CHAN_NOSETTLE
;
127 config_found(self
, &sc
->sc_channel
, scsiprint
);
131 icpsp_scsipi_request(struct scsipi_channel
*chan
, scsipi_adapter_req_t req
,
134 struct scsipi_xfer
*xs
;
135 struct scsipi_periph
*periph
;
136 struct icpsp_softc
*sc
;
137 struct icp_rawcmd
*rc
;
138 struct icp_softc
*icp
;
140 int rv
, flags
, s
, soff
;
142 sc
= (void *)chan
->chan_adapter
->adapt_dev
;
143 icp
= (struct icp_softc
*)device_parent(&sc
->sc_dv
);
146 case ADAPTER_REQ_RUN_XFER
:
148 periph
= xs
->xs_periph
;
149 flags
= xs
->xs_control
;
151 SC_DEBUG(periph
, SCSIPI_DB2
, ("icpsp_scsi_request run_xfer\n"));
153 if ((flags
& XS_CTL_RESET
) != 0) {
154 /* XXX Unimplemented. */
155 xs
->error
= XS_DRIVER_STUFFUP
;
160 #if defined(ICP_DEBUG) || defined(SCSIDEBUG)
161 if (xs
->cmdlen
> sizeof(rc
->rc_cdb
))
162 panic("%s: CDB too large", device_xname(&sc
->sc_dv
));
168 if (__predict_false((ic
= icp_ccb_alloc(icp
)) == NULL
)) {
169 xs
->error
= XS_RESOURCE_SHORTAGE
;
173 rc
= &ic
->ic_cmd
.cmd_packet
.rc
;
174 ic
->ic_sg
= rc
->rc_sg
;
175 ic
->ic_service
= ICP_SCSIRAWSERVICE
;
176 soff
= ICP_SCRATCH_SENSE
+ ic
->ic_ident
*
177 sizeof(struct scsi_sense_data
);
180 * Build the command. We don't need to actively prevent
181 * access to array components, since the controller kindly
182 * takes care of that for us.
184 ic
->ic_cmd
.cmd_opcode
= htole16(ICP_WRITE
);
185 memcpy(rc
->rc_cdb
, xs
->cmd
, xs
->cmdlen
);
188 rc
->rc_direction
= htole32((flags
& XS_CTL_DATA_IN
) != 0 ?
189 ICP_DATA_IN
: ICP_DATA_OUT
);
190 rc
->rc_mdisc_time
= 0;
191 rc
->rc_mcon_time
= 0;
192 rc
->rc_clen
= htole32(xs
->cmdlen
);
193 rc
->rc_target
= periph
->periph_target
;
194 rc
->rc_lun
= periph
->periph_lun
;
195 rc
->rc_bus
= sc
->sc_busno
;
197 rc
->rc_sense_len
= htole32(sizeof(xs
->sense
.scsi_sense
));
199 htole32(soff
+ icp
->icp_scr_seg
[0].ds_addr
);
202 if (xs
->datalen
!= 0) {
203 rv
= icp_ccb_map(icp
, ic
, xs
->data
, xs
->datalen
,
204 (flags
& XS_CTL_DATA_IN
) != 0 ? IC_XFER_IN
:
207 icp_ccb_free(icp
, ic
);
208 xs
->error
= XS_DRIVER_STUFFUP
;
213 rc
->rc_nsgent
= htole32(ic
->ic_nsgent
);
215 rc
->rc_sdlen
= htole32(xs
->datalen
);
222 ic
->ic_cmdlen
= (u_long
)ic
->ic_sg
- (u_long
)&ic
->ic_cmd
+
223 ic
->ic_nsgent
* sizeof(*ic
->ic_sg
);
225 bus_dmamap_sync(icp
->icp_dmat
, icp
->icp_scr_dmamap
, soff
,
226 sizeof(xs
->sense
.scsi_sense
), BUS_DMASYNC_PREREAD
);
229 * Fire it off to the controller.
231 ic
->ic_intr
= icpsp_intr
;
233 ic
->ic_dv
= &sc
->sc_dv
;
235 if ((flags
& XS_CTL_POLL
) != 0) {
237 rv
= icp_ccb_poll(icp
, ic
, xs
->timeout
);
239 if (xs
->datalen
!= 0)
240 icp_ccb_unmap(icp
, ic
);
241 icp_ccb_free(icp
, ic
);
242 xs
->error
= XS_TIMEOUT
;
246 * XXX We're now in a bad way, because we
247 * don't know how to abort the command.
248 * That shouldn't matter too much, since
249 * polled commands won't be used while the
255 icp_ccb_enqueue(icp
, ic
);
259 case ADAPTER_REQ_GROW_RESOURCES
:
260 case ADAPTER_REQ_SET_XFER_MODE
:
262 * Neither of these cases are supported, and neither of them
263 * is particulatly relevant, since we have an abstract view
264 * of the bus; the controller takes care of all the nitty
272 icpsp_intr(struct icp_ccb
*ic
)
274 struct scsipi_xfer
*xs
;
275 struct icpsp_softc
*sc
;
276 struct icp_softc
*icp
;
279 sc
= (struct icpsp_softc
*)ic
->ic_dv
;
280 xs
= (struct scsipi_xfer
*)ic
->ic_context
;
281 icp
= (struct icp_softc
*)device_parent(ic
->ic_dv
);
282 soff
= ICP_SCRATCH_SENSE
+ ic
->ic_ident
*
283 sizeof(struct scsi_sense_data
);
285 SC_DEBUG(xs
->xs_periph
, SCSIPI_DB2
, ("icpsp_intr\n"));
287 bus_dmamap_sync(icp
->icp_dmat
, icp
->icp_scr_dmamap
, soff
,
288 sizeof(xs
->sense
.scsi_sense
), BUS_DMASYNC_POSTREAD
);
290 if (ic
->ic_status
== ICP_S_OK
) {
291 xs
->status
= SCSI_OK
;
293 } else if (ic
->ic_status
!= ICP_S_RAW_SCSI
|| icp
->icp_info
>= 0x100) {
294 xs
->error
= XS_SELTIMEOUT
;
295 xs
->resid
= xs
->datalen
;
297 xs
->status
= icp
->icp_info
;
299 switch (xs
->status
) {
302 printf("%s: error return (%d), but SCSI_OK?\n",
303 device_xname(&sc
->sc_dv
), icp
->icp_info
);
308 memcpy(&xs
->sense
.scsi_sense
,
309 (char *)icp
->icp_scr
+ soff
,
310 sizeof(xs
->sense
.scsi_sense
));
311 xs
->error
= XS_SENSE
;
315 * XXX Don't know how to get residual count.
317 xs
->resid
= xs
->datalen
;
322 if (xs
->datalen
!= 0)
323 icp_ccb_unmap(icp
, ic
);
324 icp_ccb_free(icp
, ic
);
329 icpsp_adjqparam(device_t dv
, int openings
)
331 struct icpsp_softc
*sc
= (struct icpsp_softc
*) dv
;
335 sc
->sc_adapter
.adapt_openings
+= openings
- sc
->sc_openings
;
336 sc
->sc_openings
= openings
;