]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/dev/quicc/quicc_core.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / dev / quicc / quicc_core.c
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         u_long 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     u_long start, u_long end, u_long 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     u_long *startp, u_long *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 }