1 | /* $NetBSD: if_re_cardbus.c,v 1.27 2011/08/01 11:20:27 drochner Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2004 Jonathan Stone |
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_re_cardbus.c: |
32 | * Cardbus specific routines for Realtek 8169 ethernet adapter. |
33 | * Tested for : |
34 | * Netgear GA-511 (8169S) |
35 | * Buffalo LPC-CB-CLGT |
36 | */ |
37 | |
38 | #include <sys/cdefs.h> |
39 | __KERNEL_RCSID(0, "$NetBSD: if_re_cardbus.c,v 1.27 2011/08/01 11:20:27 drochner Exp $" ); |
40 | |
41 | #include <sys/param.h> |
42 | #include <sys/systm.h> |
43 | #include <sys/device.h> |
44 | |
45 | #include <net/if.h> |
46 | #include <net/if_ether.h> |
47 | #include <net/if_media.h> |
48 | |
49 | #include <sys/bus.h> |
50 | |
51 | #include <dev/pci/pcireg.h> |
52 | #include <dev/pci/pcivar.h> |
53 | #include <dev/pci/pcidevs.h> |
54 | |
55 | #include <dev/cardbus/cardbusvar.h> |
56 | |
57 | #include <dev/mii/mii.h> |
58 | #include <dev/mii/miivar.h> |
59 | |
60 | /* |
61 | * Default to using PIO access for this driver. On SMP systems, |
62 | * there appear to be problems with memory mapped mode: it looks like |
63 | * doing too many memory mapped access back to back in rapid succession |
64 | * can hang the bus. I'm inclined to blame this on crummy design/construction |
65 | * on the part of Realtek. Memory mapped mode does appear to work on |
66 | * uniprocessor systems though. |
67 | */ |
68 | #define RTK_USEIOSPACE |
69 | |
70 | #include <dev/ic/rtl81x9reg.h> |
71 | #include <dev/ic/rtl81x9var.h> |
72 | |
73 | #include <dev/ic/rtl8169var.h> |
74 | |
75 | /* |
76 | * Various supported device vendors/types and their names. |
77 | */ |
78 | static const struct rtk_type re_cardbus_devs[] = { |
79 | { PCI_VENDOR_REALTEK, PCI_PRODUCT_REALTEK_RT8169, |
80 | RTK_8169, "Realtek 10/100/1000baseT" }, |
81 | { 0, 0, 0, NULL } |
82 | }; |
83 | |
84 | static int re_cardbus_match(device_t, cfdata_t, void *); |
85 | static void re_cardbus_attach(device_t, device_t, void *); |
86 | static int re_cardbus_detach(device_t, int); |
87 | |
88 | struct re_cardbus_softc { |
89 | struct rtk_softc sc_rtk; /* real rtk softc */ |
90 | |
91 | /* CardBus-specific goo. */ |
92 | void *sc_ih; |
93 | cardbus_devfunc_t sc_ct; |
94 | pcitag_t sc_tag; |
95 | pcireg_t sc_csr; |
96 | int sc_bar_reg; |
97 | pcireg_t sc_bar_val; |
98 | }; |
99 | |
100 | CFATTACH_DECL_NEW(re_cardbus, sizeof(struct re_cardbus_softc), |
101 | re_cardbus_match, re_cardbus_attach, re_cardbus_detach, re_activate); |
102 | |
103 | const struct rtk_type *re_cardbus_lookup(const struct cardbus_attach_args *); |
104 | |
105 | void re_cardbus_setup(struct re_cardbus_softc *); |
106 | |
107 | int re_cardbus_enable(struct rtk_softc *); |
108 | void re_cardbus_disable(struct rtk_softc *); |
109 | |
110 | const struct rtk_type * |
111 | re_cardbus_lookup(const struct cardbus_attach_args *ca) |
112 | { |
113 | const struct rtk_type *t; |
114 | |
115 | for (t = re_cardbus_devs; t->rtk_name != NULL; t++) { |
116 | if (PCI_VENDOR(ca->ca_id) == t->rtk_vid && |
117 | PCI_PRODUCT(ca->ca_id) == t->rtk_did) { |
118 | return t; |
119 | } |
120 | } |
121 | return NULL; |
122 | } |
123 | |
124 | int |
125 | re_cardbus_match(device_t parent, cfdata_t cf, void *aux) |
126 | { |
127 | struct cardbus_attach_args *ca = aux; |
128 | |
129 | if (re_cardbus_lookup(ca) != NULL) |
130 | return 1; |
131 | |
132 | return 0; |
133 | } |
134 | |
135 | |
136 | void |
137 | re_cardbus_attach(device_t parent, device_t self, void *aux) |
138 | { |
139 | struct re_cardbus_softc *csc = device_private(self); |
140 | struct rtk_softc *sc = &csc->sc_rtk; |
141 | struct cardbus_attach_args *ca = aux; |
142 | cardbus_devfunc_t ct = ca->ca_ct; |
143 | const struct rtk_type *t; |
144 | bus_addr_t adr; |
145 | |
146 | sc->sc_dev = self; |
147 | sc->sc_dmat = ca->ca_dmat; |
148 | csc->sc_ct = ct; |
149 | csc->sc_tag = ca->ca_tag; |
150 | |
151 | t = re_cardbus_lookup(ca); |
152 | if (t == NULL) { |
153 | aprint_error("\n" ); |
154 | panic("%s: impossible" , __func__); |
155 | } |
156 | aprint_normal(": %s\n" , t->rtk_name); |
157 | |
158 | /* |
159 | * Power management hooks. |
160 | */ |
161 | sc->sc_enable = re_cardbus_enable; |
162 | sc->sc_disable = re_cardbus_disable; |
163 | |
164 | /* |
165 | * Map control/status registers. |
166 | */ |
167 | csc->sc_csr = PCI_COMMAND_MASTER_ENABLE; |
168 | #ifdef RTK_USEIOSPACE |
169 | if (Cardbus_mapreg_map(ct, RTK_PCI_LOIO, PCI_MAPREG_TYPE_IO, 0, |
170 | &sc->rtk_btag, &sc->rtk_bhandle, &adr, &sc->rtk_bsize) == 0) { |
171 | csc->sc_csr |= PCI_COMMAND_IO_ENABLE; |
172 | csc->sc_bar_reg = RTK_PCI_LOIO; |
173 | csc->sc_bar_val = adr | PCI_MAPREG_TYPE_IO; |
174 | } |
175 | #else |
176 | if (Cardbus_mapreg_map(ct, RTK_PCI_LOMEM, PCI_MAPREG_TYPE_MEM, 0, |
177 | &sc->rtk_btag, &sc->rtk_bhandle, &adr, &sc->rtk_bsize) == 0) { |
178 | csc->sc_csr |= PCI_COMMAND_MEM_ENABLE; |
179 | csc->sc_bar_reg = RTK_PCI_LOMEM; |
180 | csc->sc_bar_val = adr | PCI_MAPREG_TYPE_MEM; |
181 | } |
182 | #endif |
183 | else { |
184 | aprint_error_dev(self, "unable to map deviceregisters\n" ); |
185 | return; |
186 | } |
187 | /* |
188 | * Handle power management nonsense and initialize the |
189 | * configuration registers. |
190 | */ |
191 | re_cardbus_setup(csc); |
192 | |
193 | sc->sc_dmat = ca->ca_dmat; |
194 | re_attach(sc); |
195 | |
196 | /* |
197 | * Power down the socket. |
198 | */ |
199 | Cardbus_function_disable(csc->sc_ct); |
200 | } |
201 | |
202 | int |
203 | re_cardbus_detach(device_t self, int flags) |
204 | { |
205 | struct re_cardbus_softc *csc = device_private(self); |
206 | struct rtk_softc *sc = &csc->sc_rtk; |
207 | struct cardbus_devfunc *ct = csc->sc_ct; |
208 | int rv; |
209 | |
210 | #ifdef DIAGNOSTIC |
211 | if (ct == NULL) |
212 | panic("%s: cardbus softc, cardbus_devfunc NULL" , |
213 | device_xname(self)); |
214 | #endif |
215 | |
216 | rv = re_detach(sc); |
217 | if (rv) |
218 | return rv; |
219 | |
220 | /* |
221 | * Unhook the interrupt handler. |
222 | */ |
223 | if (csc->sc_ih != NULL) |
224 | Cardbus_intr_disestablish(ct, csc->sc_ih); |
225 | |
226 | /* |
227 | * Release bus space and close window. |
228 | */ |
229 | if (csc->sc_bar_reg != 0) |
230 | Cardbus_mapreg_unmap(ct, csc->sc_bar_reg, |
231 | sc->rtk_btag, sc->rtk_bhandle, sc->rtk_bsize); |
232 | |
233 | return 0; |
234 | } |
235 | |
236 | void |
237 | re_cardbus_setup(struct re_cardbus_softc *csc) |
238 | { |
239 | struct rtk_softc *sc = &csc->sc_rtk; |
240 | cardbus_devfunc_t ct = csc->sc_ct; |
241 | cardbus_chipset_tag_t cc = ct->ct_cc; |
242 | cardbus_function_tag_t cf = ct->ct_cf; |
243 | pcireg_t reg, command; |
244 | int pmreg; |
245 | |
246 | /* |
247 | * Handle power management nonsense. |
248 | */ |
249 | if (cardbus_get_capability(cc, cf, csc->sc_tag, |
250 | PCI_CAP_PWRMGMT, &pmreg, 0)) { |
251 | command = Cardbus_conf_read(ct, csc->sc_tag, |
252 | pmreg + PCI_PMCSR); |
253 | if (command & PCI_PMCSR_STATE_MASK) { |
254 | pcireg_t iobase, membase, irq; |
255 | |
256 | /* Save important PCI config data. */ |
257 | iobase = Cardbus_conf_read(ct, csc->sc_tag, |
258 | RTK_PCI_LOIO); |
259 | membase = Cardbus_conf_read(ct, csc->sc_tag, |
260 | RTK_PCI_LOMEM); |
261 | irq = Cardbus_conf_read(ct, csc->sc_tag, |
262 | PCI_INTERRUPT_REG); |
263 | |
264 | /* Reset the power state. */ |
265 | aprint_normal_dev(sc->sc_dev, |
266 | "chip is in D%d power mode -- setting to D0\n" , |
267 | command & PCI_PMCSR_STATE_MASK); |
268 | command &= ~PCI_PMCSR_STATE_MASK; |
269 | Cardbus_conf_write(ct, csc->sc_tag, |
270 | pmreg + PCI_PMCSR, command); |
271 | |
272 | /* Restore PCI config data. */ |
273 | Cardbus_conf_write(ct, csc->sc_tag, |
274 | RTK_PCI_LOIO, iobase); |
275 | Cardbus_conf_write(ct, csc->sc_tag, |
276 | RTK_PCI_LOMEM, membase); |
277 | Cardbus_conf_write(ct, csc->sc_tag, |
278 | PCI_INTERRUPT_REG, irq); |
279 | } |
280 | } |
281 | |
282 | /* Program the BAR */ |
283 | Cardbus_conf_write(ct, csc->sc_tag, csc->sc_bar_reg, csc->sc_bar_val); |
284 | |
285 | /* Enable the appropriate bits in the CARDBUS CSR. */ |
286 | reg = Cardbus_conf_read(ct, csc->sc_tag, PCI_COMMAND_STATUS_REG); |
287 | reg &= ~(PCI_COMMAND_IO_ENABLE|PCI_COMMAND_MEM_ENABLE); |
288 | reg |= csc->sc_csr; |
289 | Cardbus_conf_write(ct, csc->sc_tag, PCI_COMMAND_STATUS_REG, reg); |
290 | |
291 | /* |
292 | * Make sure the latency timer is set to some reasonable |
293 | * value. |
294 | */ |
295 | reg = Cardbus_conf_read(ct, csc->sc_tag, PCI_BHLC_REG); |
296 | if (PCI_LATTIMER(reg) < 0x40) { |
297 | reg &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT); |
298 | reg |= (0x40 << PCI_LATTIMER_SHIFT); |
299 | Cardbus_conf_write(ct, csc->sc_tag, PCI_BHLC_REG, reg); |
300 | } |
301 | } |
302 | |
303 | int |
304 | re_cardbus_enable(struct rtk_softc *sc) |
305 | { |
306 | struct re_cardbus_softc *csc = (struct re_cardbus_softc *)sc; |
307 | cardbus_devfunc_t ct = csc->sc_ct; |
308 | |
309 | /* |
310 | * Power on the socket. |
311 | */ |
312 | Cardbus_function_enable(ct); |
313 | |
314 | /* |
315 | * Set up the PCI configuration registers. |
316 | */ |
317 | re_cardbus_setup(csc); |
318 | |
319 | /* |
320 | * Map and establish the interrupt. |
321 | */ |
322 | csc->sc_ih = Cardbus_intr_establish(ct, IPL_NET, re_intr, sc); |
323 | if (csc->sc_ih == NULL) { |
324 | aprint_error_dev(sc->sc_dev, |
325 | "unable to establish interrupt\n" ); |
326 | Cardbus_function_disable(csc->sc_ct); |
327 | return 1; |
328 | } |
329 | return 0; |
330 | } |
331 | |
332 | void |
333 | re_cardbus_disable(struct rtk_softc *sc) |
334 | { |
335 | struct re_cardbus_softc *csc = (struct re_cardbus_softc *)sc; |
336 | cardbus_devfunc_t ct = csc->sc_ct; |
337 | |
338 | /* Unhook the interrupt handler. */ |
339 | Cardbus_intr_disestablish(ct, csc->sc_ih); |
340 | csc->sc_ih = NULL; |
341 | |
342 | /* Power down the socket. */ |
343 | Cardbus_function_disable(ct); |
344 | } |
345 | |