1 /* $OpenBSD: puc.c,v 1.4 2001/06/12 15:40:33 niklas 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 struct puc_softc { 65 struct device sc_dev; 66 67 /* static configuration data */ 68 const struct puc_device_description *sc_desc; 69 70 /* card-global dynamic data */ 71 void *sc_ih; 72 struct { 73 int mapped; 74 bus_addr_t a; 75 bus_size_t s; 76 bus_space_tag_t t; 77 bus_space_handle_t h; 78 } sc_bar_mappings[6]; /* XXX constant */ 79 80 /* per-port dynamic data */ 81 struct { 82 struct device *dev; 83 84 /* filled in by port attachments */ 85 int (*ihand) __P((void *)); 86 void *ihandarg; 87 } sc_ports[PUC_MAX_PORTS]; 88 }; 89 90 int puc_match __P((struct device *, void *, void *)); 91 void puc_attach __P((struct device *, struct device *, void *)); 92 int puc_print __P((void *, const char *)); 93 int puc_submatch __P((struct device *, void *, void *)); 94 95 struct cfattach puc_ca = { 96 sizeof(struct puc_softc), puc_match, puc_attach 97 }; 98 99 struct cfdriver puc_cd = { 100 NULL, "puc", DV_DULL 101 }; 102 103 static const struct puc_device_description * 104 puc_find_description __P((pcireg_t, pcireg_t, pcireg_t, pcireg_t)); 105 static const char * 106 puc_port_type_name __P((int)); 107 108 int 109 puc_match(parent, match, aux) 110 struct device *parent; 111 void *match, *aux; 112 { 113 struct pci_attach_args *pa = aux; 114 const struct puc_device_description *desc; 115 pcireg_t bhlc, subsys; 116 117 bhlc = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG); 118 if (PCI_HDRTYPE_TYPE(bhlc) != 0) 119 return (0); 120 121 subsys = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG); 122 123 desc = puc_find_description(PCI_VENDOR(pa->pa_id), 124 PCI_PRODUCT(pa->pa_id), PCI_VENDOR(subsys), PCI_PRODUCT(subsys)); 125 if (desc != NULL) 126 return (10); 127 128 /* 129 * Match class/subclass, so we can tell people to compile kernel 130 * with options that cause this driver to spew. 131 */ 132 if (PCI_CLASS(pa->pa_class) == PCI_CLASS_COMMUNICATIONS && 133 PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_BRIDGE_PCI) 134 return (1); 135 136 return (0); 137 } 138 139 void 140 puc_attach(parent, self, aux) 141 struct device *parent, *self; 142 void *aux; 143 { 144 struct puc_softc *sc = (struct puc_softc *)self; 145 struct pci_attach_args *pa = aux; 146 struct puc_attach_args paa; 147 pci_intr_handle_t intrhandle; 148 pcireg_t subsys; 149 int i, barindex; 150 151 subsys = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG); 152 sc->sc_desc = puc_find_description(PCI_VENDOR(pa->pa_id), 153 PCI_PRODUCT(pa->pa_id), PCI_VENDOR(subsys), PCI_PRODUCT(subsys)); 154 if (sc->sc_desc == NULL) { 155 /* 156 * This was a class/subclass match, so tell people to compile 157 * kernel with options that cause this driver to spew. 158 */ 159 #ifdef PUC_PRINT_REGS 160 printf(":\n"); 161 pci_conf_print(pa->pa_pc, pa->pa_tag, NULL); 162 #else 163 printf(": unknown PCI communications device\n"); 164 printf("%s: compile kernel with PUC_PRINT_REGS and larger\n", 165 sc->sc_dev.dv_xname); 166 printf("%s: mesage buffer (via 'options MSGBUFSIZE=...'),\n", 167 sc->sc_dev.dv_xname); 168 printf("%s: and report the result with send-pr\n", 169 sc->sc_dev.dv_xname); 170 #endif 171 return; 172 } 173 174 printf(": "); 175 for (i = 0; PUC_PORT_VALID(sc->sc_desc, i); i++) 176 printf("%s%s", i ? ", " : "", 177 puc_port_type_name(sc->sc_desc->ports[i].type)); 178 printf("\n"); 179 180 /* 181 * XXX This driver assumes that 'com' ports attached to it 182 * XXX can not be console. That isn't unreasonable, because PCI 183 * XXX devices are supposed to be dynamically mapped, and com 184 * XXX console ports want fixed addresses. When/if baseboard 185 * XXX 'com' ports are identified as PCI/communications/serial 186 * XXX devices and are known to be mapped at the standard 187 * XXX addresses, if they can be the system console then we have 188 * XXX to cope with doing the mapping right. Then this will get 189 * XXX really ugly. Of course, by then we might know the real 190 * XXX definition of PCI/communications/serial, and attach 'com' 191 * XXX directly on PCI. 192 */ 193 for (i = 0; i < 6; i++) { 194 pcireg_t bar, type; 195 196 sc->sc_bar_mappings[i].mapped = 0; 197 198 bar = pci_conf_read(pa->pa_pc, pa->pa_tag, 199 PCI_MAPREG_START + 4 * i); /* XXX const */ 200 if (bar == 0) /* BAR not implemented(?) */ 201 continue; 202 203 type = (PCI_MAPREG_TYPE(bar) == PCI_MAPREG_TYPE_IO ? 204 PCI_MAPREG_TYPE_IO : PCI_MAPREG_MEM_TYPE(bar)); 205 sc->sc_bar_mappings[i].mapped = (pci_mapreg_map(pa, 206 PCI_MAPREG_START + 4 * i, type, 0, 207 &sc->sc_bar_mappings[i].t, &sc->sc_bar_mappings[i].h, 208 &sc->sc_bar_mappings[i].a, &sc->sc_bar_mappings[i].s, 0) 209 == 0); 210 if (sc->sc_bar_mappings[i].mapped) 211 continue; 212 213 printf("%s: couldn't map BAR at offset 0x%lx\n", 214 sc->sc_dev.dv_xname, (long)(PCI_MAPREG_START + 4 * i)); 215 } 216 217 /* Map interrupt. */ 218 if (pci_intr_map(pa->pa_pc, pa->pa_intrtag, pa->pa_intrpin, 219 pa->pa_intrline, &intrhandle)) { 220 printf("%s: couldn't map interrupt\n", sc->sc_dev.dv_xname); 221 return; 222 } 223 /* 224 * XXX the sub-devices establish the interrupts, for the 225 * XXX following reasons: 226 * XXX 227 * XXX * we can't really know what IPLs they'd want 228 * XXX 229 * XXX * the MD dispatching code can ("should") dispatch 230 * XXX chained interrupts better than we can. 231 * XXX 232 * XXX It would be nice if we could indicate to the MD interrupt 233 * XXX handling code that the interrupt line used by the device 234 * XXX was a PCI (level triggered) interrupt. 235 * XXX 236 * XXX It's not pretty, but hey, what is? 237 */ 238 239 /* Configure each port. */ 240 for (i = 0; PUC_PORT_VALID(sc->sc_desc, i); i++) { 241 bus_space_handle_t subregion_handle; 242 243 /* make sure the base address register is mapped */ 244 barindex = PUC_PORT_BAR_INDEX(sc->sc_desc->ports[i].bar); 245 if (!sc->sc_bar_mappings[barindex].mapped) { 246 printf("%s: %s port uses unmapped BAR (0x%x)\n", 247 sc->sc_dev.dv_xname, 248 puc_port_type_name(sc->sc_desc->ports[i].type), 249 sc->sc_desc->ports[i].bar); 250 continue; 251 } 252 253 /* set up to configure the child device */ 254 paa.port = i; 255 paa.type = sc->sc_desc->ports[i].type; 256 paa.flags = sc->sc_desc->ports[i].flags; 257 paa.pc = pa->pa_pc; 258 paa.intrhandle = intrhandle; 259 paa.a = sc->sc_bar_mappings[barindex].a; 260 paa.t = sc->sc_bar_mappings[barindex].t; 261 262 if (bus_space_subregion(sc->sc_bar_mappings[barindex].t, 263 sc->sc_bar_mappings[barindex].h, 264 sc->sc_desc->ports[i].offset, 265 sc->sc_bar_mappings[barindex].s - 266 sc->sc_desc->ports[i].offset, 267 &subregion_handle)) { 268 printf("%s: couldn't get subregion for port %d\n", 269 sc->sc_dev.dv_xname, i); 270 continue; 271 } 272 paa.h = subregion_handle; 273 274 #if 0 275 printf("%s: port %d: %s @ (index %d) 0x%x (0x%lx, 0x%lx)\n", 276 sc->sc_dev.dv_xname, paa.port, 277 puc_port_type_name(paa.type), barindex, (int)paa.a, 278 (long)paa.t, (long)paa.h); 279 #endif 280 281 /* and configure it */ 282 sc->sc_ports[i].dev = config_found_sm(self, &paa, puc_print, 283 puc_submatch); 284 } 285 } 286 287 int 288 puc_print(aux, pnp) 289 void *aux; 290 const char *pnp; 291 { 292 struct puc_attach_args *paa = aux; 293 294 if (pnp) 295 printf("%s at %s", puc_port_type_name(paa->type), pnp); 296 printf(" port %d", paa->port); 297 return (UNCONF); 298 } 299 300 int 301 puc_submatch(parent, vcf, aux) 302 struct device *parent; 303 void *vcf, *aux; 304 { 305 struct cfdata *cf = (struct cfdata *)vcf; 306 struct puc_attach_args *aa = aux; 307 308 if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != aa->port) 309 return 0; 310 return ((*cf->cf_attach->ca_match)(parent, cf, aux)); 311 } 312 313 static const struct puc_device_description * 314 puc_find_description(vend, prod, svend, sprod) 315 pcireg_t vend, prod, svend, sprod; 316 { 317 int i; 318 319 #define checkreg(val, index) \ 320 (((val) & puc_devices[i].rmask[(index)]) == puc_devices[i].rval[(index)]) 321 #define pucdevdone(idx) \ 322 (puc_devices[idx].rval[0] == 0 && puc_devices[idx].rval[1] == 0 \ 323 && puc_devices[idx].rval[2] == 0 && puc_devices[idx].rval[3] == 0) 324 325 for (i = 0; !pucdevdone(i); i++) { 326 if (checkreg(vend, PUC_REG_VEND) && 327 checkreg(prod, PUC_REG_PROD) && 328 checkreg(svend, PUC_REG_SVEND) && 329 checkreg(sprod, PUC_REG_SPROD)) 330 return (&puc_devices[i]); 331 } 332 333 #undef devdone 334 #undef checkreg 335 336 return (NULL); 337 } 338 339 static const char * 340 puc_port_type_name(type) 341 int type; 342 { 343 344 switch (type) { 345 case PUC_PORT_TYPE_COM: 346 return "com"; 347 case PUC_PORT_TYPE_LPT: 348 return "lpt"; 349 default: 350 panic("puc_port_type_name %d", type); 351 } 352 } 353