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 }
Cache object: 530552523f013988532e0a16da25f1dd
|