mpc5200_ata.c revision 1.1 1 1.1 rkujawa /* $NetBSD: mpc5200_ata.c,v 1.1 2026/06/27 13:28:35 rkujawa Exp $ */
2 1.1 rkujawa
3 1.1 rkujawa /*-
4 1.1 rkujawa * Copyright (c) 2008, 2026 The NetBSD Foundation, Inc.
5 1.1 rkujawa * All rights reserved.
6 1.1 rkujawa *
7 1.1 rkujawa * This code is derived from software contributed to The NetBSD Foundation
8 1.1 rkujawa * by Radoslaw Kujawa and Robert Swindells.
9 1.1 rkujawa *
10 1.1 rkujawa * Redistribution and use in source and binary forms, with or without
11 1.1 rkujawa * modification, are permitted provided that the following conditions
12 1.1 rkujawa * are met:
13 1.1 rkujawa * 1. Redistributions of source code must retain the above copyright
14 1.1 rkujawa * notice, this list of conditions and the following disclaimer.
15 1.1 rkujawa * 2. Redistributions in binary form must reproduce the above copyright
16 1.1 rkujawa * notice, this list of conditions and the following disclaimer in the
17 1.1 rkujawa * documentation and/or other materials provided with the distribution.
18 1.1 rkujawa *
19 1.1 rkujawa * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 1.1 rkujawa * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.1 rkujawa * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.1 rkujawa * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 1.1 rkujawa * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 1.1 rkujawa * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 1.1 rkujawa * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 1.1 rkujawa * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.1 rkujawa * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.1 rkujawa * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.1 rkujawa * POSSIBILITY OF SUCH DAMAGE.
30 1.1 rkujawa */
31 1.1 rkujawa
32 1.1 rkujawa /*
33 1.1 rkujawa * Driver for the Freescale MPC5200B on-chip ATA Controller, in PIO mode.
34 1.1 rkujawa *
35 1.1 rkujawa * This is a reworking of Robert Swindells' MPC5200 IDE driver (mpc5200_ide.c).
36 1.1 rkujawa *
37 1.1 rkujawa * DMA is deliberately not implemented. The MPC5200B can ONLY reach memory
38 1.1 rkujawa * from the ATA FIFO through the BestComm SDMA engine, and that path is both
39 1.1 rkujawa * documented-broken and widely reported to silently corrupt data when caches
40 1.1 rkujawa * are on.
41 1.1 rkujawa */
42 1.1 rkujawa
43 1.1 rkujawa #include <sys/cdefs.h>
44 1.1 rkujawa __KERNEL_RCSID(0, "$NetBSD: mpc5200_ata.c,v 1.1 2026/06/27 13:28:35 rkujawa Exp $");
45 1.1 rkujawa
46 1.1 rkujawa #include <sys/param.h>
47 1.1 rkujawa #include <sys/systm.h>
48 1.1 rkujawa #include <sys/device.h>
49 1.1 rkujawa #include <sys/bus.h>
50 1.1 rkujawa
51 1.1 rkujawa #include <sys/endian.h>
52 1.1 rkujawa
53 1.1 rkujawa #include <machine/intr.h>
54 1.1 rkujawa
55 1.1 rkujawa #include <dev/ata/atavar.h>
56 1.1 rkujawa #include <dev/ic/wdcvar.h>
57 1.1 rkujawa
58 1.1 rkujawa #include <powerpc/mpc5200/obiovar.h>
59 1.1 rkujawa #include <powerpc/mpc5200/cdmvar.h>
60 1.1 rkujawa #include <powerpc/mpc5200/mpc5200reg.h>
61 1.1 rkujawa #include <powerpc/mpc5200/mpc5200_atareg.h>
62 1.1 rkujawa #include <powerpc/mpc5200/mpc5200_atavar.h>
63 1.1 rkujawa
64 1.1 rkujawa /*
65 1.1 rkujawa * The ATA controller's INTRQ is hardwired to SIU peripheral source 7
66 1.1 rkujawa */
67 1.1 rkujawa #define MPCATA_PERIPH_SOURCE 7
68 1.1 rkujawa #define MPCATA_IRQ (64 + MPCATA_PERIPH_SOURCE)
69 1.1 rkujawa
70 1.1 rkujawa /* Highest PIO mode the controller and this driver support. */
71 1.1 rkujawa #define MPCATA_PIO_MAX 4
72 1.1 rkujawa
73 1.1 rkujawa /*
74 1.1 rkujawa * Transceiver propagation delay added to the read-strobe widths (t2_8/t2_16),
75 1.1 rkujawa */
76 1.1 rkujawa #define MPCATA_XCVR_PROP_NS 10
77 1.1 rkujawa
78 1.1 rkujawa static int mpcata_match(device_t, cfdata_t, void *);
79 1.1 rkujawa static void mpcata_attach(device_t, device_t, void *);
80 1.1 rkujawa
81 1.1 rkujawa static void mpcata_set_modes(struct ata_channel *);
82 1.1 rkujawa static void mpcata_program_pio(struct mpcata_softc *, int);
83 1.1 rkujawa static void mpcata_datain(struct ata_channel *, int, void *, size_t);
84 1.1 rkujawa static void mpcata_dataout(struct ata_channel *, int, void *, size_t);
85 1.1 rkujawa
86 1.1 rkujawa CFATTACH_DECL_NEW(mpcata, sizeof(struct mpcata_softc),
87 1.1 rkujawa mpcata_match, mpcata_attach, NULL, NULL);
88 1.1 rkujawa
89 1.1 rkujawa /*
90 1.1 rkujawa * ATA-4 PIO-mode minimum bus timings
91 1.1 rkujawa */
92 1.1 rkujawa static const struct mpcata_pio_timing {
93 1.1 rkujawa uint16_t t0; /* cycle time */
94 1.1 rkujawa uint16_t t1; /* address setup to DIOR/DIOW */
95 1.1 rkujawa uint16_t t2_8; /* 8-bit DIOR/DIOW pulse width */
96 1.1 rkujawa uint16_t t2_16; /* 16-bit DIOR/DIOW pulse width */
97 1.1 rkujawa uint16_t t4; /* DIOW data hold */
98 1.1 rkujawa uint16_t ta; /* IORDY setup */
99 1.1 rkujawa } mpcata_pio_timing[] = {
100 1.1 rkujawa { 600, 70, 290, 165, 30, 35 }, /* PIO mode 0 */
101 1.1 rkujawa { 383, 50, 290, 125, 20, 35 }, /* PIO mode 1 */
102 1.1 rkujawa { 240, 30, 290, 100, 15, 35 }, /* PIO mode 2 */
103 1.1 rkujawa { 180, 30, 80, 80, 10, 35 }, /* PIO mode 3 */
104 1.1 rkujawa { 120, 25, 70, 70, 10, 35 }, /* PIO mode 4 */
105 1.1 rkujawa };
106 1.1 rkujawa
107 1.1 rkujawa static int
108 1.1 rkujawa mpcata_match(device_t parent, cfdata_t cf, void *aux)
109 1.1 rkujawa {
110 1.1 rkujawa struct obio_attach_args *oba = aux;
111 1.1 rkujawa
112 1.1 rkujawa if (strcmp(oba->obio_name, "ata") == 0)
113 1.1 rkujawa return 1;
114 1.1 rkujawa
115 1.1 rkujawa return 0;
116 1.1 rkujawa }
117 1.1 rkujawa
118 1.1 rkujawa static void
119 1.1 rkujawa mpcata_attach(device_t parent, device_t self, void *aux)
120 1.1 rkujawa {
121 1.1 rkujawa struct mpcata_softc *sc = device_private(self);
122 1.1 rkujawa struct obio_attach_args *oba = aux;
123 1.1 rkujawa struct ata_channel *chp = &sc->sc_channel;
124 1.1 rkujawa struct wdc_regs *wdr = &sc->sc_wdc_regs;
125 1.1 rkujawa bus_addr_t addr;
126 1.1 rkujawa bus_size_t size;
127 1.1 rkujawa int irq, ist, i;
128 1.1 rkujawa
129 1.1 rkujawa aprint_naive("\n");
130 1.1 rkujawa aprint_normal(": MPC5200 ATA controller\n");
131 1.1 rkujawa
132 1.1 rkujawa sc->sc_iot = oba->obio_bst;
133 1.1 rkujawa
134 1.1 rkujawa /*
135 1.1 rkujawa * Map the whole ATA register block.
136 1.1 rkujawa */
137 1.1 rkujawa addr = oba->obio_addr != 0 ? oba->obio_addr :
138 1.1 rkujawa (MPC5200_MBAR_DEFAULT + MPC5200_REG_ATA);
139 1.1 rkujawa size = oba->obio_size != 0 ? oba->obio_size : ATA_REG_SIZE;
140 1.1 rkujawa if (bus_space_map(sc->sc_iot, addr, size, 0, &sc->sc_ioh) != 0) {
141 1.1 rkujawa aprint_error_dev(self, "can't map registers\n");
142 1.1 rkujawa return;
143 1.1 rkujawa }
144 1.1 rkujawa
145 1.1 rkujawa sc->sc_wdcdev.sc_atac.atac_dev = self;
146 1.1 rkujawa sc->sc_wdcdev.regs = wdr;
147 1.1 rkujawa
148 1.1 rkujawa /*
149 1.1 rkujawa * Lay out the wdc command/control handles.
150 1.1 rkujawa */
151 1.1 rkujawa wdr->cmd_iot = wdr->ctl_iot = sc->sc_iot;
152 1.1 rkujawa wdr->cmd_baseioh = sc->sc_ioh;
153 1.1 rkujawa wdr->cmd_ios = size;
154 1.1 rkujawa for (i = 0; i < WDC_NREG; i++) {
155 1.1 rkujawa if (bus_space_subregion(wdr->cmd_iot, sc->sc_ioh,
156 1.1 rkujawa ATA_DRIVE_BASE + i * ATA_DRIVE_STRIDE,
157 1.1 rkujawa (i == 0) ? 2 : 1, &wdr->cmd_iohs[i]) != 0) {
158 1.1 rkujawa aprint_error_dev(self, "can't subregion taskfile\n");
159 1.1 rkujawa goto fail;
160 1.1 rkujawa }
161 1.1 rkujawa }
162 1.1 rkujawa if (bus_space_subregion(wdr->ctl_iot, sc->sc_ioh, ATA_DRIVE_CTRL, 1,
163 1.1 rkujawa &wdr->ctl_ioh) != 0) {
164 1.1 rkujawa aprint_error_dev(self, "can't subregion control register\n");
165 1.1 rkujawa goto fail;
166 1.1 rkujawa }
167 1.1 rkujawa wdc_init_shadow_regs(wdr);
168 1.1 rkujawa
169 1.1 rkujawa /* Fill in the wdc/channel description. */
170 1.1 rkujawa sc->sc_wdcdev.sc_atac.atac_cap = ATAC_CAP_DATA16;
171 1.1 rkujawa sc->sc_wdcdev.sc_atac.atac_pio_cap = MPCATA_PIO_MAX;
172 1.1 rkujawa sc->sc_wdcdev.sc_atac.atac_channels = sc->sc_chanarray;
173 1.1 rkujawa sc->sc_wdcdev.sc_atac.atac_nchannels = 1;
174 1.1 rkujawa sc->sc_wdcdev.sc_atac.atac_set_modes = mpcata_set_modes;
175 1.1 rkujawa sc->sc_wdcdev.wdc_maxdrives = 2;
176 1.1 rkujawa /* The 16-bit data register is byte-swapped relative to the host. */
177 1.1 rkujawa sc->sc_wdcdev.datain_pio = mpcata_datain;
178 1.1 rkujawa sc->sc_wdcdev.dataout_pio = mpcata_dataout;
179 1.1 rkujawa
180 1.1 rkujawa sc->sc_chanarray[0] = chp;
181 1.1 rkujawa chp->ch_channel = 0;
182 1.1 rkujawa chp->ch_atac = &sc->sc_wdcdev.sc_atac;
183 1.1 rkujawa
184 1.1 rkujawa /*
185 1.1 rkujawa * Bring the host state machine to a known state
186 1.1 rkujawa */
187 1.1 rkujawa bus_space_write_4(sc->sc_iot, sc->sc_ioh, ATA_CONFIG,
188 1.1 rkujawa ATA_CONFIG_SMR | ATA_CONFIG_FR);
189 1.1 rkujawa delay(10);
190 1.1 rkujawa mpcata_program_pio(sc, 0);
191 1.1 rkujawa bus_space_write_4(sc->sc_iot, sc->sc_ioh, ATA_CONFIG,
192 1.1 rkujawa ATA_CONFIG_IE | ATA_CONFIG_IORDY);
193 1.1 rkujawa /* Clear any latched host read/write error flags (write-1-to-clear). */
194 1.1 rkujawa bus_space_write_4(sc->sc_iot, sc->sc_ioh, ATA_HOST_STATUS,
195 1.1 rkujawa ATA_HOST_STATUS_RERR | ATA_HOST_STATUS_WERR);
196 1.1 rkujawa
197 1.1 rkujawa /*
198 1.1 rkujawa * Establish the drive interrupt.
199 1.1 rkujawa */
200 1.1 rkujawa if (!obio_decode_interrupt(oba->obio_node, 0, &irq, &ist)) {
201 1.1 rkujawa irq = MPCATA_IRQ;
202 1.1 rkujawa ist = IST_LEVEL;
203 1.1 rkujawa }
204 1.1 rkujawa sc->sc_ih = intr_establish(irq, ist, IPL_BIO, wdcintr, chp);
205 1.1 rkujawa if (sc->sc_ih == NULL) {
206 1.1 rkujawa aprint_error_dev(self,
207 1.1 rkujawa "can't establish irq %d, using polled I/O\n", irq);
208 1.1 rkujawa sc->sc_wdcdev.sc_atac.atac_cap |= ATAC_CAP_NOIRQ;
209 1.1 rkujawa } else {
210 1.1 rkujawa aprint_normal_dev(self, "interrupting at irq %d\n", irq);
211 1.1 rkujawa }
212 1.1 rkujawa
213 1.1 rkujawa wdcattach(chp);
214 1.1 rkujawa return;
215 1.1 rkujawa
216 1.1 rkujawa fail:
217 1.1 rkujawa bus_space_unmap(sc->sc_iot, sc->sc_ioh, size);
218 1.1 rkujawa }
219 1.1 rkujawa
220 1.1 rkujawa static uint8_t
221 1.1 rkujawa mpcata_ns_to_count(uint32_t ns, uint32_t ipb_hz, uint32_t extra_ns)
222 1.1 rkujawa {
223 1.1 rkujawa uint64_t period_ps = 1000000000000ULL / ipb_hz;
224 1.1 rkujawa uint64_t spec_ps = (uint64_t)(ns + extra_ns) * 1000;
225 1.1 rkujawa uint64_t count = (spec_ps + period_ps - 1) / period_ps;
226 1.1 rkujawa
227 1.1 rkujawa if (count > 255)
228 1.1 rkujawa count = 255;
229 1.1 rkujawa return (uint8_t)count;
230 1.1 rkujawa }
231 1.1 rkujawa
232 1.1 rkujawa /*
233 1.1 rkujawa * Program the host PIO timing registers for the given mode.
234 1.1 rkujawa */
235 1.1 rkujawa static void
236 1.1 rkujawa mpcata_program_pio(struct mpcata_softc *sc, int mode)
237 1.1 rkujawa {
238 1.1 rkujawa const struct mpcata_pio_timing *t;
239 1.1 rkujawa uint32_t ipb, pio1, pio2;
240 1.1 rkujawa uint8_t t0, t1, t2_8, t2_16, t4, ta;
241 1.1 rkujawa
242 1.1 rkujawa if (mode < 0 || mode > MPCATA_PIO_MAX)
243 1.1 rkujawa mode = 0;
244 1.1 rkujawa t = &mpcata_pio_timing[mode];
245 1.1 rkujawa ipb = mpc5200_cdm_get_ipb_freq(); /* never returns zero */
246 1.1 rkujawa
247 1.1 rkujawa t0 = mpcata_ns_to_count(t->t0, ipb, 0);
248 1.1 rkujawa t1 = mpcata_ns_to_count(t->t1, ipb, 0);
249 1.1 rkujawa t2_8 = mpcata_ns_to_count(t->t2_8, ipb, 2 * MPCATA_XCVR_PROP_NS);
250 1.1 rkujawa t2_16 = mpcata_ns_to_count(t->t2_16, ipb, 2 * MPCATA_XCVR_PROP_NS);
251 1.1 rkujawa t4 = mpcata_ns_to_count(t->t4, ipb, 0);
252 1.1 rkujawa ta = mpcata_ns_to_count(t->ta, ipb, 0);
253 1.1 rkujawa
254 1.1 rkujawa pio1 = ((uint32_t)t0 << 24) | ((uint32_t)t2_8 << 16) |
255 1.1 rkujawa ((uint32_t)t2_16 << 8);
256 1.1 rkujawa pio2 = ((uint32_t)t4 << 24) | ((uint32_t)t1 << 16) |
257 1.1 rkujawa ((uint32_t)ta << 8);
258 1.1 rkujawa
259 1.1 rkujawa bus_space_write_4(sc->sc_iot, sc->sc_ioh, ATA_PIO1, pio1);
260 1.1 rkujawa bus_space_write_4(sc->sc_iot, sc->sc_ioh, ATA_PIO2, pio2);
261 1.1 rkujawa sc->sc_pio_mode = mode;
262 1.1 rkujawa }
263 1.1 rkujawa
264 1.1 rkujawa static void
265 1.1 rkujawa mpcata_set_modes(struct ata_channel *chp)
266 1.1 rkujawa {
267 1.1 rkujawa struct mpcata_softc *sc = (struct mpcata_softc *)chp->ch_atac;
268 1.1 rkujawa int drive, mode = MPCATA_PIO_MAX;
269 1.1 rkujawa
270 1.1 rkujawa for (drive = 0; drive < sc->sc_wdcdev.wdc_maxdrives; drive++) {
271 1.1 rkujawa struct ata_drive_datas *drvp = &chp->ch_drive[drive];
272 1.1 rkujawa
273 1.1 rkujawa if (drvp->drive_type == ATA_DRIVET_NONE)
274 1.1 rkujawa continue;
275 1.1 rkujawa if (drvp->PIO_mode < mode)
276 1.1 rkujawa mode = drvp->PIO_mode;
277 1.1 rkujawa }
278 1.1 rkujawa
279 1.1 rkujawa mpcata_program_pio(sc, mode);
280 1.1 rkujawa
281 1.1 rkujawa for (drive = 0; drive < sc->sc_wdcdev.wdc_maxdrives; drive++) {
282 1.1 rkujawa struct ata_drive_datas *drvp = &chp->ch_drive[drive];
283 1.1 rkujawa
284 1.1 rkujawa if (drvp->drive_type == ATA_DRIVET_NONE)
285 1.1 rkujawa continue;
286 1.1 rkujawa drvp->PIO_mode = sc->sc_pio_mode;
287 1.1 rkujawa drvp->drive_flags &= ~(ATA_DRIVE_DMA | ATA_DRIVE_UDMA);
288 1.1 rkujawa }
289 1.1 rkujawa }
290 1.1 rkujawa
291 1.1 rkujawa /*
292 1.1 rkujawa * Surely this can be done in a more elegant way, but the controller is slow enough
293 1.1 rkujawa * that it doesn't matter.
294 1.1 rkujawa */
295 1.1 rkujawa static void
296 1.1 rkujawa mpcata_datain(struct ata_channel *chp, int flags, void *bf, size_t len)
297 1.1 rkujawa {
298 1.1 rkujawa struct wdc_regs *wdr = CHAN_TO_WDC_REGS(chp);
299 1.1 rkujawa uint16_t *p = bf;
300 1.1 rkujawa
301 1.1 rkujawa while (len >= sizeof(*p)) {
302 1.1 rkujawa *p++ = htole16(bus_space_read_2(wdr->cmd_iot,
303 1.1 rkujawa wdr->cmd_iohs[wd_data], 0));
304 1.1 rkujawa len -= sizeof(*p);
305 1.1 rkujawa }
306 1.1 rkujawa }
307 1.1 rkujawa
308 1.1 rkujawa static void
309 1.1 rkujawa mpcata_dataout(struct ata_channel *chp, int flags, void *bf, size_t len)
310 1.1 rkujawa {
311 1.1 rkujawa struct wdc_regs *wdr = CHAN_TO_WDC_REGS(chp);
312 1.1 rkujawa uint16_t *p = bf;
313 1.1 rkujawa
314 1.1 rkujawa while (len >= sizeof(*p)) {
315 1.1 rkujawa bus_space_write_2(wdr->cmd_iot, wdr->cmd_iohs[wd_data], 0,
316 1.1 rkujawa le16toh(*p++));
317 1.1 rkujawa len -= sizeof(*p);
318 1.1 rkujawa }
319 1.1 rkujawa }
320 1.1 rkujawa
321