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