1 | /* $NetBSD: if_rtk_cardbus.c,v 1.48 2015/04/13 16:33:24 riastradh Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2000 Masanori Kanaoka |
5 | * 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. The name of the author may not be used to endorse or promote products |
16 | * derived from this software without specific prior written permission. |
17 | * |
18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
19 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
21 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
22 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
23 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
27 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
28 | */ |
29 | |
30 | /* |
31 | * if_rtk_cardbus.c: |
32 | * Cardbus specific routines for Realtek 8139 ethernet adapter. |
33 | * Tested for |
34 | * - elecom-Laneed LD-10/100CBA (Accton MPX5030) |
35 | * - MELCO LPC3-TX-CB (Realtek 8139) |
36 | */ |
37 | |
38 | #include <sys/cdefs.h> |
39 | __KERNEL_RCSID(0, "$NetBSD: if_rtk_cardbus.c,v 1.48 2015/04/13 16:33:24 riastradh Exp $" ); |
40 | |
41 | #include "opt_inet.h" |
42 | |
43 | #include <sys/param.h> |
44 | #include <sys/systm.h> |
45 | #include <sys/callout.h> |
46 | #include <sys/device.h> |
47 | #include <sys/sockio.h> |
48 | #include <sys/mbuf.h> |
49 | #include <sys/malloc.h> |
50 | #include <sys/kernel.h> |
51 | #include <sys/socket.h> |
52 | |
53 | #include <net/if.h> |
54 | #include <net/if_arp.h> |
55 | #include <net/if_ether.h> |
56 | #include <net/if_dl.h> |
57 | #include <net/if_media.h> |
58 | #ifdef INET |
59 | #include <netinet/in.h> |
60 | #include <netinet/if_inarp.h> |
61 | #endif |
62 | |
63 | #include <sys/bus.h> |
64 | |
65 | #include <dev/pci/pcireg.h> |
66 | #include <dev/pci/pcivar.h> |
67 | #include <dev/pci/pcidevs.h> |
68 | |
69 | #include <dev/cardbus/cardbusvar.h> |
70 | #include <dev/pci/pcidevs.h> |
71 | |
72 | #include <dev/mii/mii.h> |
73 | #include <dev/mii/miivar.h> |
74 | |
75 | /* |
76 | * Default to using PIO access for this driver. On SMP systems, |
77 | * there appear to be problems with memory mapped mode: it looks like |
78 | * doing too many memory mapped access back to back in rapid succession |
79 | * can hang the bus. I'm inclined to blame this on crummy design/construction |
80 | * on the part of Realtek. Memory mapped mode does appear to work on |
81 | * uniprocessor systems though. |
82 | */ |
83 | #define RTK_USEIOSPACE |
84 | |
85 | #include <dev/ic/rtl81x9reg.h> |
86 | #include <dev/ic/rtl81x9var.h> |
87 | |
88 | /* |
89 | * Various supported device vendors/types and their names. |
90 | */ |
91 | static const struct rtk_type rtk_cardbus_devs[] = { |
92 | { PCI_VENDOR_ACCTON, PCI_PRODUCT_ACCTON_MPX5030, |
93 | RTK_8139, "Accton MPX 5030/5038 10/100BaseTX" }, |
94 | { PCI_VENDOR_DLINK, PCI_PRODUCT_DLINK_DFE690TXD, |
95 | RTK_8139, "D-Link DFE-690TXD 10/100BaseTX" }, |
96 | { PCI_VENDOR_REALTEK, PCI_PRODUCT_REALTEK_RT8138, |
97 | RTK_8139, "Realtek 8138 10/100BaseTX" }, |
98 | { PCI_VENDOR_REALTEK, PCI_PRODUCT_REALTEK_RT8139, |
99 | RTK_8139, "Realtek 8139 10/100BaseTX" }, |
100 | { PCI_VENDOR_COREGA, PCI_PRODUCT_COREGA_CB_TXD, |
101 | RTK_8139, "Corega FEther CB-TXD 10/100BaseTX" }, |
102 | { PCI_VENDOR_COREGA, PCI_PRODUCT_COREGA_2CB_TXD, |
103 | RTK_8139, "Corega FEther II CB-TXD 10/100BaseTX" }, |
104 | { PCI_VENDOR_PLANEX, PCI_PRODUCT_PLANEX_FNW_3603_TX, |
105 | RTK_8139, "Planex FNW-3603 10/100BaseTX" }, |
106 | { PCI_VENDOR_PLANEX, PCI_PRODUCT_PLANEX_FNW_3800_TX, |
107 | RTK_8139, "Planex 10/100BaseTX FNW-3800-TX" }, |
108 | { PCI_VENDOR_ABOCOM, PCI_PRODUCT_ABOCOM_FE2000VX, |
109 | RTK_8139, "AboCom FE2000VX 10/100BaseTX" }, |
110 | |
111 | { 0, 0, 0, NULL } |
112 | }; |
113 | |
114 | static int rtk_cardbus_match(device_t, cfdata_t, void *); |
115 | static void rtk_cardbus_attach(device_t, device_t, void *); |
116 | static int rtk_cardbus_detach(device_t, int); |
117 | |
118 | struct rtk_cardbus_softc { |
119 | struct rtk_softc sc_rtk; /* real rtk softc */ |
120 | |
121 | /* CardBus-specific goo. */ |
122 | void *sc_ih; |
123 | cardbus_devfunc_t sc_ct; |
124 | pcitag_t sc_tag; |
125 | pcireg_t sc_csr; |
126 | int sc_bar_reg; |
127 | pcireg_t sc_bar_val; |
128 | }; |
129 | |
130 | CFATTACH_DECL_NEW(rtk_cardbus, sizeof(struct rtk_cardbus_softc), |
131 | rtk_cardbus_match, rtk_cardbus_attach, rtk_cardbus_detach, rtk_activate); |
132 | |
133 | const struct rtk_type *rtk_cardbus_lookup(const struct cardbus_attach_args *); |
134 | |
135 | void rtk_cardbus_setup(struct rtk_cardbus_softc *); |
136 | |
137 | int rtk_cardbus_enable(struct rtk_softc *); |
138 | void rtk_cardbus_disable(struct rtk_softc *); |
139 | void rtk_cardbus_power(struct rtk_softc *, int); |
140 | |
141 | const struct rtk_type * |
142 | rtk_cardbus_lookup(const struct cardbus_attach_args *ca) |
143 | { |
144 | const struct rtk_type *t; |
145 | |
146 | for (t = rtk_cardbus_devs; t->rtk_name != NULL; t++){ |
147 | if (PCI_VENDOR(ca->ca_id) == t->rtk_vid && |
148 | PCI_PRODUCT(ca->ca_id) == t->rtk_did) { |
149 | return t; |
150 | } |
151 | } |
152 | return NULL; |
153 | } |
154 | |
155 | int |
156 | rtk_cardbus_match(device_t parent, cfdata_t cf, void *aux) |
157 | { |
158 | struct cardbus_attach_args *ca = aux; |
159 | |
160 | if (rtk_cardbus_lookup(ca) != NULL) |
161 | return 1; |
162 | |
163 | return 0; |
164 | } |
165 | |
166 | |
167 | void |
168 | rtk_cardbus_attach(device_t parent, device_t self, void *aux) |
169 | { |
170 | struct rtk_cardbus_softc *csc = device_private(self); |
171 | struct rtk_softc *sc = &csc->sc_rtk; |
172 | struct cardbus_attach_args *ca = aux; |
173 | cardbus_devfunc_t ct = ca->ca_ct; |
174 | const struct rtk_type *t; |
175 | bus_addr_t adr; |
176 | |
177 | sc->sc_dev = self; |
178 | sc->sc_dmat = ca->ca_dmat; |
179 | csc->sc_ct = ct; |
180 | csc->sc_tag = ca->ca_tag; |
181 | |
182 | t = rtk_cardbus_lookup(ca); |
183 | if (t == NULL) { |
184 | aprint_error("\n" ); |
185 | panic("%s: impossible" , __func__); |
186 | } |
187 | aprint_normal(": %s\n" , t->rtk_name); |
188 | |
189 | /* |
190 | * Power management hooks. |
191 | */ |
192 | sc->sc_enable = rtk_cardbus_enable; |
193 | sc->sc_disable = rtk_cardbus_disable; |
194 | |
195 | /* |
196 | * Map control/status registers. |
197 | */ |
198 | csc->sc_csr = PCI_COMMAND_MASTER_ENABLE; |
199 | #ifdef RTK_USEIOSPACE |
200 | if (Cardbus_mapreg_map(ct, RTK_PCI_LOIO, PCI_MAPREG_TYPE_IO, 0, |
201 | &sc->rtk_btag, &sc->rtk_bhandle, &adr, &sc->rtk_bsize) == 0) { |
202 | csc->sc_csr |= PCI_COMMAND_IO_ENABLE; |
203 | csc->sc_bar_reg = RTK_PCI_LOIO; |
204 | csc->sc_bar_val = adr | PCI_MAPREG_TYPE_IO; |
205 | } |
206 | #else |
207 | if (Cardbus_mapreg_map(ct, RTK_PCI_LOMEM, PCI_MAPREG_TYPE_MEM, 0, |
208 | &sc->rtk_btag, &sc->rtk_bhandle, &adr, &sc->rtk_bsize) == 0) { |
209 | csc->sc_csr |= PCI_COMMAND_MEM_ENABLE; |
210 | csc->sc_bar_reg = RTK_PCI_LOMEM; |
211 | csc->sc_bar_val = adr | PCI_MAPREG_TYPE_MEM; |
212 | } |
213 | #endif |
214 | else { |
215 | aprint_error_dev(self, " unable to map deviceregisters\n" ); |
216 | return; |
217 | } |
218 | /* |
219 | * Handle power management nonsense and initialize the |
220 | * configuration registers. |
221 | */ |
222 | rtk_cardbus_setup(csc); |
223 | |
224 | rtk_attach(sc); |
225 | |
226 | if (pmf_device_register(self, NULL, NULL)) |
227 | pmf_class_network_register(self, &sc->ethercom.ec_if); |
228 | else |
229 | aprint_error_dev(self, "couldn't establish power handler\n" ); |
230 | |
231 | /* |
232 | * Power down the socket. |
233 | */ |
234 | Cardbus_function_disable(csc->sc_ct); |
235 | } |
236 | |
237 | int |
238 | rtk_cardbus_detach(device_t self, int flags) |
239 | { |
240 | struct rtk_cardbus_softc *csc = device_private(self); |
241 | struct rtk_softc *sc = &csc->sc_rtk; |
242 | struct cardbus_devfunc *ct = csc->sc_ct; |
243 | int rv; |
244 | |
245 | #ifdef DIAGNOSTIC |
246 | if (ct == NULL) |
247 | panic("%s: data structure lacks" , device_xname(self)); |
248 | #endif |
249 | rv = rtk_detach(sc); |
250 | if (rv) |
251 | return rv; |
252 | /* |
253 | * Unhook the interrupt handler. |
254 | */ |
255 | if (csc->sc_ih != NULL) |
256 | Cardbus_intr_disestablish(ct, csc->sc_ih); |
257 | |
258 | /* |
259 | * Release bus space and close window. |
260 | */ |
261 | if (csc->sc_bar_reg != 0) |
262 | Cardbus_mapreg_unmap(ct, csc->sc_bar_reg, |
263 | sc->rtk_btag, sc->rtk_bhandle, sc->rtk_bsize); |
264 | |
265 | return 0; |
266 | } |
267 | |
268 | void |
269 | rtk_cardbus_setup(struct rtk_cardbus_softc *csc) |
270 | { |
271 | struct rtk_softc *sc = &csc->sc_rtk; |
272 | cardbus_devfunc_t ct = csc->sc_ct; |
273 | cardbus_chipset_tag_t cc = ct->ct_cc; |
274 | cardbus_function_tag_t cf = ct->ct_cf; |
275 | pcireg_t reg, command; |
276 | int pmreg; |
277 | |
278 | /* |
279 | * Handle power management nonsense. |
280 | */ |
281 | if (cardbus_get_capability(cc, cf, csc->sc_tag, |
282 | PCI_CAP_PWRMGMT, &pmreg, 0)) { |
283 | command = Cardbus_conf_read(ct, csc->sc_tag, |
284 | pmreg + PCI_PMCSR); |
285 | if (command & PCI_PMCSR_STATE_MASK) { |
286 | pcireg_t iobase, membase, irq; |
287 | |
288 | /* Save important PCI config data. */ |
289 | iobase = Cardbus_conf_read(ct, csc->sc_tag, |
290 | RTK_PCI_LOIO); |
291 | membase = Cardbus_conf_read(ct, csc->sc_tag, |
292 | RTK_PCI_LOMEM); |
293 | irq = Cardbus_conf_read(ct, csc->sc_tag, |
294 | PCI_INTERRUPT_REG); |
295 | |
296 | /* Reset the power state. */ |
297 | aprint_normal_dev(sc->sc_dev, |
298 | "chip is in D%d power mode -- setting to D0\n" , |
299 | command & PCI_PMCSR_STATE_MASK); |
300 | command &= ~PCI_PMCSR_STATE_MASK; |
301 | Cardbus_conf_write(ct, csc->sc_tag, |
302 | pmreg + PCI_PMCSR, command); |
303 | |
304 | /* Restore PCI config data. */ |
305 | Cardbus_conf_write(ct, csc->sc_tag, |
306 | RTK_PCI_LOIO, iobase); |
307 | Cardbus_conf_write(ct, csc->sc_tag, |
308 | RTK_PCI_LOMEM, membase); |
309 | Cardbus_conf_write(ct, csc->sc_tag, |
310 | PCI_INTERRUPT_REG, irq); |
311 | } |
312 | } |
313 | |
314 | /* Program the BAR */ |
315 | Cardbus_conf_write(ct, csc->sc_tag, csc->sc_bar_reg, csc->sc_bar_val); |
316 | |
317 | /* Enable the appropriate bits in the CARDBUS CSR. */ |
318 | reg = Cardbus_conf_read(ct, csc->sc_tag, PCI_COMMAND_STATUS_REG); |
319 | reg &= ~(PCI_COMMAND_IO_ENABLE|PCI_COMMAND_MEM_ENABLE); |
320 | reg |= csc->sc_csr; |
321 | Cardbus_conf_write(ct, csc->sc_tag, PCI_COMMAND_STATUS_REG, reg); |
322 | |
323 | /* |
324 | * Make sure the latency timer is set to some reasonable |
325 | * value. |
326 | */ |
327 | reg = Cardbus_conf_read(ct, csc->sc_tag, PCI_BHLC_REG); |
328 | if (PCI_LATTIMER(reg) < 0x20) { |
329 | reg &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT); |
330 | reg |= (0x20 << PCI_LATTIMER_SHIFT); |
331 | Cardbus_conf_write(ct, csc->sc_tag, PCI_BHLC_REG, reg); |
332 | } |
333 | } |
334 | |
335 | int |
336 | rtk_cardbus_enable(struct rtk_softc *sc) |
337 | { |
338 | struct rtk_cardbus_softc *csc = (struct rtk_cardbus_softc *)sc; |
339 | cardbus_devfunc_t ct = csc->sc_ct; |
340 | |
341 | /* |
342 | * Power on the socket. |
343 | */ |
344 | Cardbus_function_enable(ct); |
345 | |
346 | /* |
347 | * Set up the PCI configuration registers. |
348 | */ |
349 | rtk_cardbus_setup(csc); |
350 | |
351 | /* |
352 | * Map and establish the interrupt. |
353 | */ |
354 | csc->sc_ih = Cardbus_intr_establish(ct, IPL_NET, rtk_intr, sc); |
355 | if (csc->sc_ih == NULL) { |
356 | aprint_error_dev(sc->sc_dev, |
357 | "unable to establish interrupt\n" ); |
358 | Cardbus_function_disable(csc->sc_ct); |
359 | return 1; |
360 | } |
361 | return 0; |
362 | } |
363 | |
364 | void |
365 | rtk_cardbus_disable(struct rtk_softc *sc) |
366 | { |
367 | struct rtk_cardbus_softc *csc = (struct rtk_cardbus_softc *)sc; |
368 | cardbus_devfunc_t ct = csc->sc_ct; |
369 | |
370 | /* Unhook the interrupt handler. */ |
371 | Cardbus_intr_disestablish(ct, csc->sc_ih); |
372 | csc->sc_ih = NULL; |
373 | |
374 | /* Power down the socket. */ |
375 | Cardbus_function_disable(ct); |
376 | } |
377 | |