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));
+	}
+}

Reply via email to