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