1 /* $NetBSD: kauai.c,v 1.26 2008/07/28 16:54:49 macallan Exp $ */
4 * Copyright (c) 2003 Tsubai Masanari. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: kauai.c,v 1.26 2008/07/28 16:54:49 macallan Exp $");
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/device.h>
35 #include <sys/malloc.h>
37 #include <uvm/uvm_extern.h>
39 #include <machine/bus.h>
40 #include <machine/pio.h>
42 #include <dev/ata/atareg.h>
43 #include <dev/ata/atavar.h>
44 #include <dev/ic/wdcvar.h>
46 #include <dev/ofw/openfirm.h>
48 #include <dev/pci/pcivar.h>
49 #include <dev/pci/pcireg.h>
50 #include <dev/pci/pcidevs.h>
52 #include <macppc/dev/dbdma.h>
54 #define WDC_REG_NPORTS 8
55 #define WDC_AUXREG_OFFSET 0x16
56 #define WDC_AUXREG_NPORTS 1
58 #define PIO_CONFIG_REG 0x200 /* PIO and DMA access timing */
59 #define DMA_CONFIG_REG 0x210 /* UDMA access timing */
62 struct wdc_softc sc_wdcdev
;
63 struct ata_channel
*sc_chanptr
;
64 struct ata_channel sc_channel
;
65 struct wdc_regs sc_wdc_regs
;
66 struct ata_queue sc_queue
;
67 dbdma_regmap_t
*sc_dmareg
;
68 dbdma_command_t
*sc_dmacmd
;
69 u_int sc_piotiming_r
[2];
70 u_int sc_piotiming_w
[2];
71 u_int sc_dmatiming_r
[2];
72 u_int sc_dmatiming_w
[2];
73 void (*sc_calc_timing
)(struct kauai_softc
*, int);
76 static int kauai_match(device_t
, cfdata_t
, void *);
77 static void kauai_attach(device_t
, device_t
, void *);
78 static int kauai_dma_init(void *, int, int, void *, size_t, int);
79 static void kauai_dma_start(void *, int, int);
80 static int kauai_dma_finish(void *, int, int, int);
81 static void kauai_set_modes(struct ata_channel
*);
82 static void calc_timing_kauai(struct kauai_softc
*, int);
84 CFATTACH_DECL_NEW(kauai
, sizeof(struct kauai_softc
),
85 kauai_match
, kauai_attach
, NULL
, NULL
);
88 kauai_match(device_t parent
, cfdata_t match
, void *aux
)
90 struct pci_attach_args
*pa
= aux
;
92 if (PCI_VENDOR(pa
->pa_id
) == PCI_VENDOR_APPLE
) {
93 switch (PCI_PRODUCT(pa
->pa_id
)) {
94 case PCI_PRODUCT_APPLE_KAUAI
:
95 case PCI_PRODUCT_APPLE_UNINORTH_ATA
:
96 case PCI_PRODUCT_APPLE_INTREPID2_ATA
:
97 case PCI_PRODUCT_APPLE_SHASTA_ATA
:
106 kauai_attach(device_t parent
, device_t self
, void *aux
)
108 struct kauai_softc
*sc
= device_private(self
);
109 struct pci_attach_args
*pa
= aux
;
110 struct ata_channel
*chp
= &sc
->sc_channel
;
111 struct wdc_regs
*wdr
;
112 pci_intr_handle_t ih
;
113 paddr_t regbase
, dmabase
;
116 sc
->sc_wdcdev
.sc_atac
.atac_dev
= self
;
118 sc
->sc_dmacmd
= dbdma_alloc(sizeof(dbdma_command_t
) * 20);
120 node
= pcidev_to_ofdev(pa
->pa_pc
, pa
->pa_tag
);
122 aprint_error(": cannot find kauai node\n");
126 if (OF_getprop(node
, "assigned-addresses", reg
, sizeof reg
) < 12) {
127 aprint_error(": cannot get address property\n");
130 regbase
= reg
[2] + 0x2000;
131 dmabase
= reg
[2] + 0x1000;
134 * XXX PCI_INTERRUPT_REG seems to be wired to 0.
135 * XXX So use fixed intrpin and intrline values.
137 if (pci_conf_read(pa
->pa_pc
, pa
->pa_tag
, PCI_INTERRUPT_REG
) == 0) {
139 pa
->pa_intrline
= 39;
142 if (pci_intr_map(pa
, &ih
)) {
143 aprint_error(": unable to map interrupt\n");
146 aprint_normal(": interrupting at %s\n", pci_intr_string(pa
->pa_pc
, ih
));
148 sc
->sc_wdcdev
.regs
= wdr
= &sc
->sc_wdc_regs
;
150 wdr
->cmd_iot
= wdr
->ctl_iot
= pa
->pa_memt
;
152 if (bus_space_map(wdr
->cmd_iot
, regbase
, WDC_REG_NPORTS
<< 4, 0,
153 &wdr
->cmd_baseioh
) ||
154 bus_space_subregion(wdr
->cmd_iot
, wdr
->cmd_baseioh
,
155 WDC_AUXREG_OFFSET
<< 4, 1, &wdr
->ctl_ioh
)) {
156 aprint_error_dev(self
, "couldn't map registers\n");
159 for (i
= 0; i
< WDC_NREG
; i
++) {
160 if (bus_space_subregion(wdr
->cmd_iot
, wdr
->cmd_baseioh
, i
<< 4,
161 i
== 0 ? 4 : 1, &wdr
->cmd_iohs
[i
]) != 0) {
162 bus_space_unmap(wdr
->cmd_iot
, wdr
->cmd_baseioh
,
163 WDC_REG_NPORTS
<< 4);
164 aprint_error_dev(self
,
165 "couldn't subregion registers\n");
170 if (pci_intr_establish(pa
->pa_pc
, ih
, IPL_BIO
, wdcintr
, chp
) == NULL
) {
171 aprint_error_dev(self
, "unable to establish interrupt\n");
176 sc
->sc_wdcdev
.sc_atac
.atac_pio_cap
= 4;
177 sc
->sc_wdcdev
.sc_atac
.atac_dma_cap
= 2;
178 sc
->sc_wdcdev
.sc_atac
.atac_udma_cap
= 5;
179 sc
->sc_wdcdev
.sc_atac
.atac_cap
|= ATAC_CAP_DATA16
;
180 sc
->sc_wdcdev
.sc_atac
.atac_cap
|= ATAC_CAP_DMA
| ATAC_CAP_UDMA
;
181 sc
->sc_chanptr
= chp
;
182 sc
->sc_wdcdev
.sc_atac
.atac_channels
= &sc
->sc_chanptr
;
183 sc
->sc_wdcdev
.sc_atac
.atac_nchannels
= 1;
184 sc
->sc_wdcdev
.dma_arg
= sc
;
185 sc
->sc_wdcdev
.dma_init
= kauai_dma_init
;
186 sc
->sc_wdcdev
.dma_start
= kauai_dma_start
;
187 sc
->sc_wdcdev
.dma_finish
= kauai_dma_finish
;
188 sc
->sc_wdcdev
.sc_atac
.atac_set_modes
= kauai_set_modes
;
189 sc
->sc_calc_timing
= calc_timing_kauai
;
190 sc
->sc_dmareg
= (void *)dmabase
;
193 chp
->ch_atac
= &sc
->sc_wdcdev
.sc_atac
;
194 chp
->ch_queue
= &sc
->sc_queue
;
196 wdc_init_shadow_regs(chp
);
202 kauai_set_modes(struct ata_channel
*chp
)
204 struct kauai_softc
*sc
= (void *)chp
->ch_atac
;
205 struct wdc_regs
*wdr
= CHAN_TO_WDC_REGS(chp
);
206 struct ata_drive_datas
*drvp0
= &chp
->ch_drive
[0];
207 struct ata_drive_datas
*drvp1
= &chp
->ch_drive
[1];
208 struct ata_drive_datas
*drvp
;
211 if ((drvp0
->drive_flags
& DRIVE
) && (drvp1
->drive_flags
& DRIVE
)) {
212 drvp0
->PIO_mode
= drvp1
->PIO_mode
=
213 min(drvp0
->PIO_mode
, drvp1
->PIO_mode
);
216 for (drive
= 0; drive
< 2; drive
++) {
217 drvp
= &chp
->ch_drive
[drive
];
218 if (drvp
->drive_flags
& DRIVE
) {
219 (*sc
->sc_calc_timing
)(sc
, drive
);
220 bus_space_write_4(wdr
->cmd_iot
, wdr
->cmd_baseioh
,
221 PIO_CONFIG_REG
, sc
->sc_piotiming_r
[drive
]);
222 bus_space_write_4(wdr
->cmd_iot
, wdr
->cmd_baseioh
,
223 DMA_CONFIG_REG
, sc
->sc_dmatiming_r
[drive
]);
229 * IDE transfer timings
231 static const u_int pio_timing_kauai
[] = { /* 0xff000fff */
232 0x08000a92, /* Mode 0 */
238 static const u_int dma_timing_kauai
[] = { /* 0x00fff000 */
239 0x00618000, /* Mode 0 */
243 static const u_int udma_timing_kauai
[] = { /* 0x0000ffff */
244 0x000070c0, /* Mode 0 */
253 * Timing calculation for Kauai.
256 calc_timing_kauai(struct kauai_softc
*sc
, int drive
)
258 struct ata_channel
*chp
= &sc
->sc_channel
;
259 struct ata_drive_datas
*drvp
= &chp
->ch_drive
[drive
];
260 int piomode
= drvp
->PIO_mode
;
261 int dmamode
= drvp
->DMA_mode
;
262 int udmamode
= drvp
->UDMA_mode
;
263 u_int pioconf
, dmaconf
;
265 pioconf
= pio_timing_kauai
[piomode
];
268 if (drvp
->drive_flags
& DRIVE_DMA
)
269 dmaconf
|= dma_timing_kauai
[dmamode
];
270 if (drvp
->drive_flags
& DRIVE_UDMA
)
271 dmaconf
|= udma_timing_kauai
[udmamode
];
273 if (drvp
->drive_flags
& DRIVE_UDMA
)
276 sc
->sc_piotiming_r
[drive
] = sc
->sc_piotiming_w
[drive
] = pioconf
;
277 sc
->sc_dmatiming_r
[drive
] = sc
->sc_dmatiming_w
[drive
] = dmaconf
;
281 kauai_dma_init(void *v
, int channel
, int drive
, void *databuf
,
282 size_t datalen
, int flags
)
284 struct kauai_softc
*sc
= v
;
285 dbdma_command_t
*cmdp
= sc
->sc_dmacmd
;
286 struct ata_channel
*chp
= &sc
->sc_channel
;
287 struct wdc_regs
*wdr
= CHAN_TO_WDC_REGS(chp
);
288 vaddr_t va
= (vaddr_t
)databuf
;
289 int read
= flags
& WDC_DMA_READ
;
290 int cmd
= read
? DBDMA_CMD_IN_MORE
: DBDMA_CMD_OUT_MORE
;
293 bus_space_write_4(wdr
->cmd_iot
, wdr
->cmd_baseioh
, DMA_CONFIG_REG
,
294 read
? sc
->sc_dmatiming_r
[drive
] : sc
->sc_dmatiming_w
[drive
]);
295 bus_space_read_4(wdr
->cmd_iot
, wdr
->cmd_baseioh
, DMA_CONFIG_REG
);
297 offset
= va
& PGOFSET
;
299 /* if va is not page-aligned, setup the first page */
301 int rest
= PAGE_SIZE
- offset
; /* the rest of the page */
303 if (datalen
> rest
) { /* if continues to next page */
304 DBDMA_BUILD(cmdp
, cmd
, 0, rest
, vtophys(va
),
305 DBDMA_INT_NEVER
, DBDMA_WAIT_NEVER
,
313 /* now va is page-aligned */
314 while (datalen
> PAGE_SIZE
) {
315 DBDMA_BUILD(cmdp
, cmd
, 0, PAGE_SIZE
, vtophys(va
),
316 DBDMA_INT_NEVER
, DBDMA_WAIT_NEVER
, DBDMA_BRANCH_NEVER
);
317 datalen
-= PAGE_SIZE
;
322 /* the last page (datalen <= PAGE_SIZE here) */
323 cmd
= read
? DBDMA_CMD_IN_LAST
: DBDMA_CMD_OUT_LAST
;
324 DBDMA_BUILD(cmdp
, cmd
, 0, datalen
, vtophys(va
),
325 DBDMA_INT_NEVER
, DBDMA_WAIT_NEVER
, DBDMA_BRANCH_NEVER
);
328 DBDMA_BUILD(cmdp
, DBDMA_CMD_STOP
, 0, 0, 0,
329 DBDMA_INT_NEVER
, DBDMA_WAIT_NEVER
, DBDMA_BRANCH_NEVER
);
335 kauai_dma_start(void *v
, int channel
, int drive
)
337 struct kauai_softc
*sc
= v
;
339 dbdma_start(sc
->sc_dmareg
, sc
->sc_dmacmd
);
343 kauai_dma_finish(void *v
, int channel
, int drive
, int read
)
345 struct kauai_softc
*sc
= v
;
347 dbdma_stop(sc
->sc_dmareg
);