xref: /netbsd-src/sys/arch/acorn32/mainbus/pioc.c (revision a5847cc334d9a7029f6352b847e9e8d71a0f9e0c)
1 /*	$NetBSD: pioc.c,v 1.17 2011/07/19 15:59:53 dyoung Exp $	*/
2 
3 /*
4  * Copyright (c) 1997 Mark Brinicombe.
5  * Copyright (c) 1997 Causality Limited.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by Mark Brinicombe.
19  * 4. The name of the company nor the name of the author may be used to
20  *    endorse or promote products derived from this software without specific
21  *    prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
24  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  * Peripheral I/O controller - wd, fd, com, lpt Combo chip
36  *
37  * Parent device for combo chip I/O drivers
38  * Currently supports the SMC FDC37GT66[56] controllers.
39  */
40 
41 /*#define PIOC_DEBUG*/
42 
43 #include <sys/cdefs.h>
44 __KERNEL_RCSID(0, "$NetBSD: pioc.c,v 1.17 2011/07/19 15:59:53 dyoung Exp $");
45 
46 #include <sys/param.h>
47 #include <sys/systm.h>
48 #include <sys/kernel.h>
49 #include <sys/device.h>
50 #include <sys/bus.h>
51 
52 #include <arm/mainbus/mainbus.h>
53 #include <acorn32/mainbus/piocreg.h>
54 #include <acorn32/mainbus/piocvar.h>
55 
56 #include "locators.h"
57 
58 /*
59  * PIOC device.
60  *
61  * This probes and attaches the top level pioc device.
62  * It then configures any children of the pioc device.
63  */
64 
65 /*
66  * pioc softc structure.
67  *
68  * Contains the device node, bus space tag, handle and address along with
69  * other global information such as id and config registers.
70  */
71 
72 struct pioc_softc {
73 	struct device		sc_dev;			/* device node */
74 	bus_space_tag_t		sc_iot;			/* bus tag */
75 	bus_space_handle_t	sc_ioh;			/* bus handle */
76 	bus_addr_t		sc_iobase;		/* IO base address */
77  	int			sc_id;			/* chip ID */
78 	int			sc_config[PIOC_CM_REGS];/* config regs */
79 };
80 
81 /*
82  * The pioc device is a parent to the com device.
83  * This means that it needs to provide a bus space tag for
84  * a serial console.
85  *
86  * XXX - This is not fully tested yet.
87  */
88 
89 extern struct bus_space mainbus_bs_tag;
90 bus_space_tag_t comconstag = &mainbus_bs_tag;
91 
92 /* Prototypes for functions */
93 
94 static int  piocmatch(device_t, cfdata_t, void *);
95 static void piocattach(device_t, device_t, void *);
96 static int  piocprint(void *aux, const char *name);
97 #if 0
98 static int  piocsearch(device_t, cfdata_t, void *);
99 #endif
100 static int  piocsubmatch(device_t, cfdata_t,
101 			      const int *, void *);
102 static void piocgetid(bus_space_tag_t iot, bus_space_handle_t ioh,
103 			      int config_entry, int *id, int *revision);
104 
105 /* device attach and driver structure */
106 
107 CFATTACH_DECL(pioc, sizeof(struct pioc_softc),
108     piocmatch, piocattach, NULL, NULL);
109 
110 /*
111  * void piocgetid(bus_space_tag_t iot, bus_space_handle_t ioh,
112  *                int config_entry, int *id, int *revision)
113  *
114  * Enter config mode and return the id and revision
115  */
116 
117 static void
118 piocgetid(bus_space_tag_t iot, bus_space_handle_t ioh, int config_entry, int *id, int *revision)
119 {
120 	/*
121 	 * Put the chip info configuration mode and read the ID and revision
122 	 */
123 	bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, config_entry);
124 	bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, config_entry);
125 
126 	bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, PIOC_CM_CRD);
127 	*id = bus_space_read_1(iot, ioh, PIOC_CM_DATA_REG);
128 	bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, PIOC_CM_CRE);
129 	*revision = bus_space_read_1(iot, ioh, PIOC_CM_DATA_REG);
130 
131 	bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, PIOC_CM_EXIT);
132 }
133 
134 /*
135  * int piocmatch(device_t parent, cfdata_t cf, void *aux)
136  *
137  * Put the controller into config mode and probe the ID to see if
138  * we recognise it.
139  *
140  * XXX - INTRUSIVE PROBE
141  */
142 
143 static int
144 piocmatch(device_t parent, cfdata_t cf, void *aux)
145 {
146 	struct mainbus_attach_args *mb = aux;
147 	bus_space_tag_t iot;
148 	bus_space_handle_t ioh;
149 	int id, rev;
150 	int rv = 1;
151 
152 	/* We need a base address */
153 	if (mb->mb_iobase == MAINBUSCF_BASE_DEFAULT)
154 		return(0);
155 
156 	iot = mb->mb_iot;
157 	if (bus_space_map(iot, mb->mb_iobase, PIOC_SIZE, 0, &ioh))
158 		return(0);
159 
160 	mb->mb_iosize = PIOC_SIZE;
161 
162 	piocgetid(iot, ioh, PIOC_CM_ENTER_665, &id, &rev);
163 	if (id == PIOC_CM_ID_665)
164 		goto out;
165 
166 	piocgetid(iot, ioh, PIOC_CM_ENTER_666, &id, &rev);
167 	if (id == PIOC_CM_ID_666)
168 		goto out;
169 
170 	rv = 0;
171 
172 out:
173 	bus_space_unmap(iot, ioh, PIOC_SIZE);
174 	return(rv);
175 }
176 
177 /*
178  * int piocprint(void *aux, const char *name)
179  *
180  * print routine used during child configuration
181  */
182 
183 static int
184 piocprint(void *aux, const char *name)
185 {
186 	struct pioc_attach_args *pa = aux;
187 
188 	if (!name) {
189 		if (pa->pa_offset)
190 			aprint_normal(" offset 0x%x", pa->pa_offset >> 2);
191 		if (pa->pa_iosize > 1)
192 			aprint_normal("-0x%x",
193 			    ((pa->pa_offset >> 2) + pa->pa_iosize) - 1);
194 		if (pa->pa_irq != -1)
195 			aprint_normal(" irq %d", pa->pa_irq);
196 		if (pa->pa_drq != -1)
197 			aprint_normal(" drq 0x%08x", pa->pa_drq);
198 	}
199 
200 /* XXX print flags */
201 	return (QUIET);
202 }
203 
204 #if 0
205 /*
206  * int piocsearch(device_t parent, cfdata_t cf, void *aux)
207  *
208  * search function used to probe and attach the child devices.
209  *
210  * Note: since the offsets of the devices need to be specified in the
211  * config file we ignore the FSTAT_STAR.
212  */
213 
214 static int
215 piocsearch(device_t parent, cfdata_t cf, const int *ldesc, void *aux)
216 {
217 	struct pioc_softc *sc = device_private(parent);
218 	struct pioc_attach_args pa;
219 	int tryagain;
220 
221 	do {
222 		pa.pa_name = NULL;
223 		pa.pa_iobase = sc->sc_iobase;
224 		pa.pa_iosize = 0;
225 		pa.pa_iot = sc->sc_iot;
226 		if (cf->cf_loc[PIOCCF_OFFSET] == PIOCCF_OFFSET_DEFAULT) {
227 			pa.pa_offset = PIOCCF_OFFSET_DEFAULT;
228 			pa.pa_drq = PIOCCF_DACK_DEFAULT;
229 			pa.pa_irq = PIOCCF_IRQ_DEFAULT;
230 		} else {
231 			pa.pa_offset = (cf->cf_loc[PIOCCF_OFFSET] << 2);
232 			pa.pa_drq = cf->cf_loc[PIOCCF_DACK];
233 			pa.pa_irq = cf->cf_loc[PIOCCF_IRQ];
234 		}
235 
236 		tryagain = 0;
237 		if (config_match(parent, cf, &pa) > 0) {
238 			config_attach(parent, cf, &pa, piocprint);
239 /*			tryagain = (cf->cf_fstate == FSTATE_STAR);*/
240 		}
241 	} while (tryagain);
242 
243 	return (0);
244 }
245 #endif
246 
247 /*
248  * int piocsubmatch(device_t parent, cfdata_t cf, void *aux)
249  *
250  * search function used to probe and attach the child devices.
251  *
252  * Note: since the offsets of the devices need to be specified in the
253  * config file we ignore the FSTAT_STAR.
254  */
255 
256 static int
257 piocsubmatch(device_t parent, cfdata_t cf, const int *ldesc, void *aux)
258 {
259 	struct pioc_attach_args *pa = aux;
260 	int tryagain;
261 
262 	if ((pa->pa_offset >> 2) != cf->cf_loc[PIOCCF_OFFSET])
263 		return(0);
264 	do {
265 		if (pa->pa_drq == -1)
266 			pa->pa_drq = cf->cf_loc[PIOCCF_DACK];
267 		if (pa->pa_irq == -1)
268 			pa->pa_irq = cf->cf_loc[PIOCCF_IRQ];
269 		tryagain = 0;
270 		if (config_match(parent, cf, pa) > 0) {
271 			config_attach(parent, cf, pa, piocprint);
272 /*			tryagain = (cf->cf_fstate == FSTATE_STAR);*/
273 		}
274 	} while (tryagain);
275 
276 	return (0);
277 }
278 
279 /*
280  * void piocattach(device_t parent, device_t dev, void *aux)
281  *
282  * Identify the PIOC and read the config registers into the softc.
283  * Search and configure all children
284  */
285 
286 static void
287 piocattach(device_t parent, device_t self, void *aux)
288 {
289 	struct mainbus_attach_args *mb = aux;
290 	struct pioc_softc *sc = device_private(self);
291 	bus_space_tag_t iot;
292 	bus_space_handle_t ioh;
293 	int id, rev;
294 	int loop;
295 	struct pioc_attach_args pa;
296 
297 	sc->sc_iobase = mb->mb_iobase;
298 	iot = sc->sc_iot = mb->mb_iot;
299 
300 	if (bus_space_map(iot, sc->sc_iobase, PIOC_SIZE, 0, &ioh))
301 		panic("%s: couldn't map I/O space", self->dv_xname);
302 	sc->sc_ioh = ioh;
303 
304 	piocgetid(iot, ioh, PIOC_CM_ENTER_665, &id, &rev);
305 	if (id != PIOC_CM_ID_665)
306 		piocgetid(iot, ioh, PIOC_CM_ENTER_666, &id, &rev);
307 
308 	printf("\n%s: ", device_xname(self));
309 
310 	/* Do we recognise it ? */
311 	switch (id) {
312 	case PIOC_CM_ID_665:
313 	case PIOC_CM_ID_666:
314 		printf("SMC FDC37C6%xGT peripheral controller rev %d\n", id, rev);
315 		break;
316 	default:
317 		printf("Unrecognised peripheral controller id=%2x rev=%2x\n", id, rev);
318 		return;
319 	}
320 
321 	sc->sc_id = id;
322 
323 	/*
324 	 * Put the chip info configuration mode and save all the registers
325 	 */
326 
327 	switch (id) {
328 	case PIOC_CM_ID_665:
329 		bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, PIOC_CM_ENTER_665);
330 		bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, PIOC_CM_ENTER_665);
331 		break;
332 
333 	case PIOC_CM_ID_666:
334 		bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, PIOC_CM_ENTER_666);
335 		bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, PIOC_CM_ENTER_666);
336 		break;
337 	}
338 
339 	for (loop = 0; loop < PIOC_CM_REGS; ++loop) {
340 		bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, loop);
341 		sc->sc_config[loop] = bus_space_read_1(iot, ioh, PIOC_CM_DATA_REG);
342 	}
343 
344 	bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, PIOC_CM_EXIT);
345 
346 #ifdef PIOC_DEBUG
347 	printf("%s: ", device_xname(self));
348 
349 	for (loop = 0; loop < PIOC_CM_REGS; ++loop)
350 		printf("%02x ", sc->sc_config[loop]);
351 	printf("\n");
352 #endif
353 
354 	/*
355 	 * Ok as yet we cannot do specific config_found() calls
356 	 * for the children yet. This is because the pioc device does
357 	 * not know the interrupt numbers to use.
358 	 * Eventually this information will have to be provided by the
359 	 * riscpc specific code.
360 	 * Until then just do a config_search_ia() and pick the info up
361 	 * from the cfdata.
362 	 * Note the child devices require some modifications as well.
363 	 */
364 
365 	/*
366 	 * Ok Now configure the child devices of the pioc device
367 	 * Use the pioc config registers to determine the addressing
368 	 * of the children
369 	 */
370 
371 	/*
372 	 * Start by configuring the IDE controller
373 	 */
374 
375 	if (sc->sc_config[PIOC_CM_CR0] & PIOC_WDC_ENABLE) {
376 		pa.pa_name = "wdc";
377 		pa.pa_iobase = sc->sc_iobase;
378 		pa.pa_iosize = 0;
379 		pa.pa_iot = iot;
380 		if (sc->sc_config[PIOC_CM_CR5] & PIOC_WDC_SECONDARY)
381 			pa.pa_offset = (PIOC_WDC_SECONDARY_OFFSET << 2);
382 		else
383 			pa.pa_offset = (PIOC_WDC_PRIMARY_OFFSET << 2);
384 		pa.pa_drq = -1;
385 		pa.pa_irq = -1;
386 		config_found_sm_loc(self, "pioc", NULL, &pa, piocprint,
387 				    piocsubmatch);
388 	}
389 
390 	/*
391 	 * Next configure the floppy controller
392 	 */
393 
394 	if (sc->sc_config[PIOC_CM_CR0] & PIOC_FDC_ENABLE) {
395 		pa.pa_name = "fdc";
396 		pa.pa_iobase = sc->sc_iobase;
397 		pa.pa_iosize = 0;
398 		pa.pa_iot = iot;
399 		if (sc->sc_config[PIOC_CM_CR5] & PIOC_FDC_SECONDARY)
400 			pa.pa_offset = (PIOC_FDC_SECONDARY_OFFSET << 2);
401 		else
402 			pa.pa_offset = (PIOC_FDC_PRIMARY_OFFSET << 2);
403 		pa.pa_drq = -1;
404 		pa.pa_irq = -1;
405 		config_found_sm_loc(self, "pioc", NULL, &pa, piocprint,
406 				    piocsubmatch);
407 	}
408 
409 	/*
410 	 * Next configure the serial ports
411 	 */
412 
413 	/*
414 	 * XXX - There is a deficiency in the serial configuration
415 	 * If the PIOC has the serial ports configured for COM3 and COM4
416 	 * the standard COM3 and COM4 addresses are assumed rather than
417 	 * examining CR1 to determine the COM3 and COM4 addresses.
418 	 */
419 
420 	if (sc->sc_config[PIOC_CM_CR2] & PIOC_UART1_ENABLE) {
421 		pa.pa_name = "com";
422 		pa.pa_iobase = sc->sc_iobase;
423 		pa.pa_iosize = 0;
424 		pa.pa_iot = iot;
425 		switch (sc->sc_config[PIOC_CM_CR2] & PIOC_UART1_ADDR_MASK) {
426 		case PIOC_UART1_ADDR_COM1:
427 			pa.pa_offset = (PIOC_COM1_OFFSET << 2);
428 			break;
429 		case PIOC_UART1_ADDR_COM2:
430 			pa.pa_offset = (PIOC_COM2_OFFSET << 2);
431 			break;
432 		case PIOC_UART1_ADDR_COM3:
433 			pa.pa_offset = (PIOC_COM3_OFFSET << 2);
434 			break;
435 		case PIOC_UART1_ADDR_COM4:
436 			pa.pa_offset = (PIOC_COM4_OFFSET << 2);
437 			break;
438 		}
439 		pa.pa_drq = -1;
440 		pa.pa_irq = -1;
441 		config_found_sm_loc(self, "pioc", NULL, &pa, piocprint,
442 				    piocsubmatch);
443 	}
444 
445 	if (sc->sc_config[PIOC_CM_CR2] & PIOC_UART2_ENABLE) {
446 		pa.pa_name = "com";
447 		pa.pa_iobase = sc->sc_iobase;
448 		pa.pa_iosize = 0;
449 		pa.pa_iot = iot;
450 		switch (sc->sc_config[PIOC_CM_CR2] & PIOC_UART2_ADDR_MASK) {
451 		case PIOC_UART2_ADDR_COM1:
452 			pa.pa_offset = (PIOC_COM1_OFFSET << 2);
453 			break;
454 		case PIOC_UART2_ADDR_COM2:
455 			pa.pa_offset = (PIOC_COM2_OFFSET << 2);
456 			break;
457 		case PIOC_UART2_ADDR_COM3:
458 			pa.pa_offset = (PIOC_COM3_OFFSET << 2);
459 			break;
460 		case PIOC_UART2_ADDR_COM4:
461 			pa.pa_offset = (PIOC_COM4_OFFSET << 2);
462 			break;
463 		}
464 		pa.pa_drq = -1;
465 		pa.pa_irq = -1;
466 		config_found_sm_loc(self, "pioc", NULL, &pa, piocprint,
467 				    piocsubmatch);
468 	}
469 
470 	/*
471 	 * Next configure the printer port
472 	 */
473 
474 	if ((sc->sc_config[PIOC_CM_CR1] & PIOC_LPT_ADDR_MASK) != PIOC_LPT_ADDR_DISABLE) {
475 		pa.pa_name = "lpt";
476 		pa.pa_iobase = sc->sc_iobase;
477 		pa.pa_iosize = 0;
478 		pa.pa_iot = iot;
479 		switch (sc->sc_config[PIOC_CM_CR1] & PIOC_LPT_ADDR_MASK) {
480 		case PIOC_LPT_ADDR_1:
481 			pa.pa_offset = (PIOC_LPT1_OFFSET << 2);
482 			break;
483 		case PIOC_LPT_ADDR_2:
484 			pa.pa_offset = (PIOC_LPT2_OFFSET << 2);
485 			break;
486 		case PIOC_LPT_ADDR_3:
487 			pa.pa_offset = (PIOC_LPT3_OFFSET << 2);
488 			break;
489 		}
490 		pa.pa_drq = -1;
491 		pa.pa_irq = -1;
492 		config_found_sm_loc(self, "pioc", NULL, &pa, piocprint,
493 				    piocsubmatch);
494 	}
495 
496 #if 0
497 	config_search_ia(piocsearch, self, "pioc", NULL);
498 #endif
499 }
500 
501 /* End of pioc.c */
502