1 /* $OpenBSD: if_xl_pci.c,v 1.49 2024/05/24 06:02:57 jsg Exp $ */ 2 3 /* 4 * Copyright (c) 1997, 1998, 1999 5 * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Bill Paul. 18 * 4. Neither the name of the author nor the names of any co-contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 32 * THE POSSIBILITY OF SUCH DAMAGE. 33 * 34 * $FreeBSD: if_xl.c,v 1.72 2000/01/09 21:12:59 wpaul Exp $ 35 */ 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/device.h> 40 41 #include <net/if.h> 42 #include <net/if_media.h> 43 44 #include <netinet/in.h> 45 #include <netinet/if_ether.h> 46 47 #include <dev/mii/miivar.h> 48 #include <dev/pci/pcireg.h> 49 #include <dev/pci/pcivar.h> 50 #include <dev/pci/pcidevs.h> 51 52 /* 53 * The following #define causes the code to use PIO to access the 54 * chip's registers instead of memory mapped mode. The reason PIO mode 55 * is on by default is that the Etherlink XL manual seems to indicate 56 * that only the newer revision chips (3c905B) support both PIO and 57 * memory mapped access. Since we want to be compatible with the older 58 * bus master chips, we use PIO here. If you comment this out, the 59 * driver will use memory mapped I/O, which may be faster but which 60 * might not work on some devices. 61 */ 62 #define XL_USEIOSPACE 63 64 #define XL_PCI_FUNCMEM 0x0018 65 #define XL_PCI_INTR 0x0004 66 #define XL_PCI_INTRACK 0x8000 67 68 #include <dev/ic/xlreg.h> 69 70 int xl_pci_match(struct device *, void *, void *); 71 void xl_pci_attach(struct device *, struct device *, void *); 72 int xl_pci_detach(struct device *, int); 73 void xl_pci_intr_ack(struct xl_softc *); 74 #ifndef SMALL_KERNEL 75 void xl_pci_wol_power(void *); 76 #endif 77 78 struct xl_pci_softc { 79 struct xl_softc psc_softc; 80 pci_chipset_tag_t psc_pc; 81 pcitag_t psc_tag; 82 bus_size_t psc_iosize; 83 bus_size_t psc_funsize; 84 }; 85 86 const struct cfattach xl_pci_ca = { 87 sizeof(struct xl_pci_softc), xl_pci_match, xl_pci_attach, 88 xl_pci_detach, xl_activate 89 }; 90 91 const struct pci_matchid xl_pci_devices[] = { 92 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3CSOHO100TX }, 93 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C900TPO }, 94 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C900COMBO }, 95 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C900B }, 96 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C900BCOMBO }, 97 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C900BTPC }, 98 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C900BFL }, 99 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C905TX }, 100 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C905T4 }, 101 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C905BTX }, 102 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C905BT4 }, 103 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C905BCOMBO }, 104 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C905BFX }, 105 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C980TX }, 106 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C980CTX }, 107 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C905CTX }, 108 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C450 }, 109 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C555 }, 110 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C556 }, 111 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C556B }, 112 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C9201 }, 113 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C920BEMBW }, 114 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C575 }, 115 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3CCFE575BT }, 116 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3CCFE575CT }, 117 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3CCFEM656 }, 118 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3CCFEM656B }, 119 { PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3CCFEM656C }, 120 }; 121 122 int 123 xl_pci_match(struct device *parent, void *match, void *aux) 124 { 125 return (pci_matchbyid((struct pci_attach_args *)aux, xl_pci_devices, 126 nitems(xl_pci_devices))); 127 } 128 129 void 130 xl_pci_attach(struct device *parent, struct device *self, void *aux) 131 { 132 struct xl_pci_softc *psc = (void *)self; 133 struct xl_softc *sc = &psc->psc_softc; 134 struct pci_attach_args *pa = aux; 135 pci_chipset_tag_t pc = pa->pa_pc; 136 pci_intr_handle_t ih; 137 const char *intrstr = NULL; 138 bus_size_t iosize, funsize; 139 #ifndef SMALL_KERNEL 140 u_int32_t command; 141 #endif 142 143 psc->psc_pc = pc; 144 psc->psc_tag = pa->pa_tag; 145 sc->sc_dmat = pa->pa_dmat; 146 147 sc->xl_flags = 0; 148 sc->wol_power = sc->wol_power_arg = NULL; 149 150 /* set required flags */ 151 switch (PCI_PRODUCT(pa->pa_id)) { 152 case TC_DEVICEID_HURRICANE_555: 153 sc->xl_flags |= XL_FLAG_EEPROM_OFFSET_30 | XL_FLAG_8BITROM; 154 break; 155 case TC_DEVICEID_HURRICANE_556: 156 sc->xl_flags |= XL_FLAG_FUNCREG | XL_FLAG_PHYOK | 157 XL_FLAG_EEPROM_OFFSET_30 | XL_FLAG_WEIRDRESET; 158 sc->xl_flags |= XL_FLAG_INVERT_LED_PWR|XL_FLAG_INVERT_MII_PWR; 159 sc->xl_flags |= XL_FLAG_8BITROM; 160 break; 161 case TC_DEVICEID_HURRICANE_556B: 162 sc->xl_flags |= XL_FLAG_FUNCREG | XL_FLAG_PHYOK | 163 XL_FLAG_EEPROM_OFFSET_30 | XL_FLAG_WEIRDRESET; 164 sc->xl_flags |= XL_FLAG_INVERT_LED_PWR|XL_FLAG_INVERT_MII_PWR; 165 break; 166 case PCI_PRODUCT_3COM_3C9201: 167 case PCI_PRODUCT_3COM_3C920BEMBW: 168 sc->xl_flags |= XL_FLAG_PHYOK; 169 break; 170 case TC_DEVICEID_BOOMERANG_10_100BT: 171 sc->xl_flags |= XL_FLAG_NO_MMIO; 172 break; 173 case PCI_PRODUCT_3COM_3C575: 174 sc->xl_flags |= XL_FLAG_PHYOK | XL_FLAG_EEPROM_OFFSET_30 | 175 XL_FLAG_8BITROM; 176 break; 177 case PCI_PRODUCT_3COM_3CCFE575BT: 178 sc->xl_flags = XL_FLAG_PHYOK | XL_FLAG_EEPROM_OFFSET_30 | 179 XL_FLAG_8BITROM | XL_FLAG_INVERT_LED_PWR; 180 sc->xl_flags |= XL_FLAG_FUNCREG; 181 break; 182 case PCI_PRODUCT_3COM_3CCFE575CT: 183 sc->xl_flags = XL_FLAG_PHYOK | XL_FLAG_EEPROM_OFFSET_30 | 184 XL_FLAG_8BITROM | XL_FLAG_INVERT_MII_PWR; 185 sc->xl_flags |= XL_FLAG_FUNCREG; 186 break; 187 case PCI_PRODUCT_3COM_3CCFEM656: 188 sc->xl_flags = XL_FLAG_PHYOK | XL_FLAG_EEPROM_OFFSET_30 | 189 XL_FLAG_8BITROM | XL_FLAG_INVERT_LED_PWR | 190 XL_FLAG_INVERT_MII_PWR; 191 sc->xl_flags |= XL_FLAG_FUNCREG; 192 break; 193 case PCI_PRODUCT_3COM_3CCFEM656B: 194 sc->xl_flags = XL_FLAG_PHYOK | XL_FLAG_EEPROM_OFFSET_30 | 195 XL_FLAG_8BITROM | XL_FLAG_INVERT_LED_PWR | 196 XL_FLAG_INVERT_MII_PWR; 197 sc->xl_flags |= XL_FLAG_FUNCREG; 198 break; 199 case PCI_PRODUCT_3COM_3CCFEM656C: 200 sc->xl_flags = XL_FLAG_PHYOK | XL_FLAG_EEPROM_OFFSET_30 | 201 XL_FLAG_8BITROM | XL_FLAG_INVERT_MII_PWR; 202 sc->xl_flags |= XL_FLAG_FUNCREG; 203 break; 204 default: 205 break; 206 } 207 208 pci_set_powerstate(pa->pa_pc, pa->pa_tag, PCI_PMCSR_STATE_D0); 209 210 #ifndef SMALL_KERNEL 211 /* 212 * The card is WOL-capable if it supports PME# assertion 213 * from D3hot power state. Install a callback to configure 214 * PCI power state for WOL. It will be invoked when the 215 * interface stops and WOL was enabled. 216 */ 217 command = pci_conf_read(pc, pa->pa_tag, XL_PCI_CAPID); 218 if ((command >> 16) & XL_PME_CAP_D3_HOT) { 219 sc->wol_power = xl_pci_wol_power; 220 sc->wol_power_arg = psc; 221 } 222 #endif 223 224 /* 225 * Map control/status registers. 226 */ 227 #ifdef XL_USEIOSPACE 228 if (pci_mapreg_map(pa, XL_PCI_LOIO, PCI_MAPREG_TYPE_IO, 0, 229 &sc->xl_btag, &sc->xl_bhandle, NULL, &iosize, 0)) { 230 printf(": can't map i/o space\n"); 231 return; 232 } 233 #else 234 if (pci_mapreg_map(pa, XL_PCI_LOMEM, PCI_MAPREG_TYPE_MEM, 0, 235 &sc->xl_btag, &sc->xl_bhandle, NULL, &iosize, 0)) { 236 printf(": can't map i/o space\n"); 237 return; 238 } 239 #endif 240 psc->psc_iosize = iosize; 241 242 if (sc->xl_flags & XL_FLAG_FUNCREG) { 243 if (pci_mapreg_map(pa, XL_PCI_FUNCMEM, PCI_MAPREG_TYPE_MEM, 0, 244 &sc->xl_funct, &sc->xl_funch, NULL, &funsize, 0)) { 245 printf(": can't map i/o space\n"); 246 bus_space_unmap(sc->xl_btag, sc->xl_bhandle, iosize); 247 return; 248 } 249 psc->psc_funsize = funsize; 250 sc->intr_ack = xl_pci_intr_ack; 251 } 252 253 /* 254 * Allocate our interrupt. 255 */ 256 if (pci_intr_map(pa, &ih)) { 257 printf(": couldn't map interrupt\n"); 258 bus_space_unmap(sc->xl_btag, sc->xl_bhandle, iosize); 259 if (sc->xl_flags & XL_FLAG_FUNCREG) 260 bus_space_unmap(sc->xl_funct, sc->xl_funch, funsize); 261 return; 262 } 263 264 intrstr = pci_intr_string(pc, ih); 265 sc->xl_intrhand = pci_intr_establish(pc, ih, IPL_NET, xl_intr, sc, 266 self->dv_xname); 267 if (sc->xl_intrhand == NULL) { 268 printf(": couldn't establish interrupt"); 269 if (intrstr != NULL) 270 printf(" at %s", intrstr); 271 bus_space_unmap(sc->xl_btag, sc->xl_bhandle, iosize); 272 if (sc->xl_flags & XL_FLAG_FUNCREG) 273 bus_space_unmap(sc->xl_funct, sc->xl_funch, funsize); 274 return; 275 } 276 printf(": %s", intrstr); 277 278 xl_attach(sc); 279 } 280 281 int 282 xl_pci_detach(struct device *self, int flags) 283 { 284 struct xl_pci_softc *psc = (void *)self; 285 struct xl_softc *sc = &psc->psc_softc; 286 287 if (sc->xl_intrhand != NULL) { 288 pci_intr_disestablish(psc->psc_pc, sc->xl_intrhand); 289 xl_detach(sc); 290 } 291 if (psc->psc_iosize > 0) 292 bus_space_unmap(sc->xl_btag, sc->xl_bhandle, psc->psc_iosize); 293 if (psc->psc_funsize > 0) 294 bus_space_unmap(sc->xl_funct, sc->xl_funch, psc->psc_funsize); 295 return (0); 296 } 297 298 void 299 xl_pci_intr_ack(struct xl_softc *sc) 300 { 301 bus_space_write_4(sc->xl_funct, sc->xl_funch, XL_PCI_INTR, 302 XL_PCI_INTRACK); 303 } 304 305 #ifndef SMALL_KERNEL 306 void 307 xl_pci_wol_power(void *ppsc) 308 { 309 struct xl_pci_softc *psc = (struct xl_pci_softc*)ppsc; 310 u_int32_t command; 311 312 /* Make sure wake-up generation is enabled. */ 313 command = pci_conf_read(psc->psc_pc, psc->psc_tag, XL_PCI_PWRMGMTCTRL); 314 command |= XL_PME_EN; 315 pci_conf_write(psc->psc_pc, psc->psc_tag, XL_PCI_PWRMGMTCTRL, command); 316 } 317 #endif 318