Module Name: src Committed By: martin Date: Sat Feb 22 11:47:16 UTC 2025
Modified Files: src/sys/arch/evbppc/conf [netbsd-10]: WII files.wii src/sys/arch/evbppc/include [netbsd-10]: wii.h src/sys/arch/evbppc/wii [netbsd-10]: machdep.c src/sys/kern [netbsd-10]: subr_disk_mbr.c Added Files: src/sys/arch/evbppc/wii/dev [netbsd-10]: di.c Log Message: Pull up following revision(s) (requested by jmcneill in ticket #1052): sys/kern/subr_disk_mbr.c: revision 1.59 sys/arch/evbppc/include/wii.h: revision 1.10 sys/arch/evbppc/wii/machdep.c: revision 1.9 sys/arch/evbppc/conf/files.wii: revision 1.5 sys/arch/evbppc/wii/dev/di.c: revision 1.1 sys/arch/evbppc/conf/WII: revision 1.8 Handle reading larger sectors (including 2k CD-ROM blocks). Fall back to scan for ISO9660 sessions when MMC code fails. disklabel now reports ISO/UDF partitions again for cd(4). wii: Add more register definitions. wii: Add support for Wii DVD drive. This adds a virtual SCSI HBA driver that is able to read DVD video discs inserted in the Wii. wii: Early init for DVD support To generate a diff of this commit: cvs rdiff -u -r1.4.2.5 -r1.4.2.6 src/sys/arch/evbppc/conf/WII cvs rdiff -u -r1.4.2.2 -r1.4.2.3 src/sys/arch/evbppc/conf/files.wii cvs rdiff -u -r1.7.2.4 -r1.7.2.5 src/sys/arch/evbppc/include/wii.h cvs rdiff -u -r1.4.2.4 -r1.4.2.5 src/sys/arch/evbppc/wii/machdep.c cvs rdiff -u -r0 -r1.1.2.2 src/sys/arch/evbppc/wii/dev/di.c cvs rdiff -u -r1.57 -r1.57.12.1 src/sys/kern/subr_disk_mbr.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/evbppc/conf/WII diff -u src/sys/arch/evbppc/conf/WII:1.4.2.5 src/sys/arch/evbppc/conf/WII:1.4.2.6 --- src/sys/arch/evbppc/conf/WII:1.4.2.5 Sun Feb 2 14:29:59 2025 +++ src/sys/arch/evbppc/conf/WII Sat Feb 22 11:47:16 2025 @@ -1,4 +1,4 @@ -# $NetBSD: WII,v 1.4.2.5 2025/02/02 14:29:59 martin Exp $ +# $NetBSD: WII,v 1.4.2.6 2025/02/22 11:47:16 martin Exp $ # # Nintendo Wii # @@ -143,7 +143,6 @@ gpioiic0 at gpio0 offset 0 mask 0xc000 f iic0 at gpioiic0 avenc0 at iic0 addr 0x70 # A/V Encoder -#iosipc0 at hollywood0 addr 0x0d000000 irq 30 # IOS IPC resetbtn0 at hollywood0 irq 17 # Reset button ehci0 at hollywood0 addr 0x0d040000 irq 4 # EHCI @@ -157,6 +156,8 @@ sdmmc* at sdmmcbus? ld* at sdmmc? bwi* at sdmmc? # WLAN +di0 at hollywood0 addr 0x0d806000 irq 18 # Drive interface + include "dev/usb/usbdevices.config" include "dev/bluetooth/bluetoothdevices.config" Index: src/sys/arch/evbppc/conf/files.wii diff -u src/sys/arch/evbppc/conf/files.wii:1.4.2.2 src/sys/arch/evbppc/conf/files.wii:1.4.2.3 --- src/sys/arch/evbppc/conf/files.wii:1.4.2.2 Sat Feb 3 11:47:06 2024 +++ src/sys/arch/evbppc/conf/files.wii Sat Feb 22 11:47:16 2025 @@ -1,4 +1,4 @@ -# $NetBSD: files.wii,v 1.4.2.2 2024/02/03 11:47:06 martin Exp $ +# $NetBSD: files.wii,v 1.4.2.3 2025/02/22 11:47:16 martin Exp $ # # maxpartitions 16 @@ -81,3 +81,7 @@ file arch/evbppc/wii/dev/sdhc_hollywood. device avenc attach avenc at iic file arch/evbppc/wii/dev/avenc.c avenc + +device di: scsi +attach di at hollywood +file arch/evbppc/wii/dev/di.c di Index: src/sys/arch/evbppc/include/wii.h diff -u src/sys/arch/evbppc/include/wii.h:1.7.2.4 src/sys/arch/evbppc/include/wii.h:1.7.2.5 --- src/sys/arch/evbppc/include/wii.h:1.7.2.4 Mon Oct 14 16:44:42 2024 +++ src/sys/arch/evbppc/include/wii.h Sat Feb 22 11:47:15 2025 @@ -1,4 +1,4 @@ -/* $NetBSD: wii.h,v 1.7.2.4 2024/10/14 16:44:42 martin Exp $ */ +/* $NetBSD: wii.h,v 1.7.2.5 2025/02/22 11:47:15 martin Exp $ */ /*- * Copyright (c) 2024 Jared McNeill <jmcne...@invisible.ca> @@ -45,9 +45,18 @@ #define GLOBAL_BASE 0x00000000 #define GLOBAL_SIZE 0x00003400 +#define EFB_BASE 0x08000000 +#define EFB_SIZE 0x00300000 /* 3 MB */ + #define BROADWAY_BASE 0x0c000000 #define BROADWAY_SIZE 0x00000004 +#define CP_BASE 0x0c000000 +#define CP_SIZE 0x0c000080 + +#define PE_BASE 0x0c001000 +#define PE_SIZE 0x00000100 + #define VI_BASE 0x0c002000 #define VI_SIZE 0x00000100 @@ -57,6 +66,9 @@ #define DSP_BASE 0x0c005000 #define DSP_SIZE 0x00000200 +#define WGPIPE_BASE 0x0c008000 +#define WGPIPE_SIZE 0x00000004 + #define EXI_BASE 0x0d006800 #define EXI_SIZE 0x00000080 @@ -98,6 +110,9 @@ #define PI_INTSR (PI_BASE + 0x00) #define PI_INTMR (PI_BASE + 0x04) +/* GX registers */ +#define GX_WGPIPE (GX_BASE + 0x00) + /* Processor IRQs */ #define PI_IRQ_EXI 4 #define PI_IRQ_AI 5 @@ -125,8 +140,11 @@ #define HW_GPIOB_DIR (HOLLYWOOD_BASE + 0x0c4) #define HW_GPIOB_IN (HOLLYWOOD_BASE + 0x0c8) #define HW_GPIO_OWNER (HOLLYWOOD_PRIV_BASE + 0x0fc) +#define HW_COMPAT (HOLLYWOOD_PRIV_BASE + 0x180) +#define DVDVIDEO __BIT(21) #define HW_RESETS (HOLLYWOOD_PRIV_BASE + 0x194) #define RSTB_IOP __BIT(23) +#define RSTB_IODI __BIT(17) #define RSTBINB __BIT(0) #define HW_VERSION (HOLLYWOOD_BASE + 0x214) #define HWVER_MASK __BITS(7,4) @@ -134,7 +152,9 @@ /* GPIOs */ #define GPIO_SHUTDOWN 1 +#define GPIO_DI_SPIN 4 #define GPIO_SLOT_LED 5 +#define GPIO_DO_EJECT 9 /* Command line protocol */ #define WII_ARGV_MAGIC 0x5f617267 Index: src/sys/arch/evbppc/wii/machdep.c diff -u src/sys/arch/evbppc/wii/machdep.c:1.4.2.4 src/sys/arch/evbppc/wii/machdep.c:1.4.2.5 --- src/sys/arch/evbppc/wii/machdep.c:1.4.2.4 Sat Oct 26 15:28:55 2024 +++ src/sys/arch/evbppc/wii/machdep.c Sat Feb 22 11:47:16 2025 @@ -1,4 +1,4 @@ -/* $NetBSD: machdep.c,v 1.4.2.4 2024/10/26 15:28:55 martin Exp $ */ +/* $NetBSD: machdep.c,v 1.4.2.5 2025/02/22 11:47:16 martin Exp $ */ /* * Copyright (c) 2002, 2024 The NetBSD Foundation, Inc. @@ -63,7 +63,7 @@ #define _POWERPC_BUS_DMA_PRIVATE #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.4.2.4 2024/10/26 15:28:55 martin Exp $"); +__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.4.2.5 2025/02/22 11:47:16 martin Exp $"); #include "opt_compat_netbsd.h" #include "opt_ddb.h" @@ -435,8 +435,14 @@ wii_setup(void) /* Enable PPC access to SHUTDOWN GPIO. */ out32(HW_GPIO_OWNER, in32(HW_GPIO_OWNER) | __BIT(GPIO_SHUTDOWN)); + /* Enable PPC access to DI_SPIN GPIO. */ + out32(HW_GPIO_OWNER, in32(HW_GPIO_OWNER) | __BIT(GPIO_DI_SPIN)); + /* Enable PPC access to EXI bus. */ out32(HW_AIPPROT, in32(HW_AIPPROT) | ENAHBIOPI); + + /* Enable DVD video support. */ + out32(HW_COMPAT, in32(HW_COMPAT) & ~DVDVIDEO); } static void Index: src/sys/kern/subr_disk_mbr.c diff -u src/sys/kern/subr_disk_mbr.c:1.57 src/sys/kern/subr_disk_mbr.c:1.57.12.1 --- src/sys/kern/subr_disk_mbr.c:1.57 Mon May 17 08:50:36 2021 +++ src/sys/kern/subr_disk_mbr.c Sat Feb 22 11:47:15 2025 @@ -1,4 +1,4 @@ -/* $NetBSD: subr_disk_mbr.c,v 1.57 2021/05/17 08:50:36 mrg Exp $ */ +/* $NetBSD: subr_disk_mbr.c,v 1.57.12.1 2025/02/22 11:47:15 martin Exp $ */ /* * Copyright (c) 1982, 1986, 1988 Regents of the University of California. @@ -54,7 +54,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: subr_disk_mbr.c,v 1.57 2021/05/17 08:50:36 mrg Exp $"); +__KERNEL_RCSID(0, "$NetBSD: subr_disk_mbr.c,v 1.57.12.1 2025/02/22 11:47:15 martin Exp $"); #ifdef _KERNEL_OPT #include "opt_mbr.h" @@ -258,10 +258,9 @@ scan_iso_vrs_session(mbr_args_t *a, uint struct vrs_desc *vrsd; uint64_t vrs; int sector_size; - int blks, inc; + int inc; sector_size = a->lp->d_secsize; - blks = sector_size / DEV_BSIZE; inc = MAX(1, 2048 / sector_size); /* by definition */ @@ -269,7 +268,7 @@ scan_iso_vrs_session(mbr_args_t *a, uint + first_sector; /* read first vrs sector */ - if (read_sector(a, vrs * blks, 1)) + if (read_sector(a, vrs, 1)) return; /* skip all CD001 records */ @@ -280,7 +279,7 @@ scan_iso_vrs_session(mbr_args_t *a, uint *is_iso9660 = first_sector; vrs += inc; - if (read_sector(a, vrs * blks, 1)) + if (read_sector(a, vrs, 1)) return; } @@ -292,7 +291,7 @@ scan_iso_vrs_session(mbr_args_t *a, uint /* read successor */ vrs += inc; - if (read_sector(a, vrs * blks, 1)) + if (read_sector(a, vrs, 1)) return; /* check for NSR[23] */ @@ -328,7 +327,7 @@ scan_iso_vrs(mbr_args_t *a) dev = a->bp->b_dev; error = bdev_ioctl(dev, MMCGETDISCINFO, &di, FKIOCTL, curlwp); if (error) - return SCAN_CONTINUE; + goto notmmc; /* go trough all (data) tracks */ sessionnr = -1; @@ -339,7 +338,7 @@ scan_iso_vrs(mbr_args_t *a) error = bdev_ioctl(dev, MMCGETTRACKINFO, &ti, FKIOCTL, curlwp); if (error) - return SCAN_CONTINUE; + goto notmmc; new_session = (ti.sessionnr != sessionnr); sessionnr = ti.sessionnr; if (new_session) { @@ -353,6 +352,7 @@ scan_iso_vrs(mbr_args_t *a) } } } else { +notmmc: /* try start of disc */ sector = 0; scan_iso_vrs_session(a, sector, &is_iso9660, &is_udf); Added files: Index: src/sys/arch/evbppc/wii/dev/di.c diff -u /dev/null src/sys/arch/evbppc/wii/dev/di.c:1.1.2.2 --- /dev/null Sat Feb 22 11:47:16 2025 +++ src/sys/arch/evbppc/wii/dev/di.c Sat Feb 22 11:47:16 2025 @@ -0,0 +1,700 @@ +/* $NetBSD: di.c,v 1.1.2.2 2025/02/22 11:47:16 martin Exp $ */ + +/*- + * Copyright (c) 2025 Jared McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: di.c,v 1.1.2.2 2025/02/22 11:47:16 martin Exp $"); + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/device.h> +#include <sys/systm.h> +#include <sys/callout.h> +#include <sys/buf.h> +#include <sys/dvdio.h> + +#include <uvm/uvm_extern.h> + +#include <dev/scsipi/scsi_all.h> +#include <dev/scsipi/scsi_disk.h> +#include <dev/scsipi/scsipi_all.h> +#include <dev/scsipi/scsipi_cd.h> +#include <dev/scsipi/scsipi_disk.h> +#include <dev/scsipi/scsiconf.h> + +#include <machine/wii.h> +#include <machine/pio.h> +#include "hollywood.h" + +#ifdef DI_DEBUG +#define DPRINTF(dv, fmt, ...) device_printf(dv, fmt, ## __VA_ARGS__) +#else +#define DPRINTF(dv, fmt, ...) +#endif + +#define DI_REG_SIZE 0x40 + +#define DISR 0x00 +#define DISR_BRKINT __BIT(6) +#define DISR_BRKINTMASK __BIT(5) +#define DISR_TCINT __BIT(4) +#define DISR_TCINTMASK __BIT(3) +#define DISR_DEINT __BIT(2) +#define DISR_DEINTMASK __BIT(1) +#define DISR_BRK __BIT(0) +#define DICVR 0x04 +#define DICVR_CVRINT __BIT(2) +#define DICVR_CVRINTMASK __BIT(1) +#define DICVR_CVR __BIT(0) +#define DICMDBUF0 0x08 +#define DICMDBUF1 0x0c +#define DICMDBUF2 0x10 +#define DIMAR 0x14 +#define DILENGTH 0x18 +#define DICR 0x1c +#define DICR_DMA __BIT(1) +#define DICR_TSTART __BIT(0) +#define DIMMBUF 0x20 +#define DICFG 0x24 + +#define DI_CMD_INQUIRY 0x12000000 +#define DI_CMD_REPORT_KEY(x) (0xa4000000 | ((uint32_t)(x) << 16)) +#define DI_CMD_READ_DVD_STRUCT(x) (0xad000000 | ((uint32_t)(x) << 24)) +#define DI_CMD_READ_DVD 0xd0000000 +#define DI_CMD_REQUEST_ERROR 0xe0000000 +#define DI_CMD_STOP_MOTOR 0xe3000000 + +#define DVDBLOCKSIZE 2048 + +#define DI_IDLE_TIMEOUT_MS 30000 + +struct di_softc; + +static int di_match(device_t, cfdata_t, void *); +static void di_attach(device_t, device_t, void *); + +static bool di_shutdown(device_t, int); + +static int di_intr(void *); +static void di_timeout(void *); +static void di_idle(void *); + +static void di_request(struct scsipi_channel *, scsipi_adapter_req_t, + void *); +static void di_init_regs(struct di_softc *); +static void di_reset(struct di_softc *, bool); + +struct di_response_inquiry { + uint16_t revision_level; + uint16_t device_code; + uint32_t release_date; + uint8_t padding[24]; +} __aligned(4); +CTASSERT(sizeof(struct di_response_inquiry) == 0x20); + +struct di_softc { + device_t sc_dev; + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + bus_dma_tag_t sc_dmat; + + struct scsipi_adapter sc_adapter; + struct scsipi_channel sc_channel; + + struct scsipi_xfer *sc_cur_xs; + callout_t sc_timeout; + callout_t sc_idle; + int sc_pamr; + + bus_dmamap_t sc_dma_map; + void *sc_dma_addr; + size_t sc_dma_size; + bus_dma_segment_t sc_dma_segs[1]; +}; + +#define WR4(sc, reg, val) \ + bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) +#define RD4(sc, reg) \ + bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) + +CFATTACH_DECL_NEW(di, sizeof(struct di_softc), + di_match, di_attach, NULL, NULL); + +static int +di_match(device_t parent, cfdata_t cf, void *aux) +{ + return 1; +} + +static void +di_attach(device_t parent, device_t self, void *aux) +{ + struct hollywood_attach_args *haa = aux; + struct di_softc *sc = device_private(self); + struct scsipi_adapter *adapt = &sc->sc_adapter; + struct scsipi_channel *chan = &sc->sc_channel; + int error, nsegs; + + sc->sc_dev = self; + sc->sc_dmat = haa->haa_dmat; + sc->sc_bst = haa->haa_bst; + error = bus_space_map(sc->sc_bst, haa->haa_addr, DI_REG_SIZE, + 0, &sc->sc_bsh); + if (error != 0) { + aprint_error(": couldn't map registers (%d)\n", error); + return; + } + + aprint_naive("\n"); + aprint_normal(": Drive Interface\n"); + + callout_init(&sc->sc_timeout, 0); + callout_setfunc(&sc->sc_timeout, di_timeout, sc); + callout_init(&sc->sc_idle, 0); + callout_setfunc(&sc->sc_idle, di_idle, sc); + + sc->sc_dma_size = MAXPHYS; + error = bus_dmamem_alloc(sc->sc_dmat, sc->sc_dma_size, PAGE_SIZE, 0, + sc->sc_dma_segs, 1, &nsegs, BUS_DMA_WAITOK); + if (error != 0) { + aprint_error_dev(self, "bus_dmamem_alloc failed: %d\n", error); + return; + } + error = bus_dmamem_map(sc->sc_dmat, sc->sc_dma_segs, nsegs, + sc->sc_dma_size, &sc->sc_dma_addr, BUS_DMA_WAITOK); + if (error != 0) { + aprint_error_dev(self, "bus_dmamem_map failed: %d\n", error); + return; + } + error = bus_dmamap_create(sc->sc_dmat, sc->sc_dma_size, nsegs, + sc->sc_dma_size, 0, BUS_DMA_WAITOK, &sc->sc_dma_map); + if (error != 0) { + aprint_error_dev(self, "bus_dmamap_create failed: %d\n", error); + return; + } + error = bus_dmamap_load(sc->sc_dmat, sc->sc_dma_map, sc->sc_dma_addr, + sc->sc_dma_size, NULL, BUS_DMA_WAITOK); + if (error != 0) { + aprint_error_dev(self, "bus_dmamap_load failed: %d\n", error); + return; + } + + memset(adapt, 0, sizeof(*adapt)); + adapt->adapt_nchannels = 1; + adapt->adapt_request = di_request; + adapt->adapt_minphys = minphys; + adapt->adapt_dev = self; + adapt->adapt_max_periph = 1; + adapt->adapt_openings = 1; + + memset(chan, 0, sizeof(*chan)); + chan->chan_bustype = &scsi_bustype; + chan->chan_ntargets = 2; + chan->chan_nluns = 1; + chan->chan_id = 0; + chan->chan_flags = SCSIPI_CHAN_NOSETTLE; + chan->chan_adapter = adapt; + + config_found(self, chan, scsiprint, CFARGS(.iattr = "scsi")); + + hollywood_intr_establish(haa->haa_irq, IPL_BIO, di_intr, sc, + device_xname(self)); + + di_init_regs(sc); + callout_schedule(&sc->sc_idle, mstohz(DI_IDLE_TIMEOUT_MS)); + + pmf_device_register1(self, NULL, NULL, di_shutdown); +} + +static bool +di_shutdown(device_t dev, int how) +{ + struct di_softc *sc = device_private(dev); + + di_reset(sc, false); + + return true; +} + +static void +di_sense(struct scsipi_xfer *xs, uint8_t skey, uint8_t asc, uint8_t ascq) +{ + struct scsi_sense_data *sense = &xs->sense.scsi_sense; + + xs->error = XS_SENSE; + sense->response_code = SSD_RCODE_CURRENT | SSD_RCODE_VALID; + sense->flags = skey; + sense->asc = asc; + sense->ascq = ascq; +} + +static void +di_request_error_sync(struct di_softc *sc, struct scsipi_xfer *xs) +{ + uint32_t imm; + int s; + + s = splbio(); + WR4(sc, DICMDBUF0, DI_CMD_REQUEST_ERROR); + WR4(sc, DICMDBUF1, 0); + WR4(sc, DICMDBUF2, 0); + WR4(sc, DILENGTH, 4); + WR4(sc, DICR, DICR_TSTART); + while (((RD4(sc, DISR) & DISR_TCINT)) == 0) { + delay(1); + } + imm = RD4(sc, DIMMBUF); + splx(s); + + DPRINTF(sc->sc_dev, "ERR IMMBUF = 0x%08x\n", imm); + di_sense(xs, (imm >> 16) & 0xff, (imm >> 8) & 0xff, imm & 0xff); +} + +static int +di_transfer_error(struct di_softc *sc, struct scsipi_xfer *xs) +{ + if (xs == NULL) { + return 0; + } + + DPRINTF(sc->sc_dev, "transfer error\n"); + + callout_stop(&sc->sc_timeout); + di_request_error_sync(sc, xs); + sc->sc_cur_xs = NULL; + scsipi_done(xs); + + return 1; +} + +static int +di_transfer_complete(struct di_softc *sc, struct scsipi_xfer *xs) +{ + struct scsipi_generic *cmd; + struct scsipi_inquiry_data *inqbuf; + struct scsipi_read_cd_cap_data *cdcap; + struct di_response_inquiry *rinq; + uint32_t imm; + uint8_t *data; + char buf[5]; + + if (xs == NULL) { + DPRINTF(sc->sc_dev, "no active transfer\n"); + return 0; + } + + KASSERT(sc->sc_cur_xs == xs); + + cmd = xs->cmd; + + switch (cmd->opcode) { + case INQUIRY: + inqbuf = (struct scsipi_inquiry_data *)xs->data; + rinq = sc->sc_dma_addr; + + bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 0, sizeof(*rinq), + BUS_DMASYNC_POSTREAD); + + DPRINTF(sc->sc_dev, "revision_level %#x " + "device_code %#x " + "release_date %#x\n", + rinq->revision_level, + rinq->device_code, + rinq->release_date); + + memset(inqbuf, 0, sizeof(*inqbuf)); + inqbuf->device = T_CDROM; + inqbuf->dev_qual2 = SID_REMOVABLE; + strncpy(inqbuf->vendor, "NINTENDO", sizeof(inqbuf->vendor)); + snprintf(inqbuf->product, sizeof(inqbuf->product), "%08x", + rinq->release_date); + snprintf(buf, sizeof(buf), "%04x", rinq->revision_level); + memcpy(inqbuf->revision, buf, sizeof(inqbuf->revision)); + xs->resid = 0; + break; + + case SCSI_TEST_UNIT_READY: + case SCSI_REQUEST_SENSE: + imm = RD4(sc, DIMMBUF); + DPRINTF(sc->sc_dev, "TUR IMMBUF = 0x%08x\n", imm); + switch ((imm >> 24) & 0xff) { + case 0: + di_sense(xs, (imm >> 16) & 0xff, (imm >> 8) & 0xff, + imm & 0xff); + break; + default: + di_sense(xs, SKEY_MEDIUM_ERROR, 0, 0); + break; + } + break; + + case SCSI_READ_6_COMMAND: + case READ_10: + case GPCMD_REPORT_KEY: + bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 0, xs->datalen, + BUS_DMASYNC_POSTREAD); + memcpy(xs->data, sc->sc_dma_addr, xs->datalen); + xs->resid = 0; + break; + + case GPCMD_READ_DVD_STRUCTURE: + bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 0, DVDBLOCKSIZE, + BUS_DMASYNC_POSTREAD); + memcpy(xs->data + 4, sc->sc_dma_addr, xs->datalen - 4); + xs->resid = 0; + break; + + case READ_CD_CAPACITY: + cdcap = (struct scsipi_read_cd_cap_data *)xs->data; + + bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 0, DVDBLOCKSIZE, + BUS_DMASYNC_POSTREAD); + data = sc->sc_dma_addr; + _lto4b(DVDBLOCKSIZE, cdcap->length); + memcpy(cdcap->addr, &data[8], sizeof(cdcap->addr)); + break; + } + + sc->sc_cur_xs = NULL; + scsipi_done(xs); + + return 1; +} + +static int +di_intr(void *priv) +{ + struct di_softc *sc = priv; + uint32_t sr, cvr; + int ret = 0; + + sr = RD4(sc, DISR); + cvr = RD4(sc, DICVR); + + if ((sr & DISR_DEINT) != 0) { + ret |= di_transfer_error(sc, sc->sc_cur_xs); + } else if ((sr & DISR_TCINT) != 0) { + ret |= di_transfer_complete(sc, sc->sc_cur_xs); + } + + if ((cvr & DICVR_CVRINT) != 0) { + DPRINTF(sc->sc_dev, "drive %s\n", + (cvr & DICVR_CVR) == 0 ? "closed" : "opened"); + ret |= 1; + } + + WR4(sc, DISR, sr); + WR4(sc, DICVR, cvr); + + return ret; +} + +static void +di_timeout(void *priv) +{ + struct di_softc *sc = priv; + int s; + + s = splbio(); + if (sc->sc_cur_xs != NULL) { + struct scsipi_xfer *xs = sc->sc_cur_xs; + + DPRINTF(sc->sc_dev, "command %#x timeout, DISR = %#x\n", + xs->cmd->opcode, RD4(sc, DISR)); + xs->error = XS_TIMEOUT; + scsipi_done(xs); + + sc->sc_cur_xs = NULL; + } + splx(s); +} + +static void +di_idle(void *priv) +{ + struct di_softc *sc = priv; + + if ((RD4(sc, DICVR) & DICVR_CVR) != 0) { + /* Cover is opened, nothing to do. */ + return; + } + + di_reset(sc, false); +} + +static void +di_start_request(struct di_softc *sc, struct scsipi_xfer *xs) +{ + KASSERT(sc->sc_cur_xs == NULL); + sc->sc_cur_xs = xs; + if (xs->timeout != 0) { + callout_schedule(&sc->sc_timeout, mstohz(xs->timeout) + 1); + } else { + DPRINTF(sc->sc_dev, "WARNING: xfer with no timeout!\n"); + callout_schedule(&sc->sc_timeout, mstohz(15000)); + } +} + +static void +di_init_regs(struct di_softc *sc) +{ + WR4(sc, DISR, DISR_BRKINT | + DISR_TCINT | DISR_TCINTMASK | + DISR_DEINT | DISR_DEINTMASK); + WR4(sc, DICVR, DICVR_CVRINT | DICVR_CVRINTMASK); +} + +static void +di_reset(struct di_softc *sc, bool spinup) +{ + uint32_t val; + int s; + + DPRINTF(sc->sc_dev, "reset spinup=%d\n", spinup); + + s = splhigh(); + + if (spinup) { + out32(HW_GPIOB_OUT, in32(HW_GPIOB_OUT) & ~__BIT(GPIO_DI_SPIN)); + } else { + out32(HW_GPIOB_OUT, in32(HW_GPIOB_OUT) | __BIT(GPIO_DI_SPIN)); + } + + val = in32(HW_RESETS); + out32(HW_RESETS, val & ~RSTB_IODI); + delay(12); + out32(HW_RESETS, val | RSTB_IODI); + + WR4(sc, DISR, DISR_BRKINT | + DISR_TCINT | DISR_TCINTMASK | + DISR_DEINT | DISR_DEINTMASK); + WR4(sc, DICVR, DICVR_CVRINT | DICVR_CVRINTMASK); + + splx(s); +} + +static void +di_stop_motor(struct di_softc *sc, struct scsipi_xfer *xs, bool eject) +{ + uint32_t cmdflags = 0; + int s; + + if (eject) { + cmdflags |= 1 << 17; + } + + s = splbio(); + WR4(sc, DICMDBUF0, DI_CMD_STOP_MOTOR | cmdflags); + WR4(sc, DICMDBUF1, 0); + WR4(sc, DICMDBUF2, 0); + WR4(sc, DILENGTH, 4); + WR4(sc, DICR, DICR_TSTART); + di_start_request(sc, xs); + splx(s); +} + +static void +di_request(struct scsipi_channel *chan, scsipi_adapter_req_t req, void *arg) +{ + struct di_softc *sc = device_private(chan->chan_adapter->adapt_dev); + struct scsipi_xfer *xs; + struct scsipi_generic *cmd; + struct scsipi_start_stop *ss; + struct scsi_prevent_allow_medium_removal *pamr; + uint32_t blkno; + int s; + + if (req != ADAPTER_REQ_RUN_XFER) { + return; + } + + callout_stop(&sc->sc_idle); + + KASSERT(sc->sc_cur_xs == NULL); + + xs = arg; + cmd = xs->cmd; + + switch (cmd->opcode) { + case INQUIRY: + bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, + 0, sizeof(struct di_response_inquiry), + BUS_DMASYNC_PREREAD); + + s = splbio(); + WR4(sc, DICMDBUF0, DI_CMD_INQUIRY); + WR4(sc, DICMDBUF1, 0); + WR4(sc, DILENGTH, sizeof(struct di_response_inquiry)); + WR4(sc, DIMAR, sc->sc_dma_segs[0].ds_addr); + WR4(sc, DICR, DICR_TSTART | DICR_DMA); + di_start_request(sc, xs); + splx(s); + break; + + case SCSI_TEST_UNIT_READY: + case SCSI_REQUEST_SENSE: + s = splbio(); + WR4(sc, DICMDBUF0, DI_CMD_REQUEST_ERROR); + WR4(sc, DICMDBUF1, 0); + WR4(sc, DICMDBUF2, 0); + WR4(sc, DILENGTH, 4); + WR4(sc, DICR, DICR_TSTART); + di_start_request(sc, xs); + splx(s); + break; + + case SCSI_READ_6_COMMAND: + case READ_10: + if (cmd->opcode == SCSI_READ_6_COMMAND) { + blkno = _3btol(((struct scsi_rw_6 *)cmd)->addr); + } else { + KASSERT(cmd->opcode == READ_10); + blkno = _4btol(((struct scsipi_rw_10 *)cmd)->addr); + } + + if (xs->datalen == 0) { + xs->error = XS_DRIVER_STUFFUP; + scsipi_done(xs); + break; + } + + bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, + 0, xs->datalen, BUS_DMASYNC_PREREAD); + + s = splbio(); + WR4(sc, DICMDBUF0, DI_CMD_READ_DVD); + WR4(sc, DICMDBUF1, blkno); + WR4(sc, DICMDBUF2, howmany(xs->datalen, DVDBLOCKSIZE)); + WR4(sc, DILENGTH, roundup(xs->datalen, DVDBLOCKSIZE)); + WR4(sc, DIMAR, sc->sc_dma_segs[0].ds_addr); + WR4(sc, DICR, DICR_TSTART | DICR_DMA); + di_start_request(sc, xs); + splx(s); + break; + + case GPCMD_READ_DVD_STRUCTURE: + if (xs->datalen == 0) { + DPRINTF(sc->sc_dev, "zero datalen\n"); + xs->error = XS_DRIVER_STUFFUP; + scsipi_done(xs); + break; + } + + bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, + 0, xs->datalen, BUS_DMASYNC_PREREAD); + + s = splbio(); + WR4(sc, DICMDBUF0, DI_CMD_READ_DVD_STRUCT(cmd->bytes[6])); + WR4(sc, DICMDBUF1, 0); + WR4(sc, DICMDBUF2, 0); + WR4(sc, DILENGTH, roundup(xs->datalen, DVDBLOCKSIZE)); + WR4(sc, DIMAR, sc->sc_dma_segs[0].ds_addr); + WR4(sc, DICR, DICR_TSTART | DICR_DMA); + di_start_request(sc, xs); + splx(s); + break; + + case GPCMD_REPORT_KEY: + if (xs->datalen == 0) { + DPRINTF(sc->sc_dev, "zero datalen\n"); + xs->error = XS_DRIVER_STUFFUP; + scsipi_done(xs); + break; + } + + bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, + 0, xs->datalen, BUS_DMASYNC_PREREAD); + + s = splbio(); + WR4(sc, DICMDBUF0, DI_CMD_REPORT_KEY(cmd->bytes[9] >> 2)); + WR4(sc, DICMDBUF1, _4btol(&cmd->bytes[1])); + WR4(sc, DICMDBUF2, 0); + WR4(sc, DILENGTH, roundup(xs->datalen, 0x20)); + WR4(sc, DIMAR, sc->sc_dma_segs[0].ds_addr); + WR4(sc, DICR, DICR_TSTART | DICR_DMA); + di_start_request(sc, xs); + splx(s); + break; + + case READ_CD_CAPACITY: + bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, + 0, DVDBLOCKSIZE, BUS_DMASYNC_PREREAD); + + s = splbio(); + WR4(sc, DICMDBUF0, DI_CMD_READ_DVD_STRUCT(DVD_STRUCT_PHYSICAL)); + WR4(sc, DICMDBUF1, 0); + WR4(sc, DICMDBUF2, 0); + WR4(sc, DILENGTH, DVDBLOCKSIZE); + WR4(sc, DIMAR, sc->sc_dma_segs[0].ds_addr); + WR4(sc, DICR, DICR_TSTART | DICR_DMA); + di_start_request(sc, xs); + splx(s); + break; + + case GET_CONFIGURATION: + memset(xs->data, 0, sizeof(struct scsipi_get_conf_data)); + xs->resid = 0; + scsipi_done(xs); + break; + + case READ_TOC: + memset(xs->data, 0, sizeof(struct scsipi_toc_header)); + xs->resid = 0; + scsipi_done(xs); + break; + + case READ_TRACKINFO: + case READ_DISCINFO: + di_sense(xs, SKEY_ILLEGAL_REQUEST, 0, 0); + scsipi_done(xs); + break; + + case START_STOP: + ss = (struct scsipi_start_stop *)cmd; + if (ss->how == SSS_START) { + di_reset(sc, true); + scsipi_done(xs); + } else { + di_stop_motor(sc, xs, (ss->how & SSS_LOEJ) != 0); + } + break; + + case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL: + pamr = (struct scsi_prevent_allow_medium_removal *)cmd; + sc->sc_pamr = pamr->how; + scsipi_done(xs); + break; + + default: + DPRINTF(sc->sc_dev, "unsupported opcode %#x\n", cmd->opcode); + scsipi_done(xs); + } + + if (!sc->sc_pamr) { + callout_schedule(&sc->sc_idle, mstohz(DI_IDLE_TIMEOUT_MS)); + } +}