1 | /* $NetBSD: if_rtw_pci.c,v 1.23 2014/03/29 19:28:25 christos Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2004, 2005, 2010 David Young. All rights reserved. |
5 | * |
6 | * Adapted for the RTL8180 by David Young. |
7 | * |
8 | * Redistribution and use in source and binary forms, with or without |
9 | * modification, are permitted provided that the following conditions |
10 | * are met: |
11 | * 1. Redistributions of source code must retain the above copyright |
12 | * notice, this list of conditions and the following disclaimer. |
13 | * 2. Redistributions in binary form must reproduce the above copyright |
14 | * notice, this list of conditions and the following disclaimer in the |
15 | * documentation and/or other materials provided with the distribution. |
16 | * |
17 | * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY |
18 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
19 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A |
20 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David |
21 | * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
22 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED |
23 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
25 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
26 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY |
28 | * OF SUCH DAMAGE. |
29 | */ |
30 | /*- |
31 | * Copyright (c) 1998, 1999, 2000, 2002 The NetBSD Foundation, Inc. |
32 | * All rights reserved. |
33 | * |
34 | * This code is derived from software contributed to The NetBSD Foundation |
35 | * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, |
36 | * NASA Ames Research Center; Charles M. Hannum; and David Young. |
37 | * |
38 | * Redistribution and use in source and binary forms, with or without |
39 | * modification, are permitted provided that the following conditions |
40 | * are met: |
41 | * 1. Redistributions of source code must retain the above copyright |
42 | * notice, this list of conditions and the following disclaimer. |
43 | * 2. Redistributions in binary form must reproduce the above copyright |
44 | * notice, this list of conditions and the following disclaimer in the |
45 | * documentation and/or other materials provided with the distribution. |
46 | * |
47 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
48 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
49 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
50 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
51 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
52 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
53 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
54 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
55 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
56 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
57 | * POSSIBILITY OF SUCH DAMAGE. |
58 | */ |
59 | |
60 | /* |
61 | * PCI bus front-end for the Realtek RTL8180 802.11 MAC/BBP chip. |
62 | * |
63 | * Derived from the ADMtek ADM8211 PCI bus front-end. |
64 | * |
65 | * Derived from the ``Tulip'' PCI bus front-end. |
66 | */ |
67 | |
68 | #include <sys/cdefs.h> |
69 | __KERNEL_RCSID(0, "$NetBSD: if_rtw_pci.c,v 1.23 2014/03/29 19:28:25 christos Exp $" ); |
70 | |
71 | #include <sys/param.h> |
72 | #include <sys/systm.h> |
73 | #include <sys/mbuf.h> |
74 | #include <sys/malloc.h> |
75 | #include <sys/kernel.h> |
76 | #include <sys/socket.h> |
77 | #include <sys/ioctl.h> |
78 | #include <sys/errno.h> |
79 | #include <sys/device.h> |
80 | |
81 | #include <machine/endian.h> |
82 | |
83 | #include <net/if.h> |
84 | #include <net/if_dl.h> |
85 | #include <net/if_media.h> |
86 | #include <net/if_ether.h> |
87 | |
88 | #include <net80211/ieee80211_netbsd.h> |
89 | #include <net80211/ieee80211_radiotap.h> |
90 | #include <net80211/ieee80211_var.h> |
91 | |
92 | #include <sys/bus.h> |
93 | #include <sys/intr.h> |
94 | |
95 | #include <dev/ic/rtwreg.h> |
96 | #include <dev/ic/rtwvar.h> |
97 | |
98 | #include <dev/pci/pcivar.h> |
99 | #include <dev/pci/pcireg.h> |
100 | #include <dev/pci/pcidevs.h> |
101 | |
102 | /* |
103 | * PCI configuration space registers used by the RTL8180. |
104 | */ |
105 | #define RTW_PCI_IOBA PCI_BAR(0) /* i/o mapped base */ |
106 | #define RTW_PCI_MMBA PCI_BAR(1) /* memory mapped base */ |
107 | |
108 | struct rtw_pci_softc { |
109 | struct rtw_softc psc_rtw; |
110 | |
111 | pcireg_t psc_csr; |
112 | void *psc_ih; |
113 | pci_chipset_tag_t psc_pc; |
114 | pci_intr_handle_t psc_pih; |
115 | pcitag_t psc_tag; |
116 | }; |
117 | |
118 | static void rtw_pci_attach(device_t, device_t, void *); |
119 | static int rtw_pci_detach(device_t, int); |
120 | #if 0 |
121 | static void rtw_pci_funcregen(struct rtw_regs *, int); |
122 | #endif |
123 | static const struct rtw_pci_product * |
124 | rtw_pci_lookup(const struct pci_attach_args *); |
125 | static int rtw_pci_match(device_t, cfdata_t, void *); |
126 | static bool rtw_pci_resume(device_t, const pmf_qual_t *); |
127 | static int rtw_pci_setup(struct rtw_pci_softc *); |
128 | static bool rtw_pci_suspend(device_t, const pmf_qual_t *); |
129 | |
130 | CFATTACH_DECL3_NEW(rtw_pci, sizeof(struct rtw_pci_softc), |
131 | rtw_pci_match, rtw_pci_attach, rtw_pci_detach, NULL, NULL, NULL, |
132 | DVF_DETACH_SHUTDOWN); |
133 | |
134 | static const struct rtw_pci_product { |
135 | u_int32_t rpp_vendor; /* PCI vendor ID */ |
136 | u_int32_t rpp_product; /* PCI product ID */ |
137 | const char *rpp_product_name; |
138 | } rtw_pci_products[] = { |
139 | { PCI_VENDOR_REALTEK, PCI_PRODUCT_REALTEK_RT8180, |
140 | "Realtek RTL8180 802.11 MAC/BBP" }, |
141 | #ifdef RTW_DEBUG |
142 | #if 0 /* These came from openbsd, netbsd doesn't have the definitions. */ |
143 | { PCI_VENDOR_REALTEK, PCI_PRODUCT_REALTEK_RT8185, |
144 | "Realtek RTL8185 802.11 MAC/BBP" }, |
145 | { PCI_VENDOR_BELKIN2, PCI_PRODUCT_BELKIN2_F5D7010, |
146 | "Belkin F5D7010" }, |
147 | #endif |
148 | #endif |
149 | { PCI_VENDOR_BELKIN, PCI_PRODUCT_BELKIN_F5D6001, |
150 | "Belkin F5D6001" }, |
151 | { PCI_VENDOR_BELKIN, PCI_PRODUCT_BELKIN_F5D6020V3, |
152 | "Belkin F5D6020v3" }, |
153 | {PCI_VENDOR_DLINK, PCI_PRODUCT_DLINK_DWL610, |
154 | "DWL-610 D-Link Air 802.11b (RTL8180 MAC/BBP)" }, |
155 | { 0, 0, NULL }, |
156 | }; |
157 | |
158 | static const struct rtw_pci_product * |
159 | rtw_pci_lookup(const struct pci_attach_args *pa) |
160 | { |
161 | const struct rtw_pci_product *rpp; |
162 | |
163 | for (rpp = rtw_pci_products; rpp->rpp_product_name != NULL; rpp++) { |
164 | if (PCI_VENDOR(pa->pa_id) == rpp->rpp_vendor && |
165 | PCI_PRODUCT(pa->pa_id) == rpp->rpp_product) |
166 | return rpp; |
167 | } |
168 | return NULL; |
169 | } |
170 | |
171 | static int |
172 | rtw_pci_match(device_t parent, cfdata_t match, void *aux) |
173 | { |
174 | struct pci_attach_args *pa = aux; |
175 | |
176 | if (rtw_pci_lookup(pa) != NULL) |
177 | return 1; |
178 | |
179 | return 0; |
180 | } |
181 | |
182 | static void |
183 | rtw_pci_attach(device_t parent, device_t self, void *aux) |
184 | { |
185 | struct rtw_pci_softc *psc = device_private(self); |
186 | struct rtw_softc *sc = &psc->psc_rtw; |
187 | struct rtw_regs *regs = &sc->sc_regs; |
188 | struct pci_attach_args *pa = aux; |
189 | const char *intrstr = NULL; |
190 | const struct rtw_pci_product *rpp; |
191 | char intrbuf[PCI_INTRSTR_LEN]; |
192 | |
193 | sc->sc_dev = self; |
194 | sc->sc_dmat = pa->pa_dmat; |
195 | psc->psc_pc = pa->pa_pc; |
196 | psc->psc_tag = pa->pa_tag; |
197 | |
198 | rpp = rtw_pci_lookup(pa); |
199 | if (rpp == NULL) { |
200 | printf("\n" ); |
201 | panic("rtw_pci_attach: impossible" ); |
202 | } |
203 | |
204 | /* |
205 | * Get revision info, and set some chip-specific variables. |
206 | */ |
207 | sc->sc_rev = PCI_REVISION(pa->pa_class); |
208 | aprint_normal(": %s, revision %d.%d signature %08x\n" , |
209 | rpp->rpp_product_name, |
210 | (sc->sc_rev >> 4) & 0xf, sc->sc_rev & 0xf, |
211 | pci_conf_read(psc->psc_pc, psc->psc_tag, 0x80)); |
212 | |
213 | /* |
214 | * Map the device. |
215 | */ |
216 | psc->psc_csr = PCI_COMMAND_MASTER_ENABLE | |
217 | PCI_COMMAND_PARITY_ENABLE | |
218 | PCI_COMMAND_SERR_ENABLE; |
219 | if (pci_mapreg_map(pa, RTW_PCI_MMBA, PCI_MAPREG_TYPE_MEM, 0, |
220 | ®s->r_bt, ®s->r_bh, NULL, ®s->r_sz) == 0) { |
221 | RTW_DPRINTF(RTW_DEBUG_ATTACH, |
222 | ("%s: %s mapped %" PRIuMAX " bytes mem space\n" , |
223 | device_xname(self), __func__, (uintmax_t)regs->r_sz)); |
224 | psc->psc_csr |= PCI_COMMAND_MEM_ENABLE; |
225 | } else if (pci_mapreg_map(pa, RTW_PCI_IOBA, PCI_MAPREG_TYPE_IO, 0, |
226 | ®s->r_bt, ®s->r_bh, NULL, ®s->r_sz) == 0) { |
227 | RTW_DPRINTF(RTW_DEBUG_ATTACH, |
228 | ("%s: %s mapped %" PRIuMAX " bytes I/O space\n" , |
229 | device_xname(self), __func__, (uintmax_t)regs->r_sz)); |
230 | psc->psc_csr |= PCI_COMMAND_IO_ENABLE; |
231 | } else { |
232 | aprint_error_dev(self, "unable to map device registers\n" ); |
233 | return; |
234 | } |
235 | |
236 | /* |
237 | * Bring the chip out of powersave mode and initialize the |
238 | * configuration registers. |
239 | */ |
240 | if (rtw_pci_setup(psc) != 0) |
241 | return; |
242 | |
243 | /* |
244 | * Map and establish our interrupt. |
245 | */ |
246 | if (pci_intr_map(pa, &psc->psc_pih)) { |
247 | aprint_error_dev(self, "unable to map interrupt\n" ); |
248 | return; |
249 | } |
250 | intrstr = pci_intr_string(psc->psc_pc, psc->psc_pih, intrbuf, sizeof(intrbuf)); |
251 | psc->psc_ih = pci_intr_establish(psc->psc_pc, psc->psc_pih, IPL_NET, |
252 | rtw_intr, sc); |
253 | if (psc->psc_ih == NULL) { |
254 | aprint_error_dev(self, "unable to establish interrupt" ); |
255 | if (intrstr != NULL) |
256 | aprint_error(" at %s" , intrstr); |
257 | aprint_error("\n" ); |
258 | return; |
259 | } |
260 | |
261 | aprint_normal_dev(self, "interrupting at %s\n" , intrstr); |
262 | |
263 | /* |
264 | * Finish off the attach. |
265 | */ |
266 | rtw_attach(sc); |
267 | |
268 | if (pmf_device_register(self, rtw_pci_suspend, rtw_pci_resume)) { |
269 | pmf_class_network_register(self, &sc->sc_if); |
270 | /* |
271 | * Power down the socket. |
272 | */ |
273 | pmf_device_suspend(self, &sc->sc_qual); |
274 | } else |
275 | aprint_error_dev(self, "couldn't establish power handler\n" ); |
276 | } |
277 | |
278 | static int |
279 | rtw_pci_detach(device_t self, int flags) |
280 | { |
281 | struct rtw_pci_softc *psc = device_private(self); |
282 | struct rtw_softc *sc = &psc->psc_rtw; |
283 | struct rtw_regs *regs = &sc->sc_regs; |
284 | int rc; |
285 | |
286 | if ((rc = rtw_detach(sc)) != 0) |
287 | return rc; |
288 | if (psc->psc_ih != NULL) |
289 | pci_intr_disestablish(psc->psc_pc, psc->psc_ih); |
290 | bus_space_unmap(regs->r_bt, regs->r_bh, regs->r_sz); |
291 | |
292 | return 0; |
293 | } |
294 | |
295 | static bool |
296 | rtw_pci_resume(device_t self, const pmf_qual_t *qual) |
297 | { |
298 | struct rtw_pci_softc *psc = device_private(self); |
299 | struct rtw_softc *sc = &psc->psc_rtw; |
300 | |
301 | /* Establish the interrupt. */ |
302 | psc->psc_ih = pci_intr_establish(psc->psc_pc, psc->psc_pih, IPL_NET, |
303 | rtw_intr, sc); |
304 | if (psc->psc_ih == NULL) { |
305 | aprint_error_dev(sc->sc_dev, "unable to establish interrupt\n" ); |
306 | return false; |
307 | } |
308 | |
309 | return rtw_resume(self, qual); |
310 | } |
311 | |
312 | static bool |
313 | rtw_pci_suspend(device_t self, const pmf_qual_t *qual) |
314 | { |
315 | struct rtw_pci_softc *psc = device_private(self); |
316 | |
317 | if (!rtw_suspend(self, qual)) |
318 | return false; |
319 | |
320 | /* Unhook the interrupt handler. */ |
321 | pci_intr_disestablish(psc->psc_pc, psc->psc_ih); |
322 | psc->psc_ih = NULL; |
323 | return true; |
324 | } |
325 | |
326 | static int |
327 | rtw_pci_setup(struct rtw_pci_softc *psc) |
328 | { |
329 | pcitag_t tag = psc->psc_tag; |
330 | pcireg_t bhlc, csr, lattimer; |
331 | device_t self = psc->psc_rtw.sc_dev; |
332 | int rc; |
333 | |
334 | /* power up chip */ |
335 | rc = pci_activate(psc->psc_pc, psc->psc_tag, self, NULL); |
336 | |
337 | if (rc != 0 && rc != EOPNOTSUPP) { |
338 | aprint_error_dev(self, "cannot activate (%d)\n" , rc); |
339 | return rc; |
340 | } |
341 | |
342 | /* I believe the datasheet tries to warn us that the RTL8180 |
343 | * wants for 16 (0x10) to divide the latency timer. |
344 | */ |
345 | bhlc = pci_conf_read(psc->psc_pc, tag, PCI_BHLC_REG); |
346 | lattimer = rounddown(PCI_LATTIMER(bhlc), 0x10); |
347 | if (PCI_LATTIMER(bhlc) != lattimer) { |
348 | bhlc &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT); |
349 | bhlc |= (lattimer << PCI_LATTIMER_SHIFT); |
350 | pci_conf_write(psc->psc_pc, tag, PCI_BHLC_REG, bhlc); |
351 | } |
352 | |
353 | /* Enable the appropriate bits in the PCI CSR. */ |
354 | csr = pci_conf_read(psc->psc_pc, tag, PCI_COMMAND_STATUS_REG); |
355 | csr &= ~(PCI_COMMAND_IO_ENABLE|PCI_COMMAND_MEM_ENABLE); |
356 | csr |= psc->psc_csr; |
357 | pci_conf_write(psc->psc_pc, tag, PCI_COMMAND_STATUS_REG, csr); |
358 | |
359 | return 0; |
360 | } |
361 | |