1/*	$NetBSD: pioc.c,v 1.20 2021/08/07 16:18:40 thorpej 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.20 2021/08/07 16:18:40 thorpej 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
72struct pioc_softc {
73	bus_space_tag_t		sc_iot;			/* bus tag */
74	bus_space_handle_t	sc_ioh;			/* bus handle */
75	bus_addr_t		sc_iobase;		/* IO base address */
76 	int			sc_id;			/* chip ID */
77	int			sc_config[PIOC_CM_REGS];/* config regs */
78};
79
80/*
81 * The pioc device is a parent to the com device.
82 * This means that it needs to provide a bus space tag for
83 * a serial console.
84 *
85 * XXX - This is not fully tested yet.
86 */
87
88extern struct bus_space mainbus_bs_tag;
89bus_space_tag_t comconstag = &mainbus_bs_tag;
90
91/* Prototypes for functions */
92
93static int  piocmatch(device_t, cfdata_t, void *);
94static void piocattach(device_t, device_t, void *);
95static int  piocprint(void *aux, const char *name);
96#if 0
97static int  piocsearch(device_t, cfdata_t, void *);
98#endif
99static int  piocsubmatch(device_t, cfdata_t,
100			      const int *, void *);
101static void piocgetid(bus_space_tag_t iot, bus_space_handle_t ioh,
102			      int config_entry, int *id, int *revision);
103
104/* device attach and driver structure */
105
106CFATTACH_DECL_NEW(pioc, sizeof(struct pioc_softc),
107    piocmatch, piocattach, NULL, NULL);
108
109/*
110 * void piocgetid(bus_space_tag_t iot, bus_space_handle_t ioh,
111 *                int config_entry, int *id, int *revision)
112 *
113 * Enter config mode and return the id and revision
114 */
115
116static void
117piocgetid(bus_space_tag_t iot, bus_space_handle_t ioh, int config_entry, int *id, int *revision)
118{
119	/*
120	 * Put the chip info configuration mode and read the ID and revision
121	 */
122	bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, config_entry);
123	bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, config_entry);
124
125	bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, PIOC_CM_CRD);
126	*id = bus_space_read_1(iot, ioh, PIOC_CM_DATA_REG);
127	bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, PIOC_CM_CRE);
128	*revision = bus_space_read_1(iot, ioh, PIOC_CM_DATA_REG);
129
130	bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, PIOC_CM_EXIT);
131}
132
133/*
134 * int piocmatch(device_t parent, cfdata_t cf, void *aux)
135 *
136 * Put the controller into config mode and probe the ID to see if
137 * we recognise it.
138 *
139 * XXX - INTRUSIVE PROBE
140 */
141
142static int
143piocmatch(device_t parent, cfdata_t cf, void *aux)
144{
145	struct mainbus_attach_args *mb = aux;
146	bus_space_tag_t iot;
147	bus_space_handle_t ioh;
148	int id, rev;
149	int rv = 1;
150
151	/* We need a base address */
152	if (mb->mb_iobase == MAINBUSCF_BASE_DEFAULT)
153		return(0);
154
155	iot = mb->mb_iot;
156	if (bus_space_map(iot, mb->mb_iobase, PIOC_SIZE, 0, &ioh))
157		return(0);
158
159	mb->mb_iosize = PIOC_SIZE;
160
161	piocgetid(iot, ioh, PIOC_CM_ENTER_665, &id, &rev);
162	if (id == PIOC_CM_ID_665)
163		goto out;
164
165	piocgetid(iot, ioh, PIOC_CM_ENTER_666, &id, &rev);
166	if (id == PIOC_CM_ID_666)
167		goto out;
168
169	rv = 0;
170
171out:
172	bus_space_unmap(iot, ioh, PIOC_SIZE);
173	return(rv);
174}
175
176/*
177 * int piocprint(void *aux, const char *name)
178 *
179 * print routine used during child configuration
180 */
181
182static int
183piocprint(void *aux, const char *name)
184{
185	struct pioc_attach_args *pa = aux;
186
187	if (!name) {
188		if (pa->pa_offset)
189			aprint_normal(" offset 0x%x", pa->pa_offset >> 2);
190		if (pa->pa_iosize > 1)
191			aprint_normal("-0x%x",
192			    ((pa->pa_offset >> 2) + pa->pa_iosize) - 1);
193		if (pa->pa_irq != -1)
194			aprint_normal(" irq %d", pa->pa_irq);
195		if (pa->pa_drq != -1)
196			aprint_normal(" drq 0x%08x", pa->pa_drq);
197	}
198
199/* XXX print flags */
200	return (QUIET);
201}
202
203#if 0
204/*
205 * int piocsearch(device_t parent, cfdata_t cf, void *aux)
206 *
207 * search function used to probe and attach the child devices.
208 *
209 * Note: since the offsets of the devices need to be specified in the
210 * config file we ignore the FSTAT_STAR.
211 */
212
213static int
214piocsearch(device_t parent, cfdata_t cf, const int *ldesc, void *aux)
215{
216	struct pioc_softc *sc = device_private(parent);
217	struct pioc_attach_args pa;
218	int tryagain;
219
220	do {
221		pa.pa_name = NULL;
222		pa.pa_iobase = sc->sc_iobase;
223		pa.pa_iosize = 0;
224		pa.pa_iot = sc->sc_iot;
225		if (cf->cf_loc[PIOCCF_OFFSET] == PIOCCF_OFFSET_DEFAULT) {
226			pa.pa_offset = PIOCCF_OFFSET_DEFAULT;
227			pa.pa_drq = PIOCCF_DACK_DEFAULT;
228			pa.pa_irq = PIOCCF_IRQ_DEFAULT;
229		} else {
230			pa.pa_offset = (cf->cf_loc[PIOCCF_OFFSET] << 2);
231			pa.pa_drq = cf->cf_loc[PIOCCF_DACK];
232			pa.pa_irq = cf->cf_loc[PIOCCF_IRQ];
233		}
234
235		tryagain = 0;
236		if (config_probe(parent, cf, &pa)) {
237			config_attach(parent, cf, &pa, piocprint, CFARGS_NONE);
238/*			tryagain = (cf->cf_fstate == FSTATE_STAR);*/
239		}
240	} while (tryagain);
241
242	return (0);
243}
244#endif
245
246/*
247 * int piocsubmatch(device_t parent, cfdata_t cf, void *aux)
248 *
249 * search function used to probe and attach the child devices.
250 *
251 * Note: since the offsets of the devices need to be specified in the
252 * config file we ignore the FSTAT_STAR.
253 */
254
255static int
256piocsubmatch(device_t parent, cfdata_t cf, const int *ldesc, void *aux)
257{
258	struct pioc_attach_args *pa = aux;
259	int tryagain;
260
261	if ((pa->pa_offset >> 2) != cf->cf_loc[PIOCCF_OFFSET])
262		return(0);
263	do {
264		if (pa->pa_drq == -1)
265			pa->pa_drq = cf->cf_loc[PIOCCF_DACK];
266		if (pa->pa_irq == -1)
267			pa->pa_irq = cf->cf_loc[PIOCCF_IRQ];
268		tryagain = 0;
269		if (config_probe(parent, cf, pa)) {
270			config_attach(parent, cf, pa, piocprint, CFARGS_NONE);
271/*			tryagain = (cf->cf_fstate == FSTATE_STAR);*/
272		}
273	} while (tryagain);
274
275	return (0);
276}
277
278/*
279 * void piocattach(device_t parent, device_t dev, void *aux)
280 *
281 * Identify the PIOC and read the config registers into the softc.
282 * Search and configure all children
283 */
284
285static void
286piocattach(device_t parent, device_t self, void *aux)
287{
288	struct mainbus_attach_args *mb = aux;
289	struct pioc_softc *sc = device_private(self);
290	bus_space_tag_t iot;
291	bus_space_handle_t ioh;
292	int id, rev;
293	int loop;
294	struct pioc_attach_args pa;
295
296	sc->sc_iobase = mb->mb_iobase;
297	iot = sc->sc_iot = mb->mb_iot;
298
299	if (bus_space_map(iot, sc->sc_iobase, PIOC_SIZE, 0, &ioh))
300		panic("%s: couldn't map I/O space", device_xname(self));
301	sc->sc_ioh = ioh;
302
303	piocgetid(iot, ioh, PIOC_CM_ENTER_665, &id, &rev);
304	if (id != PIOC_CM_ID_665)
305		piocgetid(iot, ioh, PIOC_CM_ENTER_666, &id, &rev);
306
307	printf("\n%s: ", device_xname(self));
308
309	/* Do we recognise it ? */
310	switch (id) {
311	case PIOC_CM_ID_665:
312	case PIOC_CM_ID_666:
313		printf("SMC FDC37C6%xGT peripheral controller rev %d\n", id, rev);
314		break;
315	default:
316		printf("Unrecognised peripheral controller id=%2x rev=%2x\n", id, rev);
317		return;
318	}
319
320	sc->sc_id = id;
321
322	/*
323	 * Put the chip info configuration mode and save all the registers
324	 */
325
326	switch (id) {
327	case PIOC_CM_ID_665:
328		bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, PIOC_CM_ENTER_665);
329		bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, PIOC_CM_ENTER_665);
330		break;
331
332	case PIOC_CM_ID_666:
333		bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, PIOC_CM_ENTER_666);
334		bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, PIOC_CM_ENTER_666);
335		break;
336	}
337
338	for (loop = 0; loop < PIOC_CM_REGS; ++loop) {
339		bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, loop);
340		sc->sc_config[loop] = bus_space_read_1(iot, ioh, PIOC_CM_DATA_REG);
341	}
342
343	bus_space_write_1(iot, ioh, PIOC_CM_SELECT_REG, PIOC_CM_EXIT);
344
345#ifdef PIOC_DEBUG
346	printf("%s: ", device_xname(self));
347
348	for (loop = 0; loop < PIOC_CM_REGS; ++loop)
349		printf("%02x ", sc->sc_config[loop]);
350	printf("\n");
351#endif
352
353	/*
354	 * Ok as yet we cannot do specific config_found() calls
355	 * for the children yet. This is because the pioc device does
356	 * not know the interrupt numbers to use.
357	 * Eventually this information will have to be provided by the
358	 * riscpc specific code.
359	 * Until then just do a config_search_ia() and pick the info up
360	 * from the cfdata.
361	 * Note the child devices require some modifications as well.
362	 */
363
364	/*
365	 * Ok Now configure the child devices of the pioc device
366	 * Use the pioc config registers to determine the addressing
367	 * of the children
368	 */
369
370	/*
371	 * Start by configuring the IDE controller
372	 */
373
374	if (sc->sc_config[PIOC_CM_CR0] & PIOC_WDC_ENABLE) {
375		pa.pa_name = "wdc";
376		pa.pa_iobase = sc->sc_iobase;
377		pa.pa_iosize = 0;
378		pa.pa_iot = iot;
379		if (sc->sc_config[PIOC_CM_CR5] & PIOC_WDC_SECONDARY)
380			pa.pa_offset = (PIOC_WDC_SECONDARY_OFFSET << 2);
381		else
382			pa.pa_offset = (PIOC_WDC_PRIMARY_OFFSET << 2);
383		pa.pa_drq = -1;
384		pa.pa_irq = -1;
385		config_found(self, &pa, piocprint,
386		    CFARGS(.submatch = piocsubmatch));
387	}
388
389	/*
390	 * Next configure the floppy controller
391	 */
392
393	if (sc->sc_config[PIOC_CM_CR0] & PIOC_FDC_ENABLE) {
394		pa.pa_name = "fdc";
395		pa.pa_iobase = sc->sc_iobase;
396		pa.pa_iosize = 0;
397		pa.pa_iot = iot;
398		if (sc->sc_config[PIOC_CM_CR5] & PIOC_FDC_SECONDARY)
399			pa.pa_offset = (PIOC_FDC_SECONDARY_OFFSET << 2);
400		else
401			pa.pa_offset = (PIOC_FDC_PRIMARY_OFFSET << 2);
402		pa.pa_drq = -1;
403		pa.pa_irq = -1;
404		config_found(self, &pa, piocprint,
405		    CFARGS(.submatch = piocsubmatch));
406	}
407
408	/*
409	 * Next configure the serial ports
410	 */
411
412	/*
413	 * XXX - There is a deficiency in the serial configuration
414	 * If the PIOC has the serial ports configured for COM3 and COM4
415	 * the standard COM3 and COM4 addresses are assumed rather than
416	 * examining CR1 to determine the COM3 and COM4 addresses.
417	 */
418
419	if (sc->sc_config[PIOC_CM_CR2] & PIOC_UART1_ENABLE) {
420		pa.pa_name = "com";
421		pa.pa_iobase = sc->sc_iobase;
422		pa.pa_iosize = 0;
423		pa.pa_iot = iot;
424		switch (sc->sc_config[PIOC_CM_CR2] & PIOC_UART1_ADDR_MASK) {
425		case PIOC_UART1_ADDR_COM1:
426			pa.pa_offset = (PIOC_COM1_OFFSET << 2);
427			break;
428		case PIOC_UART1_ADDR_COM2:
429			pa.pa_offset = (PIOC_COM2_OFFSET << 2);
430			break;
431		case PIOC_UART1_ADDR_COM3:
432			pa.pa_offset = (PIOC_COM3_OFFSET << 2);
433			break;
434		case PIOC_UART1_ADDR_COM4:
435			pa.pa_offset = (PIOC_COM4_OFFSET << 2);
436			break;
437		}
438		pa.pa_drq = -1;
439		pa.pa_irq = -1;
440		config_found(self, &pa, piocprint,
441		    CFARGS(.submatch = piocsubmatch));
442	}
443
444	if (sc->sc_config[PIOC_CM_CR2] & PIOC_UART2_ENABLE) {
445		pa.pa_name = "com";
446		pa.pa_iobase = sc->sc_iobase;
447		pa.pa_iosize = 0;
448		pa.pa_iot = iot;
449		switch (sc->sc_config[PIOC_CM_CR2] & PIOC_UART2_ADDR_MASK) {
450		case PIOC_UART2_ADDR_COM1:
451			pa.pa_offset = (PIOC_COM1_OFFSET << 2);
452			break;
453		case PIOC_UART2_ADDR_COM2:
454			pa.pa_offset = (PIOC_COM2_OFFSET << 2);
455			break;
456		case PIOC_UART2_ADDR_COM3:
457			pa.pa_offset = (PIOC_COM3_OFFSET << 2);
458			break;
459		case PIOC_UART2_ADDR_COM4:
460			pa.pa_offset = (PIOC_COM4_OFFSET << 2);
461			break;
462		}
463		pa.pa_drq = -1;
464		pa.pa_irq = -1;
465		config_found(self, &pa, piocprint,
466		    CFARGS(.submatch = piocsubmatch));
467	}
468
469	/*
470	 * Next configure the printer port
471	 */
472
473	if ((sc->sc_config[PIOC_CM_CR1] & PIOC_LPT_ADDR_MASK) != PIOC_LPT_ADDR_DISABLE) {
474		pa.pa_name = "lpt";
475		pa.pa_iobase = sc->sc_iobase;
476		pa.pa_iosize = 0;
477		pa.pa_iot = iot;
478		switch (sc->sc_config[PIOC_CM_CR1] & PIOC_LPT_ADDR_MASK) {
479		case PIOC_LPT_ADDR_1:
480			pa.pa_offset = (PIOC_LPT1_OFFSET << 2);
481			break;
482		case PIOC_LPT_ADDR_2:
483			pa.pa_offset = (PIOC_LPT2_OFFSET << 2);
484			break;
485		case PIOC_LPT_ADDR_3:
486			pa.pa_offset = (PIOC_LPT3_OFFSET << 2);
487			break;
488		}
489		pa.pa_drq = -1;
490		pa.pa_irq = -1;
491		config_found(self, &pa, piocprint,
492		    CFARGS(.submatch = piocsubmatch));
493	}
494
495#if 0
496	config_search(self, NULL,
497	    CFARGS(.search = piocsearch));
498#endif
499}
500
501/* End of pioc.c */
502