[BACK]Return to puc.c CVS log [TXT][DIR] Up to [local] / sys / dev / pci

Annotation of sys/dev/pci/puc.c, Revision 1.1

1.1     ! nbrk        1: /*     $OpenBSD: puc.c,v 1.12 2007/04/26 01:31:05 gwk Exp $    */
        !             2: /*     $NetBSD: puc.c,v 1.3 1999/02/06 06:29:54 cgd Exp $      */
        !             3:
        !             4: /*
        !             5:  * Copyright (c) 1996, 1998, 1999
        !             6:  *     Christopher G. Demetriou.  All rights reserved.
        !             7:  *
        !             8:  * Redistribution and use in source and binary forms, with or without
        !             9:  * modification, are permitted provided that the following conditions
        !            10:  * are met:
        !            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:  * 3. All advertising materials mentioning features or use of this software
        !            17:  *    must display the following acknowledgement:
        !            18:  *      This product includes software developed by Christopher G. Demetriou
        !            19:  *     for the NetBSD Project.
        !            20:  * 4. The name of the author may not be used to endorse or promote products
        !            21:  *    derived from this software without specific prior written permission
        !            22:  *
        !            23:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
        !            24:  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
        !            25:  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
        !            26:  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
        !            27:  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
        !            28:  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
        !            29:  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
        !            30:  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
        !            31:  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
        !            32:  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
        !            33:  */
        !            34:
        !            35: /*
        !            36:  * PCI "universal" communication card device driver, glues com, lpt,
        !            37:  * and similar ports to PCI via bridge chip often much larger than
        !            38:  * the devices being glued.
        !            39:  *
        !            40:  * Author: Christopher G. Demetriou, May 14, 1998 (derived from NetBSD
        !            41:  * sys/dev/pci/pciide.c, revision 1.6).
        !            42:  *
        !            43:  * These devices could be (and some times are) described as
        !            44:  * communications/{serial,parallel}, etc. devices with known
        !            45:  * programming interfaces, but those programming interfaces (in
        !            46:  * particular the BAR assignments for devices, etc.) in fact are not
        !            47:  * particularly well defined.
        !            48:  *
        !            49:  * After I/we have seen more of these devices, it may be possible
        !            50:  * to generalize some of these bits.  In particular, devices which
        !            51:  * describe themselves as communications/serial/16[45]50, and
        !            52:  * communications/parallel/??? might be attached via direct
        !            53:  * 'com' and 'lpt' attachments to pci.
        !            54:  */
        !            55:
        !            56: #include <sys/param.h>
        !            57: #include <sys/systm.h>
        !            58: #include <sys/device.h>
        !            59:
        !            60: #include <dev/pci/pcireg.h>
        !            61: #include <dev/pci/pcivar.h>
        !            62: #include <dev/pci/pucvar.h>
        !            63:
        !            64: #include <dev/pci/pcidevs.h>
        !            65:
        !            66: struct puc_pci_softc {
        !            67:        struct puc_softc        sc_psc;
        !            68:
        !            69:        pci_chipset_tag_t       pc;
        !            70:        pci_intr_handle_t       ih;
        !            71: };
        !            72:
        !            73: int    puc_pci_match(struct device *, void *, void *);
        !            74: void   puc_pci_attach(struct device *, struct device *, void *);
        !            75: const char *puc_pci_intr_string(struct puc_attach_args *);
        !            76: void   *puc_pci_intr_establish(struct puc_attach_args *, int,
        !            77:     int (*)(void *), void *, char *);
        !            78:
        !            79: struct cfattach puc_pci_ca = {
        !            80:        sizeof(struct puc_pci_softc), puc_pci_match, puc_pci_attach
        !            81: };
        !            82:
        !            83: struct cfdriver puc_cd = {
        !            84:        NULL, "puc", DV_DULL
        !            85: };
        !            86:
        !            87: const char *puc_port_type_name(int);
        !            88:
        !            89: int
        !            90: puc_pci_match(struct device *parent, void *match, void *aux)
        !            91: {
        !            92:        struct pci_attach_args *pa = aux;
        !            93:        const struct puc_device_description *desc;
        !            94:        pcireg_t bhlc, subsys;
        !            95:
        !            96:        bhlc = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG);
        !            97:        if (PCI_HDRTYPE_TYPE(bhlc) != 0)
        !            98:                return (0);
        !            99:
        !           100:        subsys = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG);
        !           101:
        !           102:        desc = puc_find_description(PCI_VENDOR(pa->pa_id),
        !           103:            PCI_PRODUCT(pa->pa_id), PCI_VENDOR(subsys), PCI_PRODUCT(subsys));
        !           104:        if (desc != NULL)
        !           105:                return (10);
        !           106:
        !           107:        /*
        !           108:         * Match class/subclass, so we can tell people to compile kernel
        !           109:         * with options that cause this driver to spew.
        !           110:         */
        !           111:        if (PCI_CLASS(pa->pa_class) == PCI_CLASS_COMMUNICATIONS &&
        !           112:            PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_BRIDGE_PCI)
        !           113:                return (1);
        !           114:
        !           115:        return (0);
        !           116: }
        !           117:
        !           118: const char *
        !           119: puc_pci_intr_string(struct puc_attach_args *paa)
        !           120: {
        !           121:        struct puc_pci_softc *sc = paa->puc;
        !           122:
        !           123:        return (pci_intr_string(sc->pc, sc->ih));
        !           124: }
        !           125:
        !           126: void *
        !           127: puc_pci_intr_establish(struct puc_attach_args *paa, int type,
        !           128:     int (*func)(void *), void *arg, char *name)
        !           129: {
        !           130:        struct puc_pci_softc *sc = paa->puc;
        !           131:
        !           132:        return (pci_intr_establish(sc->pc, sc->ih, type, func, arg, name));
        !           133: }
        !           134:
        !           135: void
        !           136: puc_pci_attach(struct device *parent, struct device *self, void *aux)
        !           137: {
        !           138:        struct puc_pci_softc *psc = (struct puc_pci_softc *)self;
        !           139:        struct puc_softc *sc = &psc->sc_psc;
        !           140:        struct pci_attach_args *pa = aux;
        !           141:        struct puc_attach_args paa;
        !           142:        pcireg_t subsys;
        !           143:        int i;
        !           144:
        !           145:        subsys = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG);
        !           146:        sc->sc_desc = puc_find_description(PCI_VENDOR(pa->pa_id),
        !           147:            PCI_PRODUCT(pa->pa_id), PCI_VENDOR(subsys), PCI_PRODUCT(subsys));
        !           148:        if (sc->sc_desc == NULL) {
        !           149:                /*
        !           150:                 * This was a class/subclass match, so tell people to compile
        !           151:                 * kernel with options that cause this driver to spew.
        !           152:                 */
        !           153: #ifdef PUC_PRINT_REGS
        !           154:                printf(":\n");
        !           155:                pci_conf_print(pa->pa_pc, pa->pa_tag, NULL);
        !           156: #else
        !           157:                printf(": unknown PCI communications device\n");
        !           158:                printf("%s: compile kernel with PUC_PRINT_REGS and larger\n",
        !           159:                    sc->sc_dev.dv_xname);
        !           160:                printf("%s: message buffer (via 'options MSGBUFSIZE=...'),\n",
        !           161:                    sc->sc_dev.dv_xname);
        !           162:                printf("%s: and report the result with sendbug(1)\n",
        !           163:                    sc->sc_dev.dv_xname);
        !           164: #endif
        !           165:                return;
        !           166:        }
        !           167:
        !           168:        puc_print_ports(sc->sc_desc);
        !           169:
        !           170:        /*
        !           171:         * XXX This driver assumes that 'com' ports attached to it
        !           172:         * XXX can not be console.  That isn't unreasonable, because PCI
        !           173:         * XXX devices are supposed to be dynamically mapped, and com
        !           174:         * XXX console ports want fixed addresses.  When/if baseboard
        !           175:         * XXX 'com' ports are identified as PCI/communications/serial
        !           176:         * XXX devices and are known to be mapped at the standard
        !           177:         * XXX addresses, if they can be the system console then we have
        !           178:         * XXX to cope with doing the mapping right.  Then this will get
        !           179:         * XXX really ugly.  Of course, by then we might know the real
        !           180:         * XXX definition of PCI/communications/serial, and attach 'com'
        !           181:         * XXX directly on PCI.
        !           182:         */
        !           183:        for (i = 0; i < PUC_NBARS; i++) {
        !           184:                pcireg_t type;
        !           185:                int bar;
        !           186:
        !           187:                sc->sc_bar_mappings[i].mapped = 0;
        !           188:                bar = PCI_MAPREG_START + 4 * i;
        !           189:                if (!pci_mapreg_probe(pa->pa_pc, pa->pa_tag, bar, &type))
        !           190:                        continue;
        !           191:
        !           192:                sc->sc_bar_mappings[i].mapped = (pci_mapreg_map(pa, bar, type,
        !           193:                    0, &sc->sc_bar_mappings[i].t, &sc->sc_bar_mappings[i].h,
        !           194:                    &sc->sc_bar_mappings[i].a, &sc->sc_bar_mappings[i].s, 0)
        !           195:                      == 0);
        !           196:                sc->sc_bar_mappings[i].type = type;
        !           197:                if (sc->sc_bar_mappings[i].mapped)
        !           198:                        continue;
        !           199:
        !           200:                printf("%s: couldn't map BAR at offset 0x%lx\n",
        !           201:                    sc->sc_dev.dv_xname, (long)bar);
        !           202:        }
        !           203:
        !           204:        /* Map interrupt. */
        !           205:        psc->pc = pa->pa_pc;
        !           206:        if (pci_intr_map(pa, &psc->ih)) {
        !           207:                printf("%s: couldn't map interrupt\n", sc->sc_dev.dv_xname);
        !           208:                return;
        !           209:        }
        !           210:
        !           211:        paa.puc = sc;
        !           212:        paa.hwtype = 0; /* autodetect */
        !           213:        paa.intr_string = &puc_pci_intr_string;
        !           214:        paa.intr_establish = &puc_pci_intr_establish;
        !           215:
        !           216:        /*
        !           217:         * If this is a serial card with a known specific chip, provide
        !           218:         * the UART type.
        !           219:         */
        !           220:        if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_PLX &&
        !           221:            PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_PLX_CRONYX_OMEGA)
        !           222:                paa.hwtype = 0x08;      /* XXX COM_UART_ST16C654 */
        !           223:
        !           224:        puc_common_attach(sc, &paa);
        !           225: }
        !           226:
        !           227: void
        !           228: puc_common_attach(struct puc_softc *sc, struct puc_attach_args *paa)
        !           229: {
        !           230:        int i, bar;
        !           231:
        !           232:        /*
        !           233:         * XXX the sub-devices establish the interrupts, for the
        !           234:         * XXX following reasons:
        !           235:         * XXX
        !           236:         * XXX    * we can't really know what IPLs they'd want
        !           237:         * XXX
        !           238:         * XXX    * the MD dispatching code can ("should") dispatch
        !           239:         * XXX      chained interrupts better than we can.
        !           240:         * XXX
        !           241:         * XXX It would be nice if we could indicate to the MD interrupt
        !           242:         * XXX handling code that the interrupt line used by the device
        !           243:         * XXX was a PCI (level triggered) interrupt.
        !           244:         * XXX
        !           245:         * XXX It's not pretty, but hey, what is?
        !           246:         */
        !           247:
        !           248:        /* Configure each port. */
        !           249:        for (i = 0; PUC_PORT_VALID(sc->sc_desc, i); i++) {
        !           250:                /* Skip unknown ports */
        !           251:                if (sc->sc_desc->ports[i].type != PUC_PORT_TYPE_COM &&
        !           252:                    sc->sc_desc->ports[i].type != PUC_PORT_TYPE_LPT)
        !           253:                        continue;
        !           254:                /* make sure the base address register is mapped */
        !           255:                bar = PUC_PORT_BAR_INDEX(sc->sc_desc->ports[i].bar);
        !           256:                if (!sc->sc_bar_mappings[bar].mapped) {
        !           257:                        printf("%s: %s port uses unmapped BAR (0x%x)\n",
        !           258:                            sc->sc_dev.dv_xname,
        !           259:                            puc_port_type_name(sc->sc_desc->ports[i].type),
        !           260:                            sc->sc_desc->ports[i].bar);
        !           261:                        continue;
        !           262:                }
        !           263:
        !           264:                /* set up to configure the child device */
        !           265:                paa->port = i;
        !           266:                paa->type = sc->sc_desc->ports[i].type;
        !           267:                paa->flags = sc->sc_desc->ports[i].flags;
        !           268:                paa->a = sc->sc_bar_mappings[bar].a;
        !           269:                paa->t = sc->sc_bar_mappings[bar].t;
        !           270:
        !           271:                if (bus_space_subregion(sc->sc_bar_mappings[bar].t,
        !           272:                    sc->sc_bar_mappings[bar].h, sc->sc_desc->ports[i].offset,
        !           273:                    sc->sc_bar_mappings[bar].s - sc->sc_desc->ports[i].offset,
        !           274:                    &paa->h)) {
        !           275:                        printf("%s: couldn't get subregion for port %d\n",
        !           276:                            sc->sc_dev.dv_xname, i);
        !           277:                        continue;
        !           278:                }
        !           279:
        !           280: #if 0
        !           281:                if (autoconf_verbose)
        !           282:                        printf("%s: port %d: %s @ (index %d) 0x%x "
        !           283:                            "(0x%lx, 0x%lx)\n", sc->sc_dev.dv_xname, paa->port,
        !           284:                            puc_port_type_name(paa->type), bar, (int)paa->a,
        !           285:                            (long)paa->t, (long)paa->h);
        !           286: #endif
        !           287:
        !           288:                /* and configure it */
        !           289:                sc->sc_ports[i].dev = config_found_sm(&sc->sc_dev, paa,
        !           290:                    puc_print, puc_submatch);
        !           291:        }
        !           292: }
        !           293:
        !           294: int
        !           295: puc_print(void *aux, const char *pnp)
        !           296: {
        !           297:        struct puc_attach_args *paa = aux;
        !           298:
        !           299:        if (pnp)
        !           300:                printf("%s at %s", puc_port_type_name(paa->type), pnp);
        !           301:        printf(" port %d", paa->port);
        !           302:        return (UNCONF);
        !           303: }
        !           304:
        !           305: int
        !           306: puc_submatch(struct device *parent, void *vcf, void *aux)
        !           307: {
        !           308:        struct cfdata *cf = (struct cfdata *)vcf;
        !           309:        struct puc_attach_args *aa = aux;
        !           310:
        !           311:        if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != aa->port)
        !           312:                return 0;
        !           313:        return ((*cf->cf_attach->ca_match)(parent, cf, aux));
        !           314: }
        !           315:
        !           316: const struct puc_device_description *
        !           317: puc_find_description(u_long vend, u_long prod, u_long svend, u_long sprod)
        !           318: {
        !           319:        int i;
        !           320:
        !           321: #define checkreg(val, index) \
        !           322:     (((val) & puc_devices[i].rmask[(index)]) == puc_devices[i].rval[(index)])
        !           323: #define pucdevdone(idx) \
        !           324:     (puc_devices[idx].rval[0] == 0 && puc_devices[idx].rval[1] == 0 \
        !           325:        && puc_devices[idx].rval[2] == 0 && puc_devices[idx].rval[3] == 0)
        !           326:
        !           327:        for (i = 0; !pucdevdone(i); i++) {
        !           328:                if (checkreg(vend, PUC_REG_VEND) &&
        !           329:                    checkreg(prod, PUC_REG_PROD) &&
        !           330:                    checkreg(svend, PUC_REG_SVEND) &&
        !           331:                    checkreg(sprod, PUC_REG_SPROD))
        !           332:                        return (&puc_devices[i]);
        !           333:        }
        !           334:
        !           335: #undef devdone
        !           336: #undef checkreg
        !           337:
        !           338:        return (NULL);
        !           339: }
        !           340:
        !           341: const char *
        !           342: puc_port_type_name(int type)
        !           343: {
        !           344:
        !           345:        switch (type) {
        !           346:        case PUC_PORT_TYPE_COM:
        !           347:                return "com";
        !           348:        case PUC_PORT_TYPE_LPT:
        !           349:                return "lpt";
        !           350:        default:
        !           351:                return "unknown";
        !           352:        }
        !           353: }
        !           354:
        !           355: void
        !           356: puc_print_ports(const struct puc_device_description *desc)
        !           357: {
        !           358:        int i, ncom, nlpt;
        !           359:
        !           360:        printf(": ports: ");
        !           361:        for (i = ncom = nlpt = 0; PUC_PORT_VALID(desc, i); i++) {
        !           362:                switch (desc->ports[i].type) {
        !           363:                case PUC_PORT_TYPE_COM:
        !           364:                        ncom++;
        !           365:                break;
        !           366:                case PUC_PORT_TYPE_LPT:
        !           367:                        nlpt++;
        !           368:                break;
        !           369:                default:
        !           370:                        printf("port %d unknown type %d ", i,
        !           371:                            desc->ports[i].type);
        !           372:                }
        !           373:        }
        !           374:        if (ncom)
        !           375:                printf("%d com", ncom);
        !           376:        if (nlpt) {
        !           377:                if (ncom)
        !           378:                        printf(", ");
        !           379:                printf("%d lpt", nlpt);
        !           380:        }
        !           381:        printf("\n");
        !           382: }

CVSweb