1 /* $NetBSD: simide.c,v 1.33 2023/12/20 06:13:59 thorpej Exp $ */
2
3 /*
4 * Copyright (c) 1997-1998 Mark Brinicombe
5 * Copyright (c) 1997-1998 Causality Limited
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Mark Brinicombe
18 * for the NetBSD Project.
19 * 4. The name of the author may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 * Card driver and probe and attach functions to use generic IDE driver
34 * for the Simtec IDE podule
35 */
36
37 /*
38 * Thanks to Gareth Simpson, Simtec Electronics for providing
39 * the hardware information
40 */
41
42 #include <sys/cdefs.h>
43 __KERNEL_RCSID(0, "$NetBSD: simide.c,v 1.33 2023/12/20 06:13:59 thorpej Exp $");
44
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/conf.h>
48 #include <sys/device.h>
49 #include <sys/bus.h>
50
51 #include <machine/intr.h>
52 #include <machine/io.h>
53 #include <acorn32/podulebus/podulebus.h>
54 #include <acorn32/podulebus/simidereg.h>
55
56 #include <dev/ata/atavar.h>
57 #include <dev/ic/wdcreg.h>
58 #include <dev/ic/wdcvar.h>
59 #include <dev/podulebus/podules.h>
60
61
62 /*
63 * Simtec IDE podule device.
64 *
65 * This probes and attaches the top level Simtec IDE device to the podulebus.
66 * It then configures any children of the Simtec IDE device.
67 * The attach args specify whether it is configuring the primary or
68 * secondary channel.
69 * The children are expected to be wdc devices using simide attachments.
70 */
71
72 /*
73 * Simtec IDE card softc structure.
74 *
75 * Contains the device node, podule information and global information
76 * required by the driver such as the card version and the interrupt mask.
77 */
78
79 struct simide_softc {
80 struct wdc_softc sc_wdcdev; /* common wdc definitions */
81 struct ata_channel *sc_chanarray[2]; /* channels definition */
82 podule_t *sc_podule; /* Our podule info */
83 int sc_podule_number; /* Our podule number */
84 int sc_ctl_reg; /* Global ctl reg */
85 int sc_version; /* Card version */
86 bus_space_tag_t sc_ctliot; /* Bus tag */
87 bus_space_handle_t sc_ctlioh; /* control handle */
88 struct bus_space sc_tag; /* custom tag */
89 struct simide_channel {
90 struct ata_channel sc_channel; /* generic part */
91 irqhandler_t sc_ih; /* interrupt handler */
92 int sc_irqmask; /* IRQ mask for this channel */
93 } simide_channels[2];
94 struct wdc_regs sc_wdc_regs[2];
95 };
96
97 int simide_probe (device_t, cfdata_t, void *);
98 void simide_attach (device_t, device_t, void *);
99 void simide_shutdown (void *arg);
100 int simide_intr (void *arg);
101
102 CFATTACH_DECL_NEW(simide, sizeof(struct simide_softc),
103 simide_probe, simide_attach, NULL, NULL);
104
105
106 /*
107 * Define prototypes for custom bus space functions.
108 */
109
110 bs_rm_2_proto(simide);
111 bs_wm_2_proto(simide);
112
113 /*
114 * Create an array of address structures. These define the addresses and
115 * masks needed for the different channels.
116 *
117 * index = channel
118 */
119
120 struct {
121 u_int drive_registers;
122 u_int aux_register;
123 u_int irq_mask;
124 } simide_info[] = {
125 { PRIMARY_DRIVE_REGISTERS_POFFSET, PRIMARY_AUX_REGISTER_POFFSET,
126 CONTROL_PRIMARY_IRQ },
127 { SECONDARY_DRIVE_REGISTERS_POFFSET, SECONDARY_AUX_REGISTER_POFFSET,
128 CONTROL_SECONDARY_IRQ }
129 };
130
131 /*
132 * Card probe function
133 *
134 * Just match the manufacturer and podule ID's
135 */
136
137 int
simide_probe(device_t parent,cfdata_t cf,void * aux)138 simide_probe(device_t parent, cfdata_t cf, void *aux)
139 {
140 struct podule_attach_args *pa = (void *)aux;
141
142 return (pa->pa_product == PODULE_SIMTEC_IDE);
143 }
144
145 /*
146 * Card attach function
147 *
148 * Identify the card version and configure any children.
149 * Install a shutdown handler to kill interrupts on shutdown
150 */
151
152 void
simide_attach(device_t parent,device_t self,void * aux)153 simide_attach(device_t parent, device_t self, void *aux)
154 {
155 struct simide_softc *sc = device_private(self);
156 struct podule_attach_args *pa = (void *)aux;
157 int status;
158 u_int iobase;
159 int channel, i;
160 struct simide_channel *scp;
161 struct ata_channel *cp;
162 struct wdc_regs *wdr;
163 irqhandler_t *ihp;
164
165 /* Note the podule number and validate */
166 if (pa->pa_podule_number == -1)
167 panic("Podule has disappeared !");
168
169 sc->sc_wdcdev.sc_atac.atac_dev = self;
170 sc->sc_podule_number = pa->pa_podule_number;
171 sc->sc_podule = pa->pa_podule;
172 podules[sc->sc_podule_number].attached = 1;
173
174 sc->sc_wdcdev.regs = sc->sc_wdc_regs;
175
176 /*
177 * Ok we need our own bus tag as the register spacing
178 * is not the default.
179 *
180 * For the podulebus the bus tag cookie is the shift
181 * to apply to registers
182 * So duplicate the bus space tag and change the
183 * cookie.
184 *
185 * Also while we are at it replace the default
186 * read/write multiple short functions with
187 * optimised versions
188 */
189
190 sc->sc_tag = *pa->pa_iot;
191 sc->sc_tag.bs_cookie = (void *) DRIVE_REGISTER_SPACING_SHIFT;
192 sc->sc_tag.bs_rm_2 = simide_bs_rm_2;
193 sc->sc_tag.bs_wm_2 = simide_bs_wm_2;
194 sc->sc_ctliot = pa->pa_iot;
195
196 /* Obtain bus space handles for all the control registers */
197 if (bus_space_map(sc->sc_ctliot, pa->pa_podule->mod_base +
198 CONTROL_REGISTERS_POFFSET, CONTROL_REGISTER_SPACE, 0,
199 &sc->sc_ctlioh))
200 panic("%s: Cannot map control registers", device_xname(self));
201
202 /* Install a clean up handler to make sure IRQ's are disabled */
203 if (shutdownhook_establish(simide_shutdown, (void *)sc) == NULL)
204 panic("%s: Cannot install shutdown handler",
205 device_xname(self));
206
207 /* Set the interrupt info for this podule */
208 sc->sc_podule->irq_addr = pa->pa_podule->mod_base
209 + CONTROL_REGISTERS_POFFSET + (CONTROL_REGISTER_OFFSET << 2);
210 sc->sc_podule->irq_mask = STATUS_IRQ;
211
212 sc->sc_ctl_reg = 0;
213
214 status = bus_space_read_1(sc->sc_ctliot, sc->sc_ctlioh,
215 STATUS_REGISTER_OFFSET);
216
217 aprint_normal(":");
218 /* If any of the bits in STATUS_FAULT are zero then we have a fault. */
219 if ((status & STATUS_FAULT) != STATUS_FAULT)
220 aprint_normal(" card/cable fault (%02x) -", status);
221
222 if (!(status & STATUS_RESET))
223 aprint_normal(" (reset)");
224 if (!(status & STATUS_ADDR_TEST))
225 aprint_normal(" (addr)");
226 if (!(status & STATUS_CS_TEST))
227 aprint_normal(" (cs)");
228 if (!(status & STATUS_RW_TEST))
229 aprint_normal(" (rw)");
230
231 aprint_normal("\n");
232
233 /* Perhaps we should just abort at this point. */
234 /* if ((status & STATUS_FAULT) != STATUS_FAULT)
235 return;*/
236
237 /*
238 * Enable IDE, Obey IORDY and disabled slow mode
239 */
240 sc->sc_ctl_reg |= CONTROL_IDE_ENABLE | CONTROL_IORDY
241 | CONTROL_SLOW_MODE_OFF;
242 bus_space_write_1(sc->sc_ctliot, sc->sc_ctlioh,
243 CONTROL_REGISTER_OFFSET, sc->sc_ctl_reg);
244
245 /* Fill in wdc and channel infos */
246 sc->sc_wdcdev.sc_atac.atac_cap |= ATAC_CAP_DATA16;
247 sc->sc_wdcdev.sc_atac.atac_pio_cap = 0;
248 sc->sc_wdcdev.sc_atac.atac_channels = sc->sc_chanarray;
249 sc->sc_wdcdev.sc_atac.atac_nchannels = 2;
250 sc->sc_wdcdev.wdc_maxdrives = 2;
251 for (channel = 0 ; channel < 2; channel++) {
252 scp = &sc->simide_channels[channel];
253 sc->sc_chanarray[channel] = &scp->sc_channel;
254 cp = &scp->sc_channel;
255 wdr = &sc->sc_wdc_regs[channel];
256
257 cp->ch_channel = channel;
258 cp->ch_atac = &sc->sc_wdcdev.sc_atac;
259 wdr->cmd_iot = wdr->ctl_iot = &sc->sc_tag;
260 iobase = pa->pa_podule->mod_base;
261 if (bus_space_map(wdr->cmd_iot, iobase +
262 simide_info[channel].drive_registers,
263 DRIVE_REGISTERS_SPACE, 0, &wdr->cmd_baseioh))
264 continue;
265 for (i = 0; i < WDC_NREG; i++) {
266 if (bus_space_subregion(wdr->cmd_iot, wdr->cmd_baseioh,
267 i, i == 0 ? 4 : 1, &wdr->cmd_iohs[i]) != 0) {
268 bus_space_unmap(wdr->cmd_iot, wdr->cmd_baseioh,
269 DRIVE_REGISTERS_SPACE);
270 continue;
271 }
272 }
273 wdc_init_shadow_regs(wdr);
274 if (bus_space_map(wdr->ctl_iot, iobase +
275 simide_info[channel].aux_register, 4, 0, &wdr->ctl_ioh)) {
276 bus_space_unmap(wdr->cmd_iot, wdr->cmd_baseioh,
277 DRIVE_REGISTERS_SPACE);
278 continue;
279 }
280 /* Disable interrupts and clear any pending interrupts */
281 scp->sc_irqmask = simide_info[channel].irq_mask;
282 sc->sc_ctl_reg &= ~scp->sc_irqmask;
283 bus_space_write_1(sc->sc_ctliot, sc->sc_ctlioh,
284 CONTROL_REGISTER_OFFSET, sc->sc_ctl_reg);
285 ihp = &scp->sc_ih;
286 ihp->ih_func = simide_intr;
287 ihp->ih_arg = scp;
288 ihp->ih_level = IPL_BIO;
289 ihp->ih_name = "simide";
290 ihp->ih_maskaddr = pa->pa_podule->irq_addr;
291 ihp->ih_maskbits = scp->sc_irqmask;
292 if (irq_claim(sc->sc_podule->interrupt, ihp))
293 panic("%s: Cannot claim interrupt %d",
294 device_xname(self), sc->sc_podule->interrupt);
295 /* clear any pending interrupts and enable interrupts */
296 sc->sc_ctl_reg |= scp->sc_irqmask;
297 bus_space_write_1(sc->sc_ctliot, sc->sc_ctlioh,
298 CONTROL_REGISTER_OFFSET, sc->sc_ctl_reg);
299 wdcattach(cp);
300 }
301 }
302
303 /*
304 * Card shutdown function
305 *
306 * Called via do_shutdown_hooks() during kernel shutdown.
307 * Clear the cards's interrupt mask to stop any podule interrupts.
308 */
309
310 void
simide_shutdown(void * arg)311 simide_shutdown(void *arg)
312 {
313 struct simide_softc *sc = arg;
314
315 sc->sc_ctl_reg &= (CONTROL_PRIMARY_IRQ | CONTROL_SECONDARY_IRQ);
316
317 /* Disable card interrupts */
318 bus_space_write_1(sc->sc_ctliot, sc->sc_ctlioh,
319 CONTROL_REGISTER_OFFSET, sc->sc_ctl_reg);
320 }
321
322 /*
323 * Podule interrupt handler
324 *
325 * If the interrupt was from our card pass it on to the wdc interrupt handler
326 */
327 int
simide_intr(void * arg)328 simide_intr(void *arg)
329 {
330 struct simide_channel *scp = arg;
331 irqhandler_t *ihp = &scp->sc_ih;
332 volatile u_char *intraddr = (volatile u_char *)ihp->ih_maskaddr;
333
334 /* XXX - not bus space yet - should really be handled by podulebus */
335 if ((*intraddr) & ihp->ih_maskbits)
336 wdcintr(&scp->sc_channel);
337
338 return(0);
339 }
340