1 1.1 jmcneill /* $NetBSD: di.c,v 1.1 2025/02/12 11:33:34 jmcneill Exp $ */ 2 1.1 jmcneill 3 1.1 jmcneill /*- 4 1.1 jmcneill * Copyright (c) 2025 Jared McNeill <jmcneill (at) invisible.ca> 5 1.1 jmcneill * All rights reserved. 6 1.1 jmcneill * 7 1.1 jmcneill * Redistribution and use in source and binary forms, with or without 8 1.1 jmcneill * modification, are permitted provided that the following conditions 9 1.1 jmcneill * are met: 10 1.1 jmcneill * 1. Redistributions of source code must retain the above copyright 11 1.1 jmcneill * notice, this list of conditions and the following disclaimer. 12 1.1 jmcneill * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 jmcneill * notice, this list of conditions and the following disclaimer in the 14 1.1 jmcneill * documentation and/or other materials provided with the distribution. 15 1.1 jmcneill * 16 1.1 jmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 1.1 jmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 1.1 jmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 1.1 jmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 1.1 jmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 1.1 jmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 1.1 jmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 1.1 jmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 1.1 jmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 1.1 jmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 1.1 jmcneill * SUCH DAMAGE. 27 1.1 jmcneill */ 28 1.1 jmcneill 29 1.1 jmcneill #include <sys/cdefs.h> 30 1.1 jmcneill __KERNEL_RCSID(0, "$NetBSD: di.c,v 1.1 2025/02/12 11:33:34 jmcneill Exp $"); 31 1.1 jmcneill 32 1.1 jmcneill #include <sys/param.h> 33 1.1 jmcneill #include <sys/bus.h> 34 1.1 jmcneill #include <sys/device.h> 35 1.1 jmcneill #include <sys/systm.h> 36 1.1 jmcneill #include <sys/callout.h> 37 1.1 jmcneill #include <sys/buf.h> 38 1.1 jmcneill #include <sys/dvdio.h> 39 1.1 jmcneill 40 1.1 jmcneill #include <uvm/uvm_extern.h> 41 1.1 jmcneill 42 1.1 jmcneill #include <dev/scsipi/scsi_all.h> 43 1.1 jmcneill #include <dev/scsipi/scsi_disk.h> 44 1.1 jmcneill #include <dev/scsipi/scsipi_all.h> 45 1.1 jmcneill #include <dev/scsipi/scsipi_cd.h> 46 1.1 jmcneill #include <dev/scsipi/scsipi_disk.h> 47 1.1 jmcneill #include <dev/scsipi/scsiconf.h> 48 1.1 jmcneill 49 1.1 jmcneill #include <machine/wii.h> 50 1.1 jmcneill #include <machine/pio.h> 51 1.1 jmcneill #include "hollywood.h" 52 1.1 jmcneill 53 1.1 jmcneill #ifdef DI_DEBUG 54 1.1 jmcneill #define DPRINTF(dv, fmt, ...) device_printf(dv, fmt, ## __VA_ARGS__) 55 1.1 jmcneill #else 56 1.1 jmcneill #define DPRINTF(dv, fmt, ...) 57 1.1 jmcneill #endif 58 1.1 jmcneill 59 1.1 jmcneill #define DI_REG_SIZE 0x40 60 1.1 jmcneill 61 1.1 jmcneill #define DISR 0x00 62 1.1 jmcneill #define DISR_BRKINT __BIT(6) 63 1.1 jmcneill #define DISR_BRKINTMASK __BIT(5) 64 1.1 jmcneill #define DISR_TCINT __BIT(4) 65 1.1 jmcneill #define DISR_TCINTMASK __BIT(3) 66 1.1 jmcneill #define DISR_DEINT __BIT(2) 67 1.1 jmcneill #define DISR_DEINTMASK __BIT(1) 68 1.1 jmcneill #define DISR_BRK __BIT(0) 69 1.1 jmcneill #define DICVR 0x04 70 1.1 jmcneill #define DICVR_CVRINT __BIT(2) 71 1.1 jmcneill #define DICVR_CVRINTMASK __BIT(1) 72 1.1 jmcneill #define DICVR_CVR __BIT(0) 73 1.1 jmcneill #define DICMDBUF0 0x08 74 1.1 jmcneill #define DICMDBUF1 0x0c 75 1.1 jmcneill #define DICMDBUF2 0x10 76 1.1 jmcneill #define DIMAR 0x14 77 1.1 jmcneill #define DILENGTH 0x18 78 1.1 jmcneill #define DICR 0x1c 79 1.1 jmcneill #define DICR_DMA __BIT(1) 80 1.1 jmcneill #define DICR_TSTART __BIT(0) 81 1.1 jmcneill #define DIMMBUF 0x20 82 1.1 jmcneill #define DICFG 0x24 83 1.1 jmcneill 84 1.1 jmcneill #define DI_CMD_INQUIRY 0x12000000 85 1.1 jmcneill #define DI_CMD_REPORT_KEY(x) (0xa4000000 | ((uint32_t)(x) << 16)) 86 1.1 jmcneill #define DI_CMD_READ_DVD_STRUCT(x) (0xad000000 | ((uint32_t)(x) << 24)) 87 1.1 jmcneill #define DI_CMD_READ_DVD 0xd0000000 88 1.1 jmcneill #define DI_CMD_REQUEST_ERROR 0xe0000000 89 1.1 jmcneill #define DI_CMD_STOP_MOTOR 0xe3000000 90 1.1 jmcneill 91 1.1 jmcneill #define DVDBLOCKSIZE 2048 92 1.1 jmcneill 93 1.1 jmcneill #define DI_IDLE_TIMEOUT_MS 30000 94 1.1 jmcneill 95 1.1 jmcneill struct di_softc; 96 1.1 jmcneill 97 1.1 jmcneill static int di_match(device_t, cfdata_t, void *); 98 1.1 jmcneill static void di_attach(device_t, device_t, void *); 99 1.1 jmcneill 100 1.1 jmcneill static bool di_shutdown(device_t, int); 101 1.1 jmcneill 102 1.1 jmcneill static int di_intr(void *); 103 1.1 jmcneill static void di_timeout(void *); 104 1.1 jmcneill static void di_idle(void *); 105 1.1 jmcneill 106 1.1 jmcneill static void di_request(struct scsipi_channel *, scsipi_adapter_req_t, 107 1.1 jmcneill void *); 108 1.1 jmcneill static void di_init_regs(struct di_softc *); 109 1.1 jmcneill static void di_reset(struct di_softc *, bool); 110 1.1 jmcneill 111 1.1 jmcneill struct di_response_inquiry { 112 1.1 jmcneill uint16_t revision_level; 113 1.1 jmcneill uint16_t device_code; 114 1.1 jmcneill uint32_t release_date; 115 1.1 jmcneill uint8_t padding[24]; 116 1.1 jmcneill } __aligned(4); 117 1.1 jmcneill CTASSERT(sizeof(struct di_response_inquiry) == 0x20); 118 1.1 jmcneill 119 1.1 jmcneill struct di_softc { 120 1.1 jmcneill device_t sc_dev; 121 1.1 jmcneill bus_space_tag_t sc_bst; 122 1.1 jmcneill bus_space_handle_t sc_bsh; 123 1.1 jmcneill bus_dma_tag_t sc_dmat; 124 1.1 jmcneill 125 1.1 jmcneill struct scsipi_adapter sc_adapter; 126 1.1 jmcneill struct scsipi_channel sc_channel; 127 1.1 jmcneill 128 1.1 jmcneill struct scsipi_xfer *sc_cur_xs; 129 1.1 jmcneill callout_t sc_timeout; 130 1.1 jmcneill callout_t sc_idle; 131 1.1 jmcneill int sc_pamr; 132 1.1 jmcneill 133 1.1 jmcneill bus_dmamap_t sc_dma_map; 134 1.1 jmcneill void *sc_dma_addr; 135 1.1 jmcneill size_t sc_dma_size; 136 1.1 jmcneill bus_dma_segment_t sc_dma_segs[1]; 137 1.1 jmcneill }; 138 1.1 jmcneill 139 1.1 jmcneill #define WR4(sc, reg, val) \ 140 1.1 jmcneill bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 141 1.1 jmcneill #define RD4(sc, reg) \ 142 1.1 jmcneill bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 143 1.1 jmcneill 144 1.1 jmcneill CFATTACH_DECL_NEW(di, sizeof(struct di_softc), 145 1.1 jmcneill di_match, di_attach, NULL, NULL); 146 1.1 jmcneill 147 1.1 jmcneill static int 148 1.1 jmcneill di_match(device_t parent, cfdata_t cf, void *aux) 149 1.1 jmcneill { 150 1.1 jmcneill return 1; 151 1.1 jmcneill } 152 1.1 jmcneill 153 1.1 jmcneill static void 154 1.1 jmcneill di_attach(device_t parent, device_t self, void *aux) 155 1.1 jmcneill { 156 1.1 jmcneill struct hollywood_attach_args *haa = aux; 157 1.1 jmcneill struct di_softc *sc = device_private(self); 158 1.1 jmcneill struct scsipi_adapter *adapt = &sc->sc_adapter; 159 1.1 jmcneill struct scsipi_channel *chan = &sc->sc_channel; 160 1.1 jmcneill int error, nsegs; 161 1.1 jmcneill 162 1.1 jmcneill sc->sc_dev = self; 163 1.1 jmcneill sc->sc_dmat = haa->haa_dmat; 164 1.1 jmcneill sc->sc_bst = haa->haa_bst; 165 1.1 jmcneill error = bus_space_map(sc->sc_bst, haa->haa_addr, DI_REG_SIZE, 166 1.1 jmcneill 0, &sc->sc_bsh); 167 1.1 jmcneill if (error != 0) { 168 1.1 jmcneill aprint_error(": couldn't map registers (%d)\n", error); 169 1.1 jmcneill return; 170 1.1 jmcneill } 171 1.1 jmcneill 172 1.1 jmcneill aprint_naive("\n"); 173 1.1 jmcneill aprint_normal(": Drive Interface\n"); 174 1.1 jmcneill 175 1.1 jmcneill callout_init(&sc->sc_timeout, 0); 176 1.1 jmcneill callout_setfunc(&sc->sc_timeout, di_timeout, sc); 177 1.1 jmcneill callout_init(&sc->sc_idle, 0); 178 1.1 jmcneill callout_setfunc(&sc->sc_idle, di_idle, sc); 179 1.1 jmcneill 180 1.1 jmcneill sc->sc_dma_size = MAXPHYS; 181 1.1 jmcneill error = bus_dmamem_alloc(sc->sc_dmat, sc->sc_dma_size, PAGE_SIZE, 0, 182 1.1 jmcneill sc->sc_dma_segs, 1, &nsegs, BUS_DMA_WAITOK); 183 1.1 jmcneill if (error != 0) { 184 1.1 jmcneill aprint_error_dev(self, "bus_dmamem_alloc failed: %d\n", error); 185 1.1 jmcneill return; 186 1.1 jmcneill } 187 1.1 jmcneill error = bus_dmamem_map(sc->sc_dmat, sc->sc_dma_segs, nsegs, 188 1.1 jmcneill sc->sc_dma_size, &sc->sc_dma_addr, BUS_DMA_WAITOK); 189 1.1 jmcneill if (error != 0) { 190 1.1 jmcneill aprint_error_dev(self, "bus_dmamem_map failed: %d\n", error); 191 1.1 jmcneill return; 192 1.1 jmcneill } 193 1.1 jmcneill error = bus_dmamap_create(sc->sc_dmat, sc->sc_dma_size, nsegs, 194 1.1 jmcneill sc->sc_dma_size, 0, BUS_DMA_WAITOK, &sc->sc_dma_map); 195 1.1 jmcneill if (error != 0) { 196 1.1 jmcneill aprint_error_dev(self, "bus_dmamap_create failed: %d\n", error); 197 1.1 jmcneill return; 198 1.1 jmcneill } 199 1.1 jmcneill error = bus_dmamap_load(sc->sc_dmat, sc->sc_dma_map, sc->sc_dma_addr, 200 1.1 jmcneill sc->sc_dma_size, NULL, BUS_DMA_WAITOK); 201 1.1 jmcneill if (error != 0) { 202 1.1 jmcneill aprint_error_dev(self, "bus_dmamap_load failed: %d\n", error); 203 1.1 jmcneill return; 204 1.1 jmcneill } 205 1.1 jmcneill 206 1.1 jmcneill memset(adapt, 0, sizeof(*adapt)); 207 1.1 jmcneill adapt->adapt_nchannels = 1; 208 1.1 jmcneill adapt->adapt_request = di_request; 209 1.1 jmcneill adapt->adapt_minphys = minphys; 210 1.1 jmcneill adapt->adapt_dev = self; 211 1.1 jmcneill adapt->adapt_max_periph = 1; 212 1.1 jmcneill adapt->adapt_openings = 1; 213 1.1 jmcneill 214 1.1 jmcneill memset(chan, 0, sizeof(*chan)); 215 1.1 jmcneill chan->chan_bustype = &scsi_bustype; 216 1.1 jmcneill chan->chan_ntargets = 2; 217 1.1 jmcneill chan->chan_nluns = 1; 218 1.1 jmcneill chan->chan_id = 0; 219 1.1 jmcneill chan->chan_flags = SCSIPI_CHAN_NOSETTLE; 220 1.1 jmcneill chan->chan_adapter = adapt; 221 1.1 jmcneill 222 1.1 jmcneill config_found(self, chan, scsiprint, CFARGS(.iattr = "scsi")); 223 1.1 jmcneill 224 1.1 jmcneill hollywood_intr_establish(haa->haa_irq, IPL_BIO, di_intr, sc, 225 1.1 jmcneill device_xname(self)); 226 1.1 jmcneill 227 1.1 jmcneill di_init_regs(sc); 228 1.1 jmcneill callout_schedule(&sc->sc_idle, mstohz(DI_IDLE_TIMEOUT_MS)); 229 1.1 jmcneill 230 1.1 jmcneill pmf_device_register1(self, NULL, NULL, di_shutdown); 231 1.1 jmcneill } 232 1.1 jmcneill 233 1.1 jmcneill static bool 234 1.1 jmcneill di_shutdown(device_t dev, int how) 235 1.1 jmcneill { 236 1.1 jmcneill struct di_softc *sc = device_private(dev); 237 1.1 jmcneill 238 1.1 jmcneill di_reset(sc, false); 239 1.1 jmcneill 240 1.1 jmcneill return true; 241 1.1 jmcneill } 242 1.1 jmcneill 243 1.1 jmcneill static void 244 1.1 jmcneill di_sense(struct scsipi_xfer *xs, uint8_t skey, uint8_t asc, uint8_t ascq) 245 1.1 jmcneill { 246 1.1 jmcneill struct scsi_sense_data *sense = &xs->sense.scsi_sense; 247 1.1 jmcneill 248 1.1 jmcneill xs->error = XS_SENSE; 249 1.1 jmcneill sense->response_code = SSD_RCODE_CURRENT | SSD_RCODE_VALID; 250 1.1 jmcneill sense->flags = skey; 251 1.1 jmcneill sense->asc = asc; 252 1.1 jmcneill sense->ascq = ascq; 253 1.1 jmcneill } 254 1.1 jmcneill 255 1.1 jmcneill static void 256 1.1 jmcneill di_request_error_sync(struct di_softc *sc, struct scsipi_xfer *xs) 257 1.1 jmcneill { 258 1.1 jmcneill uint32_t imm; 259 1.1 jmcneill int s; 260 1.1 jmcneill 261 1.1 jmcneill s = splbio(); 262 1.1 jmcneill WR4(sc, DICMDBUF0, DI_CMD_REQUEST_ERROR); 263 1.1 jmcneill WR4(sc, DICMDBUF1, 0); 264 1.1 jmcneill WR4(sc, DICMDBUF2, 0); 265 1.1 jmcneill WR4(sc, DILENGTH, 4); 266 1.1 jmcneill WR4(sc, DICR, DICR_TSTART); 267 1.1 jmcneill while (((RD4(sc, DISR) & DISR_TCINT)) == 0) { 268 1.1 jmcneill delay(1); 269 1.1 jmcneill } 270 1.1 jmcneill imm = RD4(sc, DIMMBUF); 271 1.1 jmcneill splx(s); 272 1.1 jmcneill 273 1.1 jmcneill DPRINTF(sc->sc_dev, "ERR IMMBUF = 0x%08x\n", imm); 274 1.1 jmcneill di_sense(xs, (imm >> 16) & 0xff, (imm >> 8) & 0xff, imm & 0xff); 275 1.1 jmcneill } 276 1.1 jmcneill 277 1.1 jmcneill static int 278 1.1 jmcneill di_transfer_error(struct di_softc *sc, struct scsipi_xfer *xs) 279 1.1 jmcneill { 280 1.1 jmcneill if (xs == NULL) { 281 1.1 jmcneill return 0; 282 1.1 jmcneill } 283 1.1 jmcneill 284 1.1 jmcneill DPRINTF(sc->sc_dev, "transfer error\n"); 285 1.1 jmcneill 286 1.1 jmcneill callout_stop(&sc->sc_timeout); 287 1.1 jmcneill di_request_error_sync(sc, xs); 288 1.1 jmcneill sc->sc_cur_xs = NULL; 289 1.1 jmcneill scsipi_done(xs); 290 1.1 jmcneill 291 1.1 jmcneill return 1; 292 1.1 jmcneill } 293 1.1 jmcneill 294 1.1 jmcneill static int 295 1.1 jmcneill di_transfer_complete(struct di_softc *sc, struct scsipi_xfer *xs) 296 1.1 jmcneill { 297 1.1 jmcneill struct scsipi_generic *cmd; 298 1.1 jmcneill struct scsipi_inquiry_data *inqbuf; 299 1.1 jmcneill struct scsipi_read_cd_cap_data *cdcap; 300 1.1 jmcneill struct di_response_inquiry *rinq; 301 1.1 jmcneill uint32_t imm; 302 1.1 jmcneill uint8_t *data; 303 1.1 jmcneill char buf[5]; 304 1.1 jmcneill 305 1.1 jmcneill if (xs == NULL) { 306 1.1 jmcneill DPRINTF(sc->sc_dev, "no active transfer\n"); 307 1.1 jmcneill return 0; 308 1.1 jmcneill } 309 1.1 jmcneill 310 1.1 jmcneill KASSERT(sc->sc_cur_xs == xs); 311 1.1 jmcneill 312 1.1 jmcneill cmd = xs->cmd; 313 1.1 jmcneill 314 1.1 jmcneill switch (cmd->opcode) { 315 1.1 jmcneill case INQUIRY: 316 1.1 jmcneill inqbuf = (struct scsipi_inquiry_data *)xs->data; 317 1.1 jmcneill rinq = sc->sc_dma_addr; 318 1.1 jmcneill 319 1.1 jmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 0, sizeof(*rinq), 320 1.1 jmcneill BUS_DMASYNC_POSTREAD); 321 1.1 jmcneill 322 1.1 jmcneill DPRINTF(sc->sc_dev, "revision_level %#x " 323 1.1 jmcneill "device_code %#x " 324 1.1 jmcneill "release_date %#x\n", 325 1.1 jmcneill rinq->revision_level, 326 1.1 jmcneill rinq->device_code, 327 1.1 jmcneill rinq->release_date); 328 1.1 jmcneill 329 1.1 jmcneill memset(inqbuf, 0, sizeof(*inqbuf)); 330 1.1 jmcneill inqbuf->device = T_CDROM; 331 1.1 jmcneill inqbuf->dev_qual2 = SID_REMOVABLE; 332 1.1 jmcneill strncpy(inqbuf->vendor, "NINTENDO", sizeof(inqbuf->vendor)); 333 1.1 jmcneill snprintf(inqbuf->product, sizeof(inqbuf->product), "%08x", 334 1.1 jmcneill rinq->release_date); 335 1.1 jmcneill snprintf(buf, sizeof(buf), "%04x", rinq->revision_level); 336 1.1 jmcneill memcpy(inqbuf->revision, buf, sizeof(inqbuf->revision)); 337 1.1 jmcneill xs->resid = 0; 338 1.1 jmcneill break; 339 1.1 jmcneill 340 1.1 jmcneill case SCSI_TEST_UNIT_READY: 341 1.1 jmcneill case SCSI_REQUEST_SENSE: 342 1.1 jmcneill imm = RD4(sc, DIMMBUF); 343 1.1 jmcneill DPRINTF(sc->sc_dev, "TUR IMMBUF = 0x%08x\n", imm); 344 1.1 jmcneill switch ((imm >> 24) & 0xff) { 345 1.1 jmcneill case 0: 346 1.1 jmcneill di_sense(xs, (imm >> 16) & 0xff, (imm >> 8) & 0xff, 347 1.1 jmcneill imm & 0xff); 348 1.1 jmcneill break; 349 1.1 jmcneill default: 350 1.1 jmcneill di_sense(xs, SKEY_MEDIUM_ERROR, 0, 0); 351 1.1 jmcneill break; 352 1.1 jmcneill } 353 1.1 jmcneill break; 354 1.1 jmcneill 355 1.1 jmcneill case SCSI_READ_6_COMMAND: 356 1.1 jmcneill case READ_10: 357 1.1 jmcneill case GPCMD_REPORT_KEY: 358 1.1 jmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 0, xs->datalen, 359 1.1 jmcneill BUS_DMASYNC_POSTREAD); 360 1.1 jmcneill memcpy(xs->data, sc->sc_dma_addr, xs->datalen); 361 1.1 jmcneill xs->resid = 0; 362 1.1 jmcneill break; 363 1.1 jmcneill 364 1.1 jmcneill case GPCMD_READ_DVD_STRUCTURE: 365 1.1 jmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 0, DVDBLOCKSIZE, 366 1.1 jmcneill BUS_DMASYNC_POSTREAD); 367 1.1 jmcneill memcpy(xs->data + 4, sc->sc_dma_addr, xs->datalen - 4); 368 1.1 jmcneill xs->resid = 0; 369 1.1 jmcneill break; 370 1.1 jmcneill 371 1.1 jmcneill case READ_CD_CAPACITY: 372 1.1 jmcneill cdcap = (struct scsipi_read_cd_cap_data *)xs->data; 373 1.1 jmcneill 374 1.1 jmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 0, DVDBLOCKSIZE, 375 1.1 jmcneill BUS_DMASYNC_POSTREAD); 376 1.1 jmcneill data = sc->sc_dma_addr; 377 1.1 jmcneill _lto4b(DVDBLOCKSIZE, cdcap->length); 378 1.1 jmcneill memcpy(cdcap->addr, &data[8], sizeof(cdcap->addr)); 379 1.1 jmcneill break; 380 1.1 jmcneill } 381 1.1 jmcneill 382 1.1 jmcneill sc->sc_cur_xs = NULL; 383 1.1 jmcneill scsipi_done(xs); 384 1.1 jmcneill 385 1.1 jmcneill return 1; 386 1.1 jmcneill } 387 1.1 jmcneill 388 1.1 jmcneill static int 389 1.1 jmcneill di_intr(void *priv) 390 1.1 jmcneill { 391 1.1 jmcneill struct di_softc *sc = priv; 392 1.1 jmcneill uint32_t sr, cvr; 393 1.1 jmcneill int ret = 0; 394 1.1 jmcneill 395 1.1 jmcneill sr = RD4(sc, DISR); 396 1.1 jmcneill cvr = RD4(sc, DICVR); 397 1.1 jmcneill 398 1.1 jmcneill if ((sr & DISR_DEINT) != 0) { 399 1.1 jmcneill ret |= di_transfer_error(sc, sc->sc_cur_xs); 400 1.1 jmcneill } else if ((sr & DISR_TCINT) != 0) { 401 1.1 jmcneill ret |= di_transfer_complete(sc, sc->sc_cur_xs); 402 1.1 jmcneill } 403 1.1 jmcneill 404 1.1 jmcneill if ((cvr & DICVR_CVRINT) != 0) { 405 1.1 jmcneill DPRINTF(sc->sc_dev, "drive %s\n", 406 1.1 jmcneill (cvr & DICVR_CVR) == 0 ? "closed" : "opened"); 407 1.1 jmcneill ret |= 1; 408 1.1 jmcneill } 409 1.1 jmcneill 410 1.1 jmcneill WR4(sc, DISR, sr); 411 1.1 jmcneill WR4(sc, DICVR, cvr); 412 1.1 jmcneill 413 1.1 jmcneill return ret; 414 1.1 jmcneill } 415 1.1 jmcneill 416 1.1 jmcneill static void 417 1.1 jmcneill di_timeout(void *priv) 418 1.1 jmcneill { 419 1.1 jmcneill struct di_softc *sc = priv; 420 1.1 jmcneill int s; 421 1.1 jmcneill 422 1.1 jmcneill s = splbio(); 423 1.1 jmcneill if (sc->sc_cur_xs != NULL) { 424 1.1 jmcneill struct scsipi_xfer *xs = sc->sc_cur_xs; 425 1.1 jmcneill 426 1.1 jmcneill DPRINTF(sc->sc_dev, "command %#x timeout, DISR = %#x\n", 427 1.1 jmcneill xs->cmd->opcode, RD4(sc, DISR)); 428 1.1 jmcneill xs->error = XS_TIMEOUT; 429 1.1 jmcneill scsipi_done(xs); 430 1.1 jmcneill 431 1.1 jmcneill sc->sc_cur_xs = NULL; 432 1.1 jmcneill } 433 1.1 jmcneill splx(s); 434 1.1 jmcneill } 435 1.1 jmcneill 436 1.1 jmcneill static void 437 1.1 jmcneill di_idle(void *priv) 438 1.1 jmcneill { 439 1.1 jmcneill struct di_softc *sc = priv; 440 1.1 jmcneill 441 1.1 jmcneill if ((RD4(sc, DICVR) & DICVR_CVR) != 0) { 442 1.1 jmcneill /* Cover is opened, nothing to do. */ 443 1.1 jmcneill return; 444 1.1 jmcneill } 445 1.1 jmcneill 446 1.1 jmcneill di_reset(sc, false); 447 1.1 jmcneill } 448 1.1 jmcneill 449 1.1 jmcneill static void 450 1.1 jmcneill di_start_request(struct di_softc *sc, struct scsipi_xfer *xs) 451 1.1 jmcneill { 452 1.1 jmcneill KASSERT(sc->sc_cur_xs == NULL); 453 1.1 jmcneill sc->sc_cur_xs = xs; 454 1.1 jmcneill if (xs->timeout != 0) { 455 1.1 jmcneill callout_schedule(&sc->sc_timeout, mstohz(xs->timeout) + 1); 456 1.1 jmcneill } else { 457 1.1 jmcneill DPRINTF(sc->sc_dev, "WARNING: xfer with no timeout!\n"); 458 1.1 jmcneill callout_schedule(&sc->sc_timeout, mstohz(15000)); 459 1.1 jmcneill } 460 1.1 jmcneill } 461 1.1 jmcneill 462 1.1 jmcneill static void 463 1.1 jmcneill di_init_regs(struct di_softc *sc) 464 1.1 jmcneill { 465 1.1 jmcneill WR4(sc, DISR, DISR_BRKINT | 466 1.1 jmcneill DISR_TCINT | DISR_TCINTMASK | 467 1.1 jmcneill DISR_DEINT | DISR_DEINTMASK); 468 1.1 jmcneill WR4(sc, DICVR, DICVR_CVRINT | DICVR_CVRINTMASK); 469 1.1 jmcneill } 470 1.1 jmcneill 471 1.1 jmcneill static void 472 1.1 jmcneill di_reset(struct di_softc *sc, bool spinup) 473 1.1 jmcneill { 474 1.1 jmcneill uint32_t val; 475 1.1 jmcneill int s; 476 1.1 jmcneill 477 1.1 jmcneill DPRINTF(sc->sc_dev, "reset spinup=%d\n", spinup); 478 1.1 jmcneill 479 1.1 jmcneill s = splhigh(); 480 1.1 jmcneill 481 1.1 jmcneill if (spinup) { 482 1.1 jmcneill out32(HW_GPIOB_OUT, in32(HW_GPIOB_OUT) & ~__BIT(GPIO_DI_SPIN)); 483 1.1 jmcneill } else { 484 1.1 jmcneill out32(HW_GPIOB_OUT, in32(HW_GPIOB_OUT) | __BIT(GPIO_DI_SPIN)); 485 1.1 jmcneill } 486 1.1 jmcneill 487 1.1 jmcneill val = in32(HW_RESETS); 488 1.1 jmcneill out32(HW_RESETS, val & ~RSTB_IODI); 489 1.1 jmcneill delay(12); 490 1.1 jmcneill out32(HW_RESETS, val | RSTB_IODI); 491 1.1 jmcneill 492 1.1 jmcneill WR4(sc, DISR, DISR_BRKINT | 493 1.1 jmcneill DISR_TCINT | DISR_TCINTMASK | 494 1.1 jmcneill DISR_DEINT | DISR_DEINTMASK); 495 1.1 jmcneill WR4(sc, DICVR, DICVR_CVRINT | DICVR_CVRINTMASK); 496 1.1 jmcneill 497 1.1 jmcneill splx(s); 498 1.1 jmcneill } 499 1.1 jmcneill 500 1.1 jmcneill static void 501 1.1 jmcneill di_stop_motor(struct di_softc *sc, struct scsipi_xfer *xs, bool eject) 502 1.1 jmcneill { 503 1.1 jmcneill uint32_t cmdflags = 0; 504 1.1 jmcneill int s; 505 1.1 jmcneill 506 1.1 jmcneill if (eject) { 507 1.1 jmcneill cmdflags |= 1 << 17; 508 1.1 jmcneill } 509 1.1 jmcneill 510 1.1 jmcneill s = splbio(); 511 1.1 jmcneill WR4(sc, DICMDBUF0, DI_CMD_STOP_MOTOR | cmdflags); 512 1.1 jmcneill WR4(sc, DICMDBUF1, 0); 513 1.1 jmcneill WR4(sc, DICMDBUF2, 0); 514 1.1 jmcneill WR4(sc, DILENGTH, 4); 515 1.1 jmcneill WR4(sc, DICR, DICR_TSTART); 516 1.1 jmcneill di_start_request(sc, xs); 517 1.1 jmcneill splx(s); 518 1.1 jmcneill } 519 1.1 jmcneill 520 1.1 jmcneill static void 521 1.1 jmcneill di_request(struct scsipi_channel *chan, scsipi_adapter_req_t req, void *arg) 522 1.1 jmcneill { 523 1.1 jmcneill struct di_softc *sc = device_private(chan->chan_adapter->adapt_dev); 524 1.1 jmcneill struct scsipi_xfer *xs; 525 1.1 jmcneill struct scsipi_generic *cmd; 526 1.1 jmcneill struct scsipi_start_stop *ss; 527 1.1 jmcneill struct scsi_prevent_allow_medium_removal *pamr; 528 1.1 jmcneill uint32_t blkno; 529 1.1 jmcneill int s; 530 1.1 jmcneill 531 1.1 jmcneill if (req != ADAPTER_REQ_RUN_XFER) { 532 1.1 jmcneill return; 533 1.1 jmcneill } 534 1.1 jmcneill 535 1.1 jmcneill callout_stop(&sc->sc_idle); 536 1.1 jmcneill 537 1.1 jmcneill KASSERT(sc->sc_cur_xs == NULL); 538 1.1 jmcneill 539 1.1 jmcneill xs = arg; 540 1.1 jmcneill cmd = xs->cmd; 541 1.1 jmcneill 542 1.1 jmcneill switch (cmd->opcode) { 543 1.1 jmcneill case INQUIRY: 544 1.1 jmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 545 1.1 jmcneill 0, sizeof(struct di_response_inquiry), 546 1.1 jmcneill BUS_DMASYNC_PREREAD); 547 1.1 jmcneill 548 1.1 jmcneill s = splbio(); 549 1.1 jmcneill WR4(sc, DICMDBUF0, DI_CMD_INQUIRY); 550 1.1 jmcneill WR4(sc, DICMDBUF1, 0); 551 1.1 jmcneill WR4(sc, DILENGTH, sizeof(struct di_response_inquiry)); 552 1.1 jmcneill WR4(sc, DIMAR, sc->sc_dma_segs[0].ds_addr); 553 1.1 jmcneill WR4(sc, DICR, DICR_TSTART | DICR_DMA); 554 1.1 jmcneill di_start_request(sc, xs); 555 1.1 jmcneill splx(s); 556 1.1 jmcneill break; 557 1.1 jmcneill 558 1.1 jmcneill case SCSI_TEST_UNIT_READY: 559 1.1 jmcneill case SCSI_REQUEST_SENSE: 560 1.1 jmcneill s = splbio(); 561 1.1 jmcneill WR4(sc, DICMDBUF0, DI_CMD_REQUEST_ERROR); 562 1.1 jmcneill WR4(sc, DICMDBUF1, 0); 563 1.1 jmcneill WR4(sc, DICMDBUF2, 0); 564 1.1 jmcneill WR4(sc, DILENGTH, 4); 565 1.1 jmcneill WR4(sc, DICR, DICR_TSTART); 566 1.1 jmcneill di_start_request(sc, xs); 567 1.1 jmcneill splx(s); 568 1.1 jmcneill break; 569 1.1 jmcneill 570 1.1 jmcneill case SCSI_READ_6_COMMAND: 571 1.1 jmcneill case READ_10: 572 1.1 jmcneill if (cmd->opcode == SCSI_READ_6_COMMAND) { 573 1.1 jmcneill blkno = _3btol(((struct scsi_rw_6 *)cmd)->addr); 574 1.1 jmcneill } else { 575 1.1 jmcneill KASSERT(cmd->opcode == READ_10); 576 1.1 jmcneill blkno = _4btol(((struct scsipi_rw_10 *)cmd)->addr); 577 1.1 jmcneill } 578 1.1 jmcneill 579 1.1 jmcneill if (xs->datalen == 0) { 580 1.1 jmcneill xs->error = XS_DRIVER_STUFFUP; 581 1.1 jmcneill scsipi_done(xs); 582 1.1 jmcneill break; 583 1.1 jmcneill } 584 1.1 jmcneill 585 1.1 jmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 586 1.1 jmcneill 0, xs->datalen, BUS_DMASYNC_PREREAD); 587 1.1 jmcneill 588 1.1 jmcneill s = splbio(); 589 1.1 jmcneill WR4(sc, DICMDBUF0, DI_CMD_READ_DVD); 590 1.1 jmcneill WR4(sc, DICMDBUF1, blkno); 591 1.1 jmcneill WR4(sc, DICMDBUF2, howmany(xs->datalen, DVDBLOCKSIZE)); 592 1.1 jmcneill WR4(sc, DILENGTH, roundup(xs->datalen, DVDBLOCKSIZE)); 593 1.1 jmcneill WR4(sc, DIMAR, sc->sc_dma_segs[0].ds_addr); 594 1.1 jmcneill WR4(sc, DICR, DICR_TSTART | DICR_DMA); 595 1.1 jmcneill di_start_request(sc, xs); 596 1.1 jmcneill splx(s); 597 1.1 jmcneill break; 598 1.1 jmcneill 599 1.1 jmcneill case GPCMD_READ_DVD_STRUCTURE: 600 1.1 jmcneill if (xs->datalen == 0) { 601 1.1 jmcneill DPRINTF(sc->sc_dev, "zero datalen\n"); 602 1.1 jmcneill xs->error = XS_DRIVER_STUFFUP; 603 1.1 jmcneill scsipi_done(xs); 604 1.1 jmcneill break; 605 1.1 jmcneill } 606 1.1 jmcneill 607 1.1 jmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 608 1.1 jmcneill 0, xs->datalen, BUS_DMASYNC_PREREAD); 609 1.1 jmcneill 610 1.1 jmcneill s = splbio(); 611 1.1 jmcneill WR4(sc, DICMDBUF0, DI_CMD_READ_DVD_STRUCT(cmd->bytes[6])); 612 1.1 jmcneill WR4(sc, DICMDBUF1, 0); 613 1.1 jmcneill WR4(sc, DICMDBUF2, 0); 614 1.1 jmcneill WR4(sc, DILENGTH, roundup(xs->datalen, DVDBLOCKSIZE)); 615 1.1 jmcneill WR4(sc, DIMAR, sc->sc_dma_segs[0].ds_addr); 616 1.1 jmcneill WR4(sc, DICR, DICR_TSTART | DICR_DMA); 617 1.1 jmcneill di_start_request(sc, xs); 618 1.1 jmcneill splx(s); 619 1.1 jmcneill break; 620 1.1 jmcneill 621 1.1 jmcneill case GPCMD_REPORT_KEY: 622 1.1 jmcneill if (xs->datalen == 0) { 623 1.1 jmcneill DPRINTF(sc->sc_dev, "zero datalen\n"); 624 1.1 jmcneill xs->error = XS_DRIVER_STUFFUP; 625 1.1 jmcneill scsipi_done(xs); 626 1.1 jmcneill break; 627 1.1 jmcneill } 628 1.1 jmcneill 629 1.1 jmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 630 1.1 jmcneill 0, xs->datalen, BUS_DMASYNC_PREREAD); 631 1.1 jmcneill 632 1.1 jmcneill s = splbio(); 633 1.1 jmcneill WR4(sc, DICMDBUF0, DI_CMD_REPORT_KEY(cmd->bytes[9] >> 2)); 634 1.1 jmcneill WR4(sc, DICMDBUF1, _4btol(&cmd->bytes[1])); 635 1.1 jmcneill WR4(sc, DICMDBUF2, 0); 636 1.1 jmcneill WR4(sc, DILENGTH, roundup(xs->datalen, 0x20)); 637 1.1 jmcneill WR4(sc, DIMAR, sc->sc_dma_segs[0].ds_addr); 638 1.1 jmcneill WR4(sc, DICR, DICR_TSTART | DICR_DMA); 639 1.1 jmcneill di_start_request(sc, xs); 640 1.1 jmcneill splx(s); 641 1.1 jmcneill break; 642 1.1 jmcneill 643 1.1 jmcneill case READ_CD_CAPACITY: 644 1.1 jmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 645 1.1 jmcneill 0, DVDBLOCKSIZE, BUS_DMASYNC_PREREAD); 646 1.1 jmcneill 647 1.1 jmcneill s = splbio(); 648 1.1 jmcneill WR4(sc, DICMDBUF0, DI_CMD_READ_DVD_STRUCT(DVD_STRUCT_PHYSICAL)); 649 1.1 jmcneill WR4(sc, DICMDBUF1, 0); 650 1.1 jmcneill WR4(sc, DICMDBUF2, 0); 651 1.1 jmcneill WR4(sc, DILENGTH, DVDBLOCKSIZE); 652 1.1 jmcneill WR4(sc, DIMAR, sc->sc_dma_segs[0].ds_addr); 653 1.1 jmcneill WR4(sc, DICR, DICR_TSTART | DICR_DMA); 654 1.1 jmcneill di_start_request(sc, xs); 655 1.1 jmcneill splx(s); 656 1.1 jmcneill break; 657 1.1 jmcneill 658 1.1 jmcneill case GET_CONFIGURATION: 659 1.1 jmcneill memset(xs->data, 0, sizeof(struct scsipi_get_conf_data)); 660 1.1 jmcneill xs->resid = 0; 661 1.1 jmcneill scsipi_done(xs); 662 1.1 jmcneill break; 663 1.1 jmcneill 664 1.1 jmcneill case READ_TOC: 665 1.1 jmcneill memset(xs->data, 0, sizeof(struct scsipi_toc_header)); 666 1.1 jmcneill xs->resid = 0; 667 1.1 jmcneill scsipi_done(xs); 668 1.1 jmcneill break; 669 1.1 jmcneill 670 1.1 jmcneill case READ_TRACKINFO: 671 1.1 jmcneill case READ_DISCINFO: 672 1.1 jmcneill di_sense(xs, SKEY_ILLEGAL_REQUEST, 0, 0); 673 1.1 jmcneill scsipi_done(xs); 674 1.1 jmcneill break; 675 1.1 jmcneill 676 1.1 jmcneill case START_STOP: 677 1.1 jmcneill ss = (struct scsipi_start_stop *)cmd; 678 1.1 jmcneill if (ss->how == SSS_START) { 679 1.1 jmcneill di_reset(sc, true); 680 1.1 jmcneill scsipi_done(xs); 681 1.1 jmcneill } else { 682 1.1 jmcneill di_stop_motor(sc, xs, (ss->how & SSS_LOEJ) != 0); 683 1.1 jmcneill } 684 1.1 jmcneill break; 685 1.1 jmcneill 686 1.1 jmcneill case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL: 687 1.1 jmcneill pamr = (struct scsi_prevent_allow_medium_removal *)cmd; 688 1.1 jmcneill sc->sc_pamr = pamr->how; 689 1.1 jmcneill scsipi_done(xs); 690 1.1 jmcneill break; 691 1.1 jmcneill 692 1.1 jmcneill default: 693 1.1 jmcneill DPRINTF(sc->sc_dev, "unsupported opcode %#x\n", cmd->opcode); 694 1.1 jmcneill scsipi_done(xs); 695 1.1 jmcneill } 696 1.1 jmcneill 697 1.1 jmcneill if (!sc->sc_pamr) { 698 1.1 jmcneill callout_schedule(&sc->sc_idle, mstohz(DI_IDLE_TIMEOUT_MS)); 699 1.1 jmcneill } 700 1.1 jmcneill } 701