1 /* $NetBSD: icpsp.c,v 1.2 2002/04/25 18:45:35 ad 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 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 #include <sys/cdefs.h> 40 __KERNEL_RCSID(0, "$NetBSD: icpsp.c,v 1.2 2002/04/25 18:45:35 ad Exp $"); 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/kernel.h> 45 #include <sys/device.h> 46 #include <sys/queue.h> 47 #include <sys/proc.h> 48 #include <sys/buf.h> 49 #include <sys/endian.h> 50 #include <sys/malloc.h> 51 #include <sys/scsiio.h> 52 #include <sys/lock.h> 53 54 #include <machine/bswap.h> 55 #include <machine/bus.h> 56 57 #include <uvm/uvm_extern.h> 58 59 #include <dev/scsipi/scsi_all.h> 60 #include <dev/scsipi/scsi_disk.h> 61 #include <dev/scsipi/scsipi_all.h> 62 #include <dev/scsipi/scsiconf.h> 63 #include <dev/scsipi/scsi_message.h> 64 65 #include <dev/ic/icpreg.h> 66 #include <dev/ic/icpvar.h> 67 68 struct icpsp_softc { 69 struct device sc_dv; 70 struct scsipi_adapter sc_adapter; 71 struct scsipi_channel sc_channel; 72 int sc_busno; 73 }; 74 75 void icpsp_attach(struct device *, struct device *, void *); 76 void icpsp_intr(struct icp_ccb *); 77 int icpsp_match(struct device *, struct cfdata *, void *); 78 void icpsp_scsipi_request(struct scsipi_channel *, scsipi_adapter_req_t, 79 void *); 80 81 struct cfattach icpsp_ca = { 82 sizeof(struct icpsp_softc), icpsp_match, icpsp_attach 83 }; 84 85 int 86 icpsp_match(struct device *parent, struct cfdata *match, void *aux) 87 { 88 struct icp_attach_args *icpa; 89 90 icpa = aux; 91 92 return (icpa->icpa_unit >= ICPA_UNIT_SCSI); 93 } 94 95 void 96 icpsp_attach(struct device *parent, struct device *self, void *aux) 97 { 98 struct icp_attach_args *icpa; 99 struct icpsp_softc *sc; 100 struct icp_softc *icp; 101 102 icpa = (struct icp_attach_args *)aux; 103 sc = (struct icpsp_softc *)self; 104 icp = (struct icp_softc *)parent; 105 106 sc->sc_busno = icpa->icpa_unit - ICPA_UNIT_SCSI; 107 printf(": physical SCSI channel %d\n", sc->sc_busno); 108 109 sc->sc_adapter.adapt_dev = &sc->sc_dv; 110 sc->sc_adapter.adapt_nchannels = 1; 111 sc->sc_adapter.adapt_openings = icp->icp_openings; 112 sc->sc_adapter.adapt_max_periph = icp->icp_openings; 113 sc->sc_adapter.adapt_minphys = minphys; 114 sc->sc_adapter.adapt_request = icpsp_scsipi_request; 115 116 sc->sc_channel.chan_adapter = &sc->sc_adapter; 117 sc->sc_channel.chan_bustype = &scsi_bustype; 118 sc->sc_channel.chan_channel = 0; 119 sc->sc_channel.chan_ntargets = ((icp->icp_class & ICP_FC) != 0 ? 120 127 : 16); /* XXX bogus check */ 121 sc->sc_channel.chan_nluns = 7; 122 sc->sc_channel.chan_id = icp->icp_bus_id[sc->sc_busno]; 123 sc->sc_channel.chan_flags = SCSIPI_CHAN_NOSETTLE; 124 125 config_found(self, &sc->sc_channel, scsiprint); 126 } 127 128 void 129 icpsp_scsipi_request(struct scsipi_channel *chan, scsipi_adapter_req_t req, 130 void *arg) 131 { 132 struct scsipi_xfer *xs; 133 struct scsipi_periph *periph; 134 struct icpsp_softc *sc; 135 struct icp_rawcmd *rc; 136 struct icp_softc *icp; 137 struct icp_ccb *ic; 138 int rv, flags, s, soff; 139 140 sc = (void *)chan->chan_adapter->adapt_dev; 141 icp = (struct icp_softc *)sc->sc_dv.dv_parent; 142 143 switch (req) { 144 case ADAPTER_REQ_RUN_XFER: 145 xs = arg; 146 periph = xs->xs_periph; 147 flags = xs->xs_control; 148 149 SC_DEBUG(periph, SCSIPI_DB2, ("icpsp_scsi_request run_xfer\n")); 150 151 if ((flags & XS_CTL_RESET) != 0) { 152 /* XXX Unimplemented. */ 153 xs->error = XS_DRIVER_STUFFUP; 154 scsipi_done(xs); 155 return; 156 } 157 158 #if defined(ICP_DEBUG) || defined(SCSIDEBUG) 159 if (xs->cmdlen > sizeof(rc->rc_cdb)) 160 panic("%s: CDB too large\n", sc->sc_dv.dv_xname); 161 #endif 162 163 /* 164 * Allocate a CCB. 165 */ 166 ic = icp_ccb_alloc(icp); 167 rc = &ic->ic_cmd.cmd_packet.rc; 168 ic->ic_sg = rc->rc_sg; 169 ic->ic_service = ICP_SCSIRAWSERVICE; 170 soff = ICP_SCRATCH_SENSE + ic->ic_ident * 171 sizeof(struct scsipi_sense_data); 172 173 /* 174 * Build the command. We don't need to actively prevent 175 * access to array components, since the controller kindly 176 * takes care of that for us. 177 */ 178 ic->ic_cmd.cmd_opcode = htole16(ICP_WRITE); 179 memcpy(rc->rc_cdb, xs->cmd, xs->cmdlen); 180 181 rc->rc_padding0 = 0; 182 rc->rc_direction = htole32((flags & XS_CTL_DATA_IN) != 0 ? 183 ICP_DATA_IN : ICP_DATA_OUT); 184 rc->rc_mdisc_time = 0; 185 rc->rc_mcon_time = 0; 186 rc->rc_clen = htole32(xs->cmdlen); 187 rc->rc_target = periph->periph_target; 188 rc->rc_lun = periph->periph_lun; 189 rc->rc_bus = sc->sc_busno; 190 rc->rc_priority = 0; 191 rc->rc_sense_len = htole32(sizeof(xs->sense.scsi_sense)); 192 rc->rc_sense_addr = 193 htole32(soff + icp->icp_scr_seg[0].ds_addr); 194 rc->rc_padding1 = 0; 195 196 if (xs->datalen != 0) { 197 rv = icp_ccb_map(icp, ic, xs->data, xs->datalen, 198 (flags & XS_CTL_DATA_IN) != 0 ? IC_XFER_IN : 199 IC_XFER_OUT); 200 if (rv != 0) { 201 icp_ccb_free(icp, ic); 202 xs->error = XS_DRIVER_STUFFUP; 203 scsipi_done(xs); 204 return; 205 } 206 207 rc->rc_nsgent = htole32(ic->ic_nsgent); 208 rc->rc_sdata = ~0; 209 rc->rc_sdlen = htole32(xs->datalen); 210 } else { 211 rc->rc_nsgent = 0; 212 rc->rc_sdata = 0; 213 rc->rc_sdlen = 0; 214 } 215 216 ic->ic_cmdlen = (u_long)ic->ic_sg - (u_long)&ic->ic_cmd + 217 ic->ic_nsgent * sizeof(*ic->ic_sg); 218 219 bus_dmamap_sync(icp->icp_dmat, icp->icp_scr_dmamap, soff, 220 sizeof(xs->sense.scsi_sense), BUS_DMASYNC_PREREAD); 221 222 /* 223 * Fire it off to the controller. 224 */ 225 ic->ic_intr = icpsp_intr; 226 ic->ic_context = xs; 227 ic->ic_dv = &sc->sc_dv; 228 229 if ((flags & XS_CTL_POLL) != 0) { 230 s = splbio(); 231 rv = icp_ccb_poll(icp, ic, xs->timeout); 232 if (rv != 0) { 233 if (xs->datalen != 0) 234 icp_ccb_unmap(icp, ic); 235 icp_ccb_free(icp, ic); 236 xs->error = XS_TIMEOUT; 237 scsipi_done(xs); 238 239 /* 240 * XXX We're now in a bad way, because we 241 * don't know how to abort the command. 242 * That shouldn't matter too much, since 243 * polled commands won't be used while the 244 * system is running. 245 */ 246 } 247 splx(s); 248 } else 249 icp_ccb_enqueue(icp, ic); 250 251 break; 252 253 case ADAPTER_REQ_GROW_RESOURCES: 254 case ADAPTER_REQ_SET_XFER_MODE: 255 /* 256 * Neither of these cases are supported, and neither of them 257 * is particulatly relevant, since we have an abstract view 258 * of the bus; the controller takes care of all the nitty 259 * gritty. 260 */ 261 break; 262 } 263 } 264 265 void 266 icpsp_intr(struct icp_ccb *ic) 267 { 268 struct scsipi_xfer *xs; 269 struct icpsp_softc *sc; 270 struct icp_softc *icp; 271 int soff; 272 273 sc = (struct icpsp_softc *)ic->ic_dv; 274 xs = (struct scsipi_xfer *)ic->ic_context; 275 icp = (struct icp_softc *)ic->ic_dv->dv_parent; 276 soff = ICP_SCRATCH_SENSE + ic->ic_ident * 277 sizeof(struct scsipi_sense_data); 278 279 SC_DEBUG(xs->xs_periph, SCSIPI_DB2, ("icpsp_intr\n")); 280 281 bus_dmamap_sync(icp->icp_dmat, icp->icp_scr_dmamap, soff, 282 sizeof(xs->sense.scsi_sense), BUS_DMASYNC_POSTREAD); 283 284 if (ic->ic_status == ICP_S_OK) { 285 xs->status = SCSI_OK; 286 xs->resid = 0; 287 } else if (ic->ic_status != ICP_S_RAW_SCSI || icp->icp_info >= 0x100) { 288 xs->error = XS_SELTIMEOUT; 289 xs->resid = xs->datalen; 290 } else { 291 xs->status = icp->icp_info; 292 293 switch (xs->status) { 294 case SCSI_OK: 295 #ifdef DIAGNOSTIC 296 printf("%s: error return (%d), but SCSI_OK?\n", 297 sc->sc_dv.dv_xname, icp->icp_info); 298 #endif 299 xs->resid = 0; 300 break; 301 case SCSI_CHECK: 302 memcpy(&xs->sense.scsi_sense, icp->icp_scr + soff, 303 sizeof(xs->sense.scsi_sense)); 304 xs->error = XS_SENSE; 305 /* FALLTHROUGH */ 306 default: 307 /* 308 * XXX Don't know how to get residual count. 309 */ 310 xs->resid = xs->datalen; 311 break; 312 } 313 } 314 315 if (xs->datalen != 0) 316 icp_ccb_unmap(icp, ic); 317 icp_ccb_free(icp, ic); 318 scsipi_done(xs); 319 } 320