xref: /freebsd/sys/dev/quicc/quicc_core.c (revision b3e7694832e81d7a904a10f525f8797b753bf0d3)
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 
63 char quicc_driver_name[] = "quicc";
64 
65 static MALLOC_DEFINE(M_QUICC, "QUICC", "QUICC driver");
66 
67 struct quicc_device {
68 	struct rman	*qd_rman;
69 	struct resource_list qd_rlist;
70 	device_t	qd_dev;
71 	int		qd_devtype;
72 
73 	driver_filter_t	*qd_ih;
74 	void		*qd_ih_arg;
75 };
76 
77 static int
78 quicc_bfe_intr(void *arg)
79 {
80 	struct quicc_device *qd;
81 	struct quicc_softc *sc = arg;
82 	uint32_t sipnr;
83 
84 	sipnr = quicc_read4(sc->sc_rres, QUICC_REG_SIPNR_L);
85 	if (sipnr & 0x00f00000)
86 		qd = sc->sc_device;
87 	else
88 		qd = NULL;
89 
90 	if (qd == NULL || qd->qd_ih == NULL) {
91 		device_printf(sc->sc_dev, "Stray interrupt %08x\n", sipnr);
92 		return (FILTER_STRAY);
93 	}
94 
95 	return ((*qd->qd_ih)(qd->qd_ih_arg));
96 }
97 
98 int
99 quicc_bfe_attach(device_t dev)
100 {
101 	struct quicc_device *qd;
102 	struct quicc_softc *sc;
103 	struct resource_list_entry *rle;
104 	const char *sep;
105 	rman_res_t size, start;
106 	int error;
107 
108 	sc = device_get_softc(dev);
109 
110 	/*
111 	 * Re-allocate. We expect that the softc contains the information
112 	 * collected by quicc_bfe_probe() intact.
113 	 */
114 	sc->sc_rres = bus_alloc_resource_any(dev, sc->sc_rtype, &sc->sc_rrid,
115 	    RF_ACTIVE);
116 	if (sc->sc_rres == NULL)
117 		return (ENXIO);
118 
119 	start = rman_get_start(sc->sc_rres);
120 	size = rman_get_size(sc->sc_rres);
121 
122 	sc->sc_rman.rm_start = start;
123 	sc->sc_rman.rm_end = start + size - 1;
124 	sc->sc_rman.rm_type = RMAN_ARRAY;
125 	sc->sc_rman.rm_descr = "QUICC resources";
126 	error = rman_init(&sc->sc_rman);
127 	if (!error)
128 		error = rman_manage_region(&sc->sc_rman, start,
129 		    start + size - 1);
130 	if (error) {
131 		bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid,
132 		    sc->sc_rres);
133 		return (error);
134 	}
135 
136 	/*
137 	 * Allocate interrupt resource.
138 	 */
139 	sc->sc_irid = 0;
140 	sc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irid,
141 	    RF_ACTIVE | RF_SHAREABLE);
142 
143 	if (sc->sc_ires != NULL) {
144 		error = bus_setup_intr(dev, sc->sc_ires,
145 		    INTR_TYPE_TTY, quicc_bfe_intr, NULL, sc, &sc->sc_icookie);
146 		if (error) {
147 			error = bus_setup_intr(dev, sc->sc_ires,
148 			    INTR_TYPE_TTY | INTR_MPSAFE, NULL,
149 			    (driver_intr_t *)quicc_bfe_intr, sc,
150 			    &sc->sc_icookie);
151 		} else
152 			sc->sc_fastintr = 1;
153 		if (error) {
154 			device_printf(dev, "could not activate interrupt\n");
155 			bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid,
156 			    sc->sc_ires);
157 			sc->sc_ires = NULL;
158 		}
159 	}
160 
161 	if (sc->sc_ires == NULL)
162 		sc->sc_polled = 1;
163 
164 	if (bootverbose && (sc->sc_fastintr || sc->sc_polled)) {
165 		sep = "";
166 		device_print_prettyname(dev);
167 		if (sc->sc_fastintr) {
168 			printf("%sfast interrupt", sep);
169 			sep = ", ";
170 		}
171 		if (sc->sc_polled) {
172 			printf("%spolled mode", sep);
173 			sep = ", ";
174 		}
175 		printf("\n");
176 	}
177 
178 	sc->sc_device = qd = malloc(sizeof(struct quicc_device), M_QUICC,
179 	    M_WAITOK | M_ZERO);
180 
181 	qd->qd_devtype = QUICC_DEVTYPE_SCC;
182 	qd->qd_rman = &sc->sc_rman;
183 	resource_list_init(&qd->qd_rlist);
184 
185 	resource_list_add(&qd->qd_rlist, sc->sc_rtype, 0, start,
186 	    start + size - 1, size);
187 
188 	resource_list_add(&qd->qd_rlist, SYS_RES_IRQ, 0, 0xf00, 0xf00, 1);
189 	rle = resource_list_find(&qd->qd_rlist, SYS_RES_IRQ, 0);
190 	rle->res = sc->sc_ires;
191 
192 	qd->qd_dev = device_add_child(dev, NULL, -1);
193 	device_set_ivars(qd->qd_dev, (void *)qd);
194 	error = device_probe_and_attach(qd->qd_dev);
195 
196 	/* Enable all SCC interrupts. */
197 	quicc_write4(sc->sc_rres, QUICC_REG_SIMR_L, 0x00f00000);
198 
199 	/* Clear all pending interrupts. */
200 	quicc_write4(sc->sc_rres, QUICC_REG_SIPNR_H, ~0);
201 	quicc_write4(sc->sc_rres, QUICC_REG_SIPNR_L, ~0);
202 	return (error);
203 }
204 
205 int
206 quicc_bfe_detach(device_t dev)
207 {
208 	struct quicc_softc *sc;
209 
210 	sc = device_get_softc(dev);
211 
212 	bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie);
213 	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid, sc->sc_ires);
214 	bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres);
215 	return (0);
216 }
217 
218 int
219 quicc_bfe_probe(device_t dev, u_int clock)
220 {
221 	struct quicc_softc *sc;
222 	uint16_t rev;
223 
224 	sc = device_get_softc(dev);
225 	sc->sc_dev = dev;
226 	if (device_get_desc(dev) == NULL)
227 		device_set_desc(dev,
228 		    "Quad integrated communications controller");
229 
230 	sc->sc_rrid = 0;
231 	sc->sc_rtype = SYS_RES_MEMORY;
232 	sc->sc_rres = bus_alloc_resource_any(dev, sc->sc_rtype, &sc->sc_rrid,
233 	    RF_ACTIVE);
234 	if (sc->sc_rres == NULL) {
235 		sc->sc_rrid = 0;
236 		sc->sc_rtype = SYS_RES_IOPORT;
237 		sc->sc_rres = bus_alloc_resource_any(dev, sc->sc_rtype,
238 		    &sc->sc_rrid, RF_ACTIVE);
239 		if (sc->sc_rres == NULL)
240 			return (ENXIO);
241 	}
242 
243 	sc->sc_clock = clock;
244 
245 	/*
246 	 * Check that the microcode revision is 0x00e8, as documented
247 	 * in the MPC8555E PowerQUICC III Integrated Processor Family
248 	 * Reference Manual.
249 	 */
250 	rev = quicc_read2(sc->sc_rres, QUICC_PRAM_REV_NUM);
251 
252 	bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres);
253 	return ((rev == 0x00e8) ? BUS_PROBE_DEFAULT : ENXIO);
254 }
255 
256 struct resource *
257 quicc_bus_alloc_resource(device_t dev, device_t child, int type, int *rid,
258     rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
259 {
260 	struct quicc_device *qd;
261 	struct resource_list_entry *rle;
262 
263 	if (device_get_parent(child) != dev)
264 		return (NULL);
265 
266 	/* We only support default allocations. */
267 	if (!RMAN_IS_DEFAULT_RANGE(start, end))
268 		return (NULL);
269 
270 	qd = device_get_ivars(child);
271 	rle = resource_list_find(&qd->qd_rlist, type, *rid);
272 	if (rle == NULL)
273 		return (NULL);
274 
275 	if (rle->res == NULL) {
276 		rle->res = rman_reserve_resource(qd->qd_rman, rle->start,
277 		    rle->start + rle->count - 1, rle->count, flags, child);
278 		if (rle->res != NULL) {
279 			rman_set_bustag(rle->res, &bs_be_tag);
280 			rman_set_bushandle(rle->res, rle->start);
281 		}
282 	}
283 	return (rle->res);
284 }
285 
286 int
287 quicc_bus_get_resource(device_t dev, device_t child, int type, int rid,
288     rman_res_t *startp, rman_res_t *countp)
289 {
290 	struct quicc_device *qd;
291 	struct resource_list_entry *rle;
292 
293 	if (device_get_parent(child) != dev)
294 		return (EINVAL);
295 
296 	qd = device_get_ivars(child);
297 	rle = resource_list_find(&qd->qd_rlist, type, rid);
298 	if (rle == NULL)
299 		return (EINVAL);
300 
301 	if (startp != NULL)
302 		*startp = rle->start;
303 	if (countp != NULL)
304 		*countp = rle->count;
305 	return (0);
306 }
307 
308 int
309 quicc_bus_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
310 {
311 	struct quicc_device *qd;
312 	struct quicc_softc *sc;
313 	uint32_t sccr;
314 
315 	if (device_get_parent(child) != dev)
316 		return (EINVAL);
317 
318 	sc = device_get_softc(dev);
319 	qd = device_get_ivars(child);
320 
321 	switch (index) {
322 	case QUICC_IVAR_CLOCK:
323 		*result = sc->sc_clock;
324 		break;
325 	case QUICC_IVAR_BRGCLK:
326 		sccr = quicc_read4(sc->sc_rres, QUICC_REG_SCCR) & 3;
327 		*result = sc->sc_clock / ((1 << (sccr + 1)) << sccr);
328 		break;
329 	case QUICC_IVAR_DEVTYPE:
330 		*result = qd->qd_devtype;
331 		break;
332 	default:
333 		return (EINVAL);
334 	}
335 	return (0);
336 }
337 
338 int
339 quicc_bus_release_resource(device_t dev, device_t child, int type, int rid,
340     struct resource *res)
341 {
342 	struct quicc_device *qd;
343 	struct resource_list_entry *rle;
344 
345 	if (device_get_parent(child) != dev)
346 		return (EINVAL);
347 
348 	qd = device_get_ivars(child);
349 	rle = resource_list_find(&qd->qd_rlist, type, rid);
350 	return ((rle == NULL) ? EINVAL : 0);
351 }
352 
353 int
354 quicc_bus_setup_intr(device_t dev, device_t child, struct resource *r,
355     int flags, driver_filter_t *filt, void (*ihand)(void *), void *arg,
356     void **cookiep)
357 {
358 	struct quicc_device *qd;
359 	struct quicc_softc *sc;
360 
361 	if (device_get_parent(child) != dev)
362 		return (EINVAL);
363 
364 	/* Interrupt handlers must be FAST or MPSAFE. */
365 	if (filt == NULL && !(flags & INTR_MPSAFE))
366 		return (EINVAL);
367 
368 	sc = device_get_softc(dev);
369 	if (sc->sc_polled)
370 		return (ENXIO);
371 
372 	if (sc->sc_fastintr && filt == NULL) {
373 		sc->sc_fastintr = 0;
374 		bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie);
375 		bus_setup_intr(dev, sc->sc_ires, INTR_TYPE_TTY | INTR_MPSAFE,
376 		    NULL, (driver_intr_t *)quicc_bfe_intr, sc, &sc->sc_icookie);
377 	}
378 
379 	qd = device_get_ivars(child);
380 	qd->qd_ih = (filt != NULL) ? filt : (driver_filter_t *)ihand;
381 	qd->qd_ih_arg = arg;
382 	*cookiep = ihand;
383 	return (0);
384 }
385 
386 int
387 quicc_bus_teardown_intr(device_t dev, device_t child, struct resource *r,
388     void *cookie)
389 {
390 	struct quicc_device *qd;
391 
392 	if (device_get_parent(child) != dev)
393 		return (EINVAL);
394 
395 	qd = device_get_ivars(child);
396 	if (qd->qd_ih != cookie)
397 		return (EINVAL);
398 
399 	qd->qd_ih = NULL;
400 	qd->qd_ih_arg = NULL;
401 	return (0);
402 }
403