syscons,vt: improve phrasing in kern.vty man page description
[freebsd-src.git] / sys / dev / quicc / quicc_core.c
blobb0e22b4e5913c4b2891a7d9bc9e7256b9fe69d61
1 /*-
2 * Copyright 2006 by Juniper Networks.
3 * All rights reserved.
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.
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.
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
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>
42 #include <machine/bus.h>
43 #include <machine/resource.h>
44 #include <sys/rman.h>
46 #include <dev/ic/quicc.h>
48 #include <dev/quicc/quicc_bfe.h>
49 #include <dev/quicc/quicc_bus.h>
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)
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)
61 devclass_t quicc_devclass;
62 char quicc_driver_name[] = "quicc";
64 static MALLOC_DEFINE(M_QUICC, "QUICC", "QUICC driver");
66 struct quicc_device {
67 struct rman *qd_rman;
68 struct resource_list qd_rlist;
69 device_t qd_dev;
70 int qd_devtype;
72 driver_filter_t *qd_ih;
73 void *qd_ih_arg;
76 static int
77 quicc_bfe_intr(void *arg)
79 struct quicc_device *qd;
80 struct quicc_softc *sc = arg;
81 uint32_t sipnr;
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;
89 if (qd == NULL || qd->qd_ih == NULL) {
90 device_printf(sc->sc_dev, "Stray interrupt %08x\n", sipnr);
91 return (FILTER_STRAY);
94 return ((*qd->qd_ih)(qd->qd_ih_arg));
97 int
98 quicc_bfe_attach(device_t dev)
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;
107 sc = device_get_softc(dev);
110 * Re-allocate. We expect that the softc contains the information
111 * collected by quicc_bfe_probe() intact.
113 sc->sc_rres = bus_alloc_resource_any(dev, sc->sc_rtype, &sc->sc_rrid,
114 RF_ACTIVE);
115 if (sc->sc_rres == NULL)
116 return (ENXIO);
118 start = rman_get_start(sc->sc_rres);
119 size = rman_get_size(sc->sc_rres);
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);
136 * Allocate interrupt resource.
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);
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;
160 if (sc->sc_ires == NULL)
161 sc->sc_polled = 1;
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 = ", ";
170 if (sc->sc_polled) {
171 printf("%spolled mode", sep);
172 sep = ", ";
174 printf("\n");
177 sc->sc_device = qd = malloc(sizeof(struct quicc_device), M_QUICC,
178 M_WAITOK | M_ZERO);
180 qd->qd_devtype = QUICC_DEVTYPE_SCC;
181 qd->qd_rman = &sc->sc_rman;
182 resource_list_init(&qd->qd_rlist);
184 resource_list_add(&qd->qd_rlist, sc->sc_rtype, 0, start,
185 start + size - 1, size);
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;
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);
195 /* Enable all SCC interrupts. */
196 quicc_write4(sc->sc_rres, QUICC_REG_SIMR_L, 0x00f00000);
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);
205 quicc_bfe_detach(device_t dev)
207 struct quicc_softc *sc;
209 sc = device_get_softc(dev);
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);
218 quicc_bfe_probe(device_t dev, u_int clock)
220 struct quicc_softc *sc;
221 uint16_t rev;
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");
229 sc->sc_rrid = 0;
230 sc->sc_rtype = SYS_RES_MEMORY;
231 sc->sc_rres = bus_alloc_resource_any(dev, sc->sc_rtype, &sc->sc_rrid,
232 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_any(dev, sc->sc_rtype,
237 &sc->sc_rrid, RF_ACTIVE);
238 if (sc->sc_rres == NULL)
239 return (ENXIO);
242 sc->sc_clock = clock;
245 * Check that the microcode revision is 0x00e8, as documented
246 * in the MPC8555E PowerQUICC III Integrated Processor Family
247 * Reference Manual.
249 rev = quicc_read2(sc->sc_rres, QUICC_PRAM_REV_NUM);
251 bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres);
252 return ((rev == 0x00e8) ? BUS_PROBE_DEFAULT : ENXIO);
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)
259 struct quicc_device *qd;
260 struct resource_list_entry *rle;
262 if (device_get_parent(child) != dev)
263 return (NULL);
265 /* We only support default allocations. */
266 if (!RMAN_IS_DEFAULT_RANGE(start, end))
267 return (NULL);
269 qd = device_get_ivars(child);
270 rle = resource_list_find(&qd->qd_rlist, type, *rid);
271 if (rle == NULL)
272 return (NULL);
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);
282 return (rle->res);
286 quicc_bus_get_resource(device_t dev, device_t child, int type, int rid,
287 rman_res_t *startp, rman_res_t *countp)
289 struct quicc_device *qd;
290 struct resource_list_entry *rle;
292 if (device_get_parent(child) != dev)
293 return (EINVAL);
295 qd = device_get_ivars(child);
296 rle = resource_list_find(&qd->qd_rlist, type, rid);
297 if (rle == NULL)
298 return (EINVAL);
300 if (startp != NULL)
301 *startp = rle->start;
302 if (countp != NULL)
303 *countp = rle->count;
304 return (0);
308 quicc_bus_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
310 struct quicc_device *qd;
311 struct quicc_softc *sc;
312 uint32_t sccr;
314 if (device_get_parent(child) != dev)
315 return (EINVAL);
317 sc = device_get_softc(dev);
318 qd = device_get_ivars(child);
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);
334 return (0);
338 quicc_bus_release_resource(device_t dev, device_t child, int type, int rid,
339 struct resource *res)
341 struct quicc_device *qd;
342 struct resource_list_entry *rle;
344 if (device_get_parent(child) != dev)
345 return (EINVAL);
347 qd = device_get_ivars(child);
348 rle = resource_list_find(&qd->qd_rlist, type, rid);
349 return ((rle == NULL) ? EINVAL : 0);
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)
357 struct quicc_device *qd;
358 struct quicc_softc *sc;
360 if (device_get_parent(child) != dev)
361 return (EINVAL);
363 /* Interrupt handlers must be FAST or MPSAFE. */
364 if (filt == NULL && !(flags & INTR_MPSAFE))
365 return (EINVAL);
367 sc = device_get_softc(dev);
368 if (sc->sc_polled)
369 return (ENXIO);
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);
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);
386 quicc_bus_teardown_intr(device_t dev, device_t child, struct resource *r,
387 void *cookie)
389 struct quicc_device *qd;
391 if (device_get_parent(child) != dev)
392 return (EINVAL);
394 qd = device_get_ivars(child);
395 if (qd->qd_ih != cookie)
396 return (EINVAL);
398 qd->qd_ih = NULL;
399 qd->qd_ih_arg = NULL;
400 return (0);