1 | /* $NetBSD: if_athn_pci.c,v 1.12 2015/11/24 18:17:37 jakllsch Exp $ */ |
2 | /* $OpenBSD: if_athn_pci.c,v 1.11 2011/01/08 10:02:32 damien Exp $ */ |
3 | |
4 | /*- |
5 | * Copyright (c) 2009 Damien Bergamini <damien.bergamini@free.fr> |
6 | * |
7 | * Permission to use, copy, modify, and distribute this software for any |
8 | * purpose with or without fee is hereby granted, provided that the above |
9 | * copyright notice and this permission notice appear in all copies. |
10 | * |
11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
18 | */ |
19 | |
20 | /* |
21 | * PCI front-end for Atheros 802.11a/g/n chipsets. |
22 | */ |
23 | |
24 | #include <sys/cdefs.h> |
25 | __KERNEL_RCSID(0, "$NetBSD: if_athn_pci.c,v 1.12 2015/11/24 18:17:37 jakllsch Exp $" ); |
26 | |
27 | #include "opt_inet.h" |
28 | |
29 | #include <sys/param.h> |
30 | #include <sys/sockio.h> |
31 | #include <sys/mbuf.h> |
32 | #include <sys/kernel.h> |
33 | #include <sys/socket.h> |
34 | #include <sys/systm.h> |
35 | #include <sys/malloc.h> |
36 | #include <sys/callout.h> |
37 | #include <sys/device.h> |
38 | |
39 | #include <sys/bus.h> |
40 | #include <sys/intr.h> |
41 | |
42 | #include <net/if.h> |
43 | #include <net/if_ether.h> |
44 | #include <net/if_media.h> |
45 | |
46 | #include <net80211/ieee80211_var.h> |
47 | #include <net80211/ieee80211_amrr.h> |
48 | #include <net80211/ieee80211_radiotap.h> |
49 | |
50 | #include <dev/ic/athnreg.h> |
51 | #include <dev/ic/athnvar.h> |
52 | |
53 | #include <dev/pci/pcireg.h> |
54 | #include <dev/pci/pcivar.h> |
55 | #include <dev/pci/pcidevs.h> |
56 | |
57 | #define PCI_SUBSYSID_ATHEROS_COEX2WIRE 0x309b |
58 | #define PCI_SUBSYSID_ATHEROS_COEX3WIRE_SA 0x30aa |
59 | #define PCI_SUBSYSID_ATHEROS_COEX3WIRE_DA 0x30ab |
60 | |
61 | #define ATHN_PCI_MMBA PCI_BAR(0) /* memory mapped base */ |
62 | |
63 | struct athn_pci_softc { |
64 | struct athn_softc psc_sc; |
65 | |
66 | /* PCI specific goo. */ |
67 | pci_chipset_tag_t psc_pc; |
68 | pcitag_t psc_tag; |
69 | pci_intr_handle_t psc_pih; |
70 | void *psc_ih; |
71 | bus_space_tag_t psc_iot; |
72 | bus_space_handle_t psc_ioh; |
73 | bus_size_t psc_mapsz; |
74 | int psc_cap_off; |
75 | }; |
76 | |
77 | #define Static static |
78 | |
79 | Static int athn_pci_match(device_t, cfdata_t, void *); |
80 | Static void athn_pci_attach(device_t, device_t, void *); |
81 | Static int athn_pci_detach(device_t, int); |
82 | Static int athn_pci_activate(device_t, enum devact); |
83 | |
84 | CFATTACH_DECL_NEW(athn_pci, sizeof(struct athn_pci_softc), athn_pci_match, |
85 | athn_pci_attach, athn_pci_detach, athn_pci_activate); |
86 | |
87 | Static bool athn_pci_resume(device_t, const pmf_qual_t *); |
88 | Static bool athn_pci_suspend(device_t, const pmf_qual_t *); |
89 | Static uint32_t athn_pci_read(struct athn_softc *, uint32_t); |
90 | Static void athn_pci_write(struct athn_softc *, uint32_t, uint32_t); |
91 | Static void athn_pci_write_barrier(struct athn_softc *); |
92 | Static void athn_pci_disable_aspm(struct athn_softc *); |
93 | |
94 | Static int |
95 | athn_pci_match(device_t parent, cfdata_t match, void *aux) |
96 | { |
97 | static const struct { |
98 | pci_vendor_id_t apd_vendor; |
99 | pci_product_id_t apd_product; |
100 | } athn_pci_devices[] = { |
101 | { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR5416 }, |
102 | { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR5418 }, |
103 | { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9160 }, |
104 | { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9280 }, |
105 | { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9281 }, |
106 | { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9285 }, |
107 | { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR2427 }, |
108 | { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9227 }, |
109 | { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9287 }, |
110 | { PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9300 } |
111 | }; |
112 | struct pci_attach_args *pa = aux; |
113 | size_t i; |
114 | |
115 | for (i = 0; i < __arraycount(athn_pci_devices); i++) { |
116 | if (PCI_VENDOR(pa->pa_id) == athn_pci_devices[i].apd_vendor && |
117 | PCI_PRODUCT(pa->pa_id) == athn_pci_devices[i].apd_product) |
118 | /* |
119 | * Match better than 1, we prefer this driver |
120 | * over ath(4) |
121 | */ |
122 | return 10; |
123 | } |
124 | return 0; |
125 | } |
126 | |
127 | Static void |
128 | athn_pci_attach(device_t parent, device_t self, void *aux) |
129 | { |
130 | struct athn_pci_softc *psc = device_private(self); |
131 | struct athn_softc *sc = &psc->psc_sc; |
132 | struct ieee80211com *ic = &sc->sc_ic; |
133 | struct pci_attach_args *pa = aux; |
134 | const char *intrstr; |
135 | pcireg_t memtype, reg; |
136 | pci_product_id_t subsysid; |
137 | int error; |
138 | char intrbuf[PCI_INTRSTR_LEN]; |
139 | |
140 | sc->sc_dev = self; |
141 | sc->sc_dmat = pa->pa_dmat; |
142 | psc->psc_pc = pa->pa_pc; |
143 | psc->psc_tag = pa->pa_tag; |
144 | |
145 | sc->sc_ops.read = athn_pci_read; |
146 | sc->sc_ops.write = athn_pci_write; |
147 | sc->sc_ops.write_barrier = athn_pci_write_barrier; |
148 | |
149 | /* |
150 | * Get the offset of the PCI Express Capability Structure in PCI |
151 | * Configuration Space (Linux hardcodes it as 0x60.) |
152 | */ |
153 | error = pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_PCIEXPRESS, |
154 | &psc->psc_cap_off, NULL); |
155 | if (error != 0) { /* Found. */ |
156 | sc->sc_disable_aspm = athn_pci_disable_aspm; |
157 | sc->sc_flags |= ATHN_FLAG_PCIE; |
158 | } |
159 | /* |
160 | * Noone knows why this shit is necessary but there are claims that |
161 | * not doing this may cause very frequent PCI FATAL interrupts from |
162 | * the card: http://bugzilla.kernel.org/show_bug.cgi?id=13483 |
163 | */ |
164 | reg = pci_conf_read(pa->pa_pc, pa->pa_tag, 0x40); |
165 | if (reg & 0xff00) |
166 | pci_conf_write(pa->pa_pc, pa->pa_tag, 0x40, reg & ~0xff00); |
167 | |
168 | /* Change latency timer; default value yields poor results. */ |
169 | reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG); |
170 | reg &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT); |
171 | reg |= 168 << PCI_LATTIMER_SHIFT; |
172 | pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG, reg); |
173 | |
174 | /* Determine if bluetooth is also supported (combo chip.) */ |
175 | reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG); |
176 | subsysid = PCI_PRODUCT(reg); |
177 | if (subsysid == PCI_SUBSYSID_ATHEROS_COEX3WIRE_SA || |
178 | subsysid == PCI_SUBSYSID_ATHEROS_COEX3WIRE_DA) |
179 | sc->sc_flags |= ATHN_FLAG_BTCOEX3WIRE; |
180 | else if (subsysid == PCI_SUBSYSID_ATHEROS_COEX2WIRE) |
181 | sc->sc_flags |= ATHN_FLAG_BTCOEX2WIRE; |
182 | |
183 | /* |
184 | * Setup memory-mapping of PCI registers. |
185 | */ |
186 | memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, ATHN_PCI_MMBA); |
187 | if (memtype != PCI_MAPREG_TYPE_MEM && |
188 | memtype != PCI_MAPREG_MEM_TYPE_64BIT) { |
189 | aprint_error_dev(self, "bad pci register type %d\n" , |
190 | (int)memtype); |
191 | goto fail; |
192 | } |
193 | error = pci_mapreg_map(pa, ATHN_PCI_MMBA, memtype, 0, &psc->psc_iot, |
194 | &psc->psc_ioh, NULL, &psc->psc_mapsz); |
195 | if (error != 0) { |
196 | aprint_error_dev(self, "cannot map register space\n" ); |
197 | goto fail; |
198 | } |
199 | |
200 | /* |
201 | * Arrange interrupt line. |
202 | */ |
203 | if (pci_intr_map(pa, &psc->psc_pih) != 0) { |
204 | aprint_error_dev(self, "couldn't map interrupt\n" ); |
205 | goto fail1; |
206 | } |
207 | |
208 | intrstr = pci_intr_string(psc->psc_pc, psc->psc_pih, intrbuf, sizeof(intrbuf)); |
209 | psc->psc_ih = pci_intr_establish(psc->psc_pc, psc->psc_pih, IPL_NET, |
210 | athn_intr, sc); |
211 | if (psc->psc_ih == NULL) { |
212 | aprint_error_dev(self, "couldn't map interrupt\n" ); |
213 | goto fail1; |
214 | } |
215 | |
216 | ic->ic_ifp = &sc->sc_if; |
217 | if (athn_attach(sc) != 0) |
218 | goto fail2; |
219 | |
220 | aprint_verbose_dev(self, "interrupting at %s\n" , intrstr); |
221 | |
222 | if (pmf_device_register(self, athn_pci_suspend, athn_pci_resume)) { |
223 | pmf_class_network_register(self, &sc->sc_if); |
224 | pmf_device_suspend(self, &sc->sc_qual); |
225 | } |
226 | else |
227 | aprint_error_dev(self, "couldn't establish power handler\n" ); |
228 | |
229 | ieee80211_announce(ic); |
230 | return; |
231 | |
232 | fail2: |
233 | pci_intr_disestablish(psc->psc_pc, psc->psc_ih); |
234 | psc->psc_ih = NULL; |
235 | fail1: |
236 | bus_space_unmap(psc->psc_iot, psc->psc_ioh, psc->psc_mapsz); |
237 | psc->psc_mapsz = 0; |
238 | fail: |
239 | return; |
240 | } |
241 | |
242 | Static int |
243 | athn_pci_detach(device_t self, int flags) |
244 | { |
245 | struct athn_pci_softc *psc = device_private(self); |
246 | struct athn_softc *sc = &psc->psc_sc; |
247 | |
248 | if (psc->psc_ih != NULL) { |
249 | athn_detach(sc); |
250 | pci_intr_disestablish(psc->psc_pc, psc->psc_ih); |
251 | psc->psc_ih = NULL; |
252 | } |
253 | if (psc->psc_mapsz > 0) { |
254 | bus_space_unmap(psc->psc_iot, psc->psc_ioh, psc->psc_mapsz); |
255 | psc->psc_mapsz = 0; |
256 | } |
257 | return 0; |
258 | } |
259 | |
260 | Static int |
261 | athn_pci_activate(device_t self, enum devact act) |
262 | { |
263 | struct athn_pci_softc *psc = device_private(self); |
264 | struct athn_softc *sc = &psc->psc_sc; |
265 | |
266 | switch (act) { |
267 | case DVACT_DEACTIVATE: |
268 | if_deactivate(sc->sc_ic.ic_ifp); |
269 | break; |
270 | } |
271 | return 0; |
272 | } |
273 | |
274 | Static bool |
275 | athn_pci_suspend(device_t self, const pmf_qual_t *qual) |
276 | { |
277 | struct athn_pci_softc *psc = device_private(self); |
278 | struct athn_softc *sc = &psc->psc_sc; |
279 | |
280 | athn_suspend(sc); |
281 | if (psc->psc_ih != NULL) { |
282 | pci_intr_disestablish(psc->psc_pc, psc->psc_ih); |
283 | psc->psc_ih = NULL; |
284 | } |
285 | return true; |
286 | } |
287 | |
288 | Static bool |
289 | athn_pci_resume(device_t self, const pmf_qual_t *qual) |
290 | { |
291 | struct athn_pci_softc *psc = device_private(self); |
292 | struct athn_softc *sc = &psc->psc_sc; |
293 | pcireg_t reg; |
294 | |
295 | /* |
296 | * XXX: see comment in athn_attach(). |
297 | */ |
298 | reg = pci_conf_read(psc->psc_pc, psc->psc_tag, 0x40); |
299 | if (reg & 0xff00) |
300 | pci_conf_write(psc->psc_pc, psc->psc_tag, 0x40, reg & ~0xff00); |
301 | |
302 | psc->psc_ih = pci_intr_establish(psc->psc_pc, psc->psc_pih, IPL_NET, |
303 | athn_intr, sc); |
304 | if (psc->psc_ih == NULL) { |
305 | aprint_error_dev(self, "couldn't map interrupt\n" ); |
306 | return false; |
307 | } |
308 | return athn_resume(sc); |
309 | } |
310 | |
311 | Static uint32_t |
312 | athn_pci_read(struct athn_softc *sc, uint32_t addr) |
313 | { |
314 | struct athn_pci_softc *psc = (struct athn_pci_softc *)sc; |
315 | |
316 | return bus_space_read_4(psc->psc_iot, psc->psc_ioh, addr); |
317 | } |
318 | |
319 | Static void |
320 | athn_pci_write(struct athn_softc *sc, uint32_t addr, uint32_t val) |
321 | { |
322 | struct athn_pci_softc *psc = (struct athn_pci_softc *)sc; |
323 | |
324 | bus_space_write_4(psc->psc_iot, psc->psc_ioh, addr, val); |
325 | } |
326 | |
327 | Static void |
328 | athn_pci_write_barrier(struct athn_softc *sc) |
329 | { |
330 | struct athn_pci_softc *psc = (struct athn_pci_softc *)sc; |
331 | |
332 | bus_space_barrier(psc->psc_iot, psc->psc_ioh, 0, psc->psc_mapsz, |
333 | BUS_SPACE_BARRIER_WRITE); |
334 | } |
335 | |
336 | Static void |
337 | athn_pci_disable_aspm(struct athn_softc *sc) |
338 | { |
339 | struct athn_pci_softc *psc = (struct athn_pci_softc *)sc; |
340 | pcireg_t reg; |
341 | |
342 | /* Disable PCIe Active State Power Management (ASPM). */ |
343 | reg = pci_conf_read(psc->psc_pc, psc->psc_tag, |
344 | psc->psc_cap_off + PCIE_LCSR); |
345 | reg &= ~(PCIE_LCSR_ASPM_L0S | PCIE_LCSR_ASPM_L1); |
346 | pci_conf_write(psc->psc_pc, psc->psc_tag, |
347 | psc->psc_cap_off + PCIE_LCSR, reg); |
348 | } |
349 | |