xref: /netbsd-src/sys/arch/acorn32/podulebus/simide.c (revision d7cdbe2716e26606afe53001eb1aadea1d4c1e56)
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