1 /* $OpenBSD: com_cardbus.c,v 1.45 2024/05/24 06:26:47 jsg Exp $ */ 2 /* $NetBSD: com_cardbus.c,v 1.4 2000/04/17 09:21:59 joda Exp $ */ 3 4 /* 5 * Copyright (c) 2000 Johan Danielsson 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 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 * 3. Neither the name of author nor the names of any contributors may 20 * be used to endorse or promote products derived from this 21 * software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR 27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 29 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 */ 35 36 /* This is a driver for CardBus based serial devices. It is less 37 generic than it could be, but it keeps the complexity down. So far 38 it assumes that anything that reports itself as a `serial' device 39 is infact a 16x50 or 8250, which is not necessarily true (in 40 practice this shouldn't be a problem). It also does not handle 41 devices in the `multiport serial' or `modem' sub-classes, I've 42 never seen any of these, so I don't know what they might look like. 43 44 If the CardBus device only has one BAR (that is not also the CIS 45 BAR) listed in the CIS, it is assumed to be the one to use. For 46 devices with more than one BAR, the list of known devies has to be 47 updated below. */ 48 49 #include <sys/param.h> 50 #include <sys/systm.h> 51 #include <sys/tty.h> 52 #include <sys/device.h> 53 54 #include <dev/cardbus/cardbusvar.h> 55 #include <dev/pci/pcidevs.h> 56 57 #include <dev/pcmcia/pcmciareg.h> 58 59 #include <dev/ic/comreg.h> 60 #include <dev/ic/comvar.h> 61 #include <dev/ic/ns16550reg.h> 62 63 #define com_lcr com_cfcr 64 65 struct com_cardbus_softc { 66 struct com_softc cc_com; 67 void *cc_ih; 68 cardbus_devfunc_t cc_ct; 69 bus_addr_t cc_addr; 70 pcireg_t cc_base; 71 bus_size_t cc_size; 72 pcireg_t cc_csr; 73 int cc_cben; 74 pcitag_t cc_tag; 75 pcireg_t cc_reg; 76 int cc_type; 77 u_char cc_bug; 78 pci_chipset_tag_t cc_pc; 79 }; 80 81 #define DEVNAME(CSC) ((CSC)->cc_com.sc_dev.dv_xname) 82 83 int com_cardbus_match(struct device *, void *, void *); 84 void com_cardbus_attach(struct device *, struct device *, void *); 85 int com_cardbus_detach(struct device *, int); 86 87 void com_cardbus_setup(struct com_cardbus_softc *); 88 int com_cardbus_enable(struct com_softc *); 89 void com_cardbus_disable(struct com_softc *); 90 struct csdev *com_cardbus_find_csdev(struct cardbus_attach_args *); 91 int com_cardbus_gofigure(struct cardbus_attach_args *, 92 struct com_cardbus_softc *); 93 94 const struct cfattach com_cardbus_ca = { 95 sizeof(struct com_cardbus_softc), com_cardbus_match, 96 com_cardbus_attach, com_cardbus_detach, com_activate 97 }; 98 99 #define BUG_BROADCOM 0x01 100 101 /* XXX Keep this list synchronized with the corresponding one in pucdata.c */ 102 static struct csdev { 103 u_short vendor; 104 u_short product; 105 pcireg_t reg; 106 u_char type; 107 u_char bug; 108 } csdevs[] = { 109 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_GLOBALMODEM56, 110 CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO }, 111 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_MODEM56, 112 CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO }, 113 { PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_SERIAL, 114 CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO, BUG_BROADCOM }, 115 { PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_SERIAL_2, 116 CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO, BUG_BROADCOM }, 117 { PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_SERIAL_GC, 118 CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO }, 119 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_MODEM56, 120 CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO }, 121 { PCI_VENDOR_OXFORD2, PCI_PRODUCT_OXFORD2_OXCB950, 122 CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO }, 123 { PCI_VENDOR_XIRCOM, PCI_PRODUCT_XIRCOM_CBEM56G, 124 CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO }, 125 { PCI_VENDOR_XIRCOM, PCI_PRODUCT_XIRCOM_MODEM56, 126 CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO }, 127 { PCI_VENDOR_WCH, PCI_PRODUCT_WCH_CH352, 128 CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO }, 129 { PCI_VENDOR_NETMOS, PCI_PRODUCT_NETMOS_NM9820, 130 CARDBUS_BASE0_REG, PCI_MAPREG_TYPE_IO } 131 }; 132 133 static const int ncsdevs = sizeof(csdevs) / sizeof(csdevs[0]); 134 135 struct csdev* 136 com_cardbus_find_csdev(struct cardbus_attach_args *ca) 137 { 138 struct csdev *cp; 139 140 for (cp = csdevs; cp < csdevs + ncsdevs; cp++) 141 if (cp->vendor == PCI_VENDOR(ca->ca_id) && 142 cp->product == PCI_PRODUCT(ca->ca_id)) 143 return (cp); 144 return (NULL); 145 } 146 147 int 148 com_cardbus_match(struct device *parent, void *match, void *aux) 149 { 150 struct cardbus_attach_args *ca = aux; 151 152 /* known devices are ok */ 153 if (com_cardbus_find_csdev(ca) != NULL) 154 return (10); 155 156 /* as are serial devices with a known UART */ 157 if (ca->ca_cis.funcid == PCMCIA_FUNCTION_SERIAL && 158 ca->ca_cis.funce.serial.uart_present != 0 && 159 (ca->ca_cis.funce.serial.uart_type == 0 || /* 8250 */ 160 ca->ca_cis.funce.serial.uart_type == 1 || /* 16450 */ 161 ca->ca_cis.funce.serial.uart_type == 2)) /* 16550 */ 162 return (1); 163 164 return (0); 165 } 166 167 int 168 com_cardbus_gofigure(struct cardbus_attach_args *ca, 169 struct com_cardbus_softc *csc) 170 { 171 int i, index = -1; 172 pcireg_t cis_ptr; 173 struct csdev *cp; 174 175 /* If this device is listed above, use the known values, */ 176 cp = com_cardbus_find_csdev(ca); 177 if (cp != NULL) { 178 csc->cc_reg = cp->reg; 179 csc->cc_type = cp->type; 180 csc->cc_bug = cp->bug; 181 return (0); 182 } 183 184 cis_ptr = pci_conf_read(ca->ca_pc, csc->cc_tag, CARDBUS_CIS_REG); 185 186 /* otherwise try to deduce which BAR and type to use from CIS. If 187 there is only one BAR, it must be the one we should use, if 188 there are more, we're out of luck. */ 189 for (i = 0; i < 7; i++) { 190 /* ignore zero sized BARs */ 191 if (ca->ca_cis.bar[i].size == 0) 192 continue; 193 /* ignore the CIS BAR */ 194 if (CARDBUS_CIS_ASI_BAR(cis_ptr) == 195 CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags)) 196 continue; 197 if (index != -1) 198 goto multi_bar; 199 index = i; 200 } 201 if (index == -1) { 202 printf(": couldn't find any base address tuple\n"); 203 return (1); 204 } 205 csc->cc_reg = CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[index].flags); 206 if ((ca->ca_cis.bar[index].flags & 0x10) == 0) 207 csc->cc_type = PCI_MAPREG_TYPE_MEM; 208 else 209 csc->cc_type = PCI_MAPREG_TYPE_IO; 210 return (0); 211 212 multi_bar: 213 printf(": there are more than one possible base\n"); 214 215 printf("%s: address for this device, " 216 "please report the following information\n", 217 DEVNAME(csc)); 218 printf("%s: vendor 0x%x product 0x%x\n", DEVNAME(csc), 219 PCI_VENDOR(ca->ca_id), PCI_PRODUCT(ca->ca_id)); 220 for (i = 0; i < 7; i++) { 221 /* ignore zero sized BARs */ 222 if (ca->ca_cis.bar[i].size == 0) 223 continue; 224 /* ignore the CIS BAR */ 225 if (CARDBUS_CIS_ASI_BAR(cis_ptr) == 226 CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags)) 227 continue; 228 printf("%s: base address %x type %s size %x\n", 229 DEVNAME(csc), CARDBUS_CIS_ASI_BAR(ca->ca_cis.bar[i].flags), 230 (ca->ca_cis.bar[i].flags & 0x10) ? "i/o" : "mem", 231 ca->ca_cis.bar[i].size); 232 } 233 return (1); 234 } 235 236 void 237 com_cardbus_attach(struct device *parent, struct device *self, void *aux) 238 { 239 struct com_softc *sc = (struct com_softc*)self; 240 struct com_cardbus_softc *csc = (struct com_cardbus_softc*)self; 241 struct cardbus_attach_args *ca = aux; 242 cardbus_devfunc_t ct; 243 244 csc->cc_ct = ct = ca->ca_ct; 245 csc->cc_tag = pci_make_tag(ca->ca_pc, ct->ct_bus, ct->ct_dev, ct->ct_func); 246 csc->cc_pc = ca->ca_pc; 247 248 if (com_cardbus_gofigure(ca, csc) != 0) 249 return; 250 251 if (Cardbus_mapreg_map(ca->ca_ct, csc->cc_reg, csc->cc_type, 0, 252 &sc->sc_iot, &sc->sc_ioh, &csc->cc_addr, &csc->cc_size) != 0) { 253 printf(": can't map memory\n"); 254 return; 255 } 256 257 csc->cc_base = csc->cc_addr; 258 csc->cc_csr = PCI_COMMAND_MASTER_ENABLE; 259 if (csc->cc_type == PCI_MAPREG_TYPE_IO) { 260 csc->cc_base |= PCI_MAPREG_TYPE_IO; 261 csc->cc_csr |= PCI_COMMAND_IO_ENABLE; 262 csc->cc_cben = CARDBUS_IO_ENABLE; 263 } else { 264 csc->cc_csr |= PCI_COMMAND_MEM_ENABLE; 265 csc->cc_cben = CARDBUS_MEM_ENABLE; 266 } 267 268 sc->sc_iobase = csc->cc_addr; 269 sc->sc_frequency = COM_FREQ; 270 271 sc->enable = com_cardbus_enable; 272 sc->disable = com_cardbus_disable; 273 sc->enabled = 0; 274 275 if (com_cardbus_enable(sc)) 276 return; 277 sc->enabled = 1; 278 279 sc->sc_hwflags = 0; 280 sc->sc_swflags = 0; 281 282 if (csc->cc_bug & BUG_BROADCOM) 283 sc->sc_fifolen = 15; 284 285 com_attach_subr(sc); 286 } 287 288 void 289 com_cardbus_setup(struct com_cardbus_softc *csc) 290 { 291 cardbus_devfunc_t ct = csc->cc_ct; 292 cardbus_chipset_tag_t cc = ct->ct_cc; 293 pci_chipset_tag_t pc = csc->cc_pc; 294 cardbus_function_tag_t cf = ct->ct_cf; 295 pcireg_t reg; 296 297 pci_conf_write(pc, csc->cc_tag, csc->cc_reg, csc->cc_base); 298 299 /* enable accesses on cardbus bridge */ 300 cf->cardbus_ctrl(cc, csc->cc_cben); 301 cf->cardbus_ctrl(cc, CARDBUS_BM_ENABLE); 302 303 /* and the card itself */ 304 reg = pci_conf_read(pc, csc->cc_tag, PCI_COMMAND_STATUS_REG); 305 reg &= ~(PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE); 306 reg |= csc->cc_csr; 307 pci_conf_write(pc, csc->cc_tag, PCI_COMMAND_STATUS_REG, reg); 308 309 /* 310 * Make sure the latency timer is set to some reasonable 311 * value. 312 */ 313 reg = pci_conf_read(pc, csc->cc_tag, PCI_BHLC_REG); 314 if (PCI_LATTIMER(reg) < 0x20) { 315 reg &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT); 316 reg |= (0x20 << PCI_LATTIMER_SHIFT); 317 pci_conf_write(pc, csc->cc_tag, PCI_BHLC_REG, reg); 318 } 319 } 320 321 int 322 com_cardbus_enable(struct com_softc *sc) 323 { 324 struct com_cardbus_softc *csc = (struct com_cardbus_softc*)sc; 325 struct cardbus_softc *psc = 326 (struct cardbus_softc *)sc->sc_dev.dv_parent; 327 cardbus_chipset_tag_t cc = psc->sc_cc; 328 cardbus_function_tag_t cf = psc->sc_cf; 329 330 Cardbus_function_enable(csc->cc_ct); 331 332 com_cardbus_setup(csc); 333 334 /* establish the interrupt. */ 335 csc->cc_ih = cardbus_intr_establish(cc, cf, psc->sc_intrline, 336 IPL_TTY, comintr, sc, DEVNAME(csc)); 337 if (csc->cc_ih == NULL) { 338 printf(": couldn't establish interrupt\n"); 339 return (1); 340 } 341 342 printf(": irq %d", psc->sc_intrline); 343 344 return (0); 345 } 346 347 void 348 com_cardbus_disable(struct com_softc *sc) 349 { 350 struct com_cardbus_softc *csc = (struct com_cardbus_softc*)sc; 351 struct cardbus_softc *psc = 352 (struct cardbus_softc *)sc->sc_dev.dv_parent; 353 cardbus_chipset_tag_t cc = psc->sc_cc; 354 cardbus_function_tag_t cf = psc->sc_cf; 355 356 cardbus_intr_disestablish(cc, cf, csc->cc_ih); 357 Cardbus_function_disable(csc->cc_ct); 358 } 359 360 int 361 com_cardbus_detach(struct device *self, int flags) 362 { 363 struct com_cardbus_softc *csc = (struct com_cardbus_softc *) self; 364 struct com_softc *sc = (struct com_softc *) self; 365 struct cardbus_softc *psc = (struct cardbus_softc *)self->dv_parent; 366 int error; 367 368 if ((error = com_detach(self, flags)) != 0) 369 return (error); 370 371 cardbus_intr_disestablish(psc->sc_cc, psc->sc_cf, csc->cc_ih); 372 373 Cardbus_mapreg_unmap(csc->cc_ct, csc->cc_reg, sc->sc_iot, sc->sc_ioh, 374 csc->cc_size); 375 376 return (0); 377 } 378