1 /* $NetBSD: pcmcom.c,v 1.13 2002/10/02 16:52:21 thorpej 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.13 2002/10/02 16:52:21 thorpej 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 CFATTACH_DECL(pcmcom, sizeof(struct pcmcom_softc), 106 pcmcom_match, pcmcom_attach, pcmcom_detach, pcmcom_activate); 107 108 const struct pcmcom_product { 109 struct pcmcia_product pp_product; 110 int pp_nslaves; /* number of slaves */ 111 } pcmcom_products[] = { 112 { { PCMCIA_STR_SOCKET_DUAL_RS232, PCMCIA_VENDOR_SOCKET, 113 PCMCIA_PRODUCT_SOCKET_DUAL_RS232, 0 }, 114 2 }, 115 116 { { NULL } } 117 }; 118 119 int pcmcom_print __P((void *, const char *)); 120 int pcmcom_submatch __P((struct device *, struct cfdata *, void *)); 121 122 int pcmcom_check_cfe __P((struct pcmcom_softc *, 123 struct pcmcia_config_entry *)); 124 void pcmcom_attach_slave __P((struct pcmcom_softc *, int)); 125 126 int pcmcom_enable __P((struct pcmcom_softc *)); 127 void pcmcom_disable __P((struct pcmcom_softc *)); 128 129 int pcmcom_intr __P((void *)); 130 131 int 132 pcmcom_match(parent, cf, aux) 133 struct device *parent; 134 struct cfdata *cf; 135 void *aux; 136 { 137 struct pcmcia_attach_args *pa = aux; 138 139 if (pcmcia_product_lookup(pa, 140 (const struct pcmcia_product *)pcmcom_products, 141 sizeof pcmcom_products[0], NULL) != NULL) 142 return (10); /* beat com_pcmcia */ 143 return (0); 144 } 145 146 void 147 pcmcom_attach(parent, self, aux) 148 struct device *parent, *self; 149 void *aux; 150 { 151 struct pcmcom_softc *sc = (struct pcmcom_softc *)self; 152 struct pcmcia_attach_args *pa = aux; 153 struct pcmcia_config_entry *cfe; 154 const struct pcmcom_product *pp; 155 size_t size; 156 int i; 157 158 sc->sc_pf = pa->pf; 159 160 pp = (const struct pcmcom_product *)pcmcia_product_lookup(pa, 161 (const struct pcmcia_product *)pcmcom_products, 162 sizeof pcmcom_products[0], NULL); 163 if (pp == NULL) { 164 printf("\n"); 165 panic("pcmcom_attach: impossible"); 166 } 167 168 printf(": %s\n", pp->pp_product.pp_name); 169 170 /* Allocate the slave info. */ 171 sc->sc_nslaves = pp->pp_nslaves; 172 size = sizeof(struct pcmcom_slave_info) * sc->sc_nslaves; 173 sc->sc_slaves = malloc(size, M_DEVBUF, M_NOWAIT|M_ZERO); 174 if (sc->sc_slaves == NULL) { 175 printf("%s: unable to allocate slave info\n", 176 sc->sc_dev.dv_xname); 177 return; 178 } 179 180 /* 181 * The address decoders on these cards are stupid. They decode 182 * (usually) 10 bits of address, so you need to allocate the 183 * regions they request in order for the card to differentiate 184 * between serial ports. 185 */ 186 SIMPLEQ_FOREACH(cfe, &pa->pf->cfe_head, cfe_list) { 187 if (pcmcom_check_cfe(sc, cfe)) { 188 /* Found one! */ 189 break; 190 } 191 } 192 if (cfe == NULL) { 193 printf("%s: unable to find a suitable config table entry\n", 194 sc->sc_dev.dv_xname); 195 return; 196 } 197 198 /* Enable the card. */ 199 pcmcia_function_init(pa->pf, cfe); 200 if (pcmcia_function_enable(sc->sc_pf)) { 201 printf("%s: function enable failed\n", sc->sc_dev.dv_xname); 202 return; 203 } 204 205 sc->sc_enabled_count = 1; 206 207 /* Attach the children. */ 208 for (i = 0; i < sc->sc_nslaves; i++) 209 pcmcom_attach_slave(sc, i); 210 211 sc->sc_enabled_count = 0; 212 pcmcia_function_disable(sc->sc_pf); 213 } 214 215 int 216 pcmcom_check_cfe(sc, cfe) 217 struct pcmcom_softc *sc; 218 struct pcmcia_config_entry *cfe; 219 { 220 struct pcmcom_slave_info *psi; 221 int slave; 222 223 /* We need to have the same number of I/O spaces as we do slaves. */ 224 if (cfe->num_iospace != sc->sc_nslaves) 225 return (0); 226 227 for (slave = 0; slave < sc->sc_nslaves; slave++) { 228 psi = &sc->sc_slaves[slave]; 229 if (pcmcia_io_alloc(sc->sc_pf, 230 cfe->iospace[slave].start, 231 cfe->iospace[slave].length, 232 cfe->iospace[slave].length, 233 &psi->psi_pcioh)) 234 goto release; 235 } 236 237 /* If we got here, we were able to allocate space for all slaves! */ 238 return (1); 239 240 release: 241 /* Release the i/o spaces we've allocated. */ 242 for (slave--; slave >= 0; slave--) { 243 psi = &sc->sc_slaves[slave]; 244 pcmcia_io_free(sc->sc_pf, &psi->psi_pcioh); 245 } 246 return (0); 247 } 248 249 void 250 pcmcom_attach_slave(sc, slave) 251 struct pcmcom_softc *sc; 252 int slave; 253 { 254 struct pcmcom_slave_info *psi = &sc->sc_slaves[slave]; 255 struct pcmcom_attach_args pca; 256 257 printf("%s: slave %d", sc->sc_dev.dv_xname, slave); 258 259 if (pcmcia_io_map(sc->sc_pf, PCMCIA_WIDTH_IO8, 0, psi->psi_pcioh.size, 260 &psi->psi_pcioh, &psi->psi_io_window)) { 261 printf(": can't map i/o space\n"); 262 return; 263 } 264 265 printf("\n"); 266 267 pca.pca_iot = psi->psi_pcioh.iot; 268 pca.pca_ioh = psi->psi_pcioh.ioh; 269 pca.pca_slave = slave; 270 271 psi->psi_child = config_found_sm(&sc->sc_dev, &pca, pcmcom_print, 272 pcmcom_submatch); 273 } 274 275 int 276 pcmcom_detach(self, flags) 277 struct device *self; 278 int flags; 279 { 280 struct pcmcom_softc *sc = (struct pcmcom_softc *)self; 281 struct pcmcom_slave_info *psi; 282 int slave, error; 283 284 for (slave = sc->sc_nslaves - 1; slave >= 0; slave--) { 285 psi = &sc->sc_slaves[slave]; 286 if (psi->psi_child == NULL) 287 continue; 288 289 /* Detach the child. */ 290 if ((error = config_detach(psi->psi_child, flags)) != 0) 291 return (error); 292 psi->psi_child = NULL; 293 294 /* Unmap the i/o window. */ 295 pcmcia_io_unmap(sc->sc_pf, psi->psi_io_window); 296 297 /* Free the i/o space. */ 298 pcmcia_io_free(sc->sc_pf, &psi->psi_pcioh); 299 } 300 return (0); 301 } 302 303 int 304 pcmcom_activate(self, act) 305 struct device *self; 306 enum devact act; 307 { 308 struct pcmcom_softc *sc = (struct pcmcom_softc *)self; 309 struct pcmcom_slave_info *psi; 310 int slave, error = 0, s; 311 312 s = splserial(); 313 switch (act) { 314 case DVACT_ACTIVATE: 315 error = EOPNOTSUPP; 316 break; 317 318 case DVACT_DEACTIVATE: 319 for (slave = sc->sc_nslaves - 1; slave >= 0; slave--) { 320 psi = &sc->sc_slaves[slave]; 321 if (psi->psi_child == NULL) 322 continue; 323 324 /* 325 * Deactivate the child. Doing so will cause our 326 * own enabled count to drop to 0, once all children 327 * are deactivated. 328 */ 329 if ((error = config_deactivate(psi->psi_child)) != 0) 330 break; 331 } 332 break; 333 } 334 splx(s); 335 return (error); 336 } 337 338 int 339 pcmcom_print(aux, pnp) 340 void *aux; 341 const char *pnp; 342 { 343 struct pcmcom_attach_args *pca = aux; 344 345 /* only com's can attach to pcmcom's; easy... */ 346 if (pnp) 347 printf("com at %s", pnp); 348 349 printf(" slave %d", pca->pca_slave); 350 351 return (UNCONF); 352 } 353 354 int 355 pcmcom_submatch(parent, cf, aux) 356 struct device *parent; 357 struct cfdata *cf; 358 void *aux; 359 { 360 struct pcmcom_attach_args *pca = aux; 361 362 if (cf->cf_loc[PCMCOMCF_SLAVE] != pca->pca_slave && 363 cf->cf_loc[PCMCOMCF_SLAVE] != PCMCOMCF_SLAVE_DEFAULT) 364 return (0); 365 366 return (config_match(parent, cf, aux)); 367 } 368 369 int 370 pcmcom_intr(arg) 371 void *arg; 372 { 373 #if NCOM > 0 374 struct pcmcom_softc *sc = arg; 375 int i, rval = 0; 376 377 if (sc->sc_enabled_count == 0) 378 return (0); 379 380 for (i = 0; i < sc->sc_nslaves; i++) { 381 if (sc->sc_slaves[i].psi_child != NULL) 382 rval |= comintr(sc->sc_slaves[i].psi_child); 383 } 384 385 return (rval); 386 #else 387 return (0); 388 #endif /* NCOM > 0 */ 389 } 390 391 int 392 pcmcom_enable(sc) 393 struct pcmcom_softc *sc; 394 { 395 396 sc->sc_enabled_count++; 397 if (sc->sc_enabled_count > 1) 398 return (0); 399 400 /* Establish the interrupt. */ 401 sc->sc_ih = pcmcia_intr_establish(sc->sc_pf, IPL_SERIAL, 402 pcmcom_intr, sc); 403 if (sc->sc_ih == NULL) { 404 printf("%s: couldn't establish interrupt\n", 405 sc->sc_dev.dv_xname); 406 return (1); 407 } 408 409 return (pcmcia_function_enable(sc->sc_pf)); 410 } 411 412 void 413 pcmcom_disable(sc) 414 struct pcmcom_softc *sc; 415 { 416 417 sc->sc_enabled_count--; 418 if (sc->sc_enabled_count != 0) 419 return; 420 421 pcmcia_function_disable(sc->sc_pf); 422 pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih); 423 } 424 425 /****** Here begins the com attachment code. ******/ 426 427 #if NCOM_PCMCOM > 0 428 int com_pcmcom_match __P((struct device *, struct cfdata *, void *)); 429 void com_pcmcom_attach __P((struct device *, struct device *, void *)); 430 431 /* No pcmcom-specific goo in the softc; it's all in the parent. */ 432 CFATTACH_DECL(com_pcmcom, sizeof(struct com_softc), 433 com_pcmcom_match, com_pcmcom_attach, com_detach, com_activate); 434 435 int com_pcmcom_enable __P((struct com_softc *)); 436 void com_pcmcom_disable __P((struct com_softc *)); 437 438 int 439 com_pcmcom_match(parent, cf, aux) 440 struct device *parent; 441 struct cfdata *cf; 442 void *aux; 443 { 444 445 /* Device is always present. */ 446 return (1); 447 } 448 449 void 450 com_pcmcom_attach(parent, self, aux) 451 struct device *parent, *self; 452 void *aux; 453 { 454 struct com_softc *sc = (struct com_softc *)self; 455 struct pcmcom_attach_args *pca = aux; 456 457 sc->sc_iot = pca->pca_iot; 458 sc->sc_ioh = pca->pca_ioh; 459 460 sc->enabled = 1; 461 462 sc->sc_iobase = -1; 463 sc->sc_frequency = COM_FREQ; 464 465 sc->enable = com_pcmcom_enable; 466 sc->disable = com_pcmcom_disable; 467 468 com_attach_subr(sc); 469 470 sc->enabled = 0; 471 } 472 473 int 474 com_pcmcom_enable(sc) 475 struct com_softc *sc; 476 { 477 478 return (pcmcom_enable((struct pcmcom_softc *)sc->sc_dev.dv_parent)); 479 } 480 481 void 482 com_pcmcom_disable(sc) 483 struct com_softc *sc; 484 { 485 486 pcmcom_disable((struct pcmcom_softc *)sc->sc_dev.dv_parent); 487 } 488 #endif /* NCOM_PCMCOM > 0 */ 489