1 /* $NetBSD: if_rtw_cardbus.c,v 1.32 2009/10/19 23:19:39 rmind Exp $ */
4 * Copyright (c) 2004, 2005 David Young. All rights reserved.
6 * Adapted for the RTL8180 by David Young.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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
31 * Copyright (c) 1999, 2000 The NetBSD Foundation, Inc.
32 * All rights reserved.
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.
38 * Redistribution and use in source and binary forms, with or without
39 * modification, are permitted provided that the following conditions
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.
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.
61 * Cardbus front-end for the Realtek RTL8180 802.11 MAC/BBP driver.
63 * TBD factor with atw, tlp Cardbus front-ends?
66 #include <sys/cdefs.h>
67 __KERNEL_RCSID(0, "$NetBSD: if_rtw_cardbus.c,v 1.32 2009/10/19 23:19:39 rmind Exp $");
72 #include <sys/param.h>
73 #include <sys/systm.h>
75 #include <sys/malloc.h>
76 #include <sys/kernel.h>
77 #include <sys/socket.h>
78 #include <sys/ioctl.h>
79 #include <sys/errno.h>
80 #include <sys/device.h>
82 #include <machine/endian.h>
85 #include <net/if_dl.h>
86 #include <net/if_media.h>
87 #include <net/if_ether.h>
89 #include <net80211/ieee80211_netbsd.h>
90 #include <net80211/ieee80211_radiotap.h>
91 #include <net80211/ieee80211_var.h>
98 #include <netinet/in.h>
99 #include <netinet/if_inarp.h>
104 #include <sys/intr.h>
106 #include <dev/ic/rtwreg.h>
107 #include <dev/ic/rtwvar.h>
109 #include <dev/pci/pcivar.h>
110 #include <dev/pci/pcireg.h>
111 #include <dev/pci/pcidevs.h>
113 #include <dev/cardbus/cardbusvar.h>
114 #include <dev/pci/pcidevs.h>
117 * PCI configuration space registers used by the RTL8180.
119 #define RTW_PCI_IOBA 0x10 /* i/o mapped base */
120 #define RTW_PCI_MMBA 0x14 /* memory mapped base */
122 struct rtw_cardbus_softc
{
123 struct rtw_softc sc_rtw
; /* real RTL8180 softc */
125 /* CardBus-specific goo. */
126 void *sc_ih
; /* interrupt handle */
127 cardbus_devfunc_t sc_ct
; /* our CardBus devfuncs */
128 cardbustag_t sc_tag
; /* our CardBus tag */
129 int sc_csr
; /* CSR bits */
130 bus_size_t sc_mapsize
; /* size of the mapped bus space
134 int sc_bar_reg
; /* which BAR to use */
135 pcireg_t sc_bar_val
; /* value of the BAR */
137 cardbus_intr_line_t sc_intrline
; /* interrupt line */
140 int rtw_cardbus_match(device_t
, cfdata_t
, void *);
141 void rtw_cardbus_attach(device_t
, device_t
, void *);
142 int rtw_cardbus_detach(device_t
, int);
144 CFATTACH_DECL_NEW(rtw_cardbus
, sizeof(struct rtw_cardbus_softc
),
145 rtw_cardbus_match
, rtw_cardbus_attach
, rtw_cardbus_detach
, NULL
);
147 void rtw_cardbus_setup(struct rtw_cardbus_softc
*);
149 bool rtw_cardbus_resume(device_t
, pmf_qual_t
);
150 bool rtw_cardbus_suspend(device_t
, pmf_qual_t
);
152 const struct rtw_cardbus_product
*rtw_cardbus_lookup(
153 const struct cardbus_attach_args
*);
155 const struct rtw_cardbus_product
{
156 u_int32_t rcp_vendor
; /* PCI vendor ID */
157 u_int32_t rcp_product
; /* PCI product ID */
158 const char *rcp_product_name
;
159 } rtw_cardbus_products
[] = {
160 { PCI_VENDOR_REALTEK
, PCI_PRODUCT_REALTEK_RT8180
,
161 "Realtek RTL8180 802.11 MAC/BBP" },
163 { PCI_VENDOR_BELKIN
, PCI_PRODUCT_BELKIN_F5D6020V3
,
164 "Belkin F5D6020v3 802.11b (RTL8180 MAC/BBP)" },
166 { PCI_VENDOR_DLINK
, PCI_PRODUCT_DLINK_DWL610
,
167 "DWL-610 D-Link Air 802.11b (RTL8180 MAC/BBP)" },
172 const struct rtw_cardbus_product
*
173 rtw_cardbus_lookup(const struct cardbus_attach_args
*ca
)
175 const struct rtw_cardbus_product
*rcp
;
177 for (rcp
= rtw_cardbus_products
; rcp
->rcp_product_name
!= NULL
; rcp
++) {
178 if (PCI_VENDOR(ca
->ca_id
) == rcp
->rcp_vendor
&&
179 PCI_PRODUCT(ca
->ca_id
) == rcp
->rcp_product
)
186 rtw_cardbus_match(device_t parent
, cfdata_t match
, void *aux
)
188 struct cardbus_attach_args
*ca
= aux
;
190 if (rtw_cardbus_lookup(ca
) != NULL
)
197 rtw_cardbus_funcregen(struct rtw_regs
*regs
, int enable
)
200 rtw_config0123_enable(regs
, 1);
201 reg
= RTW_READ(regs
, RTW_CONFIG3
);
203 RTW_WRITE(regs
, RTW_CONFIG3
, reg
| RTW_CONFIG3_FUNCREGEN
);
205 RTW_WRITE(regs
, RTW_CONFIG3
, reg
& ~RTW_CONFIG3_FUNCREGEN
);
206 rtw_config0123_enable(regs
, 0);
210 rtw_cardbus_attach(device_t parent
, device_t self
, void *aux
)
212 struct rtw_cardbus_softc
*csc
= device_private(self
);
213 struct rtw_softc
*sc
= &csc
->sc_rtw
;
214 struct rtw_regs
*regs
= &sc
->sc_regs
;
215 struct cardbus_attach_args
*ca
= aux
;
216 cardbus_devfunc_t ct
= ca
->ca_ct
;
217 const struct rtw_cardbus_product
*rcp
;
222 sc
->sc_dmat
= ca
->ca_dmat
;
224 csc
->sc_tag
= ca
->ca_tag
;
226 rcp
= rtw_cardbus_lookup(ca
);
229 panic("rtw_cardbus_attach: impossible");
232 /* Get revision info. */
233 rev
= PCI_REVISION(ca
->ca_class
);
235 printf(": %s\n", rcp
->rcp_product_name
);
237 RTW_DPRINTF(RTW_DEBUG_ATTACH
,
238 ("%s: pass %d.%d signature %08x\n", device_xname(self
),
239 (rev
>> 4) & 0xf, rev
& 0xf,
240 cardbus_conf_read(ct
->ct_cc
, ct
->ct_cf
, csc
->sc_tag
, 0x80)));
245 csc
->sc_csr
= CARDBUS_COMMAND_MASTER_ENABLE
|
246 CARDBUS_COMMAND_PARITY_ENABLE
|
247 CARDBUS_COMMAND_SERR_ENABLE
;
248 if (Cardbus_mapreg_map(ct
, RTW_PCI_MMBA
, CARDBUS_MAPREG_TYPE_MEM
, 0,
249 ®s
->r_bt
, ®s
->r_bh
, &adr
, ®s
->r_sz
) == 0) {
250 RTW_DPRINTF(RTW_DEBUG_ATTACH
,
251 ("%s: %s mapped %" PRIuMAX
" bytes mem space\n",
252 device_xname(self
), __func__
, (uintmax_t)regs
->r_sz
));
255 (*ct
->ct_cf
->cardbus_mem_open
)(cc
, 0, adr
, adr
+csc
->sc_mapsize
);
257 csc
->sc_csr
|= CARDBUS_COMMAND_MEM_ENABLE
;
258 csc
->sc_bar_reg
= RTW_PCI_MMBA
;
259 csc
->sc_bar_val
= adr
| CARDBUS_MAPREG_TYPE_MEM
;
260 } else if (Cardbus_mapreg_map(ct
, RTW_PCI_IOBA
, CARDBUS_MAPREG_TYPE_IO
,
261 0, ®s
->r_bt
, ®s
->r_bh
, &adr
, ®s
->r_sz
) == 0) {
262 RTW_DPRINTF(RTW_DEBUG_ATTACH
,
263 ("%s: %s mapped %" PRIuMAX
" bytes I/O space\n",
264 device_xname(self
), __func__
, (uintmax_t)regs
->r_sz
));
267 (*ct
->ct_cf
->cardbus_io_open
)(cc
, 0, adr
, adr
+csc
->sc_mapsize
);
269 csc
->sc_csr
|= CARDBUS_COMMAND_IO_ENABLE
;
270 csc
->sc_bar_reg
= RTW_PCI_IOBA
;
271 csc
->sc_bar_val
= adr
| CARDBUS_MAPREG_TYPE_IO
;
273 aprint_error_dev(self
, "unable to map device registers\n");
278 * Bring the chip out of powersave mode and initialize the
279 * configuration registers.
281 rtw_cardbus_setup(csc
);
283 /* Remember which interrupt line. */
284 csc
->sc_intrline
= ca
->ca_intrline
;
287 * Finish off the attach.
291 rtw_cardbus_funcregen(regs
, 1);
293 RTW_WRITE(regs
, RTW_FEMR
, 0);
294 RTW_WRITE(regs
, RTW_FER
, RTW_READ(regs
, RTW_FER
));
296 if (pmf_device_register(self
,
297 rtw_cardbus_suspend
, rtw_cardbus_resume
)) {
298 pmf_class_network_register(self
, &sc
->sc_if
);
300 * Power down the socket.
302 pmf_device_suspend(self
, &sc
->sc_qual
);
304 aprint_error_dev(self
, "couldn't establish power handler\n");
308 rtw_cardbus_detach(device_t self
, int flags
)
310 struct rtw_cardbus_softc
*csc
= device_private(self
);
311 struct rtw_softc
*sc
= &csc
->sc_rtw
;
312 struct rtw_regs
*regs
= &sc
->sc_regs
;
313 struct cardbus_devfunc
*ct
= csc
->sc_ct
;
316 #if defined(DIAGNOSTIC)
318 panic("%s: data structure lacks", device_xname(self
));
321 if ((rc
= rtw_detach(sc
)) != 0)
325 * Unhook the interrupt handler.
327 if (csc
->sc_ih
!= NULL
)
328 cardbus_intr_disestablish(ct
->ct_cc
, ct
->ct_cf
, csc
->sc_ih
);
331 * Release bus space and close window.
333 if (csc
->sc_bar_reg
!= 0)
334 Cardbus_mapreg_unmap(ct
, csc
->sc_bar_reg
,
335 regs
->r_bt
, regs
->r_bh
, regs
->r_sz
);
341 rtw_cardbus_resume(device_t self
, pmf_qual_t qual
)
343 struct rtw_cardbus_softc
*csc
= device_private(self
);
344 struct rtw_softc
*sc
= &csc
->sc_rtw
;
345 cardbus_devfunc_t ct
= csc
->sc_ct
;
346 cardbus_chipset_tag_t cc
= ct
->ct_cc
;
347 cardbus_function_tag_t cf
= ct
->ct_cf
;
350 * Map and establish the interrupt.
352 csc
->sc_ih
= cardbus_intr_establish(cc
, cf
, csc
->sc_intrline
, IPL_NET
,
354 if (csc
->sc_ih
== NULL
) {
355 aprint_error_dev(sc
->sc_dev
,
356 "unable to establish interrupt\n");
360 rtw_cardbus_funcregen(&sc
->sc_regs
, 1);
362 RTW_WRITE(&sc
->sc_regs
, RTW_FEMR
, RTW_FEMR_INTR
);
363 RTW_WRITE(&sc
->sc_regs
, RTW_FER
, RTW_FER_INTR
);
365 return rtw_resume(self
, qual
);
369 rtw_cardbus_suspend(device_t self
, pmf_qual_t qual
)
371 struct rtw_cardbus_softc
*csc
= device_private(self
);
372 struct rtw_softc
*sc
= &csc
->sc_rtw
;
373 cardbus_devfunc_t ct
= csc
->sc_ct
;
374 cardbus_chipset_tag_t cc
= ct
->ct_cc
;
375 cardbus_function_tag_t cf
= ct
->ct_cf
;
377 if (!rtw_suspend(self
, qual
))
380 RTW_WRITE(&sc
->sc_regs
, RTW_FEMR
,
381 RTW_READ(&sc
->sc_regs
, RTW_FEMR
) & ~RTW_FEMR_INTR
);
383 rtw_cardbus_funcregen(&sc
->sc_regs
, 0);
385 /* Unhook the interrupt handler. */
386 cardbus_intr_disestablish(cc
, cf
, csc
->sc_ih
);
392 rtw_cardbus_setup(struct rtw_cardbus_softc
*csc
)
394 cardbustag_t tag
= csc
->sc_tag
;
395 cardbus_devfunc_t ct
= csc
->sc_ct
;
396 cardbus_chipset_tag_t cc
= ct
->ct_cc
;
397 cardbusreg_t bhlc
, csr
, lattimer
;
398 cardbus_function_tag_t cf
= ct
->ct_cf
;
400 (void)cardbus_set_powerstate(ct
, tag
, PCI_PWR_D0
);
402 /* I believe the datasheet tries to warn us that the RTL8180
403 * wants for 16 (0x10) to divide the latency timer.
405 bhlc
= cardbus_conf_read(cc
, cf
, tag
, CARDBUS_BHLC_REG
);
406 lattimer
= rounddown(PCI_LATTIMER(bhlc
), 0x10);
407 if (PCI_LATTIMER(bhlc
) != lattimer
) {
408 bhlc
&= ~(PCI_LATTIMER_MASK
<< PCI_LATTIMER_SHIFT
);
409 bhlc
|= (lattimer
<< PCI_LATTIMER_SHIFT
);
410 cardbus_conf_write(cc
, cf
, tag
, CARDBUS_BHLC_REG
, bhlc
);
413 /* Program the BAR. */
414 cardbus_conf_write(cc
, cf
, tag
, csc
->sc_bar_reg
, csc
->sc_bar_val
);
416 /* Enable the appropriate bits in the PCI CSR. */
417 csr
= cardbus_conf_read(cc
, cf
, tag
, PCI_COMMAND_STATUS_REG
);
418 csr
&= ~(PCI_COMMAND_IO_ENABLE
|PCI_COMMAND_MEM_ENABLE
);
420 cardbus_conf_write(cc
, cf
, tag
, PCI_COMMAND_STATUS_REG
, csr
);