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
xl_pci_match(struct device * parent,void * match,void * aux)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
xl_pci_attach(struct device * parent,struct device * self,void * aux)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
xl_pci_detach(struct device * self,int flags)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
xl_pci_intr_ack(struct xl_softc * sc)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
xl_pci_wol_power(void * ppsc)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