xref: /freebsd/sys/dev/quicc/quicc_core.c (revision 0957b409)
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 devclass_t quicc_devclass;
64 char quicc_driver_name[] = "quicc";
65 
66 static MALLOC_DEFINE(M_QUICC, "QUICC", "QUICC driver");
67 
68 struct 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 
78 static int
79 quicc_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 
99 int
100 quicc_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 
206 int
207 quicc_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 
219 int
220 quicc_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 
257 struct resource *
258 quicc_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 
287 int
288 quicc_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 
309 int
310 quicc_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 
339 int
340 quicc_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 
354 int
355 quicc_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 
387 int
388 quicc_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