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