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