xref: /openbsd/sys/dev/pci/if_xl_pci.c (revision 0f9891f1)
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