1*89ed722cSmpi /* $OpenBSD: wdc_obio.c,v 1.31 2022/03/13 12:33:01 mpi Exp $ */
2e6d856e8Smickey /* $NetBSD: wdc_obio.c,v 1.15 2001/07/25 20:26:33 bouyer Exp $ */
3d9a5f17fSdrahn
4d9a5f17fSdrahn /*-
5d9a5f17fSdrahn * Copyright (c) 1998 The NetBSD Foundation, Inc.
6d9a5f17fSdrahn * All rights reserved.
7d9a5f17fSdrahn *
8d9a5f17fSdrahn * This code is derived from software contributed to The NetBSD Foundation
9d9a5f17fSdrahn * by Charles M. Hannum and by Onno van der Linden.
10d9a5f17fSdrahn *
11d9a5f17fSdrahn * Redistribution and use in source and binary forms, with or without
12d9a5f17fSdrahn * modification, are permitted provided that the following conditions
13d9a5f17fSdrahn * are met:
14d9a5f17fSdrahn * 1. Redistributions of source code must retain the above copyright
15d9a5f17fSdrahn * notice, this list of conditions and the following disclaimer.
16d9a5f17fSdrahn * 2. Redistributions in binary form must reproduce the above copyright
17d9a5f17fSdrahn * notice, this list of conditions and the following disclaimer in the
18d9a5f17fSdrahn * documentation and/or other materials provided with the distribution.
19d9a5f17fSdrahn *
20d9a5f17fSdrahn * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21d9a5f17fSdrahn * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22d9a5f17fSdrahn * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23d9a5f17fSdrahn * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24d9a5f17fSdrahn * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25d9a5f17fSdrahn * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26d9a5f17fSdrahn * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27d9a5f17fSdrahn * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28d9a5f17fSdrahn * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29d9a5f17fSdrahn * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30d9a5f17fSdrahn * POSSIBILITY OF SUCH DAMAGE.
31d9a5f17fSdrahn */
32d9a5f17fSdrahn
33d9a5f17fSdrahn #include <sys/param.h>
34d9a5f17fSdrahn #include <sys/systm.h>
35d9a5f17fSdrahn #include <sys/device.h>
36d9a5f17fSdrahn #include <sys/malloc.h>
37d9a5f17fSdrahn
38489e49f9Smiod #include <uvm/uvm_extern.h>
39d9a5f17fSdrahn
40d9a5f17fSdrahn #include <machine/bus.h>
41d9a5f17fSdrahn #include <machine/autoconf.h>
42d9a5f17fSdrahn
43d9a5f17fSdrahn #include <dev/ofw/openfirm.h>
44d9a5f17fSdrahn #include <dev/ata/atavar.h>
45d9a5f17fSdrahn #include <dev/ata/atareg.h>
46d9a5f17fSdrahn #include <dev/ic/wdcvar.h>
47d9a5f17fSdrahn
4878a7e9fcSdrahn #include <macppc/dev/dbdma.h>
49d9a5f17fSdrahn
50d9a5f17fSdrahn #define WDC_REG_NPORTS 8
51d9a5f17fSdrahn #define WDC_AUXREG_OFFSET 0x16
52d9a5f17fSdrahn #define WDC_DEFAULT_PIO_IRQ 13 /* XXX */
53d9a5f17fSdrahn #define WDC_DEFAULT_DMA_IRQ 2 /* XXX */
54d9a5f17fSdrahn
55d9a5f17fSdrahn #define WDC_OPTIONS_DMA 0x01
56d9a5f17fSdrahn
57e6d856e8Smickey #define WDC_DMALIST_MAX 32
58d9a5f17fSdrahn
59d9a5f17fSdrahn struct wdc_obio_softc {
60d9a5f17fSdrahn struct wdc_softc sc_wdcdev;
61d9a5f17fSdrahn struct channel_softc *wdc_chanptr;
62d9a5f17fSdrahn struct channel_softc wdc_channel;
63e6d856e8Smickey
64e6d856e8Smickey bus_dma_tag_t sc_dmat;
65e6d856e8Smickey bus_dmamap_t sc_dmamap;
66d9a5f17fSdrahn dbdma_regmap_t *sc_dmareg;
67d9a5f17fSdrahn dbdma_command_t *sc_dmacmd;
68e6d856e8Smickey dbdma_t sc_dbdma;
69fe1b9aebSmiod
70fe1b9aebSmiod void *sc_ih;
71fe1b9aebSmiod int sc_use_dma;
72fe1b9aebSmiod bus_size_t sc_cmdsize;
73fe1b9aebSmiod size_t sc_dmasize;
74d9a5f17fSdrahn };
75d9a5f17fSdrahn
76c4071fd1Smillert u_int8_t wdc_obio_read_reg(struct channel_softc *, enum wdc_regs);
77c4071fd1Smillert void wdc_obio_write_reg(struct channel_softc *, enum wdc_regs, u_int8_t);
78dde48f7eSdrahn
79d9a5f17fSdrahn struct channel_softc_vtbl wdc_obio_vtbl = {
80d9a5f17fSdrahn wdc_obio_read_reg,
81d9a5f17fSdrahn wdc_obio_write_reg,
821e1caba6Skettenis wdc_default_lba48_write_reg,
83d9a5f17fSdrahn wdc_default_read_raw_multi_2,
84d9a5f17fSdrahn wdc_default_write_raw_multi_2,
85d9a5f17fSdrahn wdc_default_read_raw_multi_4,
86d9a5f17fSdrahn wdc_default_write_raw_multi_4
87d9a5f17fSdrahn };
88d9a5f17fSdrahn
89c4071fd1Smillert int wdc_obio_probe(struct device *, void *, void *);
90c4071fd1Smillert void wdc_obio_attach(struct device *, struct device *, void *);
91fe1b9aebSmiod int wdc_obio_detach(struct device *, int);
92d9a5f17fSdrahn
93*89ed722cSmpi const struct cfattach wdc_obio_ca = {
94fe1b9aebSmiod sizeof(struct wdc_obio_softc), wdc_obio_probe, wdc_obio_attach,
95c06fda6dSderaadt wdc_obio_detach
96d9a5f17fSdrahn };
97d9a5f17fSdrahn
98c4071fd1Smillert int wdc_obio_dma_init(void *, int, int, void *, size_t, int);
99c4071fd1Smillert void wdc_obio_dma_start(void *, int, int);
100576378a2Smiod int wdc_obio_dma_finish(void *, int, int, int);
101c4071fd1Smillert void wdc_obio_adjust_timing(struct channel_softc *);
102c4071fd1Smillert void wdc_obio_ata4_adjust_timing(struct channel_softc *);
103e46ce5b4Sdrahn void wdc_obio_ata6_adjust_timing(struct channel_softc *);
104d9a5f17fSdrahn
105d9a5f17fSdrahn int
wdc_obio_probe(struct device * parent,void * match,void * aux)106bf1f8dcbSdrahn wdc_obio_probe(struct device *parent, void *match, void *aux)
107d9a5f17fSdrahn {
108d9a5f17fSdrahn struct confargs *ca = aux;
109d9a5f17fSdrahn char compat[32];
110d9a5f17fSdrahn
11184b94647Smiod if (ca->ca_nreg < 8)
11284b94647Smiod return 0;
11384b94647Smiod
114d9a5f17fSdrahn /* XXX should not use name */
115d9a5f17fSdrahn if (strcmp(ca->ca_name, "ATA") == 0 ||
116d9a5f17fSdrahn strncmp(ca->ca_name, "ata", 3) == 0 ||
117d9a5f17fSdrahn strcmp(ca->ca_name, "ide") == 0)
118d9a5f17fSdrahn return 1;
119d9a5f17fSdrahn
120d9a5f17fSdrahn bzero(compat, sizeof(compat));
121d9a5f17fSdrahn OF_getprop(ca->ca_node, "compatible", compat, sizeof(compat));
122d9a5f17fSdrahn if (strcmp(compat, "heathrow-ata") == 0 ||
123d9a5f17fSdrahn strcmp(compat, "keylargo-ata") == 0)
124d9a5f17fSdrahn return 1;
125d9a5f17fSdrahn
126d9a5f17fSdrahn return 0;
127d9a5f17fSdrahn }
128d9a5f17fSdrahn
129d9a5f17fSdrahn void
wdc_obio_attach(struct device * parent,struct device * self,void * aux)130bf1f8dcbSdrahn wdc_obio_attach(struct device *parent, struct device *self, void *aux)
131d9a5f17fSdrahn {
132d9a5f17fSdrahn struct wdc_obio_softc *sc = (void *)self;
133d9a5f17fSdrahn struct confargs *ca = aux;
134d9a5f17fSdrahn struct channel_softc *chp = &sc->wdc_channel;
135fe1b9aebSmiod int intr, error;
136d9a5f17fSdrahn bus_addr_t cmdbase;
137d9a5f17fSdrahn
138fe1b9aebSmiod sc->sc_use_dma = 0;
13984b94647Smiod if (ca->ca_nreg >= 16)
140fe1b9aebSmiod sc->sc_use_dma = 1; /* Enable dma */
141d9a5f17fSdrahn
142e6d856e8Smickey sc->sc_dmat = ca->ca_dmat;
143e6d856e8Smickey if ((error = bus_dmamap_create(sc->sc_dmat,
144e6d856e8Smickey WDC_DMALIST_MAX * DBDMA_COUNT_MAX, WDC_DMALIST_MAX,
145e6d856e8Smickey DBDMA_COUNT_MAX, NBPG, BUS_DMA_NOWAIT, &sc->sc_dmamap)) != 0) {
146e6d856e8Smickey printf(": cannot create dma map, error = %d\n", error);
147e6d856e8Smickey return;
148e6d856e8Smickey }
149e6d856e8Smickey
150d9a5f17fSdrahn if (ca->ca_nintr >= 4 && ca->ca_nreg >= 8) {
151d9a5f17fSdrahn intr = ca->ca_intr[0];
152d9a5f17fSdrahn printf(" irq %d", intr);
153d9a5f17fSdrahn } else if (ca->ca_nintr == -1) {
154d9a5f17fSdrahn intr = WDC_DEFAULT_PIO_IRQ;
155d9a5f17fSdrahn printf(" irq property not found; using %d", intr);
156d9a5f17fSdrahn } else {
157d9a5f17fSdrahn printf(": couldn't get irq property\n");
158d9a5f17fSdrahn return;
159d9a5f17fSdrahn }
160d9a5f17fSdrahn
161fe1b9aebSmiod if (sc->sc_use_dma)
162f29ee009Sgrange printf(": DMA");
163d9a5f17fSdrahn
164d9a5f17fSdrahn printf("\n");
165d9a5f17fSdrahn
166d9a5f17fSdrahn chp->cmd_iot = chp->ctl_iot = ca->ca_iot;
167d9a5f17fSdrahn chp->_vtbl = &wdc_obio_vtbl;
168d9a5f17fSdrahn
169d9a5f17fSdrahn cmdbase = ca->ca_reg[0];
170fe1b9aebSmiod sc->sc_cmdsize = ca->ca_reg[1];
171d9a5f17fSdrahn
172fe1b9aebSmiod if (bus_space_map(chp->cmd_iot, cmdbase, sc->sc_cmdsize, 0,
173fe1b9aebSmiod &chp->cmd_ioh) || bus_space_subregion(chp->cmd_iot, chp->cmd_ioh,
174bf1f8dcbSdrahn /* WDC_AUXREG_OFFSET<<4 */ 0x160, 1, &chp->ctl_ioh)) {
175d9a5f17fSdrahn printf("%s: couldn't map registers\n",
176d9a5f17fSdrahn sc->sc_wdcdev.sc_dev.dv_xname);
177d9a5f17fSdrahn return;
178d9a5f17fSdrahn }
179d9a5f17fSdrahn chp->data32iot = chp->cmd_iot;
180d9a5f17fSdrahn chp->data32ioh = chp->cmd_ioh;
181d9a5f17fSdrahn
182fe1b9aebSmiod sc->sc_ih = mac_intr_establish(parent, intr, IST_LEVEL, IPL_BIO,
183fe1b9aebSmiod wdcintr, chp, sc->sc_wdcdev.sc_dev.dv_xname);
184d9a5f17fSdrahn
185e6d856e8Smickey sc->sc_wdcdev.set_modes = wdc_obio_adjust_timing;
186fe1b9aebSmiod if (sc->sc_use_dma) {
187e6d856e8Smickey sc->sc_dbdma = dbdma_alloc(sc->sc_dmat, WDC_DMALIST_MAX + 1);
188e6d856e8Smickey sc->sc_dmacmd = sc->sc_dbdma->d_addr;
189e46ce5b4Sdrahn
190d9a5f17fSdrahn sc->sc_dmareg = mapiodev(ca->ca_baseaddr + ca->ca_reg[2],
191fe1b9aebSmiod sc->sc_dmasize = ca->ca_reg[3]);
192e46ce5b4Sdrahn
193e6d856e8Smickey sc->sc_wdcdev.cap |= WDC_CAPABILITY_DMA;
194e6d856e8Smickey sc->sc_wdcdev.DMA_cap = 2;
195e6d856e8Smickey if (strcmp(ca->ca_name, "ata-4") == 0) {
196e6d856e8Smickey sc->sc_wdcdev.cap |= WDC_CAPABILITY_UDMA |
197e6d856e8Smickey WDC_CAPABILITY_MODE;
198e6d856e8Smickey sc->sc_wdcdev.UDMA_cap = 4;
199e6d856e8Smickey sc->sc_wdcdev.set_modes = wdc_obio_ata4_adjust_timing;
200e6d856e8Smickey }
201e46ce5b4Sdrahn if (strcmp(ca->ca_name, "ata-6") == 0) {
202e46ce5b4Sdrahn sc->sc_wdcdev.cap |= WDC_CAPABILITY_UDMA |
203e46ce5b4Sdrahn WDC_CAPABILITY_MODE;
204a19855b7Sdrahn sc->sc_wdcdev.UDMA_cap = 5;
205e46ce5b4Sdrahn sc->sc_wdcdev.set_modes = wdc_obio_ata6_adjust_timing;
206e46ce5b4Sdrahn }
207d9a5f17fSdrahn }
208d9a5f17fSdrahn sc->sc_wdcdev.cap |= WDC_CAPABILITY_DATA16;
209e6d856e8Smickey sc->sc_wdcdev.PIO_cap = 4;
210d9a5f17fSdrahn sc->wdc_chanptr = chp;
211d9a5f17fSdrahn sc->sc_wdcdev.channels = &sc->wdc_chanptr;
212d9a5f17fSdrahn sc->sc_wdcdev.nchannels = 1;
213d9a5f17fSdrahn sc->sc_wdcdev.dma_arg = sc;
214d9a5f17fSdrahn sc->sc_wdcdev.dma_init = wdc_obio_dma_init;
215d9a5f17fSdrahn sc->sc_wdcdev.dma_start = wdc_obio_dma_start;
216d9a5f17fSdrahn sc->sc_wdcdev.dma_finish = wdc_obio_dma_finish;
217d9a5f17fSdrahn chp->channel = 0;
218d9a5f17fSdrahn chp->wdc = &sc->sc_wdcdev;
219dde48f7eSdrahn
220dddcd211Smatthew chp->ch_queue = wdc_alloc_queue();
221d9a5f17fSdrahn if (chp->ch_queue == NULL) {
222dddcd211Smatthew printf("%s: cannot allocate channel queue",
223d9a5f17fSdrahn sc->sc_wdcdev.sc_dev.dv_xname);
224d9a5f17fSdrahn return;
225d9a5f17fSdrahn }
226d9a5f17fSdrahn
227d9a5f17fSdrahn wdcattach(chp);
228e6d856e8Smickey sc->sc_wdcdev.set_modes(chp);
229d9a5f17fSdrahn wdc_print_current_modes(chp);
230d9a5f17fSdrahn }
231d9a5f17fSdrahn
232fe1b9aebSmiod int
wdc_obio_detach(struct device * self,int flags)233fe1b9aebSmiod wdc_obio_detach(struct device *self, int flags)
234fe1b9aebSmiod {
235fe1b9aebSmiod struct wdc_obio_softc *sc = (struct wdc_obio_softc *)self;
236fe1b9aebSmiod struct channel_softc *chp = &sc->wdc_channel;
237fe1b9aebSmiod int error;
238fe1b9aebSmiod
239fe1b9aebSmiod if ((error = wdcdetach(chp, flags)) != 0)
240fe1b9aebSmiod return (error);
241fe1b9aebSmiod
242dddcd211Smatthew wdc_free_queue(chp->ch_queue);
243fe1b9aebSmiod
244fe1b9aebSmiod if (sc->sc_use_dma) {
245fe1b9aebSmiod unmapiodev((void *)sc->sc_dmareg, sc->sc_dmasize);
246fe1b9aebSmiod dbdma_free(sc->sc_dbdma);
247fe1b9aebSmiod }
248fe1b9aebSmiod mac_intr_disestablish(NULL, sc->sc_ih);
249fe1b9aebSmiod
250fe1b9aebSmiod bus_space_unmap(chp->cmd_iot, chp->cmd_ioh, sc->sc_cmdsize);
251fe1b9aebSmiod bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap);
252fe1b9aebSmiod
253fe1b9aebSmiod return (0);
254fe1b9aebSmiod }
255fe1b9aebSmiod
256d9a5f17fSdrahn /* Multiword DMA transfer timings */
257e6d856e8Smickey struct ide_timings {
258d9a5f17fSdrahn int cycle; /* minimum cycle time [ns] */
259d9a5f17fSdrahn int active; /* minimum command active time [ns] */
260e6d856e8Smickey };
261e6d856e8Smickey
262e6d856e8Smickey static const struct ide_timings pio_timing[] = {
263e6d856e8Smickey { 600, 165 }, /* Mode 0 */
264e6d856e8Smickey { 383, 125 }, /* 1 */
265e6d856e8Smickey { 240, 100 }, /* 2 */
266e6d856e8Smickey { 180, 80 }, /* 3 */
267e6d856e8Smickey { 120, 70 } /* 4 */
268e6d856e8Smickey };
269e6d856e8Smickey
270e6d856e8Smickey static const struct ide_timings dma_timing[] = {
271d9a5f17fSdrahn { 480, 215 }, /* Mode 0 */
272d9a5f17fSdrahn { 150, 80 }, /* Mode 1 */
273d9a5f17fSdrahn { 120, 70 }, /* Mode 2 */
274d9a5f17fSdrahn };
275d9a5f17fSdrahn
276e6d856e8Smickey static const struct ide_timings udma_timing[] = {
277e6d856e8Smickey {114, 0}, /* Mode 0 */
278e6d856e8Smickey { 75, 0}, /* Mode 1 */
279e6d856e8Smickey { 55, 0}, /* Mode 2 */
280e6d856e8Smickey { 45, 100}, /* Mode 3 */
281e6d856e8Smickey { 25, 100} /* Mode 4 */
282e6d856e8Smickey };
283e6d856e8Smickey
284e46ce5b4Sdrahn /* these number _guessed_ from linux driver. */
285e46ce5b4Sdrahn static u_int32_t kauai_pio_timing[] = {
286a19855b7Sdrahn /*600*/ 0x08000a92, /* Mode 0 */
287017eeeceSdrahn /*360*/ 0x08000492, /* Mode 1 */
288e46ce5b4Sdrahn /*240*/ 0x0800038b, /* Mode 2 */
289a19855b7Sdrahn /*180*/ 0x05000249, /* Mode 3 */
290a19855b7Sdrahn /*120*/ 0x04000148 /* Mode 4 */
291e46ce5b4Sdrahn
292e46ce5b4Sdrahn };
293e46ce5b4Sdrahn static u_int32_t kauai_dma_timing[] = {
294a19855b7Sdrahn /*480*/ 0x00618000, /* Mode 0 */
295a19855b7Sdrahn /*360*/ 0x00492000, /* Mode 1 */
296017eeeceSdrahn /*240*/ 0x00149000 /* Mode 2 */ /* fw value */
297e46ce5b4Sdrahn };
298e46ce5b4Sdrahn static u_int32_t kauai_udma_timing[] = {
299a19855b7Sdrahn /*120*/ 0x000070c0, /* Mode 0 */
300a19855b7Sdrahn /* 90*/ 0x00005d80, /* Mode 1 */
301a19855b7Sdrahn /* 60*/ 0x00004a60, /* Mode 2 */
302a19855b7Sdrahn /* 45*/ 0x00003a50, /* Mode 3 */
303a19855b7Sdrahn /* 30*/ 0x00002a30, /* Mode 4 */
304a19855b7Sdrahn /* 20*/ 0x00002921 /* Mode 5 */
305e46ce5b4Sdrahn };
306e46ce5b4Sdrahn
307d9a5f17fSdrahn #define TIME_TO_TICK(time) howmany((time), 30)
308e6d856e8Smickey #define PIO_REC_OFFSET 4
309e6d856e8Smickey #define PIO_REC_MIN 1
310e6d856e8Smickey #define PIO_ACT_MIN 1
311e6d856e8Smickey #define DMA_REC_OFFSET 1
312e6d856e8Smickey #define DMA_REC_MIN 1
313e6d856e8Smickey #define DMA_ACT_MIN 1
314e6d856e8Smickey
315e6d856e8Smickey #define ATA4_TIME_TO_TICK(time) howmany((time) * 1000, 7500)
316d9a5f17fSdrahn
317d9a5f17fSdrahn #define CONFIG_REG (0x200) /* IDE access timing register */
318e46ce5b4Sdrahn #define KAUAI_ULTRA_CONFIG (0x210) /* secondary config register (kauai)*/
319e46ce5b4Sdrahn
320e46ce5b4Sdrahn #define KAUAI_PIO_MASK 0xff000fff
321e46ce5b4Sdrahn #define KAUAI_DMA_MASK 0x00fff000
322e46ce5b4Sdrahn #define KAUAI_UDMA_MASK 0x0000ffff
323e46ce5b4Sdrahn #define KAUAI_UDMA_EN 0x00000001
324e46ce5b4Sdrahn
325d9a5f17fSdrahn void
wdc_obio_adjust_timing(struct channel_softc * chp)326bf1f8dcbSdrahn wdc_obio_adjust_timing(struct channel_softc *chp)
327d9a5f17fSdrahn {
328e6d856e8Smickey struct ata_drive_datas *drvp;
329d9a5f17fSdrahn u_int conf;
330e6d856e8Smickey int drive;
331e6d856e8Smickey int piomode = -1, dmamode = -1;
332e6d856e8Smickey int min_cycle, min_active;
333d9a5f17fSdrahn int cycle_tick, act_tick, inact_tick, half_tick;
334d9a5f17fSdrahn
335e6d856e8Smickey for (drive = 0; drive < 2; drive++) {
336e6d856e8Smickey drvp = &chp->ch_drive[drive];
337e6d856e8Smickey if ((drvp->drive_flags & DRIVE) == 0)
338e6d856e8Smickey continue;
339e6d856e8Smickey if (piomode == -1 || piomode > drvp->PIO_mode)
340e6d856e8Smickey piomode = drvp->PIO_mode;
341bf1f8dcbSdrahn if (drvp->drive_flags & DRIVE_DMA)
342e6d856e8Smickey if (dmamode == -1 || dmamode > drvp->DMA_mode)
343e6d856e8Smickey dmamode = drvp->DMA_mode;
344e6d856e8Smickey }
345e6d856e8Smickey if (piomode == -1)
346e6d856e8Smickey return; /* No drive */
347e6d856e8Smickey for (drive = 0; drive < 2; drive++) {
348e6d856e8Smickey drvp = &chp->ch_drive[drive];
349e6d856e8Smickey if (drvp->drive_flags & DRIVE) {
350e6d856e8Smickey drvp->PIO_mode = piomode;
351e6d856e8Smickey if (drvp->drive_flags & DRIVE_DMA)
352e6d856e8Smickey drvp->DMA_mode = dmamode;
353e6d856e8Smickey }
354e6d856e8Smickey }
355e6d856e8Smickey min_cycle = pio_timing[piomode].cycle;
356e6d856e8Smickey min_active = pio_timing[piomode].active;
357d9a5f17fSdrahn
358e6d856e8Smickey cycle_tick = TIME_TO_TICK(min_cycle);
359e6d856e8Smickey act_tick = TIME_TO_TICK(min_active);
360e6d856e8Smickey if (act_tick < PIO_ACT_MIN)
361e6d856e8Smickey act_tick = PIO_ACT_MIN;
362e6d856e8Smickey inact_tick = cycle_tick - act_tick - PIO_REC_OFFSET;
363e6d856e8Smickey if (inact_tick < PIO_REC_MIN)
364e6d856e8Smickey inact_tick = PIO_REC_MIN;
365e6d856e8Smickey /* mask: 0x000007ff */
366e6d856e8Smickey conf = (inact_tick << 5) | act_tick;
367e6d856e8Smickey if (dmamode != -1) {
368e6d856e8Smickey /* there are active DMA mode */
369d9a5f17fSdrahn
370e6d856e8Smickey min_cycle = dma_timing[dmamode].cycle;
371e6d856e8Smickey min_active = dma_timing[dmamode].active;
372e6d856e8Smickey cycle_tick = TIME_TO_TICK(min_cycle);
373e6d856e8Smickey act_tick = TIME_TO_TICK(min_active);
374e6d856e8Smickey inact_tick = cycle_tick - act_tick - DMA_REC_OFFSET;
375e6d856e8Smickey if (inact_tick < DMA_REC_MIN)
376e6d856e8Smickey inact_tick = DMA_REC_MIN;
377d9a5f17fSdrahn half_tick = 0; /* XXX */
378e6d856e8Smickey /* mask: 0xfffff800 */
3796a1aaa0fSderaadt conf |=
3806a1aaa0fSderaadt (half_tick << 21) |
381e6d856e8Smickey (inact_tick << 16) | (act_tick << 11);
382e6d856e8Smickey }
383d9a5f17fSdrahn bus_space_write_4(chp->cmd_iot, chp->cmd_ioh, CONFIG_REG, conf);
384d9a5f17fSdrahn #if 0
385d9a5f17fSdrahn printf("conf = 0x%x, cyc = %d (%d ns), act = %d (%d ns), inact = %d\n",
386e6d856e8Smickey conf, cycle_tick, min_cycle, act_tick, min_active, inact_tick);
387d9a5f17fSdrahn #endif
388d9a5f17fSdrahn }
389d9a5f17fSdrahn
390e6d856e8Smickey void
wdc_obio_ata4_adjust_timing(struct channel_softc * chp)391dde48f7eSdrahn wdc_obio_ata4_adjust_timing(struct channel_softc *chp)
392e6d856e8Smickey {
393e6d856e8Smickey struct ata_drive_datas *drvp;
394e6d856e8Smickey u_int conf;
395e6d856e8Smickey int drive;
396e6d856e8Smickey int piomode = -1, dmamode = -1;
397e6d856e8Smickey int min_cycle, min_active;
398e6d856e8Smickey int cycle_tick, act_tick, inact_tick;
399e6d856e8Smickey int udmamode = -1;
400e6d856e8Smickey
401e6d856e8Smickey for (drive = 0; drive < 2; drive++) {
402e6d856e8Smickey drvp = &chp->ch_drive[drive];
403e6d856e8Smickey if ((drvp->drive_flags & DRIVE) == 0)
404e6d856e8Smickey continue;
405e6d856e8Smickey if (piomode == -1 || piomode > drvp->PIO_mode)
406e6d856e8Smickey piomode = drvp->PIO_mode;
407bf1f8dcbSdrahn if (drvp->drive_flags & DRIVE_DMA)
408e6d856e8Smickey if (dmamode == -1 || dmamode > drvp->DMA_mode)
409e6d856e8Smickey dmamode = drvp->DMA_mode;
410e6d856e8Smickey if (drvp->drive_flags & DRIVE_UDMA) {
411e6d856e8Smickey if (udmamode == -1 || udmamode > drvp->UDMA_mode)
412e6d856e8Smickey udmamode = drvp->UDMA_mode;
413bf1f8dcbSdrahn } else
414061be5b5Sdrahn udmamode = -2;
415e6d856e8Smickey }
416e6d856e8Smickey if (piomode == -1)
417e6d856e8Smickey return; /* No drive */
418e6d856e8Smickey for (drive = 0; drive < 2; drive++) {
419e6d856e8Smickey drvp = &chp->ch_drive[drive];
420e6d856e8Smickey if (drvp->drive_flags & DRIVE) {
421e6d856e8Smickey drvp->PIO_mode = piomode;
422e6d856e8Smickey if (drvp->drive_flags & DRIVE_DMA)
423e6d856e8Smickey drvp->DMA_mode = dmamode;
424061be5b5Sdrahn if (drvp->drive_flags & DRIVE_UDMA) {
425bf1f8dcbSdrahn if (udmamode == -2)
426061be5b5Sdrahn drvp->drive_flags &= ~DRIVE_UDMA;
427bf1f8dcbSdrahn else
428e6d856e8Smickey drvp->UDMA_mode = udmamode;
429e6d856e8Smickey }
430e6d856e8Smickey }
431061be5b5Sdrahn }
432061be5b5Sdrahn
433061be5b5Sdrahn if (udmamode == -2)
434061be5b5Sdrahn udmamode = -1;
435061be5b5Sdrahn
436e6d856e8Smickey min_cycle = pio_timing[piomode].cycle;
437e6d856e8Smickey min_active = pio_timing[piomode].active;
438e6d856e8Smickey
439e6d856e8Smickey cycle_tick = ATA4_TIME_TO_TICK(min_cycle);
440e6d856e8Smickey act_tick = ATA4_TIME_TO_TICK(min_active);
441e6d856e8Smickey inact_tick = cycle_tick - act_tick;
442e6d856e8Smickey /* mask: 0x000003ff */
443e6d856e8Smickey conf = (inact_tick << 5) | act_tick;
444e6d856e8Smickey if (dmamode != -1) {
445e6d856e8Smickey /* there are active DMA mode */
446e6d856e8Smickey
447e6d856e8Smickey min_cycle = dma_timing[dmamode].cycle;
448e6d856e8Smickey min_active = dma_timing[dmamode].active;
449e6d856e8Smickey cycle_tick = ATA4_TIME_TO_TICK(min_cycle);
450e6d856e8Smickey act_tick = ATA4_TIME_TO_TICK(min_active);
451e6d856e8Smickey inact_tick = cycle_tick - act_tick;
452e6d856e8Smickey /* mask: 0x001ffc00 */
453e6d856e8Smickey conf |= (act_tick << 10) | (inact_tick << 15);
454e6d856e8Smickey }
455e6d856e8Smickey if (udmamode != -1) {
456e6d856e8Smickey min_cycle = udma_timing[udmamode].cycle;
457e6d856e8Smickey min_active = udma_timing[udmamode].active;
458e6d856e8Smickey act_tick = ATA4_TIME_TO_TICK(min_active);
459e6d856e8Smickey cycle_tick = ATA4_TIME_TO_TICK(min_cycle);
460e6d856e8Smickey /* mask: 0x1ff00000 */
461e6d856e8Smickey conf |= (cycle_tick << 21) | (act_tick << 25) | 0x100000;
462e6d856e8Smickey }
463e6d856e8Smickey
464e6d856e8Smickey bus_space_write_4(chp->cmd_iot, chp->cmd_ioh, CONFIG_REG, conf);
465e6d856e8Smickey #if 0
466e6d856e8Smickey printf("ata4 conf = 0x%x, cyc = %d (%d ns), act = %d (%d ns), inact = %d\n",
467e6d856e8Smickey conf, cycle_tick, min_cycle, act_tick, min_active, inact_tick);
468e6d856e8Smickey #endif
469e6d856e8Smickey }
470e6d856e8Smickey
471e46ce5b4Sdrahn void
wdc_obio_ata6_adjust_timing(struct channel_softc * chp)472e46ce5b4Sdrahn wdc_obio_ata6_adjust_timing(struct channel_softc *chp)
473e46ce5b4Sdrahn {
474e46ce5b4Sdrahn struct ata_drive_datas *drvp;
475e46ce5b4Sdrahn u_int conf, conf1;
476e46ce5b4Sdrahn int drive;
477e46ce5b4Sdrahn int piomode = -1, dmamode = -1;
478e46ce5b4Sdrahn int udmamode = -1;
479e46ce5b4Sdrahn
480e46ce5b4Sdrahn for (drive = 0; drive < 2; drive++) {
481e46ce5b4Sdrahn drvp = &chp->ch_drive[drive];
482e46ce5b4Sdrahn if ((drvp->drive_flags & DRIVE) == 0)
483e46ce5b4Sdrahn continue;
484e46ce5b4Sdrahn if (piomode == -1 || piomode > drvp->PIO_mode)
485e46ce5b4Sdrahn piomode = drvp->PIO_mode;
486e46ce5b4Sdrahn if (drvp->drive_flags & DRIVE_DMA) {
487e46ce5b4Sdrahn if (dmamode == -1 || dmamode > drvp->DMA_mode)
488e46ce5b4Sdrahn dmamode = drvp->DMA_mode;
489e46ce5b4Sdrahn }
490e46ce5b4Sdrahn if (drvp->drive_flags & DRIVE_UDMA) {
491e46ce5b4Sdrahn if (udmamode == -1 || udmamode > drvp->UDMA_mode)
492e46ce5b4Sdrahn udmamode = drvp->UDMA_mode;
493bf1f8dcbSdrahn } else
494e46ce5b4Sdrahn udmamode = -2;
495e46ce5b4Sdrahn }
496e46ce5b4Sdrahn if (piomode == -1)
497e46ce5b4Sdrahn return; /* No drive */
498e46ce5b4Sdrahn for (drive = 0; drive < 2; drive++) {
499e46ce5b4Sdrahn drvp = &chp->ch_drive[drive];
500e46ce5b4Sdrahn if (drvp->drive_flags & DRIVE) {
501e46ce5b4Sdrahn drvp->PIO_mode = piomode;
502e46ce5b4Sdrahn if (drvp->drive_flags & DRIVE_DMA)
503e46ce5b4Sdrahn drvp->DMA_mode = dmamode;
504e46ce5b4Sdrahn if (drvp->drive_flags & DRIVE_UDMA) {
505bf1f8dcbSdrahn if (udmamode == -2)
506e46ce5b4Sdrahn drvp->drive_flags &= ~DRIVE_UDMA;
507bf1f8dcbSdrahn else
508e46ce5b4Sdrahn drvp->UDMA_mode = udmamode;
509e46ce5b4Sdrahn }
510e46ce5b4Sdrahn }
511e46ce5b4Sdrahn }
512e46ce5b4Sdrahn
513e46ce5b4Sdrahn if (udmamode == -2)
514e46ce5b4Sdrahn udmamode = -1;
515e46ce5b4Sdrahn
516e46ce5b4Sdrahn conf = bus_space_read_4(chp->cmd_iot, chp->cmd_ioh, CONFIG_REG);
517e46ce5b4Sdrahn conf1 = bus_space_read_4(chp->cmd_iot, chp->cmd_ioh,
518e46ce5b4Sdrahn KAUAI_ULTRA_CONFIG);
519e46ce5b4Sdrahn
520e46ce5b4Sdrahn conf = (conf & ~KAUAI_PIO_MASK) | kauai_pio_timing[piomode];
521e46ce5b4Sdrahn
522bf1f8dcbSdrahn if (dmamode != -1)
523e46ce5b4Sdrahn conf = (conf & ~KAUAI_DMA_MASK) | kauai_dma_timing[dmamode];
524bf1f8dcbSdrahn if (udmamode != -1)
525e46ce5b4Sdrahn conf1 = (conf1 & ~KAUAI_UDMA_MASK) |
526e46ce5b4Sdrahn kauai_udma_timing[udmamode] | KAUAI_UDMA_EN;
527bf1f8dcbSdrahn else
528e46ce5b4Sdrahn conf1 = conf1 & ~KAUAI_UDMA_EN;
529e46ce5b4Sdrahn
530e46ce5b4Sdrahn bus_space_write_4(chp->cmd_iot, chp->cmd_ioh, CONFIG_REG, conf);
531e46ce5b4Sdrahn bus_space_write_4(chp->cmd_iot, chp->cmd_ioh, KAUAI_ULTRA_CONFIG,
532e46ce5b4Sdrahn conf1);
533e46ce5b4Sdrahn }
534e46ce5b4Sdrahn
535e6d856e8Smickey int
wdc_obio_dma_init(void * v,int channel,int drive,void * databuf,size_t datalen,int flags)536dde48f7eSdrahn wdc_obio_dma_init(void *v, int channel, int drive, void *databuf,
537dde48f7eSdrahn size_t datalen, int flags)
538d9a5f17fSdrahn {
539d9a5f17fSdrahn struct wdc_obio_softc *sc = v;
540d9a5f17fSdrahn dbdma_command_t *cmdp;
541e6d856e8Smickey u_int cmd;
542e6d856e8Smickey int i, error;
543e6d856e8Smickey
544e6d856e8Smickey if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, databuf,
545e6d856e8Smickey datalen, NULL, BUS_DMA_NOWAIT)) != 0)
546e6d856e8Smickey return (error);
547d9a5f17fSdrahn
548d9a5f17fSdrahn cmdp = sc->sc_dmacmd;
5494f08448bSdrahn cmd = (flags & WDC_DMA_READ) ? DBDMA_CMD_IN_MORE : DBDMA_CMD_OUT_MORE;
550d9a5f17fSdrahn
551e6d856e8Smickey for (i = 0; i < sc->sc_dmamap->dm_nsegs; i++, cmdp++) {
552e6d856e8Smickey if (i + 1 == sc->sc_dmamap->dm_nsegs)
5534f08448bSdrahn cmd = (flags & WDC_DMA_READ) ? DBDMA_CMD_IN_LAST :
5544f08448bSdrahn DBDMA_CMD_OUT_LAST;
555bf1f8dcbSdrahn
556e6d856e8Smickey DBDMA_BUILD(cmdp, cmd, 0, sc->sc_dmamap->dm_segs[i].ds_len,
557e6d856e8Smickey sc->sc_dmamap->dm_segs[i].ds_addr,
558d9a5f17fSdrahn DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER);
559e6d856e8Smickey }
560d9a5f17fSdrahn
561d9a5f17fSdrahn DBDMA_BUILD(cmdp, DBDMA_CMD_STOP, 0, 0, 0,
562d9a5f17fSdrahn DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER);
563d9a5f17fSdrahn
564d9a5f17fSdrahn return 0;
565d9a5f17fSdrahn }
566d9a5f17fSdrahn
567e6d856e8Smickey void
wdc_obio_dma_start(void * v,int channel,int drive)568bf1f8dcbSdrahn wdc_obio_dma_start(void *v, int channel, int drive)
569d9a5f17fSdrahn {
570d9a5f17fSdrahn struct wdc_obio_softc *sc = v;
571d9a5f17fSdrahn
572e6d856e8Smickey dbdma_start(sc->sc_dmareg, sc->sc_dbdma);
573d9a5f17fSdrahn }
574d9a5f17fSdrahn
575e6d856e8Smickey int
wdc_obio_dma_finish(void * v,int channel,int drive,int force)576576378a2Smiod wdc_obio_dma_finish(void *v, int channel, int drive, int force)
577d9a5f17fSdrahn {
578d9a5f17fSdrahn struct wdc_obio_softc *sc = v;
579d9a5f17fSdrahn
580d9a5f17fSdrahn dbdma_stop(sc->sc_dmareg);
581e6d856e8Smickey bus_dmamap_unload(sc->sc_dmat, sc->sc_dmamap);
582d9a5f17fSdrahn return 0;
583d9a5f17fSdrahn }
584d9a5f17fSdrahn
585d9a5f17fSdrahn /* read register code
586d9a5f17fSdrahn * this allows the registers to be spaced by 0x10, instead of 0x1.
587d9a5f17fSdrahn * mac hardware (obio) requires this.
588d9a5f17fSdrahn */
589d9a5f17fSdrahn
590d9a5f17fSdrahn u_int8_t
wdc_obio_read_reg(struct channel_softc * chp,enum wdc_regs reg)591dde48f7eSdrahn wdc_obio_read_reg(struct channel_softc *chp, enum wdc_regs reg)
592d9a5f17fSdrahn {
593d9a5f17fSdrahn #ifdef DIAGNOSTIC
594d9a5f17fSdrahn if (reg & _WDC_WRONLY) {
5956a1aaa0fSderaadt printf ("wdc_obio_read_reg: reading from a write-only register %d\n", reg);
596d9a5f17fSdrahn }
597d9a5f17fSdrahn #endif
598d9a5f17fSdrahn
599d9a5f17fSdrahn if (reg & _WDC_AUX)
600d9a5f17fSdrahn return (bus_space_read_1(chp->ctl_iot, chp->ctl_ioh,
601d9a5f17fSdrahn (reg & _WDC_REGMASK) << 4));
602d9a5f17fSdrahn else
603d9a5f17fSdrahn return (bus_space_read_1(chp->cmd_iot, chp->cmd_ioh,
604d9a5f17fSdrahn (reg & _WDC_REGMASK) << 4));
605d9a5f17fSdrahn }
606d9a5f17fSdrahn
607d9a5f17fSdrahn
608d9a5f17fSdrahn void
wdc_obio_write_reg(struct channel_softc * chp,enum wdc_regs reg,u_int8_t val)609bf1f8dcbSdrahn wdc_obio_write_reg(struct channel_softc *chp, enum wdc_regs reg, u_int8_t val)
610d9a5f17fSdrahn {
611d9a5f17fSdrahn #ifdef DIAGNOSTIC
612d9a5f17fSdrahn if (reg & _WDC_RDONLY) {
6136a1aaa0fSderaadt printf ("wdc_obio_write_reg: writing to a read-only register %d\n", reg);
614d9a5f17fSdrahn }
615d9a5f17fSdrahn #endif
616d9a5f17fSdrahn
617d9a5f17fSdrahn if (reg & _WDC_AUX)
618d9a5f17fSdrahn bus_space_write_1(chp->ctl_iot, chp->ctl_ioh,
619d9a5f17fSdrahn (reg & _WDC_REGMASK) << 4, val);
620d9a5f17fSdrahn else
621d9a5f17fSdrahn bus_space_write_1(chp->cmd_iot, chp->cmd_ioh,
622d9a5f17fSdrahn (reg & _WDC_REGMASK) << 4, val);
623d9a5f17fSdrahn }
624