quicc_core.c revision 176772
1238106Sdes/*-
2238106Sdes * Copyright 2006 by Juniper Networks.
3238106Sdes * All rights reserved.
4238106Sdes *
5238106Sdes * Redistribution and use in source and binary forms, with or without
6238106Sdes * modification, are permitted provided that the following conditions
7238106Sdes * are met:
8238106Sdes * 1. Redistributions of source code must retain the above copyright
9238106Sdes *    notice, this list of conditions and the following disclaimer.
10238106Sdes * 2. Redistributions in binary form must reproduce the above copyright
11238106Sdes *    notice, this list of conditions and the following disclaimer in the
12238106Sdes *    documentation and/or other materials provided with the distribution.
13238106Sdes * 3. The name of the author may not be used to endorse or promote products
14238106Sdes *    derived from this software without specific prior written permission.
15238106Sdes *
16238106Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17238106Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18238106Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19238106Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20238106Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21238106Sdes * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22238106Sdes * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23238106Sdes * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24238106Sdes * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25238106Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26238106Sdes * SUCH DAMAGE.
27238106Sdes */
28238106Sdes
29238106Sdes#include <sys/cdefs.h>
30238106Sdes__FBSDID("$FreeBSD: head/sys/dev/quicc/quicc_core.c 176772 2008-03-03 18:20:17Z raj $");
31238106Sdes
32238106Sdes#include <sys/param.h>
33238106Sdes#include <sys/systm.h>
34238106Sdes#include <sys/bus.h>
35238106Sdes#include <sys/conf.h>
36238106Sdes#include <sys/endian.h>
37238106Sdes#include <sys/kernel.h>
38238106Sdes#include <sys/malloc.h>
39238106Sdes#include <sys/queue.h>
40238106Sdes#include <sys/serial.h>
41238106Sdes
42238106Sdes#include <machine/bus.h>
43238106Sdes#include <machine/resource.h>
44238106Sdes#include <sys/rman.h>
45238106Sdes
46238106Sdes#include <dev/ic/quicc.h>
47238106Sdes
48238106Sdes#include <dev/quicc/quicc_bfe.h>
49238106Sdes#include <dev/quicc/quicc_bus.h>
50238106Sdes
51238106Sdes#define	quicc_read2(r, o)	\
52238106Sdes	bus_space_read_2((r)->r_bustag, (r)->r_bushandle, o)
53238106Sdes#define	quicc_read4(r, o)	\
54238106Sdes	bus_space_read_4((r)->r_bustag, (r)->r_bushandle, o)
55238106Sdes
56238106Sdes#define	quicc_write2(r, o, v)	\
57238106Sdes	bus_space_write_2((r)->r_bustag, (r)->r_bushandle, o, v)
58238106Sdes#define	quicc_write4(r, o, v)	\
59238106Sdes	bus_space_write_4((r)->r_bustag, (r)->r_bushandle, o, v)
60238106Sdes
61238106Sdesdevclass_t quicc_devclass;
62238106Sdeschar quicc_driver_name[] = "quicc";
63238106Sdes
64238106SdesMALLOC_DEFINE(M_QUICC, "QUICC", "QUICC driver");
65238106Sdes
66238106Sdesstruct quicc_device {
67238106Sdes	struct rman	*qd_rman;
68238106Sdes	struct resource_list qd_rlist;
69238106Sdes	device_t	qd_dev;
70238106Sdes	int		qd_devtype;
71238106Sdes
72238106Sdes	driver_filter_t	*qd_ih;
73238106Sdes	void		*qd_ih_arg;
74238106Sdes};
75238106Sdes
76238106Sdesstatic int
77238106Sdesquicc_bfe_intr(void *arg)
78238106Sdes{
79238106Sdes	struct quicc_device *qd;
80238106Sdes	struct quicc_softc *sc = arg;
81238106Sdes	uint32_t sipnr;
82238106Sdes
83238106Sdes	sipnr = quicc_read4(sc->sc_rres, QUICC_REG_SIPNR_L);
84238106Sdes	if (sipnr & 0x00f00000)
85238106Sdes		qd = sc->sc_device;
86238106Sdes	else
87238106Sdes		qd = NULL;
88238106Sdes
89238106Sdes	if (qd == NULL || qd->qd_ih == NULL) {
90238106Sdes		device_printf(sc->sc_dev, "Stray interrupt %08x\n", sipnr);
91238106Sdes		return (FILTER_STRAY);
92238106Sdes	}
93238106Sdes
94238106Sdes	return ((*qd->qd_ih)(qd->qd_ih_arg));
95238106Sdes}
96238106Sdes
97238106Sdesint
98238106Sdesquicc_bfe_attach(device_t dev)
99238106Sdes{
100238106Sdes	struct quicc_device *qd;
101238106Sdes	struct quicc_softc *sc;
102238106Sdes	struct resource_list_entry *rle;
103238106Sdes	const char *sep;
104238106Sdes	u_long size, start;
105238106Sdes	int error;
106238106Sdes
107238106Sdes	sc = device_get_softc(dev);
108238106Sdes
109238106Sdes	/*
110238106Sdes	 * Re-allocate. We expect that the softc contains the information
111238106Sdes	 * collected by quicc_bfe_probe() intact.
112238106Sdes	 */
113238106Sdes	sc->sc_rres = bus_alloc_resource(dev, sc->sc_rtype, &sc->sc_rrid,
114238106Sdes	    0, ~0, 0, RF_ACTIVE);
115238106Sdes	if (sc->sc_rres == NULL)
116238106Sdes		return (ENXIO);
117238106Sdes
118238106Sdes	start = rman_get_start(sc->sc_rres);
119238106Sdes	size = rman_get_size(sc->sc_rres);
120238106Sdes
121238106Sdes	sc->sc_rman.rm_start = start;
122238106Sdes	sc->sc_rman.rm_end = start + size - 1;
123238106Sdes	sc->sc_rman.rm_type = RMAN_ARRAY;
124238106Sdes	sc->sc_rman.rm_descr = "QUICC resources";
125238106Sdes	error = rman_init(&sc->sc_rman);
126238106Sdes	if (!error)
127238106Sdes		error = rman_manage_region(&sc->sc_rman, start,
128238106Sdes		    start + size - 1);
129238106Sdes	if (error) {
130238106Sdes		bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid,
131238106Sdes		    sc->sc_rres);
132238106Sdes		return (error);
133238106Sdes	}
134238106Sdes
135238106Sdes	/*
136238106Sdes	 * Allocate interrupt resource.
137238106Sdes	 */
138238106Sdes	sc->sc_irid = 0;
139238106Sdes	sc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irid,
140238106Sdes	    RF_ACTIVE | RF_SHAREABLE);
141238106Sdes
142238106Sdes	if (sc->sc_ires != NULL) {
143238106Sdes		error = bus_setup_intr(dev, sc->sc_ires,
144238106Sdes		    INTR_TYPE_TTY, quicc_bfe_intr, NULL, sc, &sc->sc_icookie);
145238106Sdes		if (error) {
146238106Sdes			error = bus_setup_intr(dev, sc->sc_ires,
147238106Sdes			    INTR_TYPE_TTY | INTR_MPSAFE, NULL,
148238106Sdes			    (driver_intr_t *)quicc_bfe_intr, sc,
149238106Sdes			    &sc->sc_icookie);
150238106Sdes		} else
151238106Sdes			sc->sc_fastintr = 1;
152238106Sdes		if (error) {
153238106Sdes			device_printf(dev, "could not activate interrupt\n");
154238106Sdes			bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid,
155238106Sdes			    sc->sc_ires);
156238106Sdes			sc->sc_ires = NULL;
157238106Sdes		}
158238106Sdes	}
159238106Sdes
160238106Sdes	if (sc->sc_ires == NULL)
161238106Sdes		sc->sc_polled = 1;
162238106Sdes
163238106Sdes	if (bootverbose && (sc->sc_fastintr || sc->sc_polled)) {
164238106Sdes		sep = "";
165238106Sdes		device_print_prettyname(dev);
166238106Sdes		if (sc->sc_fastintr) {
167238106Sdes			printf("%sfast interrupt", sep);
168238106Sdes			sep = ", ";
169238106Sdes		}
170238106Sdes		if (sc->sc_polled) {
171238106Sdes			printf("%spolled mode", sep);
172238106Sdes			sep = ", ";
173238106Sdes		}
174238106Sdes		printf("\n");
175238106Sdes	}
176238106Sdes
177238106Sdes	sc->sc_device = qd = malloc(sizeof(struct quicc_device), M_QUICC,
178238106Sdes	    M_WAITOK | M_ZERO);
179238106Sdes
180238106Sdes	qd->qd_devtype = QUICC_DEVTYPE_SCC;
181238106Sdes	qd->qd_rman = &sc->sc_rman;
182238106Sdes	resource_list_init(&qd->qd_rlist);
183238106Sdes
184238106Sdes	resource_list_add(&qd->qd_rlist, sc->sc_rtype, 0, start,
185238106Sdes	    start + size - 1, size);
186238106Sdes
187238106Sdes	resource_list_add(&qd->qd_rlist, SYS_RES_IRQ, 0, 0xf00, 0xf00, 1);
188238106Sdes	rle = resource_list_find(&qd->qd_rlist, SYS_RES_IRQ, 0);
189238106Sdes	rle->res = sc->sc_ires;
190238106Sdes
191238106Sdes	qd->qd_dev = device_add_child(dev, NULL, -1);
192238106Sdes	device_set_ivars(qd->qd_dev, (void *)qd);
193238106Sdes	error = device_probe_and_attach(qd->qd_dev);
194238106Sdes
195238106Sdes	/* Enable all SCC interrupts. */
196238106Sdes	quicc_write4(sc->sc_rres, QUICC_REG_SIMR_L, 0x00f00000);
197238106Sdes
198238106Sdes	/* Clear all pending interrupts. */
199238106Sdes	quicc_write4(sc->sc_rres, QUICC_REG_SIPNR_H, ~0);
200238106Sdes	quicc_write4(sc->sc_rres, QUICC_REG_SIPNR_L, ~0);
201238106Sdes	return (error);
202238106Sdes}
203238106Sdes
204238106Sdesint
205238106Sdesquicc_bfe_detach(device_t dev)
206238106Sdes{
207238106Sdes	struct quicc_softc *sc;
208238106Sdes
209238106Sdes	sc = device_get_softc(dev);
210238106Sdes
211238106Sdes	bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie);
212238106Sdes	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid, sc->sc_ires);
213238106Sdes	bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres);
214238106Sdes	return (0);
215238106Sdes}
216238106Sdes
217238106Sdesint
218238106Sdesquicc_bfe_probe(device_t dev, u_int clock)
219238106Sdes{
220238106Sdes	struct quicc_softc *sc;
221238106Sdes	uint16_t rev;
222238106Sdes
223238106Sdes	sc = device_get_softc(dev);
224238106Sdes	sc->sc_dev = dev;
225238106Sdes	if (device_get_desc(dev) == NULL)
226238106Sdes		device_set_desc(dev,
227238106Sdes		    "Quad integrated communications controller");
228238106Sdes
229238106Sdes	sc->sc_rrid = 0;
230238106Sdes	sc->sc_rtype = SYS_RES_MEMORY;
231238106Sdes	sc->sc_rres = bus_alloc_resource(dev, sc->sc_rtype, &sc->sc_rrid,
232238106Sdes	    0, ~0, 0, RF_ACTIVE);
233238106Sdes	if (sc->sc_rres == NULL) {
234238106Sdes		sc->sc_rrid = 0;
235238106Sdes		sc->sc_rtype = SYS_RES_IOPORT;
236238106Sdes		sc->sc_rres = bus_alloc_resource(dev, sc->sc_rtype,
237238106Sdes		    &sc->sc_rrid, 0, ~0, 0, RF_ACTIVE);
238238106Sdes		if (sc->sc_rres == NULL)
239238106Sdes			return (ENXIO);
240238106Sdes	}
241238106Sdes
242238106Sdes	sc->sc_clock = clock;
243238106Sdes
244238106Sdes	/*
245238106Sdes	 * Check that the microcode revision is 0x00e8, as documented
246238106Sdes	 * in the MPC8555E PowerQUICC III Integrated Processor Family
247238106Sdes	 * Reference Manual.
248238106Sdes	 */
249238106Sdes	rev = quicc_read2(sc->sc_rres, QUICC_PRAM_REV_NUM);
250238106Sdes
251238106Sdes	bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres);
252238106Sdes	return ((rev == 0x00e8) ? BUS_PROBE_DEFAULT : ENXIO);
253238106Sdes}
254238106Sdes
255238106Sdesstruct resource *
256238106Sdesquicc_bus_alloc_resource(device_t dev, device_t child, int type, int *rid,
257238106Sdes    u_long start, u_long end, u_long count, u_int flags)
258238106Sdes{
259238106Sdes	struct quicc_device *qd;
260238106Sdes	struct resource_list_entry *rle;
261238106Sdes
262238106Sdes	if (device_get_parent(child) != dev)
263238106Sdes		return (NULL);
264238106Sdes
265238106Sdes	/* We only support default allocations. */
266238106Sdes	if (start != 0UL || end != ~0UL)
267238106Sdes		return (NULL);
268238106Sdes
269238106Sdes	qd = device_get_ivars(child);
270238106Sdes	rle = resource_list_find(&qd->qd_rlist, type, *rid);
271238106Sdes	if (rle == NULL)
272238106Sdes		return (NULL);
273238106Sdes
274238106Sdes	if (rle->res == NULL) {
275238106Sdes		rle->res = rman_reserve_resource(qd->qd_rman, rle->start,
276238106Sdes		    rle->start + rle->count - 1, rle->count, flags, child);
277238106Sdes		if (rle->res != NULL) {
278238106Sdes			rman_set_bustag(rle->res, &bs_be_tag);
279238106Sdes			rman_set_bushandle(rle->res, rle->start);
280238106Sdes		}
281238106Sdes	}
282238106Sdes	return (rle->res);
283238106Sdes}
284238106Sdes
285238106Sdesint
286238106Sdesquicc_bus_get_resource(device_t dev, device_t child, int type, int rid,
287238106Sdes    u_long *startp, u_long *countp)
288238106Sdes{
289238106Sdes	struct quicc_device *qd;
290238106Sdes	struct resource_list_entry *rle;
291238106Sdes
292238106Sdes	if (device_get_parent(child) != dev)
293238106Sdes		return (EINVAL);
294238106Sdes
295238106Sdes	qd = device_get_ivars(child);
296238106Sdes	rle = resource_list_find(&qd->qd_rlist, type, rid);
297238106Sdes	if (rle == NULL)
298238106Sdes		return (EINVAL);
299238106Sdes
300238106Sdes	if (startp != NULL)
301238106Sdes		*startp = rle->start;
302238106Sdes	if (countp != NULL)
303238106Sdes		*countp = rle->count;
304238106Sdes	return (0);
305238106Sdes}
306238106Sdes
307238106Sdesint
308238106Sdesquicc_bus_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
309238106Sdes{
310238106Sdes	struct quicc_device *qd;
311238106Sdes	struct quicc_softc *sc;
312238106Sdes	uint32_t sccr;
313238106Sdes
314238106Sdes	if (device_get_parent(child) != dev)
315238106Sdes		return (EINVAL);
316238106Sdes
317238106Sdes	sc = device_get_softc(dev);
318238106Sdes	qd = device_get_ivars(child);
319238106Sdes
320238106Sdes	switch (index) {
321238106Sdes	case QUICC_IVAR_CLOCK:
322238106Sdes		*result = sc->sc_clock;
323238106Sdes		break;
324238106Sdes	case QUICC_IVAR_BRGCLK:
325238106Sdes		sccr = quicc_read4(sc->sc_rres, QUICC_REG_SCCR) & 3;
326238106Sdes		*result = sc->sc_clock / ((1 << (sccr + 1)) << sccr);
327238106Sdes		break;
328238106Sdes	case QUICC_IVAR_DEVTYPE:
329238106Sdes		*result = qd->qd_devtype;
330238106Sdes		break;
331238106Sdes	default:
332238106Sdes		return (EINVAL);
333238106Sdes	}
334238106Sdes	return (0);
335238106Sdes}
336238106Sdes
337238106Sdesint
338238106Sdesquicc_bus_release_resource(device_t dev, device_t child, int type, int rid,
339238106Sdes    struct resource *res)
340238106Sdes{
341238106Sdes	struct quicc_device *qd;
342238106Sdes	struct resource_list_entry *rle;
343238106Sdes
344238106Sdes	if (device_get_parent(child) != dev)
345238106Sdes		return (EINVAL);
346238106Sdes
347238106Sdes	qd = device_get_ivars(child);
348238106Sdes	rle = resource_list_find(&qd->qd_rlist, type, rid);
349238106Sdes	return ((rle == NULL) ? EINVAL : 0);
350238106Sdes}
351238106Sdes
352238106Sdesint
353238106Sdesquicc_bus_setup_intr(device_t dev, device_t child, struct resource *r,
354238106Sdes    int flags, driver_filter_t *filt, void (*ihand)(void *), void *arg,
355238106Sdes    void **cookiep)
356238106Sdes{
357238106Sdes	struct quicc_device *qd;
358238106Sdes	struct quicc_softc *sc;
359238106Sdes
360238106Sdes	if (device_get_parent(child) != dev)
361238106Sdes		return (EINVAL);
362238106Sdes
363238106Sdes	/* Interrupt handlers must be FAST or MPSAFE. */
364238106Sdes	if (filt == NULL && !(flags & INTR_MPSAFE))
365238106Sdes		return (EINVAL);
366238106Sdes
367238106Sdes	sc = device_get_softc(dev);
368238106Sdes	if (sc->sc_polled)
369238106Sdes		return (ENXIO);
370238106Sdes
371238106Sdes	if (sc->sc_fastintr && filt == NULL) {
372238106Sdes		sc->sc_fastintr = 0;
373238106Sdes		bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie);
374238106Sdes		bus_setup_intr(dev, sc->sc_ires, INTR_TYPE_TTY | INTR_MPSAFE,
375238106Sdes		    NULL, (driver_intr_t *)quicc_bfe_intr, sc, &sc->sc_icookie);
376238106Sdes	}
377238106Sdes
378238106Sdes	qd = device_get_ivars(child);
379238106Sdes	qd->qd_ih = (filt != NULL) ? filt : (driver_filter_t *)ihand;
380238106Sdes	qd->qd_ih_arg = arg;
381238106Sdes	*cookiep = ihand;
382238106Sdes	return (0);
383238106Sdes}
384238106Sdes
385238106Sdesint
386238106Sdesquicc_bus_teardown_intr(device_t dev, device_t child, struct resource *r,
387238106Sdes    void *cookie)
388238106Sdes{
389238106Sdes	struct quicc_device *qd;
390238106Sdes
391238106Sdes	if (device_get_parent(child) != dev)
392238106Sdes		return (EINVAL);
393238106Sdes
394238106Sdes	qd = device_get_ivars(child);
395238106Sdes	if (qd->qd_ih != cookie)
396238106Sdes		return (EINVAL);
397238106Sdes
398238106Sdes	qd->qd_ih = NULL;
399238106Sdes	qd->qd_ih_arg = NULL;
400238106Sdes	return (0);
401238106Sdes}
402238106Sdes