xref: /openbsd/sys/dev/pci/if_athn_pci.c (revision cca36db2)
1 /*	$OpenBSD: if_athn_pci.c,v 1.11 2011/01/08 10:02:32 damien Exp $	*/
2 
3 /*-
4  * Copyright (c) 2009 Damien Bergamini <damien.bergamini@free.fr>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /*
20  * PCI front-end for Atheros 802.11a/g/n chipsets.
21  */
22 
23 #include "bpfilter.h"
24 
25 #include <sys/param.h>
26 #include <sys/sockio.h>
27 #include <sys/mbuf.h>
28 #include <sys/kernel.h>
29 #include <sys/socket.h>
30 #include <sys/systm.h>
31 #include <sys/malloc.h>
32 #include <sys/timeout.h>
33 #include <sys/device.h>
34 #include <sys/workq.h>
35 
36 #include <machine/bus.h>
37 #include <machine/intr.h>
38 
39 #include <net/if.h>
40 #include <net/if_dl.h>
41 #include <net/if_media.h>
42 
43 #include <netinet/in.h>
44 #include <netinet/if_ether.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 struct athn_pci_softc {
62 	struct athn_softc	sc_sc;
63 
64 	/* PCI specific goo. */
65 	pci_chipset_tag_t	sc_pc;
66 	pcitag_t		sc_tag;
67 	void			*sc_ih;
68 	bus_space_tag_t		sc_st;
69 	bus_space_handle_t	sc_sh;
70 	bus_size_t		sc_mapsize;
71 	int			sc_cap_off;
72 	struct workq_task	sc_resume_wqt;
73 };
74 
75 int		athn_pci_match(struct device *, void *, void *);
76 void		athn_pci_attach(struct device *, struct device *, void *);
77 int		athn_pci_detach(struct device *, int);
78 int		athn_pci_activate(struct device *, int);
79 void		athn_pci_resume(void *, void *);
80 uint32_t	athn_pci_read(struct athn_softc *, uint32_t);
81 void		athn_pci_write(struct athn_softc *, uint32_t, uint32_t);
82 void		athn_pci_write_barrier(struct athn_softc *);
83 void		athn_pci_disable_aspm(struct athn_softc *);
84 
85 struct cfattach athn_pci_ca = {
86 	sizeof (struct athn_pci_softc),
87 	athn_pci_match,
88 	athn_pci_attach,
89 	athn_pci_detach,
90 	athn_pci_activate
91 };
92 
93 static const struct pci_matchid athn_pci_devices[] = {
94 	{ PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR5416 },
95 	{ PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR5418 },
96 	{ PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9160 },
97 	{ PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9280 },
98 	{ PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9281 },
99 	{ PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9285 },
100 	{ PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR2427 },
101 	{ PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9227 },
102 	{ PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9287 },
103 	{ PCI_VENDOR_ATHEROS, PCI_PRODUCT_ATHEROS_AR9300 }
104 };
105 
106 int
107 athn_pci_match(struct device *parent, void *match, void *aux)
108 {
109 	return (pci_matchbyid(aux, athn_pci_devices,
110 	    nitems(athn_pci_devices)));
111 }
112 
113 void
114 athn_pci_attach(struct device *parent, struct device *self, void *aux)
115 {
116 	struct athn_pci_softc *psc = (struct athn_pci_softc *)self;
117 	struct athn_softc *sc = &psc->sc_sc;
118 	struct pci_attach_args *pa = aux;
119 	const char *intrstr;
120 	pci_intr_handle_t ih;
121 	pcireg_t memtype, reg;
122 	pci_product_id_t subsysid;
123 	int error;
124 
125 	sc->sc_dmat = pa->pa_dmat;
126 	psc->sc_pc = pa->pa_pc;
127 	psc->sc_tag = pa->pa_tag;
128 
129 	sc->ops.read = athn_pci_read;
130 	sc->ops.write = athn_pci_write;
131 	sc->ops.write_barrier = athn_pci_write_barrier;
132 
133 	/*
134 	 * Get the offset of the PCI Express Capability Structure in PCI
135 	 * Configuration Space (Linux hardcodes it as 0x60.)
136 	 */
137 	error = pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_PCIEXPRESS,
138 	    &psc->sc_cap_off, NULL);
139 	if (error != 0) {	/* Found. */
140 		sc->sc_disable_aspm = athn_pci_disable_aspm;
141 		sc->flags |= ATHN_FLAG_PCIE;
142 	}
143 	/*
144 	 * Noone knows why this shit is necessary but there are claims that
145 	 * not doing this may cause very frequent PCI FATAL interrupts from
146 	 * the card: http://bugzilla.kernel.org/show_bug.cgi?id=13483
147 	 */
148 	reg = pci_conf_read(pa->pa_pc, pa->pa_tag, 0x40);
149 	if (reg & 0xff00)
150 		pci_conf_write(pa->pa_pc, pa->pa_tag, 0x40, reg & ~0xff00);
151 
152 	/* Change latency timer; default value yields poor results. */
153 	reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG);
154 	reg &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT);
155 	reg |= 168 << PCI_LATTIMER_SHIFT;
156 	pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG, reg);
157 
158 	/* Determine if bluetooth is also supported (combo chip.) */
159 	reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG);
160 	subsysid = PCI_PRODUCT(reg);
161 	if (subsysid == PCI_SUBSYSID_ATHEROS_COEX3WIRE_SA ||
162 	    subsysid == PCI_SUBSYSID_ATHEROS_COEX3WIRE_DA)
163 		sc->flags |= ATHN_FLAG_BTCOEX3WIRE;
164 	else if (subsysid == PCI_SUBSYSID_ATHEROS_COEX2WIRE)
165 		sc->flags |= ATHN_FLAG_BTCOEX2WIRE;
166 
167 	/* Map control/status registers. */
168 	memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, PCI_MAPREG_START);
169 	error = pci_mapreg_map(pa, PCI_MAPREG_START, memtype, 0, &psc->sc_st,
170 	    &psc->sc_sh, NULL, &psc->sc_mapsize, 0);
171 	if (error != 0) {
172 		printf(": can't map mem space\n");
173 		return;
174 	}
175 
176 	if (pci_intr_map(pa, &ih) != 0) {
177 		printf(": can't map interrupt\n");
178 		return;
179 	}
180 	intrstr = pci_intr_string(psc->sc_pc, ih);
181 	psc->sc_ih = pci_intr_establish(psc->sc_pc, ih, IPL_NET,
182 	    athn_intr, sc, sc->sc_dev.dv_xname);
183 	if (psc->sc_ih == NULL) {
184 		printf(": can't establish interrupt");
185 		if (intrstr != NULL)
186 			printf(" at %s", intrstr);
187 		printf("\n");
188 		return;
189 	}
190 	printf(": %s\n", intrstr);
191 
192 	athn_attach(sc);
193 }
194 
195 int
196 athn_pci_detach(struct device *self, int flags)
197 {
198 	struct athn_pci_softc *psc = (struct athn_pci_softc *)self;
199 	struct athn_softc *sc = &psc->sc_sc;
200 
201 	if (psc->sc_ih != NULL) {
202 		athn_detach(sc);
203 		pci_intr_disestablish(psc->sc_pc, psc->sc_ih);
204 	}
205 	if (psc->sc_mapsize > 0)
206 		bus_space_unmap(psc->sc_st, psc->sc_sh, psc->sc_mapsize);
207 
208 	return (0);
209 }
210 
211 int
212 athn_pci_activate(struct device *self, int act)
213 {
214 	struct athn_pci_softc *psc = (struct athn_pci_softc *)self;
215 	struct athn_softc *sc = &psc->sc_sc;
216 
217 	switch (act) {
218 	case DVACT_SUSPEND:
219 		athn_suspend(sc);
220 		break;
221 	case DVACT_RESUME:
222 		workq_queue_task(NULL, &psc->sc_resume_wqt, 0,
223 		    athn_pci_resume, psc, NULL);
224 		break;
225 	}
226 
227 	return (0);
228 }
229 
230 void
231 athn_pci_resume(void *arg1, void *arg2)
232 {
233 	struct athn_pci_softc *psc = arg1;
234 	struct athn_softc *sc = &psc->sc_sc;
235 	pcireg_t reg;
236 	int s;
237 
238 	reg = pci_conf_read(psc->sc_pc, psc->sc_tag, 0x40);
239 	if (reg & 0xff00)
240 		pci_conf_write(psc->sc_pc, psc->sc_tag, 0x40, reg & ~0xff00);
241 
242 	s = splnet();
243 	athn_resume(sc);
244 	splx(s);
245 }
246 
247 uint32_t
248 athn_pci_read(struct athn_softc *sc, uint32_t addr)
249 {
250 	struct athn_pci_softc *psc = (struct athn_pci_softc *)sc;
251 
252 	return (bus_space_read_4(psc->sc_st, psc->sc_sh, addr));
253 }
254 
255 void
256 athn_pci_write(struct athn_softc *sc, uint32_t addr, uint32_t val)
257 {
258 	struct athn_pci_softc *psc = (struct athn_pci_softc *)sc;
259 
260 	bus_space_write_4(psc->sc_st, psc->sc_sh, addr, val);
261 }
262 
263 void
264 athn_pci_write_barrier(struct athn_softc *sc)
265 {
266 	struct athn_pci_softc *psc = (struct athn_pci_softc *)sc;
267 
268 	bus_space_barrier(psc->sc_st, psc->sc_sh, 0, psc->sc_mapsize,
269 	    BUS_SPACE_BARRIER_WRITE);
270 }
271 
272 void
273 athn_pci_disable_aspm(struct athn_softc *sc)
274 {
275 	struct athn_pci_softc *psc = (struct athn_pci_softc *)sc;
276 	pcireg_t reg;
277 
278 	/* Disable PCIe Active State Power Management (ASPM). */
279 	reg = pci_conf_read(psc->sc_pc, psc->sc_tag,
280 	    psc->sc_cap_off + PCI_PCIE_LCSR);
281 	reg &= ~(PCI_PCIE_LCSR_ASPM_L0S | PCI_PCIE_LCSR_ASPM_L1);
282 	pci_conf_write(psc->sc_pc, psc->sc_tag,
283 	    psc->sc_cap_off + PCI_PCIE_LCSR, reg);
284 }
285