pxa_smi.c revision 179595
1/*-
2 * Copyright (c) 2006 Benno Rice.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include <sys/cdefs.h>
26__FBSDID("$FreeBSD: head/sys/arm/xscale/pxa/pxa_smi.c 179595 2008-06-06 05:08:09Z benno $");
27
28#include <sys/param.h>
29#include <sys/systm.h>
30#include <sys/bus.h>
31#include <sys/kernel.h>
32#include <sys/module.h>
33#include <sys/malloc.h>
34#include <sys/rman.h>
35#include <sys/timetc.h>
36#include <machine/bus.h>
37#include <machine/intr.h>
38
39#include <vm/vm.h>
40#include <vm/pmap.h>
41
42#include <arm/xscale/pxa/pxavar.h>
43#include <arm/xscale/pxa/pxareg.h>
44
45MALLOC_DEFINE(M_PXASMI, "PXA SMI", "Data for static memory interface devices.");
46
47struct pxa_smi_softc {
48	struct resource	*ps_res[1];
49	struct rman	ps_mem;
50	bus_space_tag_t	ps_bst;
51	bus_addr_t	ps_base;
52};
53
54struct smi_ivars {
55	struct resource_list	smid_resources;
56	bus_addr_t		smid_mem;
57};
58
59static struct resource_spec pxa_smi_spec[] = {
60	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
61	{ -1, 0 }
62};
63
64static int	pxa_smi_probe(device_t);
65static int	pxa_smi_attach(device_t);
66
67static int	pxa_smi_print_child(device_t, device_t);
68
69static int	pxa_smi_read_ivar(device_t, device_t, int, uintptr_t *);
70
71static struct resource *	pxa_smi_alloc_resource(device_t, device_t,
72				    int, int *, u_long, u_long, u_long, u_int);
73static int			pxa_smi_release_resource(device_t, device_t,
74				    int, int, struct resource *);
75static int			pxa_smi_activate_resource(device_t, device_t,
76				    int, int, struct resource *);
77
78static void	pxa_smi_add_device(device_t, const char *, int);
79
80static int
81pxa_smi_probe(device_t dev)
82{
83
84	if (resource_disabled("smi", device_get_unit(dev)))
85		return (ENXIO);
86
87	device_set_desc(dev, "Static Memory Interface");
88	return (0);
89}
90
91static int
92pxa_smi_attach(device_t dev)
93{
94	int		error, i, dunit;
95	const char	*dname;
96	struct		pxa_smi_softc *sc;
97
98	sc = (struct pxa_smi_softc *)device_get_softc(dev);
99
100	error = bus_alloc_resources(dev, pxa_smi_spec, sc->ps_res);
101	if (error) {
102		device_printf(dev, "could not allocate resources\n");
103		return (ENXIO);
104	}
105
106	sc->ps_mem.rm_type = RMAN_ARRAY;
107	sc->ps_mem.rm_descr = device_get_nameunit(dev);
108	if (rman_init(&sc->ps_mem) != 0)
109		panic("pxa_smi_attach: failed to init mem rman");
110	if (rman_manage_region(&sc->ps_mem, 0, PXA2X0_CS_SIZE * 6) != 0)
111		panic("pxa_smi_attach: failed ot set up mem rman");
112
113	sc->ps_bst = base_tag;
114	sc->ps_base = rman_get_start(sc->ps_res[0]);
115
116	i = 0;
117	while (resource_find_match(&i, &dname, &dunit, "at",
118	    device_get_nameunit(dev)) == 0) {
119		pxa_smi_add_device(dev, dname, dunit);
120	}
121
122	bus_generic_probe(dev);
123	bus_generic_attach(dev);
124
125	return (0);
126}
127
128static int
129pxa_smi_print_child(device_t dev, device_t child)
130{
131	struct	smi_ivars *smid;
132	int	retval;
133
134	smid = (struct smi_ivars *)device_get_ivars(child);
135	if (smid == NULL) {
136		device_printf(dev, "unknown device: %s\n",
137		    device_get_nameunit(child));
138		return (0);
139	}
140
141	retval = 0;
142
143	retval += bus_print_child_header(dev, child);
144
145	retval += resource_list_print_type(&smid->smid_resources, "at mem",
146	    SYS_RES_MEMORY, "%#lx");
147	retval += resource_list_print_type(&smid->smid_resources, "irq",
148	    SYS_RES_IRQ, "%ld");
149
150	retval += bus_print_child_footer(dev, child);
151
152	return (retval);
153}
154
155static int
156pxa_smi_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
157{
158	struct	pxa_smi_softc *sc;
159	struct	smi_ivars *smid;
160
161	sc = device_get_softc(dev);
162	smid = device_get_ivars(child);
163
164	switch (which) {
165	case SMI_IVAR_PHYSBASE:
166		*((bus_addr_t *)result) = smid->smid_mem;
167		break;
168
169	default:
170		return (ENOENT);
171	}
172
173	return (0);
174}
175
176static struct resource *
177pxa_smi_alloc_resource(device_t dev, device_t child, int type, int *rid,
178    u_long start, u_long end, u_long count, u_int flags)
179{
180	struct	pxa_smi_softc *sc;
181	struct	smi_ivars *smid;
182	struct	resource *rv;
183	struct	resource_list *rl;
184	struct	resource_list_entry *rle;
185	int	needactivate;
186
187	sc = (struct pxa_smi_softc *)device_get_softc(dev);
188	smid = (struct smi_ivars *)device_get_ivars(child);
189	rl = &smid->smid_resources;
190
191	if (type == SYS_RES_IOPORT)
192		type = SYS_RES_MEMORY;
193
194	rle = resource_list_find(rl, type, *rid);
195	if (rle == NULL)
196		return (NULL);
197	if (rle->res != NULL)
198		panic("pxa_smi_alloc_resource: resource is busy");
199
200	needactivate = flags & RF_ACTIVE;
201	flags &= ~RF_ACTIVE;
202
203	switch (type) {
204	case SYS_RES_MEMORY:
205		rv = rman_reserve_resource(&sc->ps_mem, rle->start, rle->end,
206		    rle->count, flags, child);
207		if (rv == NULL)
208			return (NULL);
209		rle->res = rv;
210		rman_set_rid(rv, *rid);
211		rman_set_bustag(rv, sc->ps_bst);
212		rman_set_bushandle(rv, rle->start);
213		if (needactivate) {
214			if (bus_activate_resource(child, type, *rid, rv) != 0) {
215				rman_release_resource(rv);
216				return (NULL);
217			}
218		}
219
220		break;
221
222	case SYS_RES_IRQ:
223		rv = bus_alloc_resource(dev, type, rid, rle->start, rle->end,
224		    rle->count, flags);
225		if (rv == NULL)
226			return (NULL);
227		if (needactivate) {
228			if (bus_activate_resource(child, type, *rid, rv) != 0) {
229				bus_release_resource(dev, type, *rid, rv);
230				return (NULL);
231			}
232		}
233
234		break;
235
236	default:
237		return (NULL);
238	}
239
240	return (rv);
241}
242
243static int
244pxa_smi_release_resource(device_t dev, device_t child, int type, int rid,
245    struct resource *r)
246{
247	struct	smi_ivars *smid;
248	struct	resource_list *rl;
249	struct	resource_list_entry *rle;
250
251	if (type == SYS_RES_IRQ)
252		return (bus_release_resource(dev, SYS_RES_IRQ, rid, r));
253
254	smid = (struct smi_ivars *)device_get_ivars(child);
255	rl = &smid->smid_resources;
256
257	if (type == SYS_RES_IOPORT)
258		type = SYS_RES_MEMORY;
259
260	rle = resource_list_find(rl, type, rid);
261	if (rle == NULL)
262		panic("pxa_smi_release_resource: can't find resource");
263	if (rle->res == NULL)
264		panic("pxa_smi_release_resource: resource entry not busy");
265
266	rman_release_resource(rle->res);
267	rle->res = NULL;
268
269	return (0);
270}
271
272static int
273pxa_smi_activate_resource(device_t dev, device_t child, int type, int rid,
274    struct resource *r)
275{
276	struct	pxa_smi_softc *sc;
277
278	sc = (struct pxa_smi_softc *)device_get_softc(dev);
279
280	if (type == SYS_RES_IRQ)
281		return (bus_activate_resource(dev, SYS_RES_IRQ, rid, r));
282
283	rman_set_bushandle(r, (bus_space_handle_t)pmap_mapdev(rman_get_start(r),
284	    rman_get_size(r)));
285	return (rman_activate_resource(r));
286}
287
288static device_method_t pxa_smi_methods[] = {
289	DEVMETHOD(device_probe, pxa_smi_probe),
290	DEVMETHOD(device_attach, pxa_smi_attach),
291
292	DEVMETHOD(bus_print_child, pxa_smi_print_child),
293
294	DEVMETHOD(bus_read_ivar, pxa_smi_read_ivar),
295
296	DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
297
298	DEVMETHOD(bus_alloc_resource, pxa_smi_alloc_resource),
299	DEVMETHOD(bus_release_resource, pxa_smi_release_resource),
300	DEVMETHOD(bus_activate_resource, pxa_smi_activate_resource),
301
302	{0, 0}
303};
304
305static driver_t pxa_smi_driver = {
306	"smi",
307	pxa_smi_methods,
308	sizeof(struct pxa_smi_softc),
309};
310
311static devclass_t pxa_smi_devclass;
312
313DRIVER_MODULE(smi, pxa, pxa_smi_driver, pxa_smi_devclass, 0, 0);
314
315static void
316pxa_smi_add_device(device_t dev, const char *name, int unit)
317{
318	device_t	child;
319	int		start, count;
320	struct		smi_ivars *ivars;
321
322	ivars = (struct smi_ivars *)malloc(
323	    sizeof(struct smi_ivars), M_PXASMI, M_WAITOK);
324	if (ivars == NULL)
325		return;
326
327	child = device_add_child(dev, name, unit);
328	if (child == NULL) {
329		free(ivars, M_PXASMI);
330		return;
331	}
332
333	device_set_ivars(child, ivars);
334	resource_list_init(&ivars->smid_resources);
335
336	start = 0;
337	count = 0;
338	resource_int_value(name, unit, "mem", &start);
339	resource_int_value(name, unit, "size", &count);
340	if (start > 0 || count > 0) {
341		resource_list_add(&ivars->smid_resources, SYS_RES_MEMORY, 0,
342		    start, start + count, count);
343		ivars->smid_mem = (bus_addr_t)start;
344	}
345
346	start = -1;
347	count = 0;
348	resource_int_value(name, unit, "irq", &start);
349	if (start > -1)
350		resource_list_add(&ivars->smid_resources, SYS_RES_IRQ, 0, start,
351		     start, 1);
352
353	if (resource_disabled(name, unit))
354		device_disable(child);
355}
356