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