1/* $NetBSD: podulebus.c,v 1.18 2009/03/18 16:00:08 cegger Exp $ */
2
3/*-
4 * Copyright (c) 2000 Ben Harris
5 * All rights reserved.
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. The name of the author may not be used to endorse or promote products
16 *    derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__KERNEL_RCSID(0, "$NetBSD: podulebus.c,v 1.18 2009/03/18 16:00:08 cegger Exp $");
32
33#include <sys/param.h>
34#include <sys/device.h>
35#include <sys/malloc.h>
36#include <sys/systm.h>
37#include <sys/bus.h>
38
39#include <machine/intr.h>
40#include <machine/irq.h>
41#include <machine/machdep.h>
42#include <machine/memcreg.h>
43
44#include <arch/acorn26/iobus/iocreg.h>
45#include <arch/acorn26/iobus/iocvar.h>
46#include <dev/podulebus/podulebus.h>
47#include <arch/acorn26/podulebus/podulebusreg.h>
48
49#include "locators.h"
50
51#include "podloader.h"
52#include "unixbp.h"
53
54#if NUNIXBP > 0
55#include <arch/acorn26/podulebus/unixbpvar.h>
56#endif
57
58static int podulebus_match(device_t, cfdata_t, void *);
59static void podulebus_attach(device_t, device_t , void *);
60static void podulebus_probe_podule(device_t, int);
61static int podulebus_print(void *, char const *);
62static int podulebus_submatch(device_t, cfdata_t, const int *, void *);
63static void podulebus_read_chunks(struct podulebus_attach_args *, int);
64static u_int8_t *podulebus_get_chunk(struct podulebus_attach_args *, int);
65#if NPODLOADER > 0
66void podloader_read_region(struct podulebus_attach_args *pa, u_int src,
67    u_int8_t *dest, size_t length);
68extern register_t _podloader_call(register_t, register_t, register_t,
69    void *, int);
70#endif
71
72struct podulebus_softc {
73	device_t sc_dev;
74	struct	ioc_attach_args sc_ioc;
75};
76
77CFATTACH_DECL_NEW(podulebus, sizeof(struct podulebus_softc),
78    podulebus_match, podulebus_attach, NULL, NULL);
79
80/* ARGSUSED */
81static int
82podulebus_match(device_t parent, cfdata_t cf, void *aux)
83{
84
85	/* We can't usefully probe for this */
86	return 1;
87}
88
89static void
90podulebus_attach(device_t parent, device_t self, void *aux)
91{
92	int i;
93	struct podulebus_softc *sc = device_private(self);
94	struct ioc_attach_args *ioc = aux;
95
96	sc->sc_dev = self;
97	sc->sc_ioc = *ioc;
98	aprint_normal("\n");
99
100	/* Iterate over the podules attaching them */
101	for (i = 0; i < MAX_PODULES; i++)
102		podulebus_probe_podule(self, i);
103}
104
105static void
106podulebus_probe_podule(device_t self, int slotnum)
107{
108	struct podulebus_softc *sc = device_private(self);
109	bus_space_tag_t id_bst;
110	bus_space_handle_t id_bsh;
111	int ecid, w;
112	u_int8_t extecid[EXTECID_SIZE];
113	struct podulebus_attach_args pa;
114
115	memset(&pa, 0, sizeof(pa));
116	id_bst = sc->sc_ioc.ioc_sync_t;
117	bus_space_subregion(id_bst, sc->sc_ioc.ioc_sync_h,
118			    slotnum * PODULE_GAP, PODULE_GAP, &id_bsh);
119	ecid = bus_space_read_1(id_bst, id_bsh, 0);
120	/* Skip empty or strange slots */
121	if (ecid & (ECID_NOTPRESENT | ECID_NONCONF))
122		return;
123	if ((ecid & ECID_ID_MASK) == ECID_ID_EXTEND) {
124		pa.pa_fast_t = sc->sc_ioc.ioc_fast_t;
125		bus_space_subregion(pa.pa_fast_t, sc->sc_ioc.ioc_fast_h,
126				    slotnum * PODULE_GAP, PODULE_GAP,
127				    &pa.pa_fast_h);
128		pa.pa_medium_t = sc->sc_ioc.ioc_medium_t;
129		bus_space_subregion(pa.pa_medium_t, sc->sc_ioc.ioc_medium_h,
130				    slotnum * PODULE_GAP, PODULE_GAP,
131				    &pa.pa_medium_h);
132		pa.pa_slow_t = sc->sc_ioc.ioc_slow_t;
133		bus_space_subregion(pa.pa_slow_t, sc->sc_ioc.ioc_slow_h,
134				    slotnum * PODULE_GAP, PODULE_GAP,
135				    &pa.pa_slow_h);
136		pa.pa_sync_t = sc->sc_ioc.ioc_sync_t;
137		bus_space_subregion(pa.pa_sync_t, sc->sc_ioc.ioc_sync_h,
138				    slotnum * PODULE_GAP, PODULE_GAP,
139				    &pa.pa_sync_h);
140		/* XXX This is a hack! */
141		pa.pa_mod_t = &iobus_bs_tag;
142		bus_space_map(pa.pa_mod_t,
143		    (bus_addr_t)MEMC_IO_BASE + slotnum * (PODULE_GAP << 2),
144		    (PODULE_GAP << 2), 0, &pa.pa_mod_h);
145		bus_space_read_region_1(id_bst, id_bsh, 0,
146					extecid, EXTECID_SIZE);
147		/* XXX If you thought that was a hack... */
148		pa.pa_mod_base = pa.pa_mod_h;
149		pa.pa_fast_base = pa.pa_fast_h;
150		pa.pa_medium_base = pa.pa_medium_h;
151		pa.pa_slow_base = pa.pa_slow_h;
152		pa.pa_sync_base = pa.pa_sync_h;
153		pa.pa_ecid = ecid;
154		pa.pa_flags1 = extecid[EXTECID_F1];
155		pa.pa_manufacturer = (extecid[EXTECID_MLO] |
156				      extecid[EXTECID_MHI] << 8);
157		pa.pa_product = (extecid[EXTECID_PLO] |
158				 extecid[EXTECID_PHI] << 8);
159		pa.pa_slotnum = slotnum;
160		pa.pa_ih = slotnum;
161		if (pa.pa_flags1 & EXTECID_F1_CD) {
162			w = pa.pa_flags1 & EXTECID_F1_W_MASK;
163			if (w != EXTECID_F1_W_8BIT) {
164				/* RISC OS 3 can't handle this either. */
165				aprint_error("%s:%d: ROM is not 8 bits wide; "
166				    "ignoring it\n",
167				    device_xname(self), pa.pa_slotnum);
168			} else {
169				podulebus_read_chunks(&pa, 0);
170				pa.pa_descr = podulebus_get_chunk(&pa,
171							  CHUNK_DEV_DESCR);
172			}
173		}
174		pa.pa_slotflags = 0;
175		config_found_sm_loc(self, "podulebus", NULL, &pa,
176				podulebus_print, podulebus_submatch);
177		if (pa.pa_chunks)
178			free(pa.pa_chunks, M_DEVBUF);
179		if (pa.pa_descr)
180			free(pa.pa_descr, M_DEVBUF);
181		if (pa.pa_loader)
182			free(pa.pa_loader, M_DEVBUF);
183	} else
184		aprint_normal("%s:%d: non-extended podule ignored.\n",
185		    device_xname(self), slotnum);
186}
187
188static void
189podulebus_read_chunks(struct podulebus_attach_args *pa, int useloader)
190{
191	u_int8_t chunk[8];
192	u_int ptr, nchunks, type, length, offset;
193
194	nchunks = pa->pa_nchunks;
195	if (useloader)
196		ptr = 0;
197	else
198		ptr = EXTECID_SIZE + IRQPTR_SIZE;
199	for (;;) {
200#if NPODLOADER > 0
201		if (useloader)
202			podloader_read_region(pa, ptr, chunk, 8);
203		else
204#endif
205			bus_space_read_region_1(pa->pa_sync_t, pa->pa_sync_h,
206					ptr, chunk, 8);
207		ptr += 8;
208		type = chunk[0];
209		length = chunk[1] | chunk[2] << 8 | chunk[3] << 16;
210		if (length == 0)
211			break;
212		offset = chunk[4] | chunk[5] << 8 |
213		    chunk[6] << 16 | chunk[7] <<  24;
214		if ((offset + length) > PODULE_GAP)
215			continue;
216		nchunks++;
217		pa->pa_chunks = realloc(pa->pa_chunks,
218					nchunks * sizeof(*pa->pa_chunks),
219					M_DEVBUF, M_WAITOK);
220		pa->pa_chunks[nchunks-1].pc_type = type;
221		pa->pa_chunks[nchunks-1].pc_length = length;
222		pa->pa_chunks[nchunks-1].pc_offset = offset;
223		pa->pa_chunks[nchunks-1].pc_useloader = useloader;
224	}
225	pa->pa_nchunks = nchunks;
226}
227
228static u_int8_t *
229podulebus_get_chunk(struct podulebus_attach_args *pa, int type)
230{
231	int i;
232	struct podulebus_chunk *pc;
233	u_int8_t *chunk;
234
235	for (i = 0; i < pa->pa_nchunks; i++) {
236		pc = &pa->pa_chunks[i];
237		if (pc->pc_type == type) {
238			chunk = malloc( pc->pc_length + 1, M_DEVBUF, M_WAITOK);
239#if NPODLOADER > 0
240			if (pc->pc_useloader)
241				podloader_read_region(pa, pc->pc_offset, chunk,
242				    pc->pc_length);
243			else
244#endif
245				bus_space_read_region_1(pa->pa_sync_t,
246				    pa->pa_sync_h, pc->pc_offset, chunk,
247				    pc->pc_length);
248			chunk[pc->pc_length] = 0;
249			return chunk;
250		}
251	}
252	return NULL;
253}
254
255#if NPODLOADER > 0
256int
257podulebus_initloader(struct podulebus_attach_args *pa)
258{
259
260	if (pa->pa_loader != NULL)
261		return 0;
262	pa->pa_loader = podulebus_get_chunk(pa, CHUNK_RISCOS_LOADER);
263	if (pa->pa_loader == NULL)
264		return -1;
265	podulebus_read_chunks(pa, 1);
266	if (pa->pa_descr)
267		free(pa->pa_descr, M_DEVBUF);
268	pa->pa_descr = podulebus_get_chunk(pa, CHUNK_DEV_DESCR);
269	return 0;
270}
271
272static register_t
273podloader_call(register_t r0, register_t r1, register_t r11, void *loader,
274    int entry)
275{
276	int s;
277	register_t result;
278
279	s = splhigh();
280	result = _podloader_call(r0, r1, r11, loader, entry);
281	splx(s);
282	return result;
283}
284
285int
286podloader_readbyte(struct podulebus_attach_args *pa, u_int addr)
287{
288
289	return podloader_call(0, addr, pa->pa_sync_base, pa->pa_loader, 0);
290}
291
292void
293podloader_writebyte(struct podulebus_attach_args *pa, u_int addr, int val)
294{
295
296	podloader_call(val, addr, pa->pa_sync_base, pa->pa_loader, 1);
297}
298
299void
300podloader_reset(struct podulebus_attach_args *pa)
301{
302
303	podloader_call(0, 0, pa->pa_sync_base, pa->pa_loader, 2);
304}
305
306int
307podloader_callloader(struct podulebus_attach_args *pa, u_int r0, u_int r1)
308{
309
310	return podloader_call(r0, r1, pa->pa_sync_base, pa->pa_loader, 3);
311}
312
313void
314podloader_read_region(struct podulebus_attach_args *pa, u_int src,
315    u_int8_t *dest, size_t length)
316{
317
318	while (length--)
319		*dest++ = podloader_readbyte(pa, src++);
320	podloader_reset(pa);
321}
322#endif
323
324static int
325podulebus_print(void *aux, char const *pnp)
326{
327	struct podulebus_attach_args *pa = aux;
328	char *p, *q;
329
330	if (pnp) {
331		if (pa->pa_descr) {
332			/* Restrict description to ASCII graphic characters. */
333			for (p = q = pa->pa_descr; *p != '\0'; p++)
334				if (*p >= 32 && *p < 126)
335					*q++ = *p;
336			*q++ = 0;
337			aprint_normal("%s", pa->pa_descr);
338		} else
339			aprint_normal("podule");
340		aprint_normal(" <%04x:%04x> at %s",
341		       pa->pa_manufacturer, pa->pa_product, pnp);
342	}
343	aprint_normal(" slot %d", pa->pa_slotnum);
344	return UNCONF;
345}
346
347static int
348podulebus_submatch(device_t parent, cfdata_t cf, const int *ldesc, void *aux)
349{
350	struct podulebus_attach_args *pa = aux;
351
352	if (cf->cf_loc[PODULEBUSCF_SLOT] == PODULEBUSCF_SLOT_DEFAULT ||
353	    cf->cf_loc[PODULEBUSCF_SLOT] == pa->pa_slotnum)
354		return config_match(parent, cf, aux);
355	return 0;
356}
357
358void *
359podulebus_irq_establish(podulebus_intr_handle_t slot, int ipl,
360			int (*func)(void *), void *arg, struct evcnt *ev)
361{
362
363	/* XXX: support for checking IRQ bit on podule? */
364#if NUNIXBP > 0
365	if (the_unixbp != NULL)
366		return irq_establish(IRQ_UNIXBP_BASE + slot, ipl, func, arg,
367		    ev);
368#endif
369	return irq_establish(IRQ_PIRQ, ipl, func, arg, ev);
370}
371
372void
373podulebus_readcmos(struct podulebus_attach_args *pa, u_int8_t *c)
374{
375	int i;
376
377	for (i = 0; i < 4; i++)
378		c[i] = cmos_read(PODULE_CMOS_BASE +
379		    pa->pa_slotnum * PODULE_CMOS_PERSLOT + i);
380}
381