1 /* $NetBSD: pcmcom.c,v 1.9 2002/06/01 23:51:02 lukem Exp $ */ 2 3 /*- 4 * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by RedBack Networks, Inc. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /* 40 * Device driver for multi-port PCMCIA serial cards, written by 41 * Jason R. Thorpe for RedBack Networks, Inc. 42 * 43 * Most of these cards are simply multiple UARTs sharing a single interrupt 44 * line, and rely on the fact that PCMCIA level-triggered interrupts can 45 * be shared. There are no special interrupt registers on them, as there 46 * are on most ISA multi-port serial cards. 47 * 48 * If there are other cards that have interrupt registers, they should not 49 * be glued into this driver. Rather, separate drivers should be written 50 * for those devices, as we have in the ISA multi-port serial card case. 51 */ 52 53 #include <sys/cdefs.h> 54 __KERNEL_RCSID(0, "$NetBSD: pcmcom.c,v 1.9 2002/06/01 23:51:02 lukem Exp $"); 55 56 #include <sys/param.h> 57 #include <sys/systm.h> 58 #include <sys/device.h> 59 #include <sys/termios.h> 60 #include <sys/malloc.h> 61 62 #include <machine/bus.h> 63 #include <machine/intr.h> 64 65 #include <dev/ic/comreg.h> 66 #include <dev/ic/comvar.h> 67 68 #include <dev/pcmcia/pcmciavar.h> 69 #include <dev/pcmcia/pcmciareg.h> 70 #include <dev/pcmcia/pcmciadevs.h> 71 72 #include "com.h" 73 #include "pcmcom.h" 74 75 #include "locators.h" 76 77 struct pcmcom_slave_info { 78 struct pcmcia_io_handle psi_pcioh; /* PCMCIA i/o space info */ 79 int psi_io_window; /* our i/o window */ 80 struct device *psi_child; /* child's device */ 81 }; 82 83 struct pcmcom_softc { 84 struct device sc_dev; /* generic device glue */ 85 86 struct pcmcia_function *sc_pf; /* our PCMCIA function */ 87 void *sc_ih; /* interrupt handle */ 88 int sc_enabled_count; /* enabled count */ 89 90 struct pcmcom_slave_info *sc_slaves; /* slave info */ 91 int sc_nslaves; /* slave count */ 92 }; 93 94 struct pcmcom_attach_args { 95 bus_space_tag_t pca_iot; /* I/O tag */ 96 bus_space_handle_t pca_ioh; /* I/O handle */ 97 int pca_slave; /* slave # */ 98 }; 99 100 int pcmcom_match __P((struct device *, struct cfdata *, void *)); 101 void pcmcom_attach __P((struct device *, struct device *, void *)); 102 int pcmcom_detach __P((struct device *, int)); 103 int pcmcom_activate __P((struct device *, enum devact)); 104 105 struct cfattach pcmcom_ca = { 106 sizeof(struct pcmcom_softc), pcmcom_match, pcmcom_attach, 107 pcmcom_detach, pcmcom_activate 108 }; 109 110 const struct pcmcom_product { 111 struct pcmcia_product pp_product; 112 int pp_nslaves; /* number of slaves */ 113 } pcmcom_products[] = { 114 { { PCMCIA_STR_SOCKET_DUAL_RS232, PCMCIA_VENDOR_SOCKET, 115 PCMCIA_PRODUCT_SOCKET_DUAL_RS232, 0 }, 116 2 }, 117 118 { { NULL } } 119 }; 120 121 int pcmcom_print __P((void *, const char *)); 122 int pcmcom_submatch __P((struct device *, struct cfdata *, void *)); 123 124 int pcmcom_check_cfe __P((struct pcmcom_softc *, 125 struct pcmcia_config_entry *)); 126 void pcmcom_attach_slave __P((struct pcmcom_softc *, int)); 127 128 int pcmcom_enable __P((struct pcmcom_softc *)); 129 void pcmcom_disable __P((struct pcmcom_softc *)); 130 131 int pcmcom_intr __P((void *)); 132 133 int 134 pcmcom_match(parent, cf, aux) 135 struct device *parent; 136 struct cfdata *cf; 137 void *aux; 138 { 139 struct pcmcia_attach_args *pa = aux; 140 141 if (pcmcia_product_lookup(pa, 142 (const struct pcmcia_product *)pcmcom_products, 143 sizeof pcmcom_products[0], NULL) != NULL) 144 return (10); /* beat com_pcmcia */ 145 return (0); 146 } 147 148 void 149 pcmcom_attach(parent, self, aux) 150 struct device *parent, *self; 151 void *aux; 152 { 153 struct pcmcom_softc *sc = (struct pcmcom_softc *)self; 154 struct pcmcia_attach_args *pa = aux; 155 struct pcmcia_config_entry *cfe; 156 const struct pcmcom_product *pp; 157 size_t size; 158 int i; 159 160 sc->sc_pf = pa->pf; 161 162 pp = (const struct pcmcom_product *)pcmcia_product_lookup(pa, 163 (const struct pcmcia_product *)pcmcom_products, 164 sizeof pcmcom_products[0], NULL); 165 if (pp == NULL) { 166 printf("\n"); 167 panic("pcmcom_attach: impossible"); 168 } 169 170 printf(": %s\n", pp->pp_product.pp_name); 171 172 /* Allocate the slave info. */ 173 sc->sc_nslaves = pp->pp_nslaves; 174 size = sizeof(struct pcmcom_slave_info) * sc->sc_nslaves; 175 sc->sc_slaves = malloc(size, M_DEVBUF, M_NOWAIT|M_ZERO); 176 if (sc->sc_slaves == NULL) { 177 printf("%s: unable to allocate slave info\n", 178 sc->sc_dev.dv_xname); 179 return; 180 } 181 182 /* 183 * The address decoders on these cards are stupid. They decode 184 * (usually) 10 bits of address, so you need to allocate the 185 * regions they request in order for the card to differentiate 186 * between serial ports. 187 */ 188 SIMPLEQ_FOREACH(cfe, &pa->pf->cfe_head, cfe_list) { 189 if (pcmcom_check_cfe(sc, cfe)) { 190 /* Found one! */ 191 break; 192 } 193 } 194 if (cfe == NULL) { 195 printf("%s: unable to find a suitable config table entry\n", 196 sc->sc_dev.dv_xname); 197 return; 198 } 199 200 /* Enable the card. */ 201 pcmcia_function_init(pa->pf, cfe); 202 if (pcmcia_function_enable(sc->sc_pf)) { 203 printf("%s: function enable failed\n", sc->sc_dev.dv_xname); 204 return; 205 } 206 207 sc->sc_enabled_count = 1; 208 209 /* Attach the children. */ 210 for (i = 0; i < sc->sc_nslaves; i++) 211 pcmcom_attach_slave(sc, i); 212 213 sc->sc_enabled_count = 0; 214 pcmcia_function_disable(sc->sc_pf); 215 } 216 217 int 218 pcmcom_check_cfe(sc, cfe) 219 struct pcmcom_softc *sc; 220 struct pcmcia_config_entry *cfe; 221 { 222 struct pcmcom_slave_info *psi; 223 int slave; 224 225 /* We need to have the same number of I/O spaces as we do slaves. */ 226 if (cfe->num_iospace != sc->sc_nslaves) 227 return (0); 228 229 for (slave = 0; slave < sc->sc_nslaves; slave++) { 230 psi = &sc->sc_slaves[slave]; 231 if (pcmcia_io_alloc(sc->sc_pf, 232 cfe->iospace[slave].start, 233 cfe->iospace[slave].length, 234 cfe->iospace[slave].length, 235 &psi->psi_pcioh)) 236 goto release; 237 } 238 239 /* If we got here, we were able to allocate space for all slaves! */ 240 return (1); 241 242 release: 243 /* Release the i/o spaces we've allocated. */ 244 for (slave--; slave >= 0; slave--) { 245 psi = &sc->sc_slaves[slave]; 246 pcmcia_io_free(sc->sc_pf, &psi->psi_pcioh); 247 } 248 return (0); 249 } 250 251 void 252 pcmcom_attach_slave(sc, slave) 253 struct pcmcom_softc *sc; 254 int slave; 255 { 256 struct pcmcom_slave_info *psi = &sc->sc_slaves[slave]; 257 struct pcmcom_attach_args pca; 258 259 printf("%s: slave %d", sc->sc_dev.dv_xname, slave); 260 261 if (pcmcia_io_map(sc->sc_pf, PCMCIA_WIDTH_IO8, 0, psi->psi_pcioh.size, 262 &psi->psi_pcioh, &psi->psi_io_window)) { 263 printf(": can't map i/o space\n"); 264 return; 265 } 266 267 printf("\n"); 268 269 pca.pca_iot = psi->psi_pcioh.iot; 270 pca.pca_ioh = psi->psi_pcioh.ioh; 271 pca.pca_slave = slave; 272 273 psi->psi_child = config_found_sm(&sc->sc_dev, &pca, pcmcom_print, 274 pcmcom_submatch); 275 } 276 277 int 278 pcmcom_detach(self, flags) 279 struct device *self; 280 int flags; 281 { 282 struct pcmcom_softc *sc = (struct pcmcom_softc *)self; 283 struct pcmcom_slave_info *psi; 284 int slave, error; 285 286 for (slave = sc->sc_nslaves - 1; slave >= 0; slave--) { 287 psi = &sc->sc_slaves[slave]; 288 if (psi->psi_child == NULL) 289 continue; 290 291 /* Detach the child. */ 292 if ((error = config_detach(psi->psi_child, flags)) != 0) 293 return (error); 294 psi->psi_child = NULL; 295 296 /* Unmap the i/o window. */ 297 pcmcia_io_unmap(sc->sc_pf, psi->psi_io_window); 298 299 /* Free the i/o space. */ 300 pcmcia_io_free(sc->sc_pf, &psi->psi_pcioh); 301 } 302 return (0); 303 } 304 305 int 306 pcmcom_activate(self, act) 307 struct device *self; 308 enum devact act; 309 { 310 struct pcmcom_softc *sc = (struct pcmcom_softc *)self; 311 struct pcmcom_slave_info *psi; 312 int slave, error = 0, s; 313 314 s = splserial(); 315 switch (act) { 316 case DVACT_ACTIVATE: 317 error = EOPNOTSUPP; 318 break; 319 320 case DVACT_DEACTIVATE: 321 for (slave = sc->sc_nslaves - 1; slave >= 0; slave--) { 322 psi = &sc->sc_slaves[slave]; 323 if (psi->psi_child == NULL) 324 continue; 325 326 /* 327 * Deactivate the child. Doing so will cause our 328 * own enabled count to drop to 0, once all children 329 * are deactivated. 330 */ 331 if ((error = config_deactivate(psi->psi_child)) != 0) 332 break; 333 } 334 break; 335 } 336 splx(s); 337 return (error); 338 } 339 340 int 341 pcmcom_print(aux, pnp) 342 void *aux; 343 const char *pnp; 344 { 345 struct pcmcom_attach_args *pca = aux; 346 347 /* only com's can attach to pcmcom's; easy... */ 348 if (pnp) 349 printf("com at %s", pnp); 350 351 printf(" slave %d", pca->pca_slave); 352 353 return (UNCONF); 354 } 355 356 int 357 pcmcom_submatch(parent, cf, aux) 358 struct device *parent; 359 struct cfdata *cf; 360 void *aux; 361 { 362 struct pcmcom_attach_args *pca = aux; 363 364 if (cf->cf_loc[PCMCOMCF_SLAVE] != pca->pca_slave && 365 cf->cf_loc[PCMCOMCF_SLAVE] != PCMCOMCF_SLAVE_DEFAULT) 366 return (0); 367 368 return ((*cf->cf_attach->ca_match)(parent, cf, aux)); 369 } 370 371 int 372 pcmcom_intr(arg) 373 void *arg; 374 { 375 #if NCOM > 0 376 struct pcmcom_softc *sc = arg; 377 int i, rval = 0; 378 379 if (sc->sc_enabled_count == 0) 380 return (0); 381 382 for (i = 0; i < sc->sc_nslaves; i++) { 383 if (sc->sc_slaves[i].psi_child != NULL) 384 rval |= comintr(sc->sc_slaves[i].psi_child); 385 } 386 387 return (rval); 388 #else 389 return (0); 390 #endif /* NCOM > 0 */ 391 } 392 393 int 394 pcmcom_enable(sc) 395 struct pcmcom_softc *sc; 396 { 397 398 sc->sc_enabled_count++; 399 if (sc->sc_enabled_count > 1) 400 return (0); 401 402 /* Establish the interrupt. */ 403 sc->sc_ih = pcmcia_intr_establish(sc->sc_pf, IPL_SERIAL, 404 pcmcom_intr, sc); 405 if (sc->sc_ih == NULL) { 406 printf("%s: couldn't establish interrupt\n", 407 sc->sc_dev.dv_xname); 408 return (1); 409 } 410 411 return (pcmcia_function_enable(sc->sc_pf)); 412 } 413 414 void 415 pcmcom_disable(sc) 416 struct pcmcom_softc *sc; 417 { 418 419 sc->sc_enabled_count--; 420 if (sc->sc_enabled_count != 0) 421 return; 422 423 pcmcia_function_disable(sc->sc_pf); 424 pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih); 425 } 426 427 /****** Here begins the com attachment code. ******/ 428 429 #if NCOM_PCMCOM > 0 430 int com_pcmcom_match __P((struct device *, struct cfdata *, void *)); 431 void com_pcmcom_attach __P((struct device *, struct device *, void *)); 432 433 /* No pcmcom-specific goo in the softc; it's all in the parent. */ 434 struct cfattach com_pcmcom_ca = { 435 sizeof(struct com_softc), com_pcmcom_match, com_pcmcom_attach, 436 com_detach, com_activate 437 }; 438 439 int com_pcmcom_enable __P((struct com_softc *)); 440 void com_pcmcom_disable __P((struct com_softc *)); 441 442 int 443 com_pcmcom_match(parent, cf, aux) 444 struct device *parent; 445 struct cfdata *cf; 446 void *aux; 447 { 448 449 /* Device is always present. */ 450 return (1); 451 } 452 453 void 454 com_pcmcom_attach(parent, self, aux) 455 struct device *parent, *self; 456 void *aux; 457 { 458 struct com_softc *sc = (struct com_softc *)self; 459 struct pcmcom_attach_args *pca = aux; 460 461 sc->sc_iot = pca->pca_iot; 462 sc->sc_ioh = pca->pca_ioh; 463 464 sc->enabled = 1; 465 466 sc->sc_iobase = -1; 467 sc->sc_frequency = COM_FREQ; 468 469 sc->enable = com_pcmcom_enable; 470 sc->disable = com_pcmcom_disable; 471 472 com_attach_subr(sc); 473 474 sc->enabled = 0; 475 } 476 477 int 478 com_pcmcom_enable(sc) 479 struct com_softc *sc; 480 { 481 482 return (pcmcom_enable((struct pcmcom_softc *)sc->sc_dev.dv_parent)); 483 } 484 485 void 486 com_pcmcom_disable(sc) 487 struct com_softc *sc; 488 { 489 490 pcmcom_disable((struct pcmcom_softc *)sc->sc_dev.dv_parent); 491 } 492 #endif /* NCOM_PCMCOM > 0 */ 493