di.c revision 1.1
11.1Sjmcneill/* $NetBSD: di.c,v 1.1 2026/01/09 22:54:29 jmcneill Exp $ */ 21.1Sjmcneill 31.1Sjmcneill/*- 41.1Sjmcneill * Copyright (c) 2025 Jared McNeill <jmcneill@invisible.ca> 51.1Sjmcneill * All rights reserved. 61.1Sjmcneill * 71.1Sjmcneill * Redistribution and use in source and binary forms, with or without 81.1Sjmcneill * modification, are permitted provided that the following conditions 91.1Sjmcneill * are met: 101.1Sjmcneill * 1. Redistributions of source code must retain the above copyright 111.1Sjmcneill * notice, this list of conditions and the following disclaimer. 121.1Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright 131.1Sjmcneill * notice, this list of conditions and the following disclaimer in the 141.1Sjmcneill * documentation and/or other materials provided with the distribution. 151.1Sjmcneill * 161.1Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 171.1Sjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 181.1Sjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 191.1Sjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 201.1Sjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 211.1Sjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 221.1Sjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 231.1Sjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 241.1Sjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 251.1Sjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 261.1Sjmcneill * SUCH DAMAGE. 271.1Sjmcneill */ 281.1Sjmcneill 291.1Sjmcneill#include <sys/cdefs.h> 301.1Sjmcneill__KERNEL_RCSID(0, "$NetBSD: di.c,v 1.1 2026/01/09 22:54:29 jmcneill Exp $"); 311.1Sjmcneill 321.1Sjmcneill#include <sys/param.h> 331.1Sjmcneill#include <sys/bus.h> 341.1Sjmcneill#include <sys/device.h> 351.1Sjmcneill#include <sys/systm.h> 361.1Sjmcneill#include <sys/callout.h> 371.1Sjmcneill#include <sys/buf.h> 381.1Sjmcneill#include <sys/dvdio.h> 391.1Sjmcneill 401.1Sjmcneill#include <uvm/uvm_extern.h> 411.1Sjmcneill 421.1Sjmcneill#include <dev/scsipi/scsi_all.h> 431.1Sjmcneill#include <dev/scsipi/scsi_disk.h> 441.1Sjmcneill#include <dev/scsipi/scsipi_all.h> 451.1Sjmcneill#include <dev/scsipi/scsipi_cd.h> 461.1Sjmcneill#include <dev/scsipi/scsipi_disk.h> 471.1Sjmcneill#include <dev/scsipi/scsiconf.h> 481.1Sjmcneill 491.1Sjmcneill#include <machine/wii.h> 501.1Sjmcneill#include <machine/wiiu.h> 511.1Sjmcneill#include <machine/pio.h> 521.1Sjmcneill#include "ahb.h" 531.1Sjmcneill 541.1Sjmcneill#ifdef DI_DEBUG 551.1Sjmcneill#define DPRINTF(dv, fmt, ...) device_printf(dv, fmt, ## __VA_ARGS__) 561.1Sjmcneill#else 571.1Sjmcneill#define DPRINTF(dv, fmt, ...) 581.1Sjmcneill#endif 591.1Sjmcneill 601.1Sjmcneill#define DI_REG_SIZE 0x40 611.1Sjmcneill 621.1Sjmcneill#define DISR 0x00 631.1Sjmcneill#define DISR_BRKINT __BIT(6) 641.1Sjmcneill#define DISR_BRKINTMASK __BIT(5) 651.1Sjmcneill#define DISR_TCINT __BIT(4) 661.1Sjmcneill#define DISR_TCINTMASK __BIT(3) 671.1Sjmcneill#define DISR_DEINT __BIT(2) 681.1Sjmcneill#define DISR_DEINTMASK __BIT(1) 691.1Sjmcneill#define DISR_BRK __BIT(0) 701.1Sjmcneill#define DICVR 0x04 711.1Sjmcneill#define DICVR_CVRINT __BIT(2) 721.1Sjmcneill#define DICVR_CVRINTMASK __BIT(1) 731.1Sjmcneill#define DICVR_CVR __BIT(0) 741.1Sjmcneill#define DICMDBUF0 0x08 751.1Sjmcneill#define DICMDBUF1 0x0c 761.1Sjmcneill#define DICMDBUF2 0x10 771.1Sjmcneill#define DIMAR 0x14 781.1Sjmcneill#define DILENGTH 0x18 791.1Sjmcneill#define DICR 0x1c 801.1Sjmcneill#define DICR_DMA __BIT(1) 811.1Sjmcneill#define DICR_TSTART __BIT(0) 821.1Sjmcneill#define DIMMBUF 0x20 831.1Sjmcneill#define DICFG 0x24 841.1Sjmcneill 851.1Sjmcneill#define DI_CMD_INQUIRY 0x12000000 861.1Sjmcneill#define DI_CMD_REPORT_KEY(x) (0xa4000000 | ((uint32_t)(x) << 16)) 871.1Sjmcneill#define DI_CMD_READ_DVD_STRUCT(x) (0xad000000 | ((uint32_t)(x) << 24)) 881.1Sjmcneill#define DI_CMD_READ_DVD 0xd0000000 891.1Sjmcneill#define DI_CMD_REQUEST_ERROR 0xe0000000 901.1Sjmcneill#define DI_CMD_STOP_MOTOR 0xe3000000 911.1Sjmcneill 921.1Sjmcneill#define DVDBLOCKSIZE 2048 931.1Sjmcneill 941.1Sjmcneill#define DI_IDLE_TIMEOUT_MS 30000 951.1Sjmcneill 961.1Sjmcneillstruct di_softc; 971.1Sjmcneill 981.1Sjmcneillstatic int di_match(device_t, cfdata_t, void *); 991.1Sjmcneillstatic void di_attach(device_t, device_t, void *); 1001.1Sjmcneill 1011.1Sjmcneillstatic bool di_shutdown(device_t, int); 1021.1Sjmcneill 1031.1Sjmcneillstatic int di_intr(void *); 1041.1Sjmcneillstatic void di_timeout(void *); 1051.1Sjmcneillstatic void di_idle(void *); 1061.1Sjmcneill 1071.1Sjmcneillstatic void di_request(struct scsipi_channel *, scsipi_adapter_req_t, 1081.1Sjmcneill void *); 1091.1Sjmcneillstatic void di_init_regs(struct di_softc *); 1101.1Sjmcneillstatic void di_reset(struct di_softc *, bool); 1111.1Sjmcneill 1121.1Sjmcneillstruct di_response_inquiry { 1131.1Sjmcneill uint16_t revision_level; 1141.1Sjmcneill uint16_t device_code; 1151.1Sjmcneill uint32_t release_date; 1161.1Sjmcneill uint8_t padding[24]; 1171.1Sjmcneill} __aligned(4); 1181.1SjmcneillCTASSERT(sizeof(struct di_response_inquiry) == 0x20); 1191.1Sjmcneill 1201.1Sjmcneillstruct di_softc { 1211.1Sjmcneill device_t sc_dev; 1221.1Sjmcneill bus_space_tag_t sc_bst; 1231.1Sjmcneill bus_space_handle_t sc_bsh; 1241.1Sjmcneill bus_dma_tag_t sc_dmat; 1251.1Sjmcneill 1261.1Sjmcneill struct scsipi_adapter sc_adapter; 1271.1Sjmcneill struct scsipi_channel sc_channel; 1281.1Sjmcneill 1291.1Sjmcneill struct scsipi_xfer *sc_cur_xs; 1301.1Sjmcneill callout_t sc_timeout; 1311.1Sjmcneill callout_t sc_idle; 1321.1Sjmcneill int sc_pamr; 1331.1Sjmcneill 1341.1Sjmcneill bus_dmamap_t sc_dma_map; 1351.1Sjmcneill void *sc_dma_addr; 1361.1Sjmcneill size_t sc_dma_size; 1371.1Sjmcneill bus_dma_segment_t sc_dma_segs[1]; 1381.1Sjmcneill}; 1391.1Sjmcneill 1401.1Sjmcneill#define WR4(sc, reg, val) \ 1411.1Sjmcneill bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 1421.1Sjmcneill#define RD4(sc, reg) \ 1431.1Sjmcneill bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 1441.1Sjmcneill 1451.1SjmcneillCFATTACH_DECL_NEW(di, sizeof(struct di_softc), 1461.1Sjmcneill di_match, di_attach, NULL, NULL); 1471.1Sjmcneill 1481.1Sjmcneillstatic int 1491.1Sjmcneilldi_match(device_t parent, cfdata_t cf, void *aux) 1501.1Sjmcneill{ 1511.1Sjmcneill return !wiiu_native; 1521.1Sjmcneill} 1531.1Sjmcneill 1541.1Sjmcneillstatic void 1551.1Sjmcneilldi_attach(device_t parent, device_t self, void *aux) 1561.1Sjmcneill{ 1571.1Sjmcneill struct ahb_attach_args *aaa = aux; 1581.1Sjmcneill struct di_softc *sc = device_private(self); 1591.1Sjmcneill struct scsipi_adapter *adapt = &sc->sc_adapter; 1601.1Sjmcneill struct scsipi_channel *chan = &sc->sc_channel; 1611.1Sjmcneill int error, nsegs; 1621.1Sjmcneill 1631.1Sjmcneill sc->sc_dev = self; 1641.1Sjmcneill sc->sc_dmat = aaa->aaa_dmat; 1651.1Sjmcneill sc->sc_bst = aaa->aaa_bst; 1661.1Sjmcneill error = bus_space_map(sc->sc_bst, aaa->aaa_addr, DI_REG_SIZE, 1671.1Sjmcneill 0, &sc->sc_bsh); 1681.1Sjmcneill if (error != 0) { 1691.1Sjmcneill aprint_error(": couldn't map registers (%d)\n", error); 1701.1Sjmcneill return; 1711.1Sjmcneill } 1721.1Sjmcneill 1731.1Sjmcneill aprint_naive("\n"); 1741.1Sjmcneill aprint_normal(": Drive Interface\n"); 1751.1Sjmcneill 1761.1Sjmcneill callout_init(&sc->sc_timeout, 0); 1771.1Sjmcneill callout_setfunc(&sc->sc_timeout, di_timeout, sc); 1781.1Sjmcneill callout_init(&sc->sc_idle, 0); 1791.1Sjmcneill callout_setfunc(&sc->sc_idle, di_idle, sc); 1801.1Sjmcneill 1811.1Sjmcneill sc->sc_dma_size = MAXPHYS; 1821.1Sjmcneill error = bus_dmamem_alloc(sc->sc_dmat, sc->sc_dma_size, PAGE_SIZE, 0, 1831.1Sjmcneill sc->sc_dma_segs, 1, &nsegs, BUS_DMA_WAITOK); 1841.1Sjmcneill if (error != 0) { 1851.1Sjmcneill aprint_error_dev(self, "bus_dmamem_alloc failed: %d\n", error); 1861.1Sjmcneill return; 1871.1Sjmcneill } 1881.1Sjmcneill error = bus_dmamem_map(sc->sc_dmat, sc->sc_dma_segs, nsegs, 1891.1Sjmcneill sc->sc_dma_size, &sc->sc_dma_addr, BUS_DMA_WAITOK); 1901.1Sjmcneill if (error != 0) { 1911.1Sjmcneill aprint_error_dev(self, "bus_dmamem_map failed: %d\n", error); 1921.1Sjmcneill return; 1931.1Sjmcneill } 1941.1Sjmcneill error = bus_dmamap_create(sc->sc_dmat, sc->sc_dma_size, nsegs, 1951.1Sjmcneill sc->sc_dma_size, 0, BUS_DMA_WAITOK, &sc->sc_dma_map); 1961.1Sjmcneill if (error != 0) { 1971.1Sjmcneill aprint_error_dev(self, "bus_dmamap_create failed: %d\n", error); 1981.1Sjmcneill return; 1991.1Sjmcneill } 2001.1Sjmcneill error = bus_dmamap_load(sc->sc_dmat, sc->sc_dma_map, sc->sc_dma_addr, 2011.1Sjmcneill sc->sc_dma_size, NULL, BUS_DMA_WAITOK); 2021.1Sjmcneill if (error != 0) { 2031.1Sjmcneill aprint_error_dev(self, "bus_dmamap_load failed: %d\n", error); 2041.1Sjmcneill return; 2051.1Sjmcneill } 2061.1Sjmcneill 2071.1Sjmcneill memset(adapt, 0, sizeof(*adapt)); 2081.1Sjmcneill adapt->adapt_nchannels = 1; 2091.1Sjmcneill adapt->adapt_request = di_request; 2101.1Sjmcneill adapt->adapt_minphys = minphys; 2111.1Sjmcneill adapt->adapt_dev = self; 2121.1Sjmcneill adapt->adapt_max_periph = 1; 2131.1Sjmcneill adapt->adapt_openings = 1; 2141.1Sjmcneill 2151.1Sjmcneill memset(chan, 0, sizeof(*chan)); 2161.1Sjmcneill chan->chan_bustype = &scsi_bustype; 2171.1Sjmcneill chan->chan_ntargets = 2; 2181.1Sjmcneill chan->chan_nluns = 1; 2191.1Sjmcneill chan->chan_id = 0; 2201.1Sjmcneill chan->chan_flags = SCSIPI_CHAN_NOSETTLE; 2211.1Sjmcneill chan->chan_adapter = adapt; 2221.1Sjmcneill 2231.1Sjmcneill config_found(self, chan, scsiprint, CFARGS(.iattr = "scsi")); 2241.1Sjmcneill 2251.1Sjmcneill ahb_intr_establish(aaa->aaa_irq, IPL_BIO, di_intr, sc, 2261.1Sjmcneill device_xname(self)); 2271.1Sjmcneill 2281.1Sjmcneill di_init_regs(sc); 2291.1Sjmcneill callout_schedule(&sc->sc_idle, mstohz(DI_IDLE_TIMEOUT_MS)); 2301.1Sjmcneill 2311.1Sjmcneill pmf_device_register1(self, NULL, NULL, di_shutdown); 2321.1Sjmcneill} 2331.1Sjmcneill 2341.1Sjmcneillstatic bool 2351.1Sjmcneilldi_shutdown(device_t dev, int how) 2361.1Sjmcneill{ 2371.1Sjmcneill struct di_softc *sc = device_private(dev); 2381.1Sjmcneill 2391.1Sjmcneill di_reset(sc, false); 2401.1Sjmcneill 2411.1Sjmcneill return true; 2421.1Sjmcneill} 2431.1Sjmcneill 2441.1Sjmcneillstatic void 2451.1Sjmcneilldi_sense(struct scsipi_xfer *xs, uint8_t skey, uint8_t asc, uint8_t ascq) 2461.1Sjmcneill{ 2471.1Sjmcneill struct scsi_sense_data *sense = &xs->sense.scsi_sense; 2481.1Sjmcneill 2491.1Sjmcneill xs->error = XS_SENSE; 2501.1Sjmcneill sense->response_code = SSD_RCODE_CURRENT | SSD_RCODE_VALID; 2511.1Sjmcneill sense->flags = skey; 2521.1Sjmcneill sense->asc = asc; 2531.1Sjmcneill sense->ascq = ascq; 2541.1Sjmcneill} 2551.1Sjmcneill 2561.1Sjmcneillstatic void 2571.1Sjmcneilldi_request_error_sync(struct di_softc *sc, struct scsipi_xfer *xs) 2581.1Sjmcneill{ 2591.1Sjmcneill uint32_t imm; 2601.1Sjmcneill int s; 2611.1Sjmcneill 2621.1Sjmcneill s = splbio(); 2631.1Sjmcneill WR4(sc, DICMDBUF0, DI_CMD_REQUEST_ERROR); 2641.1Sjmcneill WR4(sc, DICMDBUF1, 0); 2651.1Sjmcneill WR4(sc, DICMDBUF2, 0); 2661.1Sjmcneill WR4(sc, DILENGTH, 4); 2671.1Sjmcneill WR4(sc, DICR, DICR_TSTART); 2681.1Sjmcneill while (((RD4(sc, DISR) & DISR_TCINT)) == 0) { 2691.1Sjmcneill delay(1); 2701.1Sjmcneill } 2711.1Sjmcneill imm = RD4(sc, DIMMBUF); 2721.1Sjmcneill splx(s); 2731.1Sjmcneill 2741.1Sjmcneill DPRINTF(sc->sc_dev, "ERR IMMBUF = 0x%08x\n", imm); 2751.1Sjmcneill di_sense(xs, (imm >> 16) & 0xff, (imm >> 8) & 0xff, imm & 0xff); 2761.1Sjmcneill} 2771.1Sjmcneill 2781.1Sjmcneillstatic int 2791.1Sjmcneilldi_transfer_error(struct di_softc *sc, struct scsipi_xfer *xs) 2801.1Sjmcneill{ 2811.1Sjmcneill if (xs == NULL) { 2821.1Sjmcneill return 0; 2831.1Sjmcneill } 2841.1Sjmcneill 2851.1Sjmcneill DPRINTF(sc->sc_dev, "transfer error\n"); 2861.1Sjmcneill 2871.1Sjmcneill callout_stop(&sc->sc_timeout); 2881.1Sjmcneill di_request_error_sync(sc, xs); 2891.1Sjmcneill sc->sc_cur_xs = NULL; 2901.1Sjmcneill scsipi_done(xs); 2911.1Sjmcneill 2921.1Sjmcneill return 1; 2931.1Sjmcneill} 2941.1Sjmcneill 2951.1Sjmcneillstatic int 2961.1Sjmcneilldi_transfer_complete(struct di_softc *sc, struct scsipi_xfer *xs) 2971.1Sjmcneill{ 2981.1Sjmcneill struct scsipi_generic *cmd; 2991.1Sjmcneill struct scsipi_inquiry_data *inqbuf; 3001.1Sjmcneill struct scsipi_read_cd_cap_data *cdcap; 3011.1Sjmcneill struct di_response_inquiry *rinq; 3021.1Sjmcneill uint32_t imm; 3031.1Sjmcneill uint8_t *data; 3041.1Sjmcneill char buf[5]; 3051.1Sjmcneill 3061.1Sjmcneill if (xs == NULL) { 3071.1Sjmcneill DPRINTF(sc->sc_dev, "no active transfer\n"); 3081.1Sjmcneill return 0; 3091.1Sjmcneill } 3101.1Sjmcneill 3111.1Sjmcneill KASSERT(sc->sc_cur_xs == xs); 3121.1Sjmcneill 3131.1Sjmcneill cmd = xs->cmd; 3141.1Sjmcneill 3151.1Sjmcneill switch (cmd->opcode) { 3161.1Sjmcneill case INQUIRY: 3171.1Sjmcneill inqbuf = (struct scsipi_inquiry_data *)xs->data; 3181.1Sjmcneill rinq = sc->sc_dma_addr; 3191.1Sjmcneill 3201.1Sjmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 0, sizeof(*rinq), 3211.1Sjmcneill BUS_DMASYNC_POSTREAD); 3221.1Sjmcneill 3231.1Sjmcneill DPRINTF(sc->sc_dev, "revision_level %#x " 3241.1Sjmcneill "device_code %#x " 3251.1Sjmcneill "release_date %#x\n", 3261.1Sjmcneill rinq->revision_level, 3271.1Sjmcneill rinq->device_code, 3281.1Sjmcneill rinq->release_date); 3291.1Sjmcneill 3301.1Sjmcneill memset(inqbuf, 0, sizeof(*inqbuf)); 3311.1Sjmcneill inqbuf->device = T_CDROM; 3321.1Sjmcneill inqbuf->dev_qual2 = SID_REMOVABLE; 3331.1Sjmcneill strncpy(inqbuf->vendor, "NINTENDO", sizeof(inqbuf->vendor)); 3341.1Sjmcneill snprintf(inqbuf->product, sizeof(inqbuf->product), "%08x", 3351.1Sjmcneill rinq->release_date); 3361.1Sjmcneill snprintf(buf, sizeof(buf), "%04x", rinq->revision_level); 3371.1Sjmcneill memcpy(inqbuf->revision, buf, sizeof(inqbuf->revision)); 3381.1Sjmcneill xs->resid = 0; 3391.1Sjmcneill break; 3401.1Sjmcneill 3411.1Sjmcneill case SCSI_TEST_UNIT_READY: 3421.1Sjmcneill case SCSI_REQUEST_SENSE: 3431.1Sjmcneill imm = RD4(sc, DIMMBUF); 3441.1Sjmcneill DPRINTF(sc->sc_dev, "TUR IMMBUF = 0x%08x\n", imm); 3451.1Sjmcneill switch ((imm >> 24) & 0xff) { 3461.1Sjmcneill case 0: 3471.1Sjmcneill di_sense(xs, (imm >> 16) & 0xff, (imm >> 8) & 0xff, 3481.1Sjmcneill imm & 0xff); 3491.1Sjmcneill break; 3501.1Sjmcneill default: 3511.1Sjmcneill di_sense(xs, SKEY_MEDIUM_ERROR, 0, 0); 3521.1Sjmcneill break; 3531.1Sjmcneill } 3541.1Sjmcneill break; 3551.1Sjmcneill 3561.1Sjmcneill case SCSI_READ_6_COMMAND: 3571.1Sjmcneill case READ_10: 3581.1Sjmcneill case GPCMD_REPORT_KEY: 3591.1Sjmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 0, xs->datalen, 3601.1Sjmcneill BUS_DMASYNC_POSTREAD); 3611.1Sjmcneill memcpy(xs->data, sc->sc_dma_addr, xs->datalen); 3621.1Sjmcneill xs->resid = 0; 3631.1Sjmcneill break; 3641.1Sjmcneill 3651.1Sjmcneill case GPCMD_READ_DVD_STRUCTURE: 3661.1Sjmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 0, DVDBLOCKSIZE, 3671.1Sjmcneill BUS_DMASYNC_POSTREAD); 3681.1Sjmcneill memcpy(xs->data + 4, sc->sc_dma_addr, xs->datalen - 4); 3691.1Sjmcneill xs->resid = 0; 3701.1Sjmcneill break; 3711.1Sjmcneill 3721.1Sjmcneill case READ_CD_CAPACITY: 3731.1Sjmcneill cdcap = (struct scsipi_read_cd_cap_data *)xs->data; 3741.1Sjmcneill 3751.1Sjmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 0, DVDBLOCKSIZE, 3761.1Sjmcneill BUS_DMASYNC_POSTREAD); 3771.1Sjmcneill data = sc->sc_dma_addr; 3781.1Sjmcneill _lto4b(DVDBLOCKSIZE, cdcap->length); 3791.1Sjmcneill memcpy(cdcap->addr, &data[8], sizeof(cdcap->addr)); 3801.1Sjmcneill break; 3811.1Sjmcneill } 3821.1Sjmcneill 3831.1Sjmcneill sc->sc_cur_xs = NULL; 3841.1Sjmcneill scsipi_done(xs); 3851.1Sjmcneill 3861.1Sjmcneill return 1; 3871.1Sjmcneill} 3881.1Sjmcneill 3891.1Sjmcneillstatic int 3901.1Sjmcneilldi_intr(void *priv) 3911.1Sjmcneill{ 3921.1Sjmcneill struct di_softc *sc = priv; 3931.1Sjmcneill uint32_t sr, cvr; 3941.1Sjmcneill int ret = 0; 3951.1Sjmcneill 3961.1Sjmcneill sr = RD4(sc, DISR); 3971.1Sjmcneill cvr = RD4(sc, DICVR); 3981.1Sjmcneill 3991.1Sjmcneill if ((sr & DISR_DEINT) != 0) { 4001.1Sjmcneill ret |= di_transfer_error(sc, sc->sc_cur_xs); 4011.1Sjmcneill } else if ((sr & DISR_TCINT) != 0) { 4021.1Sjmcneill ret |= di_transfer_complete(sc, sc->sc_cur_xs); 4031.1Sjmcneill } 4041.1Sjmcneill 4051.1Sjmcneill if ((cvr & DICVR_CVRINT) != 0) { 4061.1Sjmcneill DPRINTF(sc->sc_dev, "drive %s\n", 4071.1Sjmcneill (cvr & DICVR_CVR) == 0 ? "closed" : "opened"); 4081.1Sjmcneill ret |= 1; 4091.1Sjmcneill } 4101.1Sjmcneill 4111.1Sjmcneill WR4(sc, DISR, sr); 4121.1Sjmcneill WR4(sc, DICVR, cvr); 4131.1Sjmcneill 4141.1Sjmcneill return ret; 4151.1Sjmcneill} 4161.1Sjmcneill 4171.1Sjmcneillstatic void 4181.1Sjmcneilldi_timeout(void *priv) 4191.1Sjmcneill{ 4201.1Sjmcneill struct di_softc *sc = priv; 4211.1Sjmcneill int s; 4221.1Sjmcneill 4231.1Sjmcneill s = splbio(); 4241.1Sjmcneill if (sc->sc_cur_xs != NULL) { 4251.1Sjmcneill struct scsipi_xfer *xs = sc->sc_cur_xs; 4261.1Sjmcneill 4271.1Sjmcneill DPRINTF(sc->sc_dev, "command %#x timeout, DISR = %#x\n", 4281.1Sjmcneill xs->cmd->opcode, RD4(sc, DISR)); 4291.1Sjmcneill xs->error = XS_TIMEOUT; 4301.1Sjmcneill scsipi_done(xs); 4311.1Sjmcneill 4321.1Sjmcneill sc->sc_cur_xs = NULL; 4331.1Sjmcneill } 4341.1Sjmcneill splx(s); 4351.1Sjmcneill} 4361.1Sjmcneill 4371.1Sjmcneillstatic void 4381.1Sjmcneilldi_idle(void *priv) 4391.1Sjmcneill{ 4401.1Sjmcneill struct di_softc *sc = priv; 4411.1Sjmcneill 4421.1Sjmcneill if ((RD4(sc, DICVR) & DICVR_CVR) != 0) { 4431.1Sjmcneill /* Cover is opened, nothing to do. */ 4441.1Sjmcneill return; 4451.1Sjmcneill } 4461.1Sjmcneill 4471.1Sjmcneill di_reset(sc, false); 4481.1Sjmcneill} 4491.1Sjmcneill 4501.1Sjmcneillstatic void 4511.1Sjmcneilldi_start_request(struct di_softc *sc, struct scsipi_xfer *xs) 4521.1Sjmcneill{ 4531.1Sjmcneill KASSERT(sc->sc_cur_xs == NULL); 4541.1Sjmcneill sc->sc_cur_xs = xs; 4551.1Sjmcneill if (xs->timeout != 0) { 4561.1Sjmcneill callout_schedule(&sc->sc_timeout, mstohz(xs->timeout) + 1); 4571.1Sjmcneill } else { 4581.1Sjmcneill DPRINTF(sc->sc_dev, "WARNING: xfer with no timeout!\n"); 4591.1Sjmcneill callout_schedule(&sc->sc_timeout, mstohz(15000)); 4601.1Sjmcneill } 4611.1Sjmcneill} 4621.1Sjmcneill 4631.1Sjmcneillstatic void 4641.1Sjmcneilldi_init_regs(struct di_softc *sc) 4651.1Sjmcneill{ 4661.1Sjmcneill WR4(sc, DISR, DISR_BRKINT | 4671.1Sjmcneill DISR_TCINT | DISR_TCINTMASK | 4681.1Sjmcneill DISR_DEINT | DISR_DEINTMASK); 4691.1Sjmcneill WR4(sc, DICVR, DICVR_CVRINT | DICVR_CVRINTMASK); 4701.1Sjmcneill} 4711.1Sjmcneill 4721.1Sjmcneillstatic void 4731.1Sjmcneilldi_reset(struct di_softc *sc, bool spinup) 4741.1Sjmcneill{ 4751.1Sjmcneill uint32_t val; 4761.1Sjmcneill int s; 4771.1Sjmcneill 4781.1Sjmcneill DPRINTF(sc->sc_dev, "reset spinup=%d\n", spinup); 4791.1Sjmcneill 4801.1Sjmcneill s = splhigh(); 4811.1Sjmcneill 4821.1Sjmcneill if (spinup) { 4831.1Sjmcneill out32(HW_GPIOB_OUT, in32(HW_GPIOB_OUT) & ~__BIT(GPIO_DI_SPIN)); 4841.1Sjmcneill } else { 4851.1Sjmcneill out32(HW_GPIOB_OUT, in32(HW_GPIOB_OUT) | __BIT(GPIO_DI_SPIN)); 4861.1Sjmcneill } 4871.1Sjmcneill 4881.1Sjmcneill val = in32(HW_RESETS); 4891.1Sjmcneill out32(HW_RESETS, val & ~RSTB_IODI); 4901.1Sjmcneill delay(12); 4911.1Sjmcneill out32(HW_RESETS, val | RSTB_IODI); 4921.1Sjmcneill 4931.1Sjmcneill WR4(sc, DISR, DISR_BRKINT | 4941.1Sjmcneill DISR_TCINT | DISR_TCINTMASK | 4951.1Sjmcneill DISR_DEINT | DISR_DEINTMASK); 4961.1Sjmcneill WR4(sc, DICVR, DICVR_CVRINT | DICVR_CVRINTMASK); 4971.1Sjmcneill 4981.1Sjmcneill splx(s); 4991.1Sjmcneill} 5001.1Sjmcneill 5011.1Sjmcneillstatic void 5021.1Sjmcneilldi_stop_motor(struct di_softc *sc, struct scsipi_xfer *xs, bool eject) 5031.1Sjmcneill{ 5041.1Sjmcneill uint32_t cmdflags = 0; 5051.1Sjmcneill int s; 5061.1Sjmcneill 5071.1Sjmcneill if (eject) { 5081.1Sjmcneill cmdflags |= 1 << 17; 5091.1Sjmcneill } 5101.1Sjmcneill 5111.1Sjmcneill s = splbio(); 5121.1Sjmcneill WR4(sc, DICMDBUF0, DI_CMD_STOP_MOTOR | cmdflags); 5131.1Sjmcneill WR4(sc, DICMDBUF1, 0); 5141.1Sjmcneill WR4(sc, DICMDBUF2, 0); 5151.1Sjmcneill WR4(sc, DILENGTH, 4); 5161.1Sjmcneill WR4(sc, DICR, DICR_TSTART); 5171.1Sjmcneill di_start_request(sc, xs); 5181.1Sjmcneill splx(s); 5191.1Sjmcneill} 5201.1Sjmcneill 5211.1Sjmcneillstatic void 5221.1Sjmcneilldi_request(struct scsipi_channel *chan, scsipi_adapter_req_t req, void *arg) 5231.1Sjmcneill{ 5241.1Sjmcneill struct di_softc *sc = device_private(chan->chan_adapter->adapt_dev); 5251.1Sjmcneill struct scsipi_xfer *xs; 5261.1Sjmcneill struct scsipi_generic *cmd; 5271.1Sjmcneill struct scsipi_start_stop *ss; 5281.1Sjmcneill struct scsi_prevent_allow_medium_removal *pamr; 5291.1Sjmcneill uint32_t blkno; 5301.1Sjmcneill int s; 5311.1Sjmcneill 5321.1Sjmcneill if (req != ADAPTER_REQ_RUN_XFER) { 5331.1Sjmcneill return; 5341.1Sjmcneill } 5351.1Sjmcneill 5361.1Sjmcneill callout_stop(&sc->sc_idle); 5371.1Sjmcneill 5381.1Sjmcneill KASSERT(sc->sc_cur_xs == NULL); 5391.1Sjmcneill 5401.1Sjmcneill xs = arg; 5411.1Sjmcneill cmd = xs->cmd; 5421.1Sjmcneill 5431.1Sjmcneill switch (cmd->opcode) { 5441.1Sjmcneill case INQUIRY: 5451.1Sjmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 5461.1Sjmcneill 0, sizeof(struct di_response_inquiry), 5471.1Sjmcneill BUS_DMASYNC_PREREAD); 5481.1Sjmcneill 5491.1Sjmcneill s = splbio(); 5501.1Sjmcneill WR4(sc, DICMDBUF0, DI_CMD_INQUIRY); 5511.1Sjmcneill WR4(sc, DICMDBUF1, 0); 5521.1Sjmcneill WR4(sc, DILENGTH, sizeof(struct di_response_inquiry)); 5531.1Sjmcneill WR4(sc, DIMAR, sc->sc_dma_segs[0].ds_addr); 5541.1Sjmcneill WR4(sc, DICR, DICR_TSTART | DICR_DMA); 5551.1Sjmcneill di_start_request(sc, xs); 5561.1Sjmcneill splx(s); 5571.1Sjmcneill break; 5581.1Sjmcneill 5591.1Sjmcneill case SCSI_TEST_UNIT_READY: 5601.1Sjmcneill case SCSI_REQUEST_SENSE: 5611.1Sjmcneill s = splbio(); 5621.1Sjmcneill WR4(sc, DICMDBUF0, DI_CMD_REQUEST_ERROR); 5631.1Sjmcneill WR4(sc, DICMDBUF1, 0); 5641.1Sjmcneill WR4(sc, DICMDBUF2, 0); 5651.1Sjmcneill WR4(sc, DILENGTH, 4); 5661.1Sjmcneill WR4(sc, DICR, DICR_TSTART); 5671.1Sjmcneill di_start_request(sc, xs); 5681.1Sjmcneill splx(s); 5691.1Sjmcneill break; 5701.1Sjmcneill 5711.1Sjmcneill case SCSI_READ_6_COMMAND: 5721.1Sjmcneill case READ_10: 5731.1Sjmcneill if (cmd->opcode == SCSI_READ_6_COMMAND) { 5741.1Sjmcneill blkno = _3btol(((struct scsi_rw_6 *)cmd)->addr); 5751.1Sjmcneill } else { 5761.1Sjmcneill KASSERT(cmd->opcode == READ_10); 5771.1Sjmcneill blkno = _4btol(((struct scsipi_rw_10 *)cmd)->addr); 5781.1Sjmcneill } 5791.1Sjmcneill 5801.1Sjmcneill if (xs->datalen == 0) { 5811.1Sjmcneill xs->error = XS_DRIVER_STUFFUP; 5821.1Sjmcneill scsipi_done(xs); 5831.1Sjmcneill break; 5841.1Sjmcneill } 5851.1Sjmcneill 5861.1Sjmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 5871.1Sjmcneill 0, xs->datalen, BUS_DMASYNC_PREREAD); 5881.1Sjmcneill 5891.1Sjmcneill s = splbio(); 5901.1Sjmcneill WR4(sc, DICMDBUF0, DI_CMD_READ_DVD); 5911.1Sjmcneill WR4(sc, DICMDBUF1, blkno); 5921.1Sjmcneill WR4(sc, DICMDBUF2, howmany(xs->datalen, DVDBLOCKSIZE)); 5931.1Sjmcneill WR4(sc, DILENGTH, roundup(xs->datalen, DVDBLOCKSIZE)); 5941.1Sjmcneill WR4(sc, DIMAR, sc->sc_dma_segs[0].ds_addr); 5951.1Sjmcneill WR4(sc, DICR, DICR_TSTART | DICR_DMA); 5961.1Sjmcneill di_start_request(sc, xs); 5971.1Sjmcneill splx(s); 5981.1Sjmcneill break; 5991.1Sjmcneill 6001.1Sjmcneill case GPCMD_READ_DVD_STRUCTURE: 6011.1Sjmcneill if (xs->datalen == 0) { 6021.1Sjmcneill DPRINTF(sc->sc_dev, "zero datalen\n"); 6031.1Sjmcneill xs->error = XS_DRIVER_STUFFUP; 6041.1Sjmcneill scsipi_done(xs); 6051.1Sjmcneill break; 6061.1Sjmcneill } 6071.1Sjmcneill 6081.1Sjmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 6091.1Sjmcneill 0, xs->datalen, BUS_DMASYNC_PREREAD); 6101.1Sjmcneill 6111.1Sjmcneill s = splbio(); 6121.1Sjmcneill WR4(sc, DICMDBUF0, DI_CMD_READ_DVD_STRUCT(cmd->bytes[6])); 6131.1Sjmcneill WR4(sc, DICMDBUF1, 0); 6141.1Sjmcneill WR4(sc, DICMDBUF2, 0); 6151.1Sjmcneill WR4(sc, DILENGTH, roundup(xs->datalen, DVDBLOCKSIZE)); 6161.1Sjmcneill WR4(sc, DIMAR, sc->sc_dma_segs[0].ds_addr); 6171.1Sjmcneill WR4(sc, DICR, DICR_TSTART | DICR_DMA); 6181.1Sjmcneill di_start_request(sc, xs); 6191.1Sjmcneill splx(s); 6201.1Sjmcneill break; 6211.1Sjmcneill 6221.1Sjmcneill case GPCMD_REPORT_KEY: 6231.1Sjmcneill if (xs->datalen == 0) { 6241.1Sjmcneill DPRINTF(sc->sc_dev, "zero datalen\n"); 6251.1Sjmcneill xs->error = XS_DRIVER_STUFFUP; 6261.1Sjmcneill scsipi_done(xs); 6271.1Sjmcneill break; 6281.1Sjmcneill } 6291.1Sjmcneill 6301.1Sjmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 6311.1Sjmcneill 0, xs->datalen, BUS_DMASYNC_PREREAD); 6321.1Sjmcneill 6331.1Sjmcneill s = splbio(); 6341.1Sjmcneill WR4(sc, DICMDBUF0, DI_CMD_REPORT_KEY(cmd->bytes[9] >> 2)); 6351.1Sjmcneill WR4(sc, DICMDBUF1, _4btol(&cmd->bytes[1])); 6361.1Sjmcneill WR4(sc, DICMDBUF2, 0); 6371.1Sjmcneill WR4(sc, DILENGTH, roundup(xs->datalen, 0x20)); 6381.1Sjmcneill WR4(sc, DIMAR, sc->sc_dma_segs[0].ds_addr); 6391.1Sjmcneill WR4(sc, DICR, DICR_TSTART | DICR_DMA); 6401.1Sjmcneill di_start_request(sc, xs); 6411.1Sjmcneill splx(s); 6421.1Sjmcneill break; 6431.1Sjmcneill 6441.1Sjmcneill case READ_CD_CAPACITY: 6451.1Sjmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 6461.1Sjmcneill 0, DVDBLOCKSIZE, BUS_DMASYNC_PREREAD); 6471.1Sjmcneill 6481.1Sjmcneill s = splbio(); 6491.1Sjmcneill WR4(sc, DICMDBUF0, DI_CMD_READ_DVD_STRUCT(DVD_STRUCT_PHYSICAL)); 6501.1Sjmcneill WR4(sc, DICMDBUF1, 0); 6511.1Sjmcneill WR4(sc, DICMDBUF2, 0); 6521.1Sjmcneill WR4(sc, DILENGTH, DVDBLOCKSIZE); 6531.1Sjmcneill WR4(sc, DIMAR, sc->sc_dma_segs[0].ds_addr); 6541.1Sjmcneill WR4(sc, DICR, DICR_TSTART | DICR_DMA); 6551.1Sjmcneill di_start_request(sc, xs); 6561.1Sjmcneill splx(s); 6571.1Sjmcneill break; 6581.1Sjmcneill 6591.1Sjmcneill case GET_CONFIGURATION: 6601.1Sjmcneill memset(xs->data, 0, sizeof(struct scsipi_get_conf_data)); 6611.1Sjmcneill xs->resid = 0; 6621.1Sjmcneill scsipi_done(xs); 6631.1Sjmcneill break; 6641.1Sjmcneill 6651.1Sjmcneill case READ_TOC: 6661.1Sjmcneill memset(xs->data, 0, sizeof(struct scsipi_toc_header)); 6671.1Sjmcneill xs->resid = 0; 6681.1Sjmcneill scsipi_done(xs); 6691.1Sjmcneill break; 6701.1Sjmcneill 6711.1Sjmcneill case READ_TRACKINFO: 6721.1Sjmcneill case READ_DISCINFO: 6731.1Sjmcneill di_sense(xs, SKEY_ILLEGAL_REQUEST, 0, 0); 6741.1Sjmcneill scsipi_done(xs); 6751.1Sjmcneill break; 6761.1Sjmcneill 6771.1Sjmcneill case START_STOP: 6781.1Sjmcneill ss = (struct scsipi_start_stop *)cmd; 6791.1Sjmcneill if (ss->how == SSS_START) { 6801.1Sjmcneill di_reset(sc, true); 6811.1Sjmcneill scsipi_done(xs); 6821.1Sjmcneill } else { 6831.1Sjmcneill di_stop_motor(sc, xs, (ss->how & SSS_LOEJ) != 0); 6841.1Sjmcneill } 6851.1Sjmcneill break; 6861.1Sjmcneill 6871.1Sjmcneill case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL: 6881.1Sjmcneill pamr = (struct scsi_prevent_allow_medium_removal *)cmd; 6891.1Sjmcneill sc->sc_pamr = pamr->how; 6901.1Sjmcneill scsipi_done(xs); 6911.1Sjmcneill break; 6921.1Sjmcneill 6931.1Sjmcneill default: 6941.1Sjmcneill DPRINTF(sc->sc_dev, "unsupported opcode %#x\n", cmd->opcode); 6951.1Sjmcneill scsipi_done(xs); 6961.1Sjmcneill } 6971.1Sjmcneill 6981.1Sjmcneill if (!sc->sc_pamr) { 6991.1Sjmcneill callout_schedule(&sc->sc_idle, mstohz(DI_IDLE_TIMEOUT_MS)); 7001.1Sjmcneill } 7011.1Sjmcneill} 702