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