fhc.c revision 143129
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 143129 2005-03-04 16:01:57Z 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/ofw/ofw_bus.h>
38#include <dev/ofw/openfirm.h>
39
40#include <machine/bus.h>
41#include <machine/bus_common.h>
42#include <machine/resource.h>
43
44#include <sys/rman.h>
45
46#include <sparc64/fhc/fhcreg.h>
47#include <sparc64/fhc/fhcvar.h>
48#include <sparc64/sbus/ofw_sbus.h>
49
50struct fhc_clr {
51	driver_intr_t		*fc_func;
52	void			*fc_arg;
53	void			*fc_cookie;
54	bus_space_tag_t		fc_bt;
55	bus_space_handle_t	fc_bh;
56};
57
58struct fhc_devinfo {
59	char			*fdi_compat;
60	char			*fdi_model;
61	char			*fdi_name;
62	char			*fdi_type;
63	phandle_t		fdi_node;
64	struct resource_list	fdi_rl;
65};
66
67static void fhc_intr_stub(void *);
68
69int
70fhc_probe(device_t dev)
71{
72
73	return (0);
74}
75
76int
77fhc_attach(device_t dev)
78{
79	struct fhc_devinfo *fdi;
80	struct sbus_regs *reg;
81	struct fhc_softc *sc;
82	phandle_t child;
83	phandle_t node;
84	bus_addr_t size;
85	bus_addr_t off;
86	device_t cdev;
87	uint32_t ctrl;
88	char *name;
89	int nreg;
90	int i;
91
92	sc = device_get_softc(dev);
93	node = sc->sc_node;
94
95	if (OF_getprop_alloc(node, "board-model", 1, (void **)&name) != -1) {
96		device_printf(dev, "board %d, %s\n", sc->sc_board, name);
97		free(name, M_OFWPROP);
98	}
99
100	for (i = FHC_FANFAIL; i <= FHC_TOD; i++) {
101		bus_space_write_4(sc->sc_bt[i], sc->sc_bh[i], FHC_ICLR, 0x0);
102		bus_space_read_4(sc->sc_bt[i], sc->sc_bh[i], FHC_ICLR);
103	}
104
105	sc->sc_ign = sc->sc_board << 1;
106	bus_space_write_4(sc->sc_bt[FHC_IGN], sc->sc_bh[FHC_IGN], 0x0,
107	    sc->sc_ign);
108	sc->sc_ign = bus_space_read_4(sc->sc_bt[FHC_IGN],
109	    sc->sc_bh[FHC_IGN], 0x0);
110
111	ctrl = bus_space_read_4(sc->sc_bt[FHC_INTERNAL],
112	    sc->sc_bh[FHC_INTERNAL], FHC_CTRL);
113	if ((sc->sc_flags & FHC_CENTRAL) == 0)
114		ctrl |= FHC_CTRL_IXIST;
115	ctrl &= ~(FHC_CTRL_AOFF | FHC_CTRL_BOFF | FHC_CTRL_SLINE);
116	bus_space_write_4(sc->sc_bt[FHC_INTERNAL], sc->sc_bh[FHC_INTERNAL],
117	    FHC_CTRL, ctrl);
118	ctrl = bus_space_read_4(sc->sc_bt[FHC_INTERNAL],
119	    sc->sc_bh[FHC_INTERNAL], FHC_CTRL);
120
121	sc->sc_nrange = OF_getprop_alloc(node, "ranges",
122	    sizeof(*sc->sc_ranges), (void **)&sc->sc_ranges);
123	if (sc->sc_nrange == -1) {
124		device_printf(dev, "can't get ranges\n");
125		return (ENXIO);
126	}
127
128	for (child = OF_child(node); child != 0; child = OF_peer(child)) {
129		if ((OF_getprop_alloc(child, "name", 1, (void **)&name)) == -1)
130			continue;
131		cdev = device_add_child(dev, NULL, -1);
132		if (cdev != NULL) {
133			fdi = malloc(sizeof(*fdi), M_DEVBUF, M_WAITOK | M_ZERO);
134			if (fdi == NULL)
135				continue;
136			fdi->fdi_name = name;
137			fdi->fdi_node = child;
138			OF_getprop_alloc(child, "compatible", 1,
139			    (void **)&fdi->fdi_compat);
140			OF_getprop_alloc(child, "device_type", 1,
141			    (void **)&fdi->fdi_type);
142			OF_getprop_alloc(child, "model", 1,
143			    (void **)&fdi->fdi_model);
144			resource_list_init(&fdi->fdi_rl);
145			nreg = OF_getprop_alloc(child, "reg", sizeof(*reg),
146			    (void **)&reg);
147			if (nreg != -1) {
148				for (i = 0; i < nreg; i++) {
149					off = reg[i].sbr_offset;
150					size = reg[i].sbr_size;
151					resource_list_add(&fdi->fdi_rl,
152					    SYS_RES_MEMORY, i, off, off + size,
153					    size);
154				}
155				free(reg, M_OFWPROP);
156			}
157			device_set_ivars(cdev, fdi);
158		} else
159			free(name, M_OFWPROP);
160	}
161
162	return (bus_generic_attach(dev));
163}
164
165int
166fhc_print_child(device_t dev, device_t child)
167{
168	struct fhc_devinfo *fdi;
169	int rv;
170
171	fdi = device_get_ivars(child);
172	rv = bus_print_child_header(dev, child);
173	rv += resource_list_print_type(&fdi->fdi_rl, "mem",
174	    SYS_RES_MEMORY, "%#lx");
175	rv += bus_print_child_footer(dev, child);
176	return (rv);
177}
178
179void
180fhc_probe_nomatch(device_t dev, device_t child)
181{
182	struct fhc_devinfo *fdi;
183
184	fdi = device_get_ivars(child);
185	device_printf(dev, "<%s>", fdi->fdi_name);
186	resource_list_print_type(&fdi->fdi_rl, "mem", SYS_RES_MEMORY, "%#lx");
187	printf(" type %s (no driver attached)\n",
188	    fdi->fdi_type != NULL ? fdi->fdi_type : "unknown");
189}
190
191int
192fhc_setup_intr(device_t bus, device_t child, struct resource *r, int flags,
193    driver_intr_t *func, void *arg, void **cookiep)
194{
195	struct fhc_softc *sc;
196	struct fhc_clr *fc;
197	int error;
198	int rid;
199
200	sc = device_get_softc(bus);
201	rid = rman_get_rid(r);
202
203	fc = malloc(sizeof(*fc), M_DEVBUF, M_WAITOK | M_ZERO);
204	if (fc == NULL)
205		return (0);
206	fc->fc_func = func;
207	fc->fc_arg = arg;
208	fc->fc_bt = sc->sc_bt[rid];
209	fc->fc_bh = sc->sc_bh[rid];
210
211	bus_space_write_4(sc->sc_bt[rid], sc->sc_bh[rid], FHC_IMAP,
212	    rman_get_start(r));
213	bus_space_read_4(sc->sc_bt[rid], sc->sc_bh[rid], FHC_IMAP);
214
215	error = bus_generic_setup_intr(bus, child, r, flags, fhc_intr_stub,
216	    fc, cookiep);
217	if (error != 0) {
218		free(fc, M_DEVBUF);
219		return (error);
220	}
221	fc->fc_cookie = *cookiep;
222	*cookiep = fc;
223
224	bus_space_write_4(sc->sc_bt[rid], sc->sc_bh[rid], FHC_ICLR, 0x0);
225	bus_space_write_4(sc->sc_bt[rid], sc->sc_bh[rid], FHC_IMAP,
226	    INTMAP_ENABLE(rman_get_start(r), PCPU_GET(mid)));
227	bus_space_read_4(sc->sc_bt[rid], sc->sc_bh[rid], FHC_IMAP);
228
229	return (error);
230}
231
232int
233fhc_teardown_intr(device_t bus, device_t child, struct resource *r,
234    void *cookie)
235{
236	struct fhc_clr *fc;
237	int error;
238
239	fc = cookie;
240	error = bus_generic_teardown_intr(bus, child, r, fc->fc_cookie);
241	if (error != 0)
242		free(fc, M_DEVBUF);
243	return (error);
244}
245
246static void
247fhc_intr_stub(void *arg)
248{
249	struct fhc_clr *fc = arg;
250
251	fc->fc_func(fc->fc_arg);
252
253	bus_space_write_4(fc->fc_bt, fc->fc_bh, FHC_ICLR, 0x0);
254	bus_space_read_4(fc->fc_bt, fc->fc_bh, FHC_ICLR);
255}
256
257struct resource *
258fhc_alloc_resource(device_t bus, device_t child, int type, int *rid,
259    u_long start, u_long end, u_long count, u_int flags)
260{
261	struct resource_list_entry *rle;
262	struct fhc_devinfo *fdi;
263	struct fhc_softc *sc;
264	struct resource *res;
265	bus_addr_t coffset;
266	bus_addr_t cend;
267	bus_addr_t phys;
268	int isdefault;
269	uint32_t map;
270	uint32_t vec;
271	int i;
272
273	isdefault = (start == 0UL && end == ~0UL);
274	res = NULL;
275	sc = device_get_softc(bus);
276	switch (type) {
277	case SYS_RES_IRQ:
278		if (!isdefault || count != 1 || *rid < FHC_FANFAIL ||
279		    *rid > FHC_TOD)
280			break;
281
282		map = bus_space_read_4(sc->sc_bt[*rid], sc->sc_bh[*rid],
283		    FHC_IMAP);
284		vec = INTINO(map) | (sc->sc_ign << INTMAP_IGN_SHIFT);
285		bus_space_write_4(sc->sc_bt[*rid], sc->sc_bh[*rid],
286		    FHC_IMAP, vec);
287		bus_space_read_4(sc->sc_bt[*rid], sc->sc_bh[*rid], FHC_IMAP);
288
289		res = bus_generic_alloc_resource(bus, child, type, rid,
290		    vec, vec, 1, flags);
291		if (res != NULL)
292			rman_set_rid(res, *rid);
293		break;
294	case SYS_RES_MEMORY:
295		fdi = device_get_ivars(child);
296		rle = resource_list_find(&fdi->fdi_rl, type, *rid);
297		if (rle == NULL)
298			return (NULL);
299		if (rle->res != NULL)
300			panic("%s: resource entry is busy", __func__);
301		if (isdefault) {
302			start = rle->start;
303			count = ulmax(count, rle->count);
304			end = ulmax(rle->end, start + count - 1);
305		}
306		for (i = 0; i < sc->sc_nrange; i++) {
307			coffset = sc->sc_ranges[i].coffset;
308			cend = coffset + sc->sc_ranges[i].size - 1;
309			if (start >= coffset && end <= cend) {
310				start -= coffset;
311				end -= coffset;
312				phys = sc->sc_ranges[i].poffset |
313				    ((bus_addr_t)sc->sc_ranges[i].pspace << 32);
314				res = bus_generic_alloc_resource(bus, child,
315				    type, rid, phys + start, phys + end,
316				    count, flags);
317				rle->res = res;
318				break;
319			}
320		}
321		break;
322	default:
323		break;
324	}
325	return (res);
326}
327
328int
329fhc_release_resource(device_t bus, device_t child, int type, int rid,
330    struct resource *r)
331{
332	struct resource_list_entry *rle;
333	struct fhc_devinfo *fdi;
334	int error;
335
336	error = bus_generic_release_resource(bus, child, type, rid, r);
337	if (type != SYS_RES_MEMORY || error != 0)
338		return (error);
339	fdi = device_get_ivars(child);
340	rle = resource_list_find(&fdi->fdi_rl, type, rid);
341	if (rle == NULL)
342		panic("%s: can't find resource", __func__);
343	if (rle->res == NULL)
344		panic("%s: resource entry is not busy", __func__);
345	rle->res = NULL;
346	return (error);
347}
348
349const char *
350fhc_get_compat(device_t bus, device_t dev)
351{
352	struct fhc_devinfo *dinfo;
353
354	dinfo = device_get_ivars(dev);
355	return (dinfo->fdi_compat);
356}
357
358const char *
359fhc_get_model(device_t bus, device_t dev)
360{
361	struct fhc_devinfo *dinfo;
362
363	dinfo = device_get_ivars(dev);
364	return (dinfo->fdi_model);
365}
366
367const char *
368fhc_get_name(device_t bus, device_t dev)
369{
370	struct fhc_devinfo *dinfo;
371
372	dinfo = device_get_ivars(dev);
373	return (dinfo->fdi_name);
374}
375
376phandle_t
377fhc_get_node(device_t bus, device_t dev)
378{
379	struct fhc_devinfo *dinfo;
380
381	dinfo = device_get_ivars(dev);
382	return (dinfo->fdi_node);
383}
384
385const char *
386fhc_get_type(device_t bus, device_t dev)
387{
388	struct fhc_devinfo *dinfo;
389
390	dinfo = device_get_ivars(dev);
391	return (dinfo->fdi_type);
392}
393