1 /* $NetBSD: pcmcom.c,v 1.45 2023/05/10 00:12:12 riastradh 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 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Device driver for multi-port PCMCIA serial cards, written by 34 * Jason R. Thorpe for RedBack Networks, Inc. 35 * 36 * Most of these cards are simply multiple UARTs sharing a single interrupt 37 * line, and rely on the fact that PCMCIA level-triggered interrupts can 38 * be shared. There are no special interrupt registers on them, as there 39 * are on most ISA multi-port serial cards. 40 * 41 * If there are other cards that have interrupt registers, they should not 42 * be glued into this driver. Rather, separate drivers should be written 43 * for those devices, as we have in the ISA multi-port serial card case. 44 */ 45 46 #include <sys/cdefs.h> 47 __KERNEL_RCSID(0, "$NetBSD: pcmcom.c,v 1.45 2023/05/10 00:12:12 riastradh Exp $"); 48 49 #include <sys/param.h> 50 #include <sys/systm.h> 51 #include <sys/device.h> 52 #include <sys/termios.h> 53 54 #include <sys/bus.h> 55 #include <sys/intr.h> 56 57 #include <dev/ic/comreg.h> 58 #include <dev/ic/comvar.h> 59 60 #include <dev/pcmcia/pcmciavar.h> 61 #include <dev/pcmcia/pcmciareg.h> 62 #include <dev/pcmcia/pcmciadevs.h> 63 64 #include "com.h" 65 #include "pcmcom.h" 66 67 #include "locators.h" 68 69 struct pcmcom_softc { 70 device_t sc_dev; /* generic device glue */ 71 72 struct pcmcia_function *sc_pf; /* our PCMCIA function */ 73 void *sc_ih; /* interrupt handle */ 74 int sc_enabled_count; /* enabled count */ 75 76 #define NSLAVES 8 77 device_t sc_slaves[NSLAVES]; /* slave info */ 78 int sc_nslaves; /* slave count */ 79 80 int sc_state; 81 #define PCMCOM_ATTACHED 3 82 }; 83 84 struct pcmcom_attach_args { 85 bus_space_tag_t pca_iot; /* I/O tag */ 86 bus_space_handle_t pca_ioh; /* I/O handle */ 87 int pca_slave; /* slave # */ 88 }; 89 90 int pcmcom_match(device_t, cfdata_t, void *); 91 int pcmcom_validate_config(struct pcmcia_config_entry *); 92 void pcmcom_attach(device_t, device_t, void *); 93 int pcmcom_detach(device_t, int); 94 void pcmcom_childdet(device_t, device_t); 95 96 CFATTACH_DECL_NEW(pcmcom, sizeof(struct pcmcom_softc), 97 pcmcom_match, pcmcom_attach, pcmcom_detach, NULL); 98 99 const struct pcmcia_product pcmcom_products[] = { 100 { PCMCIA_VENDOR_SOCKET, PCMCIA_PRODUCT_SOCKET_DUAL_RS232, 101 PCMCIA_CIS_INVALID }, 102 #if 0 /* does not work */ 103 { PCMCIA_VENDOR_SOCKET, PCMCIA_PRODUCT_SOCKET_DUAL_RS232_A, 104 PCMCIA_CIS_INVALID }, 105 #endif 106 }; 107 const size_t pcmcom_nproducts = __arraycount(pcmcom_products); 108 109 int pcmcom_print(void *, const char *); 110 111 int pcmcom_enable(struct pcmcom_softc *); 112 void pcmcom_disable(struct pcmcom_softc *); 113 114 int pcmcom_intr(void *); 115 116 int 117 pcmcom_match(device_t parent, cfdata_t cf, void *aux) 118 { 119 struct pcmcia_attach_args *pa = aux; 120 121 if (pcmcia_product_lookup(pa, pcmcom_products, pcmcom_nproducts, 122 sizeof(pcmcom_products[0]), NULL)) 123 return (2); /* beat com_pcmcia */ 124 return (0); 125 } 126 127 int 128 pcmcom_validate_config(struct pcmcia_config_entry *cfe) 129 { 130 if (cfe->iftype != PCMCIA_IFTYPE_IO || 131 cfe->num_iospace < 1 || cfe->num_iospace > NSLAVES) 132 return (EINVAL); 133 return (0); 134 } 135 136 void 137 pcmcom_attach(device_t parent, device_t self, void *aux) 138 { 139 struct pcmcom_softc *sc = device_private(self); 140 struct pcmcia_attach_args *pa = aux; 141 struct pcmcia_config_entry *cfe; 142 int slave; 143 int error; 144 int locs[PCMCOMCF_NLOCS]; 145 146 sc->sc_dev = self; 147 148 sc->sc_pf = pa->pf; 149 150 error = pcmcia_function_configure(pa->pf, pcmcom_validate_config); 151 if (error) { 152 aprint_error_dev(self, "configure failed, error=%d\n", error); 153 return; 154 } 155 156 cfe = pa->pf->cfe; 157 sc->sc_nslaves = cfe->num_iospace; 158 159 error = pcmcom_enable(sc); 160 if (error) 161 goto fail; 162 163 /* Attach the children. */ 164 for (slave = 0; slave < sc->sc_nslaves; slave++) { 165 struct pcmcom_attach_args pca; 166 167 printf("%s: slave %d\n", device_xname(self), slave); 168 169 pca.pca_iot = cfe->iospace[slave].handle.iot; 170 pca.pca_ioh = cfe->iospace[slave].handle.ioh; 171 pca.pca_slave = slave; 172 173 locs[PCMCOMCF_SLAVE] = slave; 174 175 sc->sc_slaves[slave] = 176 config_found(sc->sc_dev, &pca, pcmcom_print, 177 CFARGS(.submatch = config_stdsubmatch, 178 .locators = locs)); 179 } 180 181 pcmcom_disable(sc); 182 sc->sc_state = PCMCOM_ATTACHED; 183 return; 184 185 fail: 186 pcmcia_function_unconfigure(pa->pf); 187 } 188 189 void 190 pcmcom_childdet(device_t self, device_t child) 191 { 192 struct pcmcom_softc *sc = device_private(self); 193 int slave; 194 195 for (slave = sc->sc_nslaves - 1; slave >= 0; slave--) { 196 if (sc->sc_slaves[slave] == child) 197 sc->sc_slaves[slave] = NULL; 198 } 199 } 200 201 int 202 pcmcom_detach(device_t self, int flags) 203 { 204 struct pcmcom_softc *sc = device_private(self); 205 int error; 206 207 error = config_detach_children(self, flags); 208 if (error) 209 return error; 210 211 if (sc->sc_state != PCMCOM_ATTACHED) 212 return 0; 213 pcmcia_function_unconfigure(sc->sc_pf); 214 return 0; 215 } 216 217 int 218 pcmcom_print(void *aux, const char *pnp) 219 { 220 struct pcmcom_attach_args *pca = aux; 221 222 /* only com's can attach to pcmcom's; easy... */ 223 if (pnp) 224 aprint_normal("com at %s", pnp); 225 226 aprint_normal(" slave %d", pca->pca_slave); 227 228 return (UNCONF); 229 } 230 231 int 232 pcmcom_intr(void *arg) 233 { 234 #if NCOM > 0 235 struct pcmcom_softc *sc = arg; 236 int i, rval = 0; 237 238 if (sc->sc_enabled_count == 0) 239 return (0); 240 241 for (i = 0; i < sc->sc_nslaves; i++) { 242 if (sc->sc_slaves[i]) 243 rval |= comintr(sc->sc_slaves[i]); 244 } 245 246 return (rval); 247 #else 248 return (0); 249 #endif /* NCOM > 0 */ 250 } 251 252 int 253 pcmcom_enable(struct pcmcom_softc *sc) 254 { 255 int error; 256 257 if (sc->sc_enabled_count++ != 0) 258 return (0); 259 260 /* Establish the interrupt. */ 261 sc->sc_ih = pcmcia_intr_establish(sc->sc_pf, IPL_SERIAL, 262 pcmcom_intr, sc); 263 if (!sc->sc_ih) 264 return (EIO); 265 266 error = pcmcia_function_enable(sc->sc_pf); 267 if (error) { 268 pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih); 269 sc->sc_ih = 0; 270 } 271 272 return (error); 273 } 274 275 void 276 pcmcom_disable(struct pcmcom_softc *sc) 277 { 278 279 if (--sc->sc_enabled_count != 0) 280 return; 281 282 pcmcia_function_disable(sc->sc_pf); 283 pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih); 284 sc->sc_ih = 0; 285 } 286 287 /****** Here begins the com attachment code. ******/ 288 289 #if NCOM_PCMCOM > 0 290 int com_pcmcom_match(device_t, cfdata_t , void *); 291 void com_pcmcom_attach(device_t, device_t, void *); 292 293 /* No pcmcom-specific goo in the softc; it's all in the parent. */ 294 CFATTACH_DECL_NEW(com_pcmcom, sizeof(struct com_softc), 295 com_pcmcom_match, com_pcmcom_attach, com_detach, NULL); 296 297 int com_pcmcom_enable(struct com_softc *); 298 void com_pcmcom_disable(struct com_softc *); 299 300 int 301 com_pcmcom_match(device_t parent, cfdata_t cf, void *aux) 302 { 303 304 /* Device is always present. */ 305 return (1); 306 } 307 308 void 309 com_pcmcom_attach(device_t parent, device_t self, void *aux) 310 { 311 struct com_softc *sc = device_private(self); 312 struct pcmcom_attach_args *pca = aux; 313 314 sc->sc_dev = self; 315 com_init_regs(&sc->sc_regs, pca->pca_iot, pca->pca_ioh, -1); 316 sc->enabled = 1; 317 318 sc->sc_frequency = COM_FREQ; 319 320 sc->enable = com_pcmcom_enable; 321 sc->disable = com_pcmcom_disable; 322 323 com_attach_subr(sc); 324 325 sc->enabled = 0; 326 } 327 328 int 329 com_pcmcom_enable(struct com_softc *sc) 330 { 331 return pcmcom_enable(device_private(device_parent(sc->sc_dev))); 332 } 333 334 void 335 com_pcmcom_disable(struct com_softc *sc) 336 { 337 338 pcmcom_disable(device_private(device_parent(sc->sc_dev))); 339 } 340 #endif /* NCOM_PCMCOM > 0 */ 341