quicc_core.c revision 176772
1/*-
2 * Copyright 2006 by Juniper Networks.
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 * 3. The name of the author may not be used to endorse or promote products
14 *    derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: head/sys/dev/quicc/quicc_core.c 176772 2008-03-03 18:20:17Z raj $");
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/bus.h>
35#include <sys/conf.h>
36#include <sys/endian.h>
37#include <sys/kernel.h>
38#include <sys/malloc.h>
39#include <sys/queue.h>
40#include <sys/serial.h>
41
42#include <machine/bus.h>
43#include <machine/resource.h>
44#include <sys/rman.h>
45
46#include <dev/ic/quicc.h>
47
48#include <dev/quicc/quicc_bfe.h>
49#include <dev/quicc/quicc_bus.h>
50
51#define	quicc_read2(r, o)	\
52	bus_space_read_2((r)->r_bustag, (r)->r_bushandle, o)
53#define	quicc_read4(r, o)	\
54	bus_space_read_4((r)->r_bustag, (r)->r_bushandle, o)
55
56#define	quicc_write2(r, o, v)	\
57	bus_space_write_2((r)->r_bustag, (r)->r_bushandle, o, v)
58#define	quicc_write4(r, o, v)	\
59	bus_space_write_4((r)->r_bustag, (r)->r_bushandle, o, v)
60
61devclass_t quicc_devclass;
62char quicc_driver_name[] = "quicc";
63
64MALLOC_DEFINE(M_QUICC, "QUICC", "QUICC driver");
65
66struct quicc_device {
67	struct rman	*qd_rman;
68	struct resource_list qd_rlist;
69	device_t	qd_dev;
70	int		qd_devtype;
71
72	driver_filter_t	*qd_ih;
73	void		*qd_ih_arg;
74};
75
76static int
77quicc_bfe_intr(void *arg)
78{
79	struct quicc_device *qd;
80	struct quicc_softc *sc = arg;
81	uint32_t sipnr;
82
83	sipnr = quicc_read4(sc->sc_rres, QUICC_REG_SIPNR_L);
84	if (sipnr & 0x00f00000)
85		qd = sc->sc_device;
86	else
87		qd = NULL;
88
89	if (qd == NULL || qd->qd_ih == NULL) {
90		device_printf(sc->sc_dev, "Stray interrupt %08x\n", sipnr);
91		return (FILTER_STRAY);
92	}
93
94	return ((*qd->qd_ih)(qd->qd_ih_arg));
95}
96
97int
98quicc_bfe_attach(device_t dev)
99{
100	struct quicc_device *qd;
101	struct quicc_softc *sc;
102	struct resource_list_entry *rle;
103	const char *sep;
104	u_long size, start;
105	int error;
106
107	sc = device_get_softc(dev);
108
109	/*
110	 * Re-allocate. We expect that the softc contains the information
111	 * collected by quicc_bfe_probe() intact.
112	 */
113	sc->sc_rres = bus_alloc_resource(dev, sc->sc_rtype, &sc->sc_rrid,
114	    0, ~0, 0, RF_ACTIVE);
115	if (sc->sc_rres == NULL)
116		return (ENXIO);
117
118	start = rman_get_start(sc->sc_rres);
119	size = rman_get_size(sc->sc_rres);
120
121	sc->sc_rman.rm_start = start;
122	sc->sc_rman.rm_end = start + size - 1;
123	sc->sc_rman.rm_type = RMAN_ARRAY;
124	sc->sc_rman.rm_descr = "QUICC resources";
125	error = rman_init(&sc->sc_rman);
126	if (!error)
127		error = rman_manage_region(&sc->sc_rman, start,
128		    start + size - 1);
129	if (error) {
130		bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid,
131		    sc->sc_rres);
132		return (error);
133	}
134
135	/*
136	 * Allocate interrupt resource.
137	 */
138	sc->sc_irid = 0;
139	sc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irid,
140	    RF_ACTIVE | RF_SHAREABLE);
141
142	if (sc->sc_ires != NULL) {
143		error = bus_setup_intr(dev, sc->sc_ires,
144		    INTR_TYPE_TTY, quicc_bfe_intr, NULL, sc, &sc->sc_icookie);
145		if (error) {
146			error = bus_setup_intr(dev, sc->sc_ires,
147			    INTR_TYPE_TTY | INTR_MPSAFE, NULL,
148			    (driver_intr_t *)quicc_bfe_intr, sc,
149			    &sc->sc_icookie);
150		} else
151			sc->sc_fastintr = 1;
152		if (error) {
153			device_printf(dev, "could not activate interrupt\n");
154			bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid,
155			    sc->sc_ires);
156			sc->sc_ires = NULL;
157		}
158	}
159
160	if (sc->sc_ires == NULL)
161		sc->sc_polled = 1;
162
163	if (bootverbose && (sc->sc_fastintr || sc->sc_polled)) {
164		sep = "";
165		device_print_prettyname(dev);
166		if (sc->sc_fastintr) {
167			printf("%sfast interrupt", sep);
168			sep = ", ";
169		}
170		if (sc->sc_polled) {
171			printf("%spolled mode", sep);
172			sep = ", ";
173		}
174		printf("\n");
175	}
176
177	sc->sc_device = qd = malloc(sizeof(struct quicc_device), M_QUICC,
178	    M_WAITOK | M_ZERO);
179
180	qd->qd_devtype = QUICC_DEVTYPE_SCC;
181	qd->qd_rman = &sc->sc_rman;
182	resource_list_init(&qd->qd_rlist);
183
184	resource_list_add(&qd->qd_rlist, sc->sc_rtype, 0, start,
185	    start + size - 1, size);
186
187	resource_list_add(&qd->qd_rlist, SYS_RES_IRQ, 0, 0xf00, 0xf00, 1);
188	rle = resource_list_find(&qd->qd_rlist, SYS_RES_IRQ, 0);
189	rle->res = sc->sc_ires;
190
191	qd->qd_dev = device_add_child(dev, NULL, -1);
192	device_set_ivars(qd->qd_dev, (void *)qd);
193	error = device_probe_and_attach(qd->qd_dev);
194
195	/* Enable all SCC interrupts. */
196	quicc_write4(sc->sc_rres, QUICC_REG_SIMR_L, 0x00f00000);
197
198	/* Clear all pending interrupts. */
199	quicc_write4(sc->sc_rres, QUICC_REG_SIPNR_H, ~0);
200	quicc_write4(sc->sc_rres, QUICC_REG_SIPNR_L, ~0);
201	return (error);
202}
203
204int
205quicc_bfe_detach(device_t dev)
206{
207	struct quicc_softc *sc;
208
209	sc = device_get_softc(dev);
210
211	bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie);
212	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid, sc->sc_ires);
213	bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres);
214	return (0);
215}
216
217int
218quicc_bfe_probe(device_t dev, u_int clock)
219{
220	struct quicc_softc *sc;
221	uint16_t rev;
222
223	sc = device_get_softc(dev);
224	sc->sc_dev = dev;
225	if (device_get_desc(dev) == NULL)
226		device_set_desc(dev,
227		    "Quad integrated communications controller");
228
229	sc->sc_rrid = 0;
230	sc->sc_rtype = SYS_RES_MEMORY;
231	sc->sc_rres = bus_alloc_resource(dev, sc->sc_rtype, &sc->sc_rrid,
232	    0, ~0, 0, RF_ACTIVE);
233	if (sc->sc_rres == NULL) {
234		sc->sc_rrid = 0;
235		sc->sc_rtype = SYS_RES_IOPORT;
236		sc->sc_rres = bus_alloc_resource(dev, sc->sc_rtype,
237		    &sc->sc_rrid, 0, ~0, 0, RF_ACTIVE);
238		if (sc->sc_rres == NULL)
239			return (ENXIO);
240	}
241
242	sc->sc_clock = clock;
243
244	/*
245	 * Check that the microcode revision is 0x00e8, as documented
246	 * in the MPC8555E PowerQUICC III Integrated Processor Family
247	 * Reference Manual.
248	 */
249	rev = quicc_read2(sc->sc_rres, QUICC_PRAM_REV_NUM);
250
251	bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres);
252	return ((rev == 0x00e8) ? BUS_PROBE_DEFAULT : ENXIO);
253}
254
255struct resource *
256quicc_bus_alloc_resource(device_t dev, device_t child, int type, int *rid,
257    u_long start, u_long end, u_long count, u_int flags)
258{
259	struct quicc_device *qd;
260	struct resource_list_entry *rle;
261
262	if (device_get_parent(child) != dev)
263		return (NULL);
264
265	/* We only support default allocations. */
266	if (start != 0UL || end != ~0UL)
267		return (NULL);
268
269	qd = device_get_ivars(child);
270	rle = resource_list_find(&qd->qd_rlist, type, *rid);
271	if (rle == NULL)
272		return (NULL);
273
274	if (rle->res == NULL) {
275		rle->res = rman_reserve_resource(qd->qd_rman, rle->start,
276		    rle->start + rle->count - 1, rle->count, flags, child);
277		if (rle->res != NULL) {
278			rman_set_bustag(rle->res, &bs_be_tag);
279			rman_set_bushandle(rle->res, rle->start);
280		}
281	}
282	return (rle->res);
283}
284
285int
286quicc_bus_get_resource(device_t dev, device_t child, int type, int rid,
287    u_long *startp, u_long *countp)
288{
289	struct quicc_device *qd;
290	struct resource_list_entry *rle;
291
292	if (device_get_parent(child) != dev)
293		return (EINVAL);
294
295	qd = device_get_ivars(child);
296	rle = resource_list_find(&qd->qd_rlist, type, rid);
297	if (rle == NULL)
298		return (EINVAL);
299
300	if (startp != NULL)
301		*startp = rle->start;
302	if (countp != NULL)
303		*countp = rle->count;
304	return (0);
305}
306
307int
308quicc_bus_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
309{
310	struct quicc_device *qd;
311	struct quicc_softc *sc;
312	uint32_t sccr;
313
314	if (device_get_parent(child) != dev)
315		return (EINVAL);
316
317	sc = device_get_softc(dev);
318	qd = device_get_ivars(child);
319
320	switch (index) {
321	case QUICC_IVAR_CLOCK:
322		*result = sc->sc_clock;
323		break;
324	case QUICC_IVAR_BRGCLK:
325		sccr = quicc_read4(sc->sc_rres, QUICC_REG_SCCR) & 3;
326		*result = sc->sc_clock / ((1 << (sccr + 1)) << sccr);
327		break;
328	case QUICC_IVAR_DEVTYPE:
329		*result = qd->qd_devtype;
330		break;
331	default:
332		return (EINVAL);
333	}
334	return (0);
335}
336
337int
338quicc_bus_release_resource(device_t dev, device_t child, int type, int rid,
339    struct resource *res)
340{
341	struct quicc_device *qd;
342	struct resource_list_entry *rle;
343
344	if (device_get_parent(child) != dev)
345		return (EINVAL);
346
347	qd = device_get_ivars(child);
348	rle = resource_list_find(&qd->qd_rlist, type, rid);
349	return ((rle == NULL) ? EINVAL : 0);
350}
351
352int
353quicc_bus_setup_intr(device_t dev, device_t child, struct resource *r,
354    int flags, driver_filter_t *filt, void (*ihand)(void *), void *arg,
355    void **cookiep)
356{
357	struct quicc_device *qd;
358	struct quicc_softc *sc;
359
360	if (device_get_parent(child) != dev)
361		return (EINVAL);
362
363	/* Interrupt handlers must be FAST or MPSAFE. */
364	if (filt == NULL && !(flags & INTR_MPSAFE))
365		return (EINVAL);
366
367	sc = device_get_softc(dev);
368	if (sc->sc_polled)
369		return (ENXIO);
370
371	if (sc->sc_fastintr && filt == NULL) {
372		sc->sc_fastintr = 0;
373		bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie);
374		bus_setup_intr(dev, sc->sc_ires, INTR_TYPE_TTY | INTR_MPSAFE,
375		    NULL, (driver_intr_t *)quicc_bfe_intr, sc, &sc->sc_icookie);
376	}
377
378	qd = device_get_ivars(child);
379	qd->qd_ih = (filt != NULL) ? filt : (driver_filter_t *)ihand;
380	qd->qd_ih_arg = arg;
381	*cookiep = ihand;
382	return (0);
383}
384
385int
386quicc_bus_teardown_intr(device_t dev, device_t child, struct resource *r,
387    void *cookie)
388{
389	struct quicc_device *qd;
390
391	if (device_get_parent(child) != dev)
392		return (EINVAL);
393
394	qd = device_get_ivars(child);
395	if (qd->qd_ih != cookie)
396		return (EINVAL);
397
398	qd->qd_ih = NULL;
399	qd->qd_ih_arg = NULL;
400	return (0);
401}
402