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