]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/scc/scc_core.c
MFV r366990: less v563.
[FreeBSD/FreeBSD.git] / sys / dev / scc / scc_core.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2004-2006 Marcel Moolenaar
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  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 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/lock.h>
37 #include <sys/kernel.h>
38 #include <sys/malloc.h>
39 #include <sys/mutex.h>
40 #include <sys/queue.h>
41 #include <sys/serial.h>
42
43 #include <machine/bus.h>
44 #include <machine/resource.h>
45 #include <sys/rman.h>
46
47 #include <dev/scc/scc_bfe.h>
48 #include <dev/scc/scc_bus.h>
49
50 #include "scc_if.h"
51
52 devclass_t scc_devclass;
53 const char scc_driver_name[] = "scc";
54
55 static MALLOC_DEFINE(M_SCC, "SCC", "SCC driver");
56
57 static int
58 scc_bfe_intr(void *arg)
59 {
60         struct scc_softc *sc = arg;
61         struct scc_chan *ch;
62         struct scc_class *cl;
63         struct scc_mode *m;
64         int c, i, ipend, isrc;
65
66         cl = sc->sc_class;
67         while (!sc->sc_leaving && (ipend = SCC_IPEND(sc)) != 0) {
68                 i = 0, isrc = SER_INT_OVERRUN;
69                 while (ipend) {
70                         while (i < SCC_ISRCCNT && !(ipend & isrc))
71                                 i++, isrc <<= 1;
72                         KASSERT(i < SCC_ISRCCNT, ("%s", __func__));
73                         ipend &= ~isrc;
74                         for (c = 0; c < cl->cl_channels; c++) {
75                                 ch = &sc->sc_chan[c];
76                                 if (!(ch->ch_ipend & isrc))
77                                         continue;
78                                 m = &ch->ch_mode[0];
79                                 if (m->ih_src[i] == NULL)
80                                         continue;
81                                 if ((*m->ih_src[i])(m->ih_arg))
82                                         ch->ch_ipend &= ~isrc;
83                         }
84                 }
85                 for (c = 0; c < cl->cl_channels; c++) {
86                         ch = &sc->sc_chan[c];
87                         if (!ch->ch_ipend)
88                                 continue;
89                         m = &ch->ch_mode[0];
90                         if (m->ih != NULL)
91                                 (*m->ih)(m->ih_arg);
92                         else
93                                 SCC_ICLEAR(sc, ch);
94                 }
95                 return (FILTER_HANDLED);
96         }
97         return (FILTER_STRAY);
98 }
99
100 int
101 scc_bfe_attach(device_t dev, u_int ipc)
102 {
103         struct resource_list_entry *rle;
104         struct scc_chan *ch;
105         struct scc_class *cl;
106         struct scc_mode *m;
107         struct scc_softc *sc, *sc0;
108         const char *sep;
109         bus_space_handle_t bh;
110         rman_res_t base, size, start, sz;
111         int c, error, mode, sysdev;
112
113         /*
114          * The sc_class field defines the type of SCC we're going to work
115          * with and thus the size of the softc. Replace the generic softc
116          * with one that matches the SCC now that we're certain we handle
117          * the device.
118          */
119         sc0 = device_get_softc(dev);
120         cl = sc0->sc_class;
121         if (cl->size > sizeof(*sc)) {
122                 sc = malloc(cl->size, M_SCC, M_WAITOK|M_ZERO);
123                 bcopy(sc0, sc, sizeof(*sc));
124                 device_set_softc(dev, sc);
125         } else
126                 sc = sc0;
127
128         size = abs(cl->cl_range) << sc->sc_bas.regshft;
129
130         mtx_init(&sc->sc_hwmtx, "scc_hwmtx", NULL, MTX_SPIN);
131
132         /*
133          * Re-allocate. We expect that the softc contains the information
134          * collected by scc_bfe_probe() intact.
135          */
136         sc->sc_rres = bus_alloc_resource_anywhere(dev, sc->sc_rtype,
137             &sc->sc_rrid, cl->cl_channels * size, RF_ACTIVE);
138         if (sc->sc_rres == NULL)
139                 return (ENXIO);
140         sc->sc_bas.bsh = rman_get_bushandle(sc->sc_rres);
141         sc->sc_bas.bst = rman_get_bustag(sc->sc_rres);
142
143         /*
144          * Allocate interrupt resources. There may be a different interrupt
145          * per channel. We allocate them all...
146          */
147         sc->sc_chan = malloc(sizeof(struct scc_chan) * cl->cl_channels,
148             M_SCC, M_WAITOK | M_ZERO);
149         for (c = 0; c < cl->cl_channels; c++) {
150                 ch = &sc->sc_chan[c];
151                 /*
152                  * XXX temporary hack. If we have more than 1 interrupt
153                  * per channel, allocate the first for the channel. At
154                  * this time only the macio bus front-end has more than
155                  * 1 interrupt per channel and we don't use the 2nd and
156                  * 3rd, because we don't support DMA yet.
157                  */
158                 ch->ch_irid = c * ipc;
159                 ch->ch_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ,
160                     &ch->ch_irid, RF_ACTIVE | RF_SHAREABLE);
161                 if (ipc == 0)
162                         break;
163         }
164
165         /*
166          * Create the control structures for our children. Probe devices
167          * and query them to see if we can reset the hardware.
168          */
169         sysdev = 0;
170         base = rman_get_start(sc->sc_rres);
171         sz = (size != 0) ? size : rman_get_size(sc->sc_rres);
172         start = base + ((cl->cl_range < 0) ? size * (cl->cl_channels - 1) : 0);
173         for (c = 0; c < cl->cl_channels; c++) {
174                 ch = &sc->sc_chan[c];
175                 resource_list_init(&ch->ch_rlist);
176                 ch->ch_nr = c + 1;
177
178                 if (!SCC_ENABLED(sc, ch))
179                         goto next;
180
181                 ch->ch_enabled = 1;
182                 resource_list_add(&ch->ch_rlist, sc->sc_rtype, 0, start,
183                     start + sz - 1, sz);
184                 rle = resource_list_find(&ch->ch_rlist, sc->sc_rtype, 0);
185                 rle->res = &ch->ch_rres;
186                 bus_space_subregion(rman_get_bustag(sc->sc_rres),
187                     rman_get_bushandle(sc->sc_rres), start - base, sz, &bh);
188                 rman_set_bushandle(rle->res, bh);
189                 rman_set_bustag(rle->res, rman_get_bustag(sc->sc_rres));
190
191                 resource_list_add(&ch->ch_rlist, SYS_RES_IRQ, 0, c, c, 1);
192                 rle = resource_list_find(&ch->ch_rlist, SYS_RES_IRQ, 0);
193                 rle->res = (ch->ch_ires != NULL) ? ch->ch_ires :
194                             sc->sc_chan[0].ch_ires;
195
196                 for (mode = 0; mode < SCC_NMODES; mode++) {
197                         m = &ch->ch_mode[mode];
198                         m->m_chan = ch;
199                         m->m_mode = 1U << mode;
200                         if ((cl->cl_modes & m->m_mode) == 0 || ch->ch_sysdev)
201                                 continue;
202                         m->m_dev = device_add_child(dev, NULL, -1);
203                         device_set_ivars(m->m_dev, (void *)m);
204                         error = device_probe_child(dev, m->m_dev);
205                         if (!error) {
206                                 m->m_probed = 1;
207                                 m->m_sysdev = SERDEV_SYSDEV(m->m_dev) ? 1 : 0;
208                                 ch->ch_sysdev |= m->m_sysdev;
209                         }
210                 }
211
212          next:
213                 start += (cl->cl_range < 0) ? -size : size;
214                 sysdev |= ch->ch_sysdev;
215         }
216
217         /*
218          * Have the hardware driver initialize the hardware. Tell it
219          * whether or not a hardware reset should be performed.
220          */
221         if (bootverbose) {
222                 device_printf(dev, "%sresetting hardware\n",
223                     (sysdev) ? "not " : "");
224         }
225         error = SCC_ATTACH(sc, !sysdev);
226         if (error)
227                 goto fail;
228
229         /*
230          * Setup our interrupt handler. Make it FAST under the assumption
231          * that our children's are fast as well. We make it MPSAFE as soon
232          * as a child sets up a MPSAFE interrupt handler.
233          * Of course, if we can't setup a fast handler, we make it MPSAFE
234          * right away.
235          */
236         for (c = 0; c < cl->cl_channels; c++) {
237                 ch = &sc->sc_chan[c];
238                 if (ch->ch_ires == NULL)
239                         continue;
240                 error = bus_setup_intr(dev, ch->ch_ires,
241                     INTR_TYPE_TTY, scc_bfe_intr, NULL, sc,
242                     &ch->ch_icookie);
243                 if (error) {
244                         error = bus_setup_intr(dev, ch->ch_ires,
245                             INTR_TYPE_TTY | INTR_MPSAFE, NULL,
246                             (driver_intr_t *)scc_bfe_intr, sc, &ch->ch_icookie);
247                 } else
248                         sc->sc_fastintr = 1;
249
250                 if (error) {
251                         device_printf(dev, "could not activate interrupt\n");
252                         bus_release_resource(dev, SYS_RES_IRQ, ch->ch_irid,
253                             ch->ch_ires);
254                         ch->ch_ires = NULL;
255                 }
256         }
257         sc->sc_polled = 1;
258         for (c = 0; c < cl->cl_channels; c++) {
259                 if (sc->sc_chan[0].ch_ires != NULL)
260                         sc->sc_polled = 0;
261         }
262
263         /*
264          * Attach all child devices that were probed successfully.
265          */
266         for (c = 0; c < cl->cl_channels; c++) {
267                 ch = &sc->sc_chan[c];
268                 for (mode = 0; mode < SCC_NMODES; mode++) {
269                         m = &ch->ch_mode[mode];
270                         if (!m->m_probed)
271                                 continue;
272                         error = device_attach(m->m_dev);
273                         if (error)
274                                 continue;
275                         m->m_attached = 1;
276                 }
277         }
278
279         if (bootverbose && (sc->sc_fastintr || sc->sc_polled)) {
280                 sep = "";
281                 device_print_prettyname(dev);
282                 if (sc->sc_fastintr) {
283                         printf("%sfast interrupt", sep);
284                         sep = ", ";
285                 }
286                 if (sc->sc_polled) {
287                         printf("%spolled mode", sep);
288                         sep = ", ";
289                 }
290                 printf("\n");
291         }
292
293         return (0);
294
295  fail:
296         for (c = 0; c < cl->cl_channels; c++) {
297                 ch = &sc->sc_chan[c];
298                 if (ch->ch_ires == NULL)
299                         continue;
300                 bus_release_resource(dev, SYS_RES_IRQ, ch->ch_irid,
301                     ch->ch_ires);
302         }
303         bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres);
304         return (error);
305 }
306
307 int
308 scc_bfe_detach(device_t dev)
309 {
310         struct scc_chan *ch;
311         struct scc_class *cl;
312         struct scc_mode *m;
313         struct scc_softc *sc;
314         int chan, error, mode;
315
316         sc = device_get_softc(dev);
317         cl = sc->sc_class;
318
319         /* Detach our children. */
320         error = 0;
321         for (chan = 0; chan < cl->cl_channels; chan++) {
322                 ch = &sc->sc_chan[chan];
323                 for (mode = 0; mode < SCC_NMODES; mode++) {
324                         m = &ch->ch_mode[mode];
325                         if (!m->m_attached)
326                                 continue;
327                         if (device_detach(m->m_dev) != 0)
328                                 error = ENXIO;
329                         else
330                                 m->m_attached = 0;
331                 }
332         }
333
334         if (error)
335                 return (error);
336
337         for (chan = 0; chan < cl->cl_channels; chan++) {
338                 ch = &sc->sc_chan[chan];
339                 if (ch->ch_ires == NULL)
340                         continue;
341                 bus_teardown_intr(dev, ch->ch_ires, ch->ch_icookie);
342                 bus_release_resource(dev, SYS_RES_IRQ, ch->ch_irid,
343                     ch->ch_ires);
344         }
345         bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres);
346
347         free(sc->sc_chan, M_SCC);
348
349         mtx_destroy(&sc->sc_hwmtx);
350         return (0);
351 }
352
353 int
354 scc_bfe_probe(device_t dev, u_int regshft, u_int rclk, u_int rid)
355 {
356         struct scc_softc *sc;
357         struct scc_class *cl;
358         u_long size, sz;
359         int error;
360
361         /*
362          * Initialize the instance. Note that the instance (=softc) does
363          * not necessarily match the hardware specific softc. We can't do
364          * anything about it now, because we may not attach to the device.
365          * Hardware drivers cannot use any of the class specific fields
366          * while probing.
367          */
368         sc = device_get_softc(dev);
369         cl = sc->sc_class;
370         kobj_init((kobj_t)sc, (kobj_class_t)cl);
371         sc->sc_dev = dev;
372         if (device_get_desc(dev) == NULL)
373                 device_set_desc(dev, cl->name);
374
375         size = abs(cl->cl_range) << regshft;
376
377         /*
378          * Allocate the register resource. We assume that all SCCs have a
379          * single register window in either I/O port space or memory mapped
380          * I/O space. Any SCC that needs multiple windows will consequently
381          * not be supported by this driver as-is.
382          */
383         sc->sc_rrid = rid;
384         sc->sc_rtype = SYS_RES_MEMORY;
385         sc->sc_rres = bus_alloc_resource_anywhere(dev, sc->sc_rtype,
386             &sc->sc_rrid, cl->cl_channels * size, RF_ACTIVE);
387         if (sc->sc_rres == NULL) {
388                 sc->sc_rrid = rid;
389                 sc->sc_rtype = SYS_RES_IOPORT;
390                 sc->sc_rres = bus_alloc_resource_anywhere(dev, sc->sc_rtype,
391                     &sc->sc_rrid, cl->cl_channels * size, RF_ACTIVE);
392                 if (sc->sc_rres == NULL)
393                         return (ENXIO);
394         }
395
396         /*
397          * Fill in the bus access structure and call the hardware specific
398          * probe method.
399          */
400         sz = (size != 0) ? size : rman_get_size(sc->sc_rres);
401         sc->sc_bas.bsh = rman_get_bushandle(sc->sc_rres);
402         sc->sc_bas.bst = rman_get_bustag(sc->sc_rres);
403         sc->sc_bas.range = sz;
404         sc->sc_bas.rclk = rclk;
405         sc->sc_bas.regshft = regshft;
406
407         error = SCC_PROBE(sc);
408         bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres);
409         return ((error == 0) ? BUS_PROBE_DEFAULT : error);
410 }
411
412 struct resource *
413 scc_bus_alloc_resource(device_t dev, device_t child, int type, int *rid,
414     rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
415 {
416         struct resource_list_entry *rle;
417         struct scc_chan *ch;
418         struct scc_mode *m;
419
420         if (device_get_parent(child) != dev)
421                 return (NULL);
422
423         /* We only support default allocations. */
424         if (!RMAN_IS_DEFAULT_RANGE(start, end))
425                 return (NULL);
426
427         m = device_get_ivars(child);
428         ch = m->m_chan;
429         rle = resource_list_find(&ch->ch_rlist, type, 0);
430         if (rle == NULL)
431                 return (NULL);
432         *rid = 0;
433         return (rle->res);
434 }
435
436 int
437 scc_bus_get_resource(device_t dev, device_t child, int type, int rid,
438     rman_res_t *startp, rman_res_t *countp)
439 {
440         struct resource_list_entry *rle;
441         struct scc_chan *ch;
442         struct scc_mode *m;
443
444         if (device_get_parent(child) != dev)
445                 return (EINVAL);
446
447         m = device_get_ivars(child);
448         ch = m->m_chan;
449         rle = resource_list_find(&ch->ch_rlist, type, rid);
450         if (rle == NULL)
451                 return (EINVAL);
452
453         if (startp != NULL)
454                 *startp = rle->start;
455         if (countp != NULL)
456                 *countp = rle->count;
457         return (0);
458 }
459
460 int
461 scc_bus_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
462 {
463         struct scc_chan *ch;
464         struct scc_class *cl;
465         struct scc_mode *m;
466         struct scc_softc *sc;
467
468         if (device_get_parent(child) != dev)
469                 return (EINVAL);
470
471         sc = device_get_softc(dev);
472         cl = sc->sc_class;
473         m = device_get_ivars(child);
474         ch = m->m_chan;
475
476         switch (index) {
477         case SCC_IVAR_CHANNEL:
478                 *result = ch->ch_nr;
479                 break;
480         case SCC_IVAR_CLASS:
481                 *result = cl->cl_class;
482                 break;
483         case SCC_IVAR_CLOCK:
484                 *result = sc->sc_bas.rclk;
485                 break;
486         case SCC_IVAR_MODE:
487                 *result = m->m_mode;
488                 break;
489         case SCC_IVAR_REGSHFT:
490                 *result = sc->sc_bas.regshft;
491                 break;
492         case SCC_IVAR_HWMTX:
493                 *result = (uintptr_t)&sc->sc_hwmtx;
494                 break;
495         default:
496                 return (EINVAL);
497         }
498         return (0);
499 }
500
501 int
502 scc_bus_release_resource(device_t dev, device_t child, int type, int rid,
503     struct resource *res)
504 {
505         struct resource_list_entry *rle;
506         struct scc_chan *ch;
507         struct scc_mode *m;
508
509         if (device_get_parent(child) != dev)
510                 return (EINVAL);
511
512         m = device_get_ivars(child);
513         ch = m->m_chan;
514         rle = resource_list_find(&ch->ch_rlist, type, rid);
515         return ((rle == NULL) ? EINVAL : 0);
516 }
517
518 int
519 scc_bus_setup_intr(device_t dev, device_t child, struct resource *r, int flags,
520     driver_filter_t *filt, void (*ihand)(void *), void *arg, void **cookiep)
521 {
522         struct scc_chan *ch;
523         struct scc_mode *m;
524         struct scc_softc *sc;
525         int c, i, isrc;
526
527         if (device_get_parent(child) != dev)
528                 return (EINVAL);
529
530         /* Interrupt handlers must be FAST or MPSAFE. */
531         if (filt == NULL && !(flags & INTR_MPSAFE))
532                 return (EINVAL);
533
534         sc = device_get_softc(dev);
535         if (sc->sc_polled)
536                 return (ENXIO);
537
538         if (sc->sc_fastintr && filt == NULL) {
539                 sc->sc_fastintr = 0;
540                 for (c = 0; c < sc->sc_class->cl_channels; c++) {
541                         ch = &sc->sc_chan[c];
542                         if (ch->ch_ires == NULL)
543                                 continue;
544                         bus_teardown_intr(dev, ch->ch_ires, ch->ch_icookie);
545                         bus_setup_intr(dev, ch->ch_ires,
546                             INTR_TYPE_TTY | INTR_MPSAFE, NULL,
547                             (driver_intr_t *)scc_bfe_intr, sc, &ch->ch_icookie);
548                 }
549         }
550
551         m = device_get_ivars(child);
552         m->m_hasintr = 1;
553         m->m_fastintr = (filt != NULL) ? 1 : 0;
554         m->ih = (filt != NULL) ? filt : (driver_filter_t *)ihand;
555         m->ih_arg = arg;
556
557         i = 0, isrc = SER_INT_OVERRUN;
558         while (i < SCC_ISRCCNT) {
559                 m->ih_src[i] = SERDEV_IHAND(child, isrc);
560                 if (m->ih_src[i] != NULL)
561                         m->ih = NULL;
562                 i++, isrc <<= 1;
563         }
564         return (0);
565 }
566
567 int
568 scc_bus_teardown_intr(device_t dev, device_t child, struct resource *r,
569     void *cookie)
570 {
571         struct scc_mode *m;
572         int i;
573
574         if (device_get_parent(child) != dev)
575                 return (EINVAL);
576
577         m = device_get_ivars(child);
578         if (!m->m_hasintr)
579                 return (EINVAL);
580
581         m->m_hasintr = 0;
582         m->m_fastintr = 0;
583         m->ih = NULL;
584         m->ih_arg = NULL;
585         for (i = 0; i < SCC_ISRCCNT; i++)
586                 m->ih_src[i] = NULL;
587         return (0);
588 }