fhc.c revision 152684
1/*-
2 * Copyright (c) 2003 Jake Burkholder.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/sparc64/fhc/fhc.c 152684 2005-11-22 16:39:44Z marius $");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/bus.h>
33#include <sys/kernel.h>
34#include <sys/malloc.h>
35#include <sys/pcpu.h>
36
37#include <dev/led/led.h>
38#include <dev/ofw/ofw_bus.h>
39#include <dev/ofw/ofw_bus_subr.h>
40#include <dev/ofw/openfirm.h>
41
42#include <machine/bus.h>
43#include <machine/bus_common.h>
44#include <machine/resource.h>
45
46#include <sys/rman.h>
47
48#include <sparc64/fhc/fhcreg.h>
49#include <sparc64/fhc/fhcvar.h>
50#include <sparc64/sbus/ofw_sbus.h>
51
52struct fhc_clr {
53	driver_intr_t		*fc_func;
54	void			*fc_arg;
55	void			*fc_cookie;
56	bus_space_tag_t		fc_bt;
57	bus_space_handle_t	fc_bh;
58};
59
60struct fhc_devinfo {
61	struct ofw_bus_devinfo	fdi_obdinfo;
62	struct resource_list	fdi_rl;
63};
64
65static void fhc_intr_stub(void *);
66static void fhc_led_func(void *, int);
67static int fhc_print_res(struct fhc_devinfo *);
68
69int
70fhc_probe(device_t dev)
71{
72
73	return (0);
74}
75
76int
77fhc_attach(device_t dev)
78{
79	char ledname[sizeof("boardXX")];
80	struct fhc_devinfo *fdi;
81	struct sbus_regs *reg;
82	struct fhc_softc *sc;
83	phandle_t child;
84	phandle_t node;
85	device_t cdev;
86	uint32_t ctrl;
87	uint32_t *intr;
88	uint32_t iv;
89	char *name;
90	int nintr;
91	int nreg;
92	int i;
93
94	sc = device_get_softc(dev);
95	node = sc->sc_node;
96
97	device_printf(dev, "board %d, ", sc->sc_board);
98	if (OF_getprop_alloc(node, "board-model", 1, (void **)&name) != -1) {
99		printf("model %s\n", name);
100		free(name, M_OFWPROP);
101	} else
102		printf("model unknown\n");
103
104	for (i = FHC_FANFAIL; i <= FHC_TOD; i++) {
105		bus_space_write_4(sc->sc_bt[i], sc->sc_bh[i], FHC_ICLR, 0x0);
106		bus_space_read_4(sc->sc_bt[i], sc->sc_bh[i], FHC_ICLR);
107	}
108
109	sc->sc_ign = sc->sc_board << 1;
110	bus_space_write_4(sc->sc_bt[FHC_IGN], sc->sc_bh[FHC_IGN], 0x0,
111	    sc->sc_ign);
112	sc->sc_ign = bus_space_read_4(sc->sc_bt[FHC_IGN],
113	    sc->sc_bh[FHC_IGN], 0x0);
114
115	ctrl = bus_space_read_4(sc->sc_bt[FHC_INTERNAL],
116	    sc->sc_bh[FHC_INTERNAL], FHC_CTRL);
117	if ((sc->sc_flags & FHC_CENTRAL) == 0)
118		ctrl |= FHC_CTRL_IXIST;
119	ctrl &= ~(FHC_CTRL_AOFF | FHC_CTRL_BOFF | FHC_CTRL_SLINE);
120	bus_space_write_4(sc->sc_bt[FHC_INTERNAL], sc->sc_bh[FHC_INTERNAL],
121	    FHC_CTRL, ctrl);
122	bus_space_read_4(sc->sc_bt[FHC_INTERNAL], sc->sc_bh[FHC_INTERNAL],
123	    FHC_CTRL);
124
125	sc->sc_nrange = OF_getprop_alloc(node, "ranges",
126	    sizeof(*sc->sc_ranges), (void **)&sc->sc_ranges);
127	if (sc->sc_nrange == -1) {
128		device_printf(dev, "can't get ranges\n");
129		return (ENXIO);
130	}
131
132	if ((sc->sc_flags & FHC_CENTRAL) == 0) {
133		snprintf(ledname, sizeof(ledname), "board%d", sc->sc_board);
134		sc->sc_led_dev = led_create(fhc_led_func, sc, ledname);
135	}
136
137	for (child = OF_child(node); child != 0; child = OF_peer(child)) {
138		fdi = malloc(sizeof(*fdi), M_DEVBUF, M_WAITOK | M_ZERO);
139		if (ofw_bus_gen_setup_devinfo(&fdi->fdi_obdinfo, child) != 0) {
140			free(fdi, M_DEVBUF);
141			continue;
142		}
143		nreg = OF_getprop_alloc(child, "reg", sizeof(*reg),
144		    (void **)&reg);
145		if (nreg == -1) {
146			device_printf(dev, "<%s>: incomplete\n",
147			    fdi->fdi_obdinfo.obd_name);
148			ofw_bus_gen_destroy_devinfo(&fdi->fdi_obdinfo);
149			free(fdi, M_DEVBUF);
150			continue;
151		}
152		resource_list_init(&fdi->fdi_rl);
153		for (i = 0; i < nreg; i++)
154			resource_list_add(&fdi->fdi_rl, SYS_RES_MEMORY, i,
155			    reg[i].sbr_offset, reg[i].sbr_offset +
156			    reg[i].sbr_size, reg[i].sbr_size);
157		free(reg, M_OFWPROP);
158		nintr = OF_getprop_alloc(child, "interrupts", sizeof(*intr),
159		    (void **)&intr);
160		if (nintr != -1) {
161			for (i = 0; i < nintr; i++) {
162				iv = INTINO(intr[i]) |
163				    (sc->sc_ign << INTMAP_IGN_SHIFT);
164				resource_list_add(&fdi->fdi_rl, SYS_RES_IRQ, i,
165				    iv, iv, 1);
166			}
167			free(intr, M_OFWPROP);
168		}
169		cdev = device_add_child(dev, NULL, -1);
170		if (cdev == NULL) {
171			device_printf(dev, "<%s>: device_add_child failed\n",
172			    fdi->fdi_obdinfo.obd_name);
173			resource_list_free(&fdi->fdi_rl);
174			ofw_bus_gen_destroy_devinfo(&fdi->fdi_obdinfo);
175			free(fdi, M_DEVBUF);
176			continue;
177		}
178		device_set_ivars(cdev, fdi);
179	}
180
181	return (bus_generic_attach(dev));
182}
183
184int
185fhc_print_child(device_t dev, device_t child)
186{
187	int rv;
188
189	rv = bus_print_child_header(dev, child);
190	rv += fhc_print_res(device_get_ivars(child));
191	rv += bus_print_child_footer(dev, child);
192	return (rv);
193}
194
195void
196fhc_probe_nomatch(device_t dev, device_t child)
197{
198	const char *type;
199
200	device_printf(dev, "<%s>", ofw_bus_get_name(child));
201	fhc_print_res(device_get_ivars(child));
202	type = ofw_bus_get_type(child);
203	printf(" type %s (no driver attached)\n",
204	    type != NULL ? type : "unknown");
205}
206
207int
208fhc_setup_intr(device_t bus, device_t child, struct resource *r, int flags,
209    driver_intr_t *func, void *arg, void **cookiep)
210{
211	struct fhc_softc *sc;
212	struct fhc_clr *fc;
213	bus_space_tag_t bt;
214	bus_space_handle_t bh;
215	int error;
216	int i;
217	long vec;
218	uint32_t inr;
219
220	sc = device_get_softc(bus);
221	vec = rman_get_start(r);
222
223	bt = NULL;
224	bh = 0;
225	inr = 0;
226	for (i = FHC_FANFAIL; i <= FHC_TOD; i++) {
227		if (INTINO(bus_space_read_4(sc->sc_bt[i], sc->sc_bh[i],
228		    FHC_IMAP)) == INTINO(vec)){
229			bt = sc->sc_bt[i];
230			bh = sc->sc_bh[i];
231			inr = INTINO(vec) | (sc->sc_ign << INTMAP_IGN_SHIFT);
232			break;
233		}
234	}
235	if (inr == 0)
236		return (0);
237
238	fc = malloc(sizeof(*fc), M_DEVBUF, M_WAITOK | M_ZERO);
239	if (fc == NULL)
240		return (0);
241	fc->fc_func = func;
242	fc->fc_arg = arg;
243	fc->fc_bt = bt;
244	fc->fc_bh = bh;
245
246	bus_space_write_4(bt, bh, FHC_IMAP, inr);
247	bus_space_read_4(bt, bh, FHC_IMAP);
248
249	error = bus_generic_setup_intr(bus, child, r, flags, fhc_intr_stub,
250	    fc, cookiep);
251	if (error != 0) {
252		free(fc, M_DEVBUF);
253		return (error);
254	}
255	fc->fc_cookie = *cookiep;
256	*cookiep = fc;
257
258	bus_space_write_4(bt, bh, FHC_ICLR, 0x0);
259	bus_space_write_4(bt, bh, FHC_IMAP, INTMAP_ENABLE(inr, PCPU_GET(mid)));
260	bus_space_read_4(bt, bh, FHC_IMAP);
261
262	return (error);
263}
264
265int
266fhc_teardown_intr(device_t bus, device_t child, struct resource *r,
267    void *cookie)
268{
269	struct fhc_clr *fc;
270	int error;
271
272	fc = cookie;
273	error = bus_generic_teardown_intr(bus, child, r, fc->fc_cookie);
274	if (error != 0)
275		free(fc, M_DEVBUF);
276	return (error);
277}
278
279static void
280fhc_intr_stub(void *arg)
281{
282	struct fhc_clr *fc = arg;
283
284	fc->fc_func(fc->fc_arg);
285
286	bus_space_write_4(fc->fc_bt, fc->fc_bh, FHC_ICLR, 0x0);
287	bus_space_read_4(fc->fc_bt, fc->fc_bh, FHC_ICLR);
288}
289
290struct resource *
291fhc_alloc_resource(device_t bus, device_t child, int type, int *rid,
292    u_long start, u_long end, u_long count, u_int flags)
293{
294	struct resource_list *rl;
295	struct resource_list_entry *rle;
296	struct fhc_softc *sc;
297	struct resource *res;
298	bus_addr_t coffset;
299	bus_addr_t cend;
300	bus_addr_t phys;
301	int isdefault;
302	int passthrough;
303	int i;
304
305	isdefault = (start == 0UL && end == ~0UL);
306	passthrough = (device_get_parent(child) != bus);
307	res = NULL;
308	rle = NULL;
309	rl = BUS_GET_RESOURCE_LIST(bus, child);
310	sc = device_get_softc(bus);
311	rle = resource_list_find(rl, type, *rid);
312	switch (type) {
313	case SYS_RES_IRQ:
314		return (resource_list_alloc(rl, bus, child, type, rid, start,
315		    end, count, flags));
316	case SYS_RES_MEMORY:
317		if (!passthrough) {
318			if (rle == NULL)
319				return (NULL);
320			if (rle->res != NULL)
321				panic("%s: resource entry is busy", __func__);
322			if (isdefault) {
323				start = rle->start;
324				count = ulmax(count, rle->count);
325				end = ulmax(rle->end, start + count - 1);
326			}
327		}
328		for (i = 0; i < sc->sc_nrange; i++) {
329			coffset = sc->sc_ranges[i].coffset;
330			cend = coffset + sc->sc_ranges[i].size - 1;
331			if (start >= coffset && end <= cend) {
332				start -= coffset;
333				end -= coffset;
334				phys = sc->sc_ranges[i].poffset |
335				    ((bus_addr_t)sc->sc_ranges[i].pspace << 32);
336				res = bus_generic_alloc_resource(bus, child,
337				    type, rid, phys + start, phys + end,
338				    count, flags);
339				if (!passthrough)
340					rle->res = res;
341				break;
342			}
343		}
344		break;
345	}
346	return (res);
347}
348
349struct resource_list *
350fhc_get_resource_list(device_t bus, device_t child)
351{
352	struct fhc_devinfo *fdi;
353
354	fdi = device_get_ivars(child);
355	return (&fdi->fdi_rl);
356}
357
358const struct ofw_bus_devinfo *
359fhc_get_devinfo(device_t bus, device_t child)
360{
361	struct fhc_devinfo *fdi;
362
363	fdi = device_get_ivars(child);
364	return (&fdi->fdi_obdinfo);
365}
366
367static void
368fhc_led_func(void *arg, int onoff)
369{
370	struct fhc_softc *sc;
371	uint32_t ctrl;
372
373	sc = (struct fhc_softc *)arg;
374
375	ctrl = bus_space_read_4(sc->sc_bt[FHC_INTERNAL],
376	    sc->sc_bh[FHC_INTERNAL], FHC_CTRL);
377	if (onoff)
378		ctrl |= FHC_CTRL_RLED;
379	else
380		ctrl &= ~FHC_CTRL_RLED;
381	ctrl &= ~(FHC_CTRL_AOFF | FHC_CTRL_BOFF | FHC_CTRL_SLINE);
382	bus_space_write_4(sc->sc_bt[FHC_INTERNAL], sc->sc_bh[FHC_INTERNAL],
383	    FHC_CTRL, ctrl);
384	bus_space_read_4(sc->sc_bt[FHC_INTERNAL], sc->sc_bh[FHC_INTERNAL],
385	    FHC_CTRL);
386}
387
388static int
389fhc_print_res(struct fhc_devinfo *fdi)
390{
391	int rv;
392
393	rv = 0;
394	rv += resource_list_print_type(&fdi->fdi_rl, "mem", SYS_RES_MEMORY,
395	    "%#lx");
396	rv += resource_list_print_type(&fdi->fdi_rl, "irq", SYS_RES_IRQ, "%ld");
397	return (rv);
398}
399