Home | History | Annotate | Line # | Download | only in dev
      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