1176772Sraj/*-
2176772Sraj * Copyright 2006 by Juniper Networks.
3176772Sraj * All rights reserved.
4176772Sraj *
5176772Sraj * Redistribution and use in source and binary forms, with or without
6176772Sraj * modification, are permitted provided that the following conditions
7176772Sraj * are met:
8176772Sraj * 1. Redistributions of source code must retain the above copyright
9176772Sraj *    notice, this list of conditions and the following disclaimer.
10176772Sraj * 2. Redistributions in binary form must reproduce the above copyright
11176772Sraj *    notice, this list of conditions and the following disclaimer in the
12176772Sraj *    documentation and/or other materials provided with the distribution.
13176772Sraj * 3. The name of the author may not be used to endorse or promote products
14176772Sraj *    derived from this software without specific prior written permission.
15176772Sraj *
16176772Sraj * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17176772Sraj * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18176772Sraj * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19176772Sraj * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20176772Sraj * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21176772Sraj * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22176772Sraj * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23176772Sraj * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24176772Sraj * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25176772Sraj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26176772Sraj * SUCH DAMAGE.
27176772Sraj */
28176772Sraj
29176772Sraj#include <sys/cdefs.h>
30176772Sraj__FBSDID("$FreeBSD$");
31176772Sraj
32176772Sraj#include <sys/param.h>
33176772Sraj#include <sys/systm.h>
34176772Sraj#include <sys/bus.h>
35176772Sraj#include <sys/conf.h>
36176772Sraj#include <sys/endian.h>
37176772Sraj#include <sys/kernel.h>
38176772Sraj#include <sys/malloc.h>
39176772Sraj#include <sys/queue.h>
40176772Sraj#include <sys/serial.h>
41176772Sraj
42176772Sraj#include <machine/bus.h>
43176772Sraj#include <machine/resource.h>
44176772Sraj#include <sys/rman.h>
45176772Sraj
46176772Sraj#include <dev/ic/quicc.h>
47176772Sraj
48176772Sraj#include <dev/quicc/quicc_bfe.h>
49176772Sraj#include <dev/quicc/quicc_bus.h>
50176772Sraj
51176772Sraj#define	quicc_read2(r, o)	\
52176772Sraj	bus_space_read_2((r)->r_bustag, (r)->r_bushandle, o)
53176772Sraj#define	quicc_read4(r, o)	\
54176772Sraj	bus_space_read_4((r)->r_bustag, (r)->r_bushandle, o)
55176772Sraj
56176772Sraj#define	quicc_write2(r, o, v)	\
57176772Sraj	bus_space_write_2((r)->r_bustag, (r)->r_bushandle, o, v)
58176772Sraj#define	quicc_write4(r, o, v)	\
59176772Sraj	bus_space_write_4((r)->r_bustag, (r)->r_bushandle, o, v)
60176772Sraj
61176772Srajdevclass_t quicc_devclass;
62176772Srajchar quicc_driver_name[] = "quicc";
63176772Sraj
64227293Sedstatic MALLOC_DEFINE(M_QUICC, "QUICC", "QUICC driver");
65176772Sraj
66176772Srajstruct quicc_device {
67176772Sraj	struct rman	*qd_rman;
68176772Sraj	struct resource_list qd_rlist;
69176772Sraj	device_t	qd_dev;
70176772Sraj	int		qd_devtype;
71176772Sraj
72176772Sraj	driver_filter_t	*qd_ih;
73176772Sraj	void		*qd_ih_arg;
74176772Sraj};
75176772Sraj
76176772Srajstatic int
77176772Srajquicc_bfe_intr(void *arg)
78176772Sraj{
79176772Sraj	struct quicc_device *qd;
80176772Sraj	struct quicc_softc *sc = arg;
81176772Sraj	uint32_t sipnr;
82176772Sraj
83176772Sraj	sipnr = quicc_read4(sc->sc_rres, QUICC_REG_SIPNR_L);
84176772Sraj	if (sipnr & 0x00f00000)
85176772Sraj		qd = sc->sc_device;
86176772Sraj	else
87176772Sraj		qd = NULL;
88176772Sraj
89176772Sraj	if (qd == NULL || qd->qd_ih == NULL) {
90176772Sraj		device_printf(sc->sc_dev, "Stray interrupt %08x\n", sipnr);
91176772Sraj		return (FILTER_STRAY);
92176772Sraj	}
93176772Sraj
94176772Sraj	return ((*qd->qd_ih)(qd->qd_ih_arg));
95176772Sraj}
96176772Sraj
97176772Srajint
98176772Srajquicc_bfe_attach(device_t dev)
99176772Sraj{
100176772Sraj	struct quicc_device *qd;
101176772Sraj	struct quicc_softc *sc;
102176772Sraj	struct resource_list_entry *rle;
103176772Sraj	const char *sep;
104294883Sjhibbits	rman_res_t size, start;
105176772Sraj	int error;
106176772Sraj
107176772Sraj	sc = device_get_softc(dev);
108176772Sraj
109176772Sraj	/*
110176772Sraj	 * Re-allocate. We expect that the softc contains the information
111176772Sraj	 * collected by quicc_bfe_probe() intact.
112176772Sraj	 */
113295790Sjhibbits	sc->sc_rres = bus_alloc_resource_any(dev, sc->sc_rtype, &sc->sc_rrid,
114295790Sjhibbits	    RF_ACTIVE);
115176772Sraj	if (sc->sc_rres == NULL)
116176772Sraj		return (ENXIO);
117176772Sraj
118176772Sraj	start = rman_get_start(sc->sc_rres);
119176772Sraj	size = rman_get_size(sc->sc_rres);
120176772Sraj
121176772Sraj	sc->sc_rman.rm_start = start;
122176772Sraj	sc->sc_rman.rm_end = start + size - 1;
123176772Sraj	sc->sc_rman.rm_type = RMAN_ARRAY;
124176772Sraj	sc->sc_rman.rm_descr = "QUICC resources";
125176772Sraj	error = rman_init(&sc->sc_rman);
126176772Sraj	if (!error)
127176772Sraj		error = rman_manage_region(&sc->sc_rman, start,
128176772Sraj		    start + size - 1);
129176772Sraj	if (error) {
130176772Sraj		bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid,
131176772Sraj		    sc->sc_rres);
132176772Sraj		return (error);
133176772Sraj	}
134176772Sraj
135176772Sraj	/*
136176772Sraj	 * Allocate interrupt resource.
137176772Sraj	 */
138176772Sraj	sc->sc_irid = 0;
139176772Sraj	sc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irid,
140176772Sraj	    RF_ACTIVE | RF_SHAREABLE);
141176772Sraj
142176772Sraj	if (sc->sc_ires != NULL) {
143176772Sraj		error = bus_setup_intr(dev, sc->sc_ires,
144176772Sraj		    INTR_TYPE_TTY, quicc_bfe_intr, NULL, sc, &sc->sc_icookie);
145176772Sraj		if (error) {
146176772Sraj			error = bus_setup_intr(dev, sc->sc_ires,
147176772Sraj			    INTR_TYPE_TTY | INTR_MPSAFE, NULL,
148176772Sraj			    (driver_intr_t *)quicc_bfe_intr, sc,
149176772Sraj			    &sc->sc_icookie);
150176772Sraj		} else
151176772Sraj			sc->sc_fastintr = 1;
152176772Sraj		if (error) {
153176772Sraj			device_printf(dev, "could not activate interrupt\n");
154176772Sraj			bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid,
155176772Sraj			    sc->sc_ires);
156176772Sraj			sc->sc_ires = NULL;
157176772Sraj		}
158176772Sraj	}
159176772Sraj
160176772Sraj	if (sc->sc_ires == NULL)
161176772Sraj		sc->sc_polled = 1;
162176772Sraj
163176772Sraj	if (bootverbose && (sc->sc_fastintr || sc->sc_polled)) {
164176772Sraj		sep = "";
165176772Sraj		device_print_prettyname(dev);
166176772Sraj		if (sc->sc_fastintr) {
167176772Sraj			printf("%sfast interrupt", sep);
168176772Sraj			sep = ", ";
169176772Sraj		}
170176772Sraj		if (sc->sc_polled) {
171176772Sraj			printf("%spolled mode", sep);
172176772Sraj			sep = ", ";
173176772Sraj		}
174176772Sraj		printf("\n");
175176772Sraj	}
176176772Sraj
177176772Sraj	sc->sc_device = qd = malloc(sizeof(struct quicc_device), M_QUICC,
178176772Sraj	    M_WAITOK | M_ZERO);
179176772Sraj
180176772Sraj	qd->qd_devtype = QUICC_DEVTYPE_SCC;
181176772Sraj	qd->qd_rman = &sc->sc_rman;
182176772Sraj	resource_list_init(&qd->qd_rlist);
183176772Sraj
184176772Sraj	resource_list_add(&qd->qd_rlist, sc->sc_rtype, 0, start,
185176772Sraj	    start + size - 1, size);
186176772Sraj
187176772Sraj	resource_list_add(&qd->qd_rlist, SYS_RES_IRQ, 0, 0xf00, 0xf00, 1);
188176772Sraj	rle = resource_list_find(&qd->qd_rlist, SYS_RES_IRQ, 0);
189176772Sraj	rle->res = sc->sc_ires;
190176772Sraj
191176772Sraj	qd->qd_dev = device_add_child(dev, NULL, -1);
192176772Sraj	device_set_ivars(qd->qd_dev, (void *)qd);
193176772Sraj	error = device_probe_and_attach(qd->qd_dev);
194176772Sraj
195176772Sraj	/* Enable all SCC interrupts. */
196176772Sraj	quicc_write4(sc->sc_rres, QUICC_REG_SIMR_L, 0x00f00000);
197176772Sraj
198176772Sraj	/* Clear all pending interrupts. */
199176772Sraj	quicc_write4(sc->sc_rres, QUICC_REG_SIPNR_H, ~0);
200176772Sraj	quicc_write4(sc->sc_rres, QUICC_REG_SIPNR_L, ~0);
201176772Sraj	return (error);
202176772Sraj}
203176772Sraj
204176772Srajint
205176772Srajquicc_bfe_detach(device_t dev)
206176772Sraj{
207176772Sraj	struct quicc_softc *sc;
208176772Sraj
209176772Sraj	sc = device_get_softc(dev);
210176772Sraj
211176772Sraj	bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie);
212176772Sraj	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid, sc->sc_ires);
213176772Sraj	bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres);
214176772Sraj	return (0);
215176772Sraj}
216176772Sraj
217176772Srajint
218176772Srajquicc_bfe_probe(device_t dev, u_int clock)
219176772Sraj{
220176772Sraj	struct quicc_softc *sc;
221176772Sraj	uint16_t rev;
222176772Sraj
223176772Sraj	sc = device_get_softc(dev);
224176772Sraj	sc->sc_dev = dev;
225176772Sraj	if (device_get_desc(dev) == NULL)
226176772Sraj		device_set_desc(dev,
227176772Sraj		    "Quad integrated communications controller");
228176772Sraj
229176772Sraj	sc->sc_rrid = 0;
230176772Sraj	sc->sc_rtype = SYS_RES_MEMORY;
231295790Sjhibbits	sc->sc_rres = bus_alloc_resource_any(dev, sc->sc_rtype, &sc->sc_rrid,
232295790Sjhibbits	    RF_ACTIVE);
233176772Sraj	if (sc->sc_rres == NULL) {
234176772Sraj		sc->sc_rrid = 0;
235176772Sraj		sc->sc_rtype = SYS_RES_IOPORT;
236295790Sjhibbits		sc->sc_rres = bus_alloc_resource_any(dev, sc->sc_rtype,
237295790Sjhibbits		    &sc->sc_rrid, RF_ACTIVE);
238176772Sraj		if (sc->sc_rres == NULL)
239176772Sraj			return (ENXIO);
240176772Sraj	}
241176772Sraj
242176772Sraj	sc->sc_clock = clock;
243176772Sraj
244176772Sraj	/*
245176772Sraj	 * Check that the microcode revision is 0x00e8, as documented
246176772Sraj	 * in the MPC8555E PowerQUICC III Integrated Processor Family
247176772Sraj	 * Reference Manual.
248176772Sraj	 */
249176772Sraj	rev = quicc_read2(sc->sc_rres, QUICC_PRAM_REV_NUM);
250176772Sraj
251176772Sraj	bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres);
252176772Sraj	return ((rev == 0x00e8) ? BUS_PROBE_DEFAULT : ENXIO);
253176772Sraj}
254176772Sraj
255176772Srajstruct resource *
256176772Srajquicc_bus_alloc_resource(device_t dev, device_t child, int type, int *rid,
257294883Sjhibbits    rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
258176772Sraj{
259176772Sraj	struct quicc_device *qd;
260176772Sraj	struct resource_list_entry *rle;
261176772Sraj
262176772Sraj	if (device_get_parent(child) != dev)
263176772Sraj		return (NULL);
264176772Sraj
265176772Sraj	/* We only support default allocations. */
266296298Sjhibbits	if (!RMAN_IS_DEFAULT_RANGE(start, end))
267176772Sraj		return (NULL);
268176772Sraj
269176772Sraj	qd = device_get_ivars(child);
270176772Sraj	rle = resource_list_find(&qd->qd_rlist, type, *rid);
271176772Sraj	if (rle == NULL)
272176772Sraj		return (NULL);
273176772Sraj
274176772Sraj	if (rle->res == NULL) {
275176772Sraj		rle->res = rman_reserve_resource(qd->qd_rman, rle->start,
276176772Sraj		    rle->start + rle->count - 1, rle->count, flags, child);
277176772Sraj		if (rle->res != NULL) {
278176772Sraj			rman_set_bustag(rle->res, &bs_be_tag);
279176772Sraj			rman_set_bushandle(rle->res, rle->start);
280176772Sraj		}
281176772Sraj	}
282176772Sraj	return (rle->res);
283176772Sraj}
284176772Sraj
285176772Srajint
286176772Srajquicc_bus_get_resource(device_t dev, device_t child, int type, int rid,
287294883Sjhibbits    rman_res_t *startp, rman_res_t *countp)
288176772Sraj{
289176772Sraj	struct quicc_device *qd;
290176772Sraj	struct resource_list_entry *rle;
291176772Sraj
292176772Sraj	if (device_get_parent(child) != dev)
293176772Sraj		return (EINVAL);
294176772Sraj
295176772Sraj	qd = device_get_ivars(child);
296176772Sraj	rle = resource_list_find(&qd->qd_rlist, type, rid);
297176772Sraj	if (rle == NULL)
298176772Sraj		return (EINVAL);
299176772Sraj
300176772Sraj	if (startp != NULL)
301176772Sraj		*startp = rle->start;
302176772Sraj	if (countp != NULL)
303176772Sraj		*countp = rle->count;
304176772Sraj	return (0);
305176772Sraj}
306176772Sraj
307176772Srajint
308176772Srajquicc_bus_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
309176772Sraj{
310176772Sraj	struct quicc_device *qd;
311176772Sraj	struct quicc_softc *sc;
312176772Sraj	uint32_t sccr;
313176772Sraj
314176772Sraj	if (device_get_parent(child) != dev)
315176772Sraj		return (EINVAL);
316176772Sraj
317176772Sraj	sc = device_get_softc(dev);
318176772Sraj	qd = device_get_ivars(child);
319176772Sraj
320176772Sraj	switch (index) {
321176772Sraj	case QUICC_IVAR_CLOCK:
322176772Sraj		*result = sc->sc_clock;
323176772Sraj		break;
324176772Sraj	case QUICC_IVAR_BRGCLK:
325176772Sraj		sccr = quicc_read4(sc->sc_rres, QUICC_REG_SCCR) & 3;
326176772Sraj		*result = sc->sc_clock / ((1 << (sccr + 1)) << sccr);
327176772Sraj		break;
328176772Sraj	case QUICC_IVAR_DEVTYPE:
329176772Sraj		*result = qd->qd_devtype;
330176772Sraj		break;
331176772Sraj	default:
332176772Sraj		return (EINVAL);
333176772Sraj	}
334176772Sraj	return (0);
335176772Sraj}
336176772Sraj
337176772Srajint
338176772Srajquicc_bus_release_resource(device_t dev, device_t child, int type, int rid,
339176772Sraj    struct resource *res)
340176772Sraj{
341176772Sraj	struct quicc_device *qd;
342176772Sraj	struct resource_list_entry *rle;
343176772Sraj
344176772Sraj	if (device_get_parent(child) != dev)
345176772Sraj		return (EINVAL);
346176772Sraj
347176772Sraj	qd = device_get_ivars(child);
348176772Sraj	rle = resource_list_find(&qd->qd_rlist, type, rid);
349176772Sraj	return ((rle == NULL) ? EINVAL : 0);
350176772Sraj}
351176772Sraj
352176772Srajint
353176772Srajquicc_bus_setup_intr(device_t dev, device_t child, struct resource *r,
354176772Sraj    int flags, driver_filter_t *filt, void (*ihand)(void *), void *arg,
355176772Sraj    void **cookiep)
356176772Sraj{
357176772Sraj	struct quicc_device *qd;
358176772Sraj	struct quicc_softc *sc;
359176772Sraj
360176772Sraj	if (device_get_parent(child) != dev)
361176772Sraj		return (EINVAL);
362176772Sraj
363176772Sraj	/* Interrupt handlers must be FAST or MPSAFE. */
364176772Sraj	if (filt == NULL && !(flags & INTR_MPSAFE))
365176772Sraj		return (EINVAL);
366176772Sraj
367176772Sraj	sc = device_get_softc(dev);
368176772Sraj	if (sc->sc_polled)
369176772Sraj		return (ENXIO);
370176772Sraj
371176772Sraj	if (sc->sc_fastintr && filt == NULL) {
372176772Sraj		sc->sc_fastintr = 0;
373176772Sraj		bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie);
374176772Sraj		bus_setup_intr(dev, sc->sc_ires, INTR_TYPE_TTY | INTR_MPSAFE,
375176772Sraj		    NULL, (driver_intr_t *)quicc_bfe_intr, sc, &sc->sc_icookie);
376176772Sraj	}
377176772Sraj
378176772Sraj	qd = device_get_ivars(child);
379176772Sraj	qd->qd_ih = (filt != NULL) ? filt : (driver_filter_t *)ihand;
380176772Sraj	qd->qd_ih_arg = arg;
381176772Sraj	*cookiep = ihand;
382176772Sraj	return (0);
383176772Sraj}
384176772Sraj
385176772Srajint
386176772Srajquicc_bus_teardown_intr(device_t dev, device_t child, struct resource *r,
387176772Sraj    void *cookie)
388176772Sraj{
389176772Sraj	struct quicc_device *qd;
390176772Sraj
391176772Sraj	if (device_get_parent(child) != dev)
392176772Sraj		return (EINVAL);
393176772Sraj
394176772Sraj	qd = device_get_ivars(child);
395176772Sraj	if (qd->qd_ih != cookie)
396176772Sraj		return (EINVAL);
397176772Sraj
398176772Sraj	qd->qd_ih = NULL;
399176772Sraj	qd->qd_ih_arg = NULL;
400176772Sraj	return (0);
401176772Sraj}
402