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