1 /* $NetBSD: pcmcom.c,v 1.21 2005/02/04 02:10:45 perry Exp $ */ 2 3 /*- 4 * Copyright (c) 1998, 2000, 2004 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.21 2005/02/04 02:10:45 perry 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_softc { 78 struct device sc_dev; /* generic device glue */ 79 80 struct pcmcia_function *sc_pf; /* our PCMCIA function */ 81 void *sc_ih; /* interrupt handle */ 82 int sc_enabled_count; /* enabled count */ 83 84 #define NSLAVES 8 85 struct device *sc_slaves[NSLAVES]; /* slave info */ 86 int sc_nslaves; /* slave count */ 87 88 int sc_state; 89 #define PCMCOM_ATTACHED 3 90 }; 91 92 struct pcmcom_attach_args { 93 bus_space_tag_t pca_iot; /* I/O tag */ 94 bus_space_handle_t pca_ioh; /* I/O handle */ 95 int pca_slave; /* slave # */ 96 }; 97 98 int pcmcom_match(struct device *, struct cfdata *, void *); 99 int pcmcom_validate_config(struct pcmcia_config_entry *); 100 void pcmcom_attach(struct device *, struct device *, void *); 101 int pcmcom_detach(struct device *, int); 102 int pcmcom_activate(struct device *, enum devact); 103 104 CFATTACH_DECL(pcmcom, sizeof(struct pcmcom_softc), 105 pcmcom_match, pcmcom_attach, pcmcom_detach, pcmcom_activate); 106 107 const struct pcmcia_product pcmcom_products[] = { 108 { PCMCIA_VENDOR_SOCKET, PCMCIA_PRODUCT_SOCKET_DUAL_RS232, 109 PCMCIA_CIS_INVALID }, 110 }; 111 const size_t pcmcom_nproducts = 112 sizeof(pcmcom_products) / sizeof(pcmcom_products[0]); 113 114 int pcmcom_print(void *, const char *); 115 int pcmcom_submatch(struct device *, struct cfdata *, 116 const locdesc_t *, void *); 117 118 int pcmcom_enable(struct pcmcom_softc *); 119 void pcmcom_disable(struct pcmcom_softc *); 120 121 int pcmcom_intr(void *); 122 123 int 124 pcmcom_match(parent, cf, aux) 125 struct device *parent; 126 struct cfdata *cf; 127 void *aux; 128 { 129 struct pcmcia_attach_args *pa = aux; 130 131 if (pcmcia_product_lookup(pa, pcmcom_products, pcmcom_nproducts, 132 sizeof(pcmcom_products[0]), NULL)) 133 return (2); /* beat com_pcmcia */ 134 return (0); 135 } 136 137 int 138 pcmcom_validate_config(cfe) 139 struct pcmcia_config_entry *cfe; 140 { 141 if (cfe->iftype != PCMCIA_IFTYPE_IO || 142 cfe->num_iospace < 1 || cfe->num_iospace > NSLAVES) 143 return (EINVAL); 144 return (0); 145 } 146 147 void 148 pcmcom_attach(parent, self, aux) 149 struct device *parent, *self; 150 void *aux; 151 { 152 struct pcmcom_softc *sc = (void *)self; 153 struct pcmcia_attach_args *pa = aux; 154 struct pcmcia_config_entry *cfe; 155 int slave; 156 int error; 157 int help[2]; 158 locdesc_t *ldesc = (void *)help; /* XXX */ 159 160 sc->sc_pf = pa->pf; 161 162 error = pcmcia_function_configure(pa->pf, pcmcom_validate_config); 163 if (error) { 164 aprint_error("%s: configure failed, error=%d\n", self->dv_xname, 165 error); 166 return; 167 } 168 169 cfe = pa->pf->cfe; 170 sc->sc_nslaves = cfe->num_iospace; 171 172 error = pcmcom_enable(sc); 173 if (error) 174 goto fail; 175 176 /* Attach the children. */ 177 for (slave = 0; slave < sc->sc_nslaves; slave++) { 178 struct pcmcom_attach_args pca; 179 180 printf("%s: slave %d\n", self->dv_xname, slave); 181 182 pca.pca_iot = cfe->iospace[slave].handle.iot; 183 pca.pca_ioh = cfe->iospace[slave].handle.ioh; 184 pca.pca_slave = slave; 185 186 ldesc->len = 1; 187 ldesc->locs[PCMCOMCF_SLAVE] = slave; 188 189 sc->sc_slaves[slave] = config_found_sm_loc(&sc->sc_dev, 190 "pcmcom", ldesc, 191 &pca, pcmcom_print, pcmcom_submatch); 192 } 193 194 pcmcom_disable(sc); 195 sc->sc_state = PCMCOM_ATTACHED; 196 return; 197 198 fail: 199 pcmcia_function_unconfigure(pa->pf); 200 } 201 202 int 203 pcmcom_detach(self, flags) 204 struct device *self; 205 int flags; 206 { 207 struct pcmcom_softc *sc = (void *)self; 208 int slave, error; 209 210 if (sc->sc_state != PCMCOM_ATTACHED) 211 return (0); 212 213 for (slave = sc->sc_nslaves - 1; slave >= 0; slave--) { 214 if (sc->sc_slaves[slave]) { 215 /* Detach the child. */ 216 error = config_detach(sc->sc_slaves[slave], flags); 217 if (error) 218 return (error); 219 sc->sc_slaves[slave] = 0; 220 } 221 } 222 223 pcmcia_function_unconfigure(sc->sc_pf); 224 225 return (0); 226 } 227 228 int 229 pcmcom_activate(self, act) 230 struct device *self; 231 enum devact act; 232 { 233 struct pcmcom_softc *sc = (void *)self; 234 int slave, error = 0, s; 235 236 s = splserial(); 237 switch (act) { 238 case DVACT_ACTIVATE: 239 error = EOPNOTSUPP; 240 break; 241 242 case DVACT_DEACTIVATE: 243 for (slave = sc->sc_nslaves - 1; slave >= 0; slave--) { 244 if (sc->sc_slaves[slave]) { 245 /* 246 * Deactivate the child. Doing so will cause 247 * our own enabled count to drop to 0, once all 248 * children are deactivated. 249 */ 250 error = config_deactivate(sc->sc_slaves[slave]); 251 if (error) 252 break; 253 } 254 } 255 break; 256 } 257 splx(s); 258 return (error); 259 } 260 261 int 262 pcmcom_print(aux, pnp) 263 void *aux; 264 const char *pnp; 265 { 266 struct pcmcom_attach_args *pca = aux; 267 268 /* only com's can attach to pcmcom's; easy... */ 269 if (pnp) 270 aprint_normal("com at %s", pnp); 271 272 aprint_normal(" slave %d", pca->pca_slave); 273 274 return (UNCONF); 275 } 276 277 int 278 pcmcom_submatch(parent, cf, ldesc, aux) 279 struct device *parent; 280 struct cfdata *cf; 281 const locdesc_t *ldesc; 282 void *aux; 283 { 284 285 if (cf->cf_loc[PCMCOMCF_SLAVE] != PCMCOMCF_SLAVE_DEFAULT && 286 cf->cf_loc[PCMCOMCF_SLAVE] != ldesc->locs[PCMCOMCF_SLAVE]); 287 return (0); 288 289 return (config_match(parent, cf, aux)); 290 } 291 292 int 293 pcmcom_intr(arg) 294 void *arg; 295 { 296 #if NCOM > 0 297 struct pcmcom_softc *sc = arg; 298 int i, rval = 0; 299 300 if (sc->sc_enabled_count == 0) 301 return (0); 302 303 for (i = 0; i < sc->sc_nslaves; i++) { 304 if (sc->sc_slaves[i]) 305 rval |= comintr(sc->sc_slaves[i]); 306 } 307 308 return (rval); 309 #else 310 return (0); 311 #endif /* NCOM > 0 */ 312 } 313 314 int 315 pcmcom_enable(sc) 316 struct pcmcom_softc *sc; 317 { 318 int error; 319 320 if (sc->sc_enabled_count++ != 0) 321 return (0); 322 323 /* Establish the interrupt. */ 324 sc->sc_ih = pcmcia_intr_establish(sc->sc_pf, IPL_SERIAL, 325 pcmcom_intr, sc); 326 if (!sc->sc_ih) 327 return (EIO); 328 329 error = pcmcia_function_enable(sc->sc_pf); 330 if (error) { 331 pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih); 332 sc->sc_ih = 0; 333 } 334 335 return (error); 336 } 337 338 void 339 pcmcom_disable(sc) 340 struct pcmcom_softc *sc; 341 { 342 343 if (--sc->sc_enabled_count != 0) 344 return; 345 346 pcmcia_function_disable(sc->sc_pf); 347 pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih); 348 sc->sc_ih = 0; 349 } 350 351 /****** Here begins the com attachment code. ******/ 352 353 #if NCOM_PCMCOM > 0 354 int com_pcmcom_match(struct device *, struct cfdata *, void *); 355 void com_pcmcom_attach(struct device *, struct device *, void *); 356 357 /* No pcmcom-specific goo in the softc; it's all in the parent. */ 358 CFATTACH_DECL(com_pcmcom, sizeof(struct com_softc), 359 com_pcmcom_match, com_pcmcom_attach, com_detach, com_activate); 360 361 int com_pcmcom_enable(struct com_softc *); 362 void com_pcmcom_disable(struct com_softc *); 363 364 int 365 com_pcmcom_match(parent, cf, aux) 366 struct device *parent; 367 struct cfdata *cf; 368 void *aux; 369 { 370 371 /* Device is always present. */ 372 return (1); 373 } 374 375 void 376 com_pcmcom_attach(parent, self, aux) 377 struct device *parent, *self; 378 void *aux; 379 { 380 struct com_softc *sc = (struct com_softc *)self; 381 struct pcmcom_attach_args *pca = aux; 382 383 sc->sc_iot = pca->pca_iot; 384 sc->sc_ioh = pca->pca_ioh; 385 386 sc->enabled = 1; 387 388 sc->sc_iobase = -1; 389 sc->sc_frequency = COM_FREQ; 390 391 sc->enable = com_pcmcom_enable; 392 sc->disable = com_pcmcom_disable; 393 394 com_attach_subr(sc); 395 396 sc->enabled = 0; 397 } 398 399 int 400 com_pcmcom_enable(sc) 401 struct com_softc *sc; 402 { 403 404 return (pcmcom_enable((struct pcmcom_softc *)sc->sc_dev.dv_parent)); 405 } 406 407 void 408 com_pcmcom_disable(sc) 409 struct com_softc *sc; 410 { 411 412 pcmcom_disable((struct pcmcom_softc *)sc->sc_dev.dv_parent); 413 } 414 #endif /* NCOM_PCMCOM > 0 */ 415