1 | /* $NetBSD: icpsp.c,v 1.26 2014/03/07 13:19:26 skrll Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2002 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Andrew Doran. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
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. |
18 | * |
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. |
30 | */ |
31 | |
32 | #include <sys/cdefs.h> |
33 | __KERNEL_RCSID(0, "$NetBSD: icpsp.c,v 1.26 2014/03/07 13:19:26 skrll Exp $" ); |
34 | |
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> |
40 | #include <sys/proc.h> |
41 | #include <sys/buf.h> |
42 | #include <sys/endian.h> |
43 | #include <sys/malloc.h> |
44 | #include <sys/scsiio.h> |
45 | |
46 | #include <sys/bswap.h> |
47 | #include <sys/bus.h> |
48 | |
49 | #include <dev/scsipi/scsi_all.h> |
50 | #include <dev/scsipi/scsi_disk.h> |
51 | #include <dev/scsipi/scsipi_all.h> |
52 | #include <dev/scsipi/scsiconf.h> |
53 | #include <dev/scsipi/scsi_message.h> |
54 | |
55 | #include <dev/ic/icpreg.h> |
56 | #include <dev/ic/icpvar.h> |
57 | |
58 | struct icpsp_softc { |
59 | device_t sc_dv; |
60 | struct scsipi_adapter sc_adapter; |
61 | struct scsipi_channel sc_channel; |
62 | int sc_busno; |
63 | int sc_openings; |
64 | }; |
65 | |
66 | void icpsp_attach(device_t, device_t, void *); |
67 | void icpsp_intr(struct icp_ccb *); |
68 | int icpsp_match(device_t, cfdata_t, void *); |
69 | void icpsp_scsipi_request(struct scsipi_channel *, scsipi_adapter_req_t, |
70 | void *); |
71 | |
72 | void icpsp_adjqparam(device_t, int); |
73 | |
74 | CFATTACH_DECL_NEW(icpsp, sizeof(struct icpsp_softc), |
75 | icpsp_match, icpsp_attach, NULL, NULL); |
76 | |
77 | static const struct icp_servicecb icpsp_servicecb = { |
78 | icpsp_adjqparam, |
79 | }; |
80 | |
81 | int |
82 | icpsp_match(device_t parent, cfdata_t match, |
83 | void *aux) |
84 | { |
85 | struct icp_attach_args *icpa; |
86 | |
87 | icpa = aux; |
88 | |
89 | return (icpa->icpa_unit >= ICPA_UNIT_SCSI); |
90 | } |
91 | |
92 | void |
93 | icpsp_attach(device_t parent, device_t self, void *aux) |
94 | { |
95 | struct icp_attach_args *icpa; |
96 | struct icpsp_softc *sc; |
97 | struct icp_softc *icp; |
98 | |
99 | icpa = (struct icp_attach_args *)aux; |
100 | sc = device_private(self); |
101 | icp = device_private(parent); |
102 | |
103 | sc->sc_dv = self; |
104 | sc->sc_busno = icpa->icpa_unit - ICPA_UNIT_SCSI; |
105 | sc->sc_openings = icp->icp_openings; |
106 | printf(": physical SCSI channel %d\n" , sc->sc_busno); |
107 | |
108 | icp_register_servicecb(icp, icpa->icpa_unit, &icpsp_servicecb); |
109 | |
110 | sc->sc_adapter.adapt_dev = sc->sc_dv; |
111 | sc->sc_adapter.adapt_nchannels = 1; |
112 | sc->sc_adapter.adapt_openings = icp->icp_openings; |
113 | sc->sc_adapter.adapt_max_periph = icp->icp_openings; |
114 | sc->sc_adapter.adapt_minphys = minphys; |
115 | sc->sc_adapter.adapt_request = icpsp_scsipi_request; |
116 | |
117 | sc->sc_channel.chan_adapter = &sc->sc_adapter; |
118 | sc->sc_channel.chan_bustype = &scsi_bustype; |
119 | sc->sc_channel.chan_channel = 0; |
120 | sc->sc_channel.chan_ntargets = ((icp->icp_class & ICP_FC) != 0 ? |
121 | 127 : 16); /* XXX bogus check */ |
122 | sc->sc_channel.chan_nluns = 8; |
123 | sc->sc_channel.chan_id = icp->icp_bus_id[sc->sc_busno]; |
124 | sc->sc_channel.chan_flags = SCSIPI_CHAN_NOSETTLE; |
125 | |
126 | config_found(self, &sc->sc_channel, scsiprint); |
127 | } |
128 | |
129 | void |
130 | icpsp_scsipi_request(struct scsipi_channel *chan, scsipi_adapter_req_t req, |
131 | void *arg) |
132 | { |
133 | struct scsipi_xfer *xs; |
134 | struct scsipi_periph *periph; |
135 | struct icpsp_softc *sc; |
136 | struct icp_rawcmd *rc; |
137 | struct icp_softc *icp; |
138 | struct icp_ccb *ic; |
139 | int rv, flags, s, soff; |
140 | |
141 | sc = device_private(chan->chan_adapter->adapt_dev); |
142 | icp = device_private(device_parent(sc->sc_dv)); |
143 | |
144 | switch (req) { |
145 | case ADAPTER_REQ_RUN_XFER: |
146 | xs = arg; |
147 | periph = xs->xs_periph; |
148 | flags = xs->xs_control; |
149 | |
150 | SC_DEBUG(periph, SCSIPI_DB2, ("icpsp_scsi_request run_xfer\n" )); |
151 | |
152 | if ((flags & XS_CTL_RESET) != 0) { |
153 | /* XXX Unimplemented. */ |
154 | xs->error = XS_DRIVER_STUFFUP; |
155 | scsipi_done(xs); |
156 | return; |
157 | } |
158 | |
159 | #if defined(ICP_DEBUG) || defined(SCSIDEBUG) |
160 | if (xs->cmdlen > sizeof(rc->rc_cdb)) |
161 | panic("%s: CDB too large" , device_xname(sc->sc_dv)); |
162 | #endif |
163 | |
164 | /* |
165 | * Allocate a CCB. |
166 | */ |
167 | if (__predict_false((ic = icp_ccb_alloc(icp)) == NULL)) { |
168 | xs->error = XS_RESOURCE_SHORTAGE; |
169 | scsipi_done(xs); |
170 | return; |
171 | } |
172 | rc = &ic->ic_cmd.cmd_packet.rc; |
173 | ic->ic_sg = rc->rc_sg; |
174 | ic->ic_service = ICP_SCSIRAWSERVICE; |
175 | soff = ICP_SCRATCH_SENSE + ic->ic_ident * |
176 | sizeof(struct scsi_sense_data); |
177 | |
178 | /* |
179 | * Build the command. We don't need to actively prevent |
180 | * access to array components, since the controller kindly |
181 | * takes care of that for us. |
182 | */ |
183 | ic->ic_cmd.cmd_opcode = htole16(ICP_WRITE); |
184 | memcpy(rc->rc_cdb, xs->cmd, xs->cmdlen); |
185 | |
186 | rc->rc_padding0 = 0; |
187 | rc->rc_direction = htole32((flags & XS_CTL_DATA_IN) != 0 ? |
188 | ICP_DATA_IN : ICP_DATA_OUT); |
189 | rc->rc_mdisc_time = 0; |
190 | rc->rc_mcon_time = 0; |
191 | rc->rc_clen = htole32(xs->cmdlen); |
192 | rc->rc_target = periph->periph_target; |
193 | rc->rc_lun = periph->periph_lun; |
194 | rc->rc_bus = sc->sc_busno; |
195 | rc->rc_priority = 0; |
196 | rc->rc_sense_len = htole32(sizeof(xs->sense.scsi_sense)); |
197 | rc->rc_sense_addr = |
198 | htole32(soff + icp->icp_scr_seg[0].ds_addr); |
199 | rc->rc_padding1 = 0; |
200 | |
201 | if (xs->datalen != 0) { |
202 | rv = icp_ccb_map(icp, ic, xs->data, xs->datalen, |
203 | (flags & XS_CTL_DATA_IN) != 0 ? IC_XFER_IN : |
204 | IC_XFER_OUT); |
205 | if (rv != 0) { |
206 | icp_ccb_free(icp, ic); |
207 | xs->error = XS_DRIVER_STUFFUP; |
208 | scsipi_done(xs); |
209 | return; |
210 | } |
211 | |
212 | rc->rc_nsgent = htole32(ic->ic_nsgent); |
213 | rc->rc_sdata = ~0; |
214 | rc->rc_sdlen = htole32(xs->datalen); |
215 | } else { |
216 | rc->rc_nsgent = 0; |
217 | rc->rc_sdata = 0; |
218 | rc->rc_sdlen = 0; |
219 | } |
220 | |
221 | ic->ic_cmdlen = (u_long)ic->ic_sg - (u_long)&ic->ic_cmd + |
222 | ic->ic_nsgent * sizeof(*ic->ic_sg); |
223 | |
224 | bus_dmamap_sync(icp->icp_dmat, icp->icp_scr_dmamap, soff, |
225 | sizeof(xs->sense.scsi_sense), BUS_DMASYNC_PREREAD); |
226 | |
227 | /* |
228 | * Fire it off to the controller. |
229 | */ |
230 | ic->ic_intr = icpsp_intr; |
231 | ic->ic_context = xs; |
232 | ic->ic_dv = sc->sc_dv; |
233 | |
234 | if ((flags & XS_CTL_POLL) != 0) { |
235 | s = splbio(); |
236 | rv = icp_ccb_poll(icp, ic, xs->timeout); |
237 | if (rv != 0) { |
238 | if (xs->datalen != 0) |
239 | icp_ccb_unmap(icp, ic); |
240 | icp_ccb_free(icp, ic); |
241 | xs->error = XS_TIMEOUT; |
242 | scsipi_done(xs); |
243 | |
244 | /* |
245 | * XXX We're now in a bad way, because we |
246 | * don't know how to abort the command. |
247 | * That shouldn't matter too much, since |
248 | * polled commands won't be used while the |
249 | * system is running. |
250 | */ |
251 | } |
252 | splx(s); |
253 | } else |
254 | icp_ccb_enqueue(icp, ic); |
255 | |
256 | break; |
257 | |
258 | case ADAPTER_REQ_GROW_RESOURCES: |
259 | case ADAPTER_REQ_SET_XFER_MODE: |
260 | /* |
261 | * Neither of these cases are supported, and neither of them |
262 | * is particulatly relevant, since we have an abstract view |
263 | * of the bus; the controller takes care of all the nitty |
264 | * gritty. |
265 | */ |
266 | break; |
267 | } |
268 | } |
269 | |
270 | void |
271 | icpsp_intr(struct icp_ccb *ic) |
272 | { |
273 | struct scsipi_xfer *xs; |
274 | struct icp_softc *icp; |
275 | int soff; |
276 | |
277 | #ifdef DIAGNOSTIC |
278 | struct icpsp_softc *sc = device_private(ic->ic_dv); |
279 | #endif |
280 | xs = ic->ic_context; |
281 | icp = device_private(device_parent(ic->ic_dv)); |
282 | soff = ICP_SCRATCH_SENSE + ic->ic_ident * |
283 | sizeof(struct scsi_sense_data); |
284 | |
285 | SC_DEBUG(xs->xs_periph, SCSIPI_DB2, ("icpsp_intr\n" )); |
286 | |
287 | bus_dmamap_sync(icp->icp_dmat, icp->icp_scr_dmamap, soff, |
288 | sizeof(xs->sense.scsi_sense), BUS_DMASYNC_POSTREAD); |
289 | |
290 | if (ic->ic_status == ICP_S_OK) { |
291 | xs->status = SCSI_OK; |
292 | xs->resid = 0; |
293 | } else if (ic->ic_status != ICP_S_RAW_SCSI || icp->icp_info >= 0x100) { |
294 | xs->error = XS_SELTIMEOUT; |
295 | xs->resid = xs->datalen; |
296 | } else { |
297 | xs->status = icp->icp_info; |
298 | |
299 | switch (xs->status) { |
300 | case SCSI_OK: |
301 | #ifdef DIAGNOSTIC |
302 | printf("%s: error return (%d), but SCSI_OK?\n" , |
303 | device_xname(sc->sc_dv), icp->icp_info); |
304 | #endif |
305 | xs->resid = 0; |
306 | break; |
307 | case SCSI_CHECK: |
308 | memcpy(&xs->sense.scsi_sense, |
309 | (char *)icp->icp_scr + soff, |
310 | sizeof(xs->sense.scsi_sense)); |
311 | xs->error = XS_SENSE; |
312 | /* FALLTHROUGH */ |
313 | default: |
314 | /* |
315 | * XXX Don't know how to get residual count. |
316 | */ |
317 | xs->resid = xs->datalen; |
318 | break; |
319 | } |
320 | } |
321 | |
322 | if (xs->datalen != 0) |
323 | icp_ccb_unmap(icp, ic); |
324 | icp_ccb_free(icp, ic); |
325 | scsipi_done(xs); |
326 | } |
327 | |
328 | void |
329 | icpsp_adjqparam(device_t self, int openings) |
330 | { |
331 | struct icpsp_softc *sc = device_private(self); |
332 | int s; |
333 | |
334 | s = splbio(); |
335 | sc->sc_adapter.adapt_openings += openings - sc->sc_openings; |
336 | sc->sc_openings = openings; |
337 | splx(s); |
338 | } |
339 | |