From 30b88baa5b4a771a19cd3d99cb335b5b6f8cdd95 Mon Sep 17 00:00:00 2001 From: Jonathan Gray Date: Wed, 29 Dec 2004 01:02:32 +0000 Subject: Driver for Realtek 802.11 devices from NetBSD. Not yet working. --- sys/dev/cardbus/files.cardbus | 7 +- sys/dev/cardbus/if_rtw_cardbus.c | 510 ++++++ sys/dev/ic/max2820reg.h | 190 ++ sys/dev/ic/rtw.c | 3611 ++++++++++++++++++++++++++++++++++++++ sys/dev/ic/rtwphy.c | 651 +++++++ sys/dev/ic/rtwphy.h | 10 + sys/dev/ic/rtwphyio.c | 363 ++++ sys/dev/ic/rtwphyio.h | 41 + sys/dev/ic/rtwreg.h | 1100 ++++++++++++ sys/dev/ic/rtwvar.h | 468 +++++ sys/dev/ic/sa2400reg.h | 263 +++ sys/dev/pci/files.pci | 6 +- sys/dev/pci/if_rtw_pci.c | 306 ++++ 13 files changed, 7524 insertions(+), 2 deletions(-) create mode 100644 sys/dev/cardbus/if_rtw_cardbus.c create mode 100644 sys/dev/ic/max2820reg.h create mode 100644 sys/dev/ic/rtw.c create mode 100644 sys/dev/ic/rtwphy.c create mode 100644 sys/dev/ic/rtwphy.h create mode 100644 sys/dev/ic/rtwphyio.c create mode 100644 sys/dev/ic/rtwphyio.h create mode 100644 sys/dev/ic/rtwreg.h create mode 100644 sys/dev/ic/rtwvar.h create mode 100644 sys/dev/ic/sa2400reg.h create mode 100644 sys/dev/pci/if_rtw_pci.c (limited to 'sys/dev') diff --git a/sys/dev/cardbus/files.cardbus b/sys/dev/cardbus/files.cardbus index 0e81f33461c..4cd3678c26d 100644 --- a/sys/dev/cardbus/files.cardbus +++ b/sys/dev/cardbus/files.cardbus @@ -1,4 +1,4 @@ -# $OpenBSD: files.cardbus,v 1.11 2004/12/07 05:42:41 dlg Exp $ +# $OpenBSD: files.cardbus,v 1.12 2004/12/29 01:02:30 jsg Exp $ # $NetBSD: files.cardbus,v 1.8 2000/01/26 06:37:24 thorpej Exp $ # # files.cardbus @@ -61,6 +61,11 @@ file dev/cardbus/if_ath_cardbus.c ath_cardbus attach atw at cardbus with atw_cardbus file dev/cardbus/if_atw_cardbus.c atw_cardbus +# Realtek RTL8180 +# +attach rtw at cardbus with rtw_cardbus +file dev/cardbus/if_rtw_cardbus.c rtw_cardbus + # # EHCI USB controller # diff --git a/sys/dev/cardbus/if_rtw_cardbus.c b/sys/dev/cardbus/if_rtw_cardbus.c new file mode 100644 index 00000000000..ed073de8823 --- /dev/null +++ b/sys/dev/cardbus/if_rtw_cardbus.c @@ -0,0 +1,510 @@ +/* $OpenBSD: if_rtw_cardbus.c,v 1.1 2004/12/29 01:02:30 jsg Exp $ */ +/* $NetBSD: if_rtw_cardbus.c,v 1.4 2004/12/20 21:05:34 dyoung Exp $ */ + +/*- + * Copyright (c) 2004, 2005 David Young. All rights reserved. + * + * Adapted for the RTL8180 by David Young. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of David Young may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David + * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ +/*- + * Copyright (c) 1999, 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Cardbus front-end for the Realtek RTL8180 802.11 MAC/BBP driver. + * + * TBD factor with atw, tlp Cardbus front-ends? + */ + +#include + +#include "bpfilter.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#ifdef INET +#include +#include +#endif + +#include +#include +#include + +#if NBPFILTER > 0 +#include +#endif + +#ifdef NS +#include +#include +#endif + +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +/* + * PCI configuration space registers used by the RTL8180. + */ +#define RTW_PCI_IOBA 0x10 /* i/o mapped base */ +#define RTW_PCI_MMBA 0x14 /* memory mapped base */ + +struct rtw_cardbus_softc { + struct rtw_softc sc_rtw; /* real RTL8180 softc */ + + /* CardBus-specific goo. */ + void *sc_ih; /* interrupt handle */ + cardbus_devfunc_t sc_ct; /* our CardBus devfuncs */ + cardbustag_t sc_tag; /* our CardBus tag */ + int sc_csr; /* CSR bits */ + bus_size_t sc_mapsize; /* size of the mapped bus space + * region + */ + + int sc_cben; /* CardBus enables */ + int sc_bar_reg; /* which BAR to use */ + pcireg_t sc_bar_val; /* value of the BAR */ + + int sc_intrline; /* interrupt line */ +}; + +int rtw_cardbus_match(struct device *, void *, void *); +void rtw_cardbus_attach(struct device *, struct device *, void *); +int rtw_cardbus_detach(struct device *, int); +void rtw_cardbus_intr_ack(struct rtw_regs *); +void rtw_cardbus_funcregen(struct rtw_regs *, int); + +struct cfattach rtw_cardbus_ca = { + sizeof(struct rtw_cardbus_softc), rtw_cardbus_match, rtw_cardbus_attach, + rtw_cardbus_detach +}; + +void rtw_cardbus_setup(struct rtw_cardbus_softc *); + +int rtw_cardbus_enable(struct rtw_softc *); +void rtw_cardbus_disable(struct rtw_softc *); +void rtw_cardbus_power(struct rtw_softc *, int); + +const struct rtw_cardbus_product *rtw_cardbus_lookup( + const struct cardbus_attach_args *); + +const struct rtw_cardbus_product { + u_int32_t rcp_vendor; /* PCI vendor ID */ + u_int32_t rcp_product; /* PCI product ID */ + const char *rcp_product_name; +} rtw_cardbus_products[] = { + { PCI_VENDOR_REALTEK, PCI_PRODUCT_REALTEK_RT8180, + "Realtek RTL8180 802.11 MAC/BBP" }, + + { PCI_VENDOR_BELKIN2, PCI_PRODUCT_BELKIN2_F5D6020V3, + "Belkin F5D6020v3 802.11b (RTL8180 MAC/BBP)" }, + + { 0, 0, NULL }, +}; + +const struct rtw_cardbus_product * +rtw_cardbus_lookup(const struct cardbus_attach_args *ca) +{ + const struct rtw_cardbus_product *rcp; + + for (rcp = rtw_cardbus_products; + rcp->rcp_product_name != NULL; + rcp++) { + if (PCI_VENDOR(ca->ca_id) == rcp->rcp_vendor && + PCI_PRODUCT(ca->ca_id) == rcp->rcp_product) + return (rcp); + } + return (NULL); +} + +int +rtw_cardbus_match(struct device *parent, void *match, void *aux) +{ + struct cardbus_attach_args *ca = aux; + + if (rtw_cardbus_lookup(ca) != NULL) + return (1); + + return (0); +} + +void +rtw_cardbus_intr_ack(struct rtw_regs *regs) +{ + RTW_WRITE(regs, RTW_FER, RTW_FER_INTR); +} + +void +rtw_cardbus_funcregen(struct rtw_regs *regs, int enable) +{ + u_int32_t reg; + rtw_config0123_enable(regs, 1); + reg = RTW_READ(regs, RTW_CONFIG3); + if (enable) { + RTW_WRITE(regs, RTW_CONFIG3, reg | RTW_CONFIG3_FUNCREGEN); + } else { + RTW_WRITE(regs, RTW_CONFIG3, reg & ~RTW_CONFIG3_FUNCREGEN); + } + rtw_config0123_enable(regs, 0); +} + +void +rtw_cardbus_attach(struct device *parent, struct device *self, void *aux) +{ + struct rtw_cardbus_softc *csc = (void *)self; + struct rtw_softc *sc = &csc->sc_rtw; + struct rtw_regs *regs = &sc->sc_regs; + struct cardbus_attach_args *ca = aux; + cardbus_devfunc_t ct = ca->ca_ct; + const struct rtw_cardbus_product *rcp; + bus_addr_t adr; + int rev; + + sc->sc_dmat = ca->ca_dmat; + csc->sc_ct = ct; + csc->sc_tag = ca->ca_tag; + + rcp = rtw_cardbus_lookup(ca); + if (rcp == NULL) { + printf("\n"); + panic("rtw_cardbus_attach: impossible"); + } + + /* + * Power management hooks. + */ + sc->sc_enable = rtw_cardbus_enable; + sc->sc_disable = rtw_cardbus_disable; + sc->sc_power = rtw_cardbus_power; + + sc->sc_intr_ack = rtw_cardbus_intr_ack; + + /* Get revision info. */ + rev = PCI_REVISION(ca->ca_class); + + RTW_DPRINTF(RTW_DEBUG_ATTACH, + ("%s: pass %d.%d signature %08x\n", sc->sc_dev.dv_xname, + (rev >> 4) & 0xf, rev & 0xf, + cardbus_conf_read(ct->ct_cc, ct->ct_cf, csc->sc_tag, 0x80))); + + /* + * Map the device. + */ + csc->sc_csr = CARDBUS_COMMAND_MASTER_ENABLE; + if (Cardbus_mapreg_map(ct, RTW_PCI_MMBA, + CARDBUS_MAPREG_TYPE_MEM, 0, ®s->r_bt, ®s->r_bh, &adr, + &csc->sc_mapsize) == 0) { + RTW_DPRINTF(RTW_DEBUG_ATTACH, + ("%s: %s mapped %lu bytes mem space\n", + sc->sc_dev.dv_xname, __func__, (long)csc->sc_mapsize)); +#if rbus +#else + (*ct->ct_cf->cardbus_mem_open)(cc, 0, adr, adr+csc->sc_mapsize); +#endif + csc->sc_cben = CARDBUS_MEM_ENABLE; + csc->sc_csr |= CARDBUS_COMMAND_MEM_ENABLE; + csc->sc_bar_reg = RTW_PCI_MMBA; + csc->sc_bar_val = adr | CARDBUS_MAPREG_TYPE_MEM; + } else if (Cardbus_mapreg_map(ct, RTW_PCI_IOBA, + CARDBUS_MAPREG_TYPE_IO, 0, ®s->r_bt, ®s->r_bh, &adr, + &csc->sc_mapsize) == 0) { + RTW_DPRINTF(RTW_DEBUG_ATTACH, + ("%s: %s mapped %lu bytes I/O space\n", + sc->sc_dev.dv_xname, __func__, (long)csc->sc_mapsize)); +#if rbus +#else + (*ct->ct_cf->cardbus_io_open)(cc, 0, adr, adr+csc->sc_mapsize); +#endif + csc->sc_cben = CARDBUS_IO_ENABLE; + csc->sc_csr |= CARDBUS_COMMAND_IO_ENABLE; + csc->sc_bar_reg = RTW_PCI_IOBA; + csc->sc_bar_val = adr | CARDBUS_MAPREG_TYPE_IO; + } else { + printf("%s: unable to map device registers\n", + sc->sc_dev.dv_xname); + return; + } + + /* + * Bring the chip out of powersave mode and initialize the + * configuration registers. + */ + rtw_cardbus_setup(csc); + + /* Remember which interrupt line. */ + csc->sc_intrline = ca->ca_intrline; + + printf("%s, irq %d\n", sc->sc_dev.dv_xname, + csc->sc_intrline); + /* + * Finish off the attach. + */ + rtw_attach(sc); + + rtw_cardbus_funcregen(regs, 1); + + RTW_WRITE(regs, RTW_FEMR, RTW_FEMR_INTR); + RTW_WRITE(regs, RTW_FER, RTW_FER_INTR); + + /* + * Power down the socket. + */ + Cardbus_function_disable(csc->sc_ct); +} + +int +rtw_cardbus_detach(struct device *self, int flags) +{ + struct rtw_cardbus_softc *csc = (void *)self; + struct rtw_softc *sc = &csc->sc_rtw; + struct rtw_regs *regs = &sc->sc_regs; + struct cardbus_devfunc *ct = csc->sc_ct; + int rv; + +#if defined(DIAGNOSTIC) + if (ct == NULL) + panic("%s: data structure lacks", sc->sc_dev.dv_xname); +#endif + + rv = rtw_detach(sc); + if (rv) + return (rv); + + rtw_cardbus_funcregen(regs, 0); + + /* + * Unhook the interrupt handler. + */ + if (csc->sc_ih != NULL) + cardbus_intr_disestablish(ct->ct_cc, ct->ct_cf, csc->sc_ih); + + /* + * Release bus space and close window. + */ + if (csc->sc_bar_reg != 0) + Cardbus_mapreg_unmap(ct, csc->sc_bar_reg, + regs->r_bt, regs->r_bh, csc->sc_mapsize); + + return (0); +} + +int +rtw_cardbus_enable(struct rtw_softc *sc) +{ + struct rtw_cardbus_softc *csc = (void *) sc; + cardbus_devfunc_t ct = csc->sc_ct; + cardbus_chipset_tag_t cc = ct->ct_cc; + cardbus_function_tag_t cf = ct->ct_cf; + + /* + * Power on the socket. + */ + Cardbus_function_enable(ct); + + /* + * Set up the PCI configuration registers. + */ + rtw_cardbus_setup(csc); + + /* + * Map and establish the interrupt. + */ + csc->sc_ih = cardbus_intr_establish(cc, cf, csc->sc_intrline, IPL_NET, + rtw_intr, sc); + if (csc->sc_ih == NULL) { + printf("%s: unable to establish interrupt at %d\n", + sc->sc_dev.dv_xname, csc->sc_intrline); + Cardbus_function_disable(csc->sc_ct); + return (1); + } + + rtw_cardbus_funcregen(&sc->sc_regs, 1); + + RTW_WRITE(&sc->sc_regs, RTW_FEMR, RTW_FEMR_INTR); + RTW_WRITE(&sc->sc_regs, RTW_FER, RTW_FER_INTR); + + return (0); +} + +void +rtw_cardbus_disable(struct rtw_softc *sc) +{ + struct rtw_cardbus_softc *csc = (void *) sc; + cardbus_devfunc_t ct = csc->sc_ct; + cardbus_chipset_tag_t cc = ct->ct_cc; + cardbus_function_tag_t cf = ct->ct_cf; + + RTW_WRITE(&sc->sc_regs, RTW_FEMR, + RTW_READ(&sc->sc_regs, RTW_FEMR) & ~RTW_FEMR_INTR); + + rtw_cardbus_funcregen(&sc->sc_regs, 0); + + /* Unhook the interrupt handler. */ + cardbus_intr_disestablish(cc, cf, csc->sc_ih); + csc->sc_ih = NULL; + + /* Power down the socket. */ + Cardbus_function_disable(ct); +} + +void +rtw_cardbus_power(struct rtw_softc *sc, int why) +{ + struct rtw_cardbus_softc *csc = (void *) sc; + + RTW_DPRINTF(RTW_DEBUG_ATTACH, + ("%s: rtw_cardbus_power\n", sc->sc_dev.dv_xname)); + + if (why == PWR_RESUME) { + /* + * Give the PCI configuration registers a kick + * in the head. + */ +#ifdef DIAGNOSTIC + if ((sc->sc_flags & RTW_F_ENABLED) == 0) + panic("rtw_cardbus_power"); +#endif + rtw_cardbus_setup(csc); + } +} + +void +rtw_cardbus_setup(struct rtw_cardbus_softc *csc) +{ + struct rtw_softc *sc = &csc->sc_rtw; + cardbus_devfunc_t ct = csc->sc_ct; + cardbus_chipset_tag_t cc = ct->ct_cc; + cardbus_function_tag_t cf = ct->ct_cf; + pcireg_t reg; + int pmreg; + + if (cardbus_get_capability(cc, cf, csc->sc_tag, + PCI_CAP_PWRMGMT, &pmreg, 0)) { + reg = cardbus_conf_read(cc, cf, csc->sc_tag, pmreg + 4) & 0x03; +#if 1 /* XXX Probably not right for CardBus. */ + if (reg == 3) { + /* + * The card has lost all configuration data in + * this state, so punt. + */ + printf("%s: unable to wake up from power state D3\n", + sc->sc_dev.dv_xname); + return; + } +#endif + if (reg != 0) { + printf("%s: waking up from power state D%d\n", + sc->sc_dev.dv_xname, reg); + cardbus_conf_write(cc, cf, csc->sc_tag, + pmreg + 4, 0); + } + } + + /* Program the BAR. */ + cardbus_conf_write(cc, cf, csc->sc_tag, csc->sc_bar_reg, + csc->sc_bar_val); + + /* Make sure the right access type is on the CardBus bridge. */ + (*ct->ct_cf->cardbus_ctrl)(cc, csc->sc_cben); + (*ct->ct_cf->cardbus_ctrl)(cc, CARDBUS_BM_ENABLE); + + /* Enable the appropriate bits in the PCI CSR. */ + reg = cardbus_conf_read(cc, cf, csc->sc_tag, + CARDBUS_COMMAND_STATUS_REG); + reg &= ~(CARDBUS_COMMAND_IO_ENABLE|CARDBUS_COMMAND_MEM_ENABLE); + reg |= csc->sc_csr; + cardbus_conf_write(cc, cf, csc->sc_tag, CARDBUS_COMMAND_STATUS_REG, + reg); + + /* + * Make sure the latency timer is set to some reasonable + * value. + */ + reg = cardbus_conf_read(cc, cf, csc->sc_tag, CARDBUS_BHLC_REG); + if (CARDBUS_LATTIMER(reg) < 0x20) { + reg &= ~(CARDBUS_LATTIMER_MASK << CARDBUS_LATTIMER_SHIFT); + reg |= (0x20 << CARDBUS_LATTIMER_SHIFT); + cardbus_conf_write(cc, cf, csc->sc_tag, CARDBUS_BHLC_REG, reg); + } +} diff --git a/sys/dev/ic/max2820reg.h b/sys/dev/ic/max2820reg.h new file mode 100644 index 00000000000..9be5b749d8d --- /dev/null +++ b/sys/dev/ic/max2820reg.h @@ -0,0 +1,190 @@ +/* $OpenBSD: max2820reg.h,v 1.1 2004/12/29 01:02:31 jsg Exp $ */ +/* $NetBSD: max2820reg.h,v 1.1 2004/09/26 02:29:15 dyoung Exp $ */ + +/* + * Copyright (c) 2004 David Young. All rights reserved. + * + * This code was written by David Young. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David + * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#ifndef _DEV_IC_MAX2820REG_H_ +#define _DEV_IC_MAX2820REG_H_ + +/* + * Serial bus format for Maxim MAX2820/MAX2820A/MAX2821/MAX2821A + * 2.4GHz 802.11b Zero-IF Transceivers + */ +#define MAX2820_TWI_ADDR_MASK BITS(15,12) +#define MAX2820_TWI_DATA_MASK BITS(11,0) + +/* + * Registers for Maxim MAX2820/MAX2820A/MAX2821/MAX2821A 2.4GHz + * 802.11b Zero-IF Transceivers + */ +#define MAX2820_TEST 0 /* Test Register */ +#define MAX2820_TEST_DEFAULT BITS(2,0) /* Always set to this value. */ + +#define MAX2820_ENABLE 1 /* Block-Enable Register */ +#define MAX2820_ENABLE_RSVD1 BIT(11) /* reserved */ +#define MAX2820_ENABLE_PAB BIT(10) /* Transmit Baseband Filters + * Enable + * PAB_EN = SHDNB && + * (MAX2820_ENABLE_PAB || + * TX_ON) + */ +#define MAX2820_ENABLE_TXFLT BIT(9) /* Transmit Baseband Filters + * Enable + * TXFLT_EN = SHDNB && + * (MAX2820_ENABLE_TXFLT || + * TX_ON) + */ +#define MAX2820_ENABLE_TXUVD BIT(8) /* Tx Upconverter, VGA, and + * Driver Amp Enable + * TXUVD_EN = SHDNB && + * (MAX2820_ENABLE_TXUVD || + * TX_ON) + */ +#define MAX2820_ENABLE_DET BIT(7) /* Receive Detector Enable + * DET_EN = SHDNB && + * (MAX2820_ENABLE_DET || + * RX_ON) + */ +#define MAX2820_ENABLE_RXDFA BIT(6) /* Rx Downconverter, Filters, + * and AGC Amps Enable + * RXDFA_EN = SHDNB && + * (MAX2820_ENABLE_RXDFA || + * RX_ON) + */ +#define MAX2820_ENABLE_RXLNA BIT(5) /* Receive LNA Enable + * AT_EN = SHDNB && + * (MAX2820_ENABLE_RXLNA || + * RX_ON) + */ +#define MAX2820_ENABLE_AT BIT(4) /* Auto-tuner Enable + * AT_EN = SHDNB && + * (MAX2820_ENABLE_AT || + * RX_ON || TX_ON) + */ +#define MAX2820_ENABLE_CP BIT(3) /* PLL Charge-Pump Enable + * CP_EN = SHDNB + * && MAX2820_ENABLE_CP + */ +#define MAX2820_ENABLE_PLL BIT(2) /* PLL Enable + * PLL_EN = SHDNB + * && MAX2820_ENABLE_PLL + */ +#define MAX2820_ENABLE_VCO BIT(1) /* VCO Enable + * VCO_EN = SHDNB + * && MAX2820_ENABLE_VCO + */ +#define MAX2820_ENABLE_RSVD0 BIT(0) /* reserved */ +#define MAX2820_ENABLE_DEFAULT (MAX2820_ENABLE_AT|MAX2820_ENABLE_CP|\ + MAX2820_ENABLE_PLL|MAX2820_ENABLE_VCO) + +#define MAX2820_SYNTH 2 /* Synthesizer Register */ +#define MAX2820_SYNTH_RSVD0 BITS(11,7) /* reserved */ +#define MAX2820_SYNTH_ICP BIT(6) /* Charge-Pump Current Select + * 0 = +/-1mA + * 1 = +/-2mA + */ +#define MAX2820_SYNTH_R_MASK BITS(5,0) /* Reference Frequency Divider + * 0 = 22MHz + * 1 = 44MHz + */ +#define MAX2820_SYNTH_R_22MHZ LSHIFT(0, MAX2820_SYNTH_R_MASK) +#define MAX2820_SYNTH_R_44MHZ LSHIFT(1, MAX2820_SYNTH_R_MASK) +#define MAX2820_SYNTH_ICP_DEFAULT MAX2820_SYNTH_ICP +#define MAX2820_SYNTH_R_DEFAULT LSHIFT(0, MAX2820_SYNTH_R_MASK) + +#define MAX2820_CHANNEL 3 /* Channel Frequency Register */ +#define MAX2820_CHANNEL_RSVD BITS(11,7) /* reserved */ +#define MAX2820_CHANNEL_CF_MASK BITS(6,0) /* Channel Frequency Select + * fLO = 2400MHz + CF * 1MHz + */ +#define MAX2820_CHANNEL_RSVD_DEFAULT LSHIFT(0, MAX2820_CHANNEL_RSVD) +#define MAX2820_CHANNEL_CF_DEFAULT LSHIFT(37, MAX2820_CHANNEL_CF_MASK) + +#define MAX2820_RECEIVE 4 /* Receiver Settings Register + * MAX2820/MAX2821 + */ +#define MAX2820_RECEIVE_2C_MASK BITS(11,9) /* VGA DC Offset Nulling + * Parameter 2 + */ +#define MAX2820_RECEIVE_1C_MASK BITS(8,6) /* VGA DC Offset Nulling + * Parameter 1 + */ +#define MAX2820_RECEIVE_DL_MASK BITS(5,4) /* Rx Level Detector Midpoint + * Select + * 11, 01 = 50.2mVp + * 10 = 70.9mVp + * 00 = 35.5mVp + */ +#define MAX2820_RECEIVE_SF BIT(3) /* Special Function Select + * 0 = OFF + * 1 = ON + */ +#define MAX2820_RECEIVE_BW_MASK BITS(2,0) /* Receive Filter -3dB Frequency + * Select (all frequencies are + * approximate) + */ +/* 8.5MHz */ +#define MAX2820_RECEIVE_BW_8_5MHZ LSHIFT(0, MAX2820_RECEIVE_BW_MASK) +#define MAX2820_RECEIVE_BW_8MHZ LSHIFT(1, MAX2820_RECEIVE_BW_MASK) +#define MAX2820_RECEIVE_BW_7_5MHZ LSHIFT(2, MAX2820_RECEIVE_BW_MASK) +#define MAX2820_RECEIVE_BW_7MHZ LSHIFT(3, MAX2820_RECEIVE_BW_MASK) +#define MAX2820_RECEIVE_BW_6_5MHZ LSHIFT(4, MAX2820_RECEIVE_BW_MASK) +#define MAX2820_RECEIVE_BW_6MHZ LSHIFT(5, MAX2820_RECEIVE_BW_MASK) +#define MAX2820_RECEIVE_2C_DEFAULT LSHIFT(7, MAX2820_RECEIVE_2C_MASK) +#define MAX2820_RECEIVE_1C_DEFAULT LSHIFT(7, MAX2820_RECEIVE_1C_MASK) +#define MAX2820_RECEIVE_DL_DEFAULT LSHIFT(1, MAX2820_RECEIVE_DL_MASK) +#define MAX2820_RECEIVE_SF_DEFAULT LSHIFT(0, MAX2820_RECEIVE_SF) +#define MAX2820_RECEIVE_BW_DEFAULT MAX2820_RECEIVE_BW_7_5MHZ + +#define MAX2820A_RECEIVE 4 /* Receiver Settings Register, + * MAX2820A/MAX2821A + */ +/* VGA DC Offset Nulling Parameter 2 */ +#define MAX2820A_RECEIVE_2C_MASK BITS(11,9) +#define MAX2820A_RECEIVE_2C_DEFAULT LSHIFT(7, MAX2820A_RECEIVE_2C_MASK) +/* VGA DC Offset Nulling Parameter 1 */ +#define MAX2820A_RECEIVE_1C_MASK BITS(8,6) +#define MAX2820A_RECEIVE_1C_DEFAULT LSHIFT(7, MAX2820A_RECEIVE_1C_MASK) +#define MAX2820A_RECEIVE_RSVD0_MASK BITS(5,3) +#define MAX2820A_RECEIVE_RSVD0_DEFAULT LSHIFT(2, MAX2820A_RECEIVE_RSVD0_MASK) +#define MAX2820A_RECEIVE_RSVD1_MASK BITS(2,0) +#define MAX2820A_RECEIVE_RSVD1_DEFAULT LSHIFT(2,MAX2820_RECEIVE_RSVD1_MASK) + +#define MAX2820_TRANSMIT 5 /* Transmitter Settings Reg. */ +#define MAX2820_TRANSMIT_RSVD_MASK BITS(11,4) /* reserved */ +#define MAX2820_TRANSMIT_PA_MASK BITS(3,0) /* PA Bias Select + * 15 = Highest + * 0 = Lowest + */ +#define MAX2820_TRANSMIT_PA_DEFAULT LSHIFT(0, MAX2820_TRANSMIT_PA_MASK) + +#endif /* _DEV_IC_MAX2820REG_H_ */ diff --git a/sys/dev/ic/rtw.c b/sys/dev/ic/rtw.c new file mode 100644 index 00000000000..dcd99ae6d3c --- /dev/null +++ b/sys/dev/ic/rtw.c @@ -0,0 +1,3611 @@ +/* $OpenBSD: rtw.c,v 1.1 2004/12/29 01:02:31 jsg Exp $ */ +/* $NetBSD: rtw.c,v 1.29 2004/12/27 19:49:16 dyoung Exp $ */ +/*- + * Copyright (c) 2004, 2005 David Young. All rights reserved. + * + * Programmed for NetBSD by David Young. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of David Young may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David + * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ +/* + * Device driver for the Realtek RTL8180 802.11 MAC/BBP. + */ + +#include +#include "bpfilter.h" + +#include +#include +#include +#include +#include +#if 0 +#include +#include +#endif +#include +#include +#include +#include + +#include +#include +#include /* splnet */ + +#include + +#include +#include + +#if NBPFILTER > 0 +#include +#endif + +#ifdef INET +#include +#include +#endif + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define KASSERT2(__cond, __msg) \ + do { \ + if (!(__cond)) \ + panic __msg ; \ + } while (0) + +int rtw_rfprog_fallback = 0; +int rtw_host_rfio = 0; +int rtw_flush_rfio = 1; +int rtw_rfio_delay = 0; + +#ifdef RTW_DEBUG +int rtw_debug = 0; +#endif /* RTW_DEBUG */ + +#define NEXT_ATTACH_STATE(sc, state) do { \ + DPRINTF(sc, RTW_DEBUG_ATTACH, \ + ("%s: attach state %s\n", __func__, #state)); \ + sc->sc_attach_state = state; \ +} while (0) + +int rtw_dwelltime = 1000; /* milliseconds */ + +void rtw_start(struct ifnet *); +void rtw_srom_defaults(struct rtw_srom *, u_int32_t *, u_int8_t *, + enum rtw_rfchipid *, u_int32_t *); +void rtw_txdesc_blk_init_all(struct rtw_txdesc_blk *); +void rtw_txctl_blk_init_all(struct rtw_txctl_blk *); +void rtw_txdescs_sync(bus_dma_tag_t, bus_dmamap_t , struct rtw_txdesc_blk *, + u_int, u_int, int); +void rtw_txdescs_sync_all(bus_dma_tag_t, bus_dmamap_t, + struct rtw_txdesc_blk *); +void rtw_rxbufs_release(bus_dma_tag_t, struct rtw_rxctl *); +void rtw_rxdesc_init_all(bus_dma_tag_t, bus_dmamap_t, + struct rtw_rxdesc *, struct rtw_rxctl *, int); +void rtw_io_enable(struct rtw_regs *, u_int8_t, int); +void rtw_intr_rx(struct rtw_softc *, u_int16_t); +void rtw_intr_beacon(struct rtw_softc *, u_int16_t); +void rtw_intr_atim(struct rtw_softc *); +void rtw_transmit_config(struct rtw_regs *); +void rtw_pktfilt_load(struct rtw_softc *); +void rtw_start(struct ifnet *); +void rtw_watchdog(struct ifnet *); +void rtw_start_beacon(struct rtw_softc *, int); +void rtw_next_scan(void *); +void rtw_recv_mgmt(struct ieee80211com *, struct mbuf *, + struct ieee80211_node *, int, int, u_int32_t); +struct ieee80211_node * rtw_node_alloc(struct ieee80211com *); +void rtw_node_free(struct ieee80211com *, struct ieee80211_node *); +void rtw_media_status(struct ifnet *, struct ifmediareq *); +void rtw_txctl_blk_cleanup_all(struct rtw_softc *); +void rtw_txdesc_blk_setup(struct rtw_txdesc_blk *, struct rtw_txdesc *, + u_int, bus_addr_t, bus_addr_t); +void rtw_txdesc_blk_setup_all(struct rtw_softc *); +void rtw_intr_tx(struct rtw_softc *, u_int16_t); +void rtw_intr_ioerror(struct rtw_softc *, u_int16_t); +void rtw_intr_timeout(struct rtw_softc *); +void rtw_stop(struct ifnet *, int); +void rtw_maxim_pwrstate(struct rtw_regs *, enum rtw_pwrstate, int, int); +void rtw_philips_pwrstate(struct rtw_regs *, enum rtw_pwrstate, int, int); +void rtw_pwrstate0(struct rtw_softc *, enum rtw_pwrstate, int, int); +void rtw_recv_beacon(struct rtw_softc *, struct mbuf *, + struct ieee80211_node *, int, int, u_int32_t); +void rtw_join_bss(struct rtw_softc *, uint8_t *, enum ieee80211_opmode, + uint16_t); +void rtw_set_access1(struct rtw_regs *, enum rtw_access, enum rtw_access); +int rtw_srom_parse(struct rtw_srom *, u_int32_t *, u_int8_t *, + enum rtw_rfchipid *, u_int32_t *, enum rtw_locale *, const char *); +int rtw_srom_read(struct rtw_regs *, u_int32_t, struct rtw_srom *, + const char *); +void rtw_set_rfprog(struct rtw_regs *, enum rtw_rfchipid, + const char *); +u_int8_t rtw_chan2txpower(struct rtw_srom *, struct ieee80211com *, + struct ieee80211_channel *); +int rtw_txctl_blk_init(struct rtw_txctl_blk *); +int rtw_rxctl_init_all(bus_dma_tag_t, struct rtw_rxctl *, u_int *, + const char *); +void rtw_txbuf_release(bus_dma_tag_t, struct ieee80211com *, + struct rtw_txctl *); +void rtw_txbufs_release(bus_dma_tag_t, bus_dmamap_t, + struct ieee80211com *, struct rtw_txctl_blk *); +void rtw_hwring_setup(struct rtw_softc *); +void rtw_swring_setup(struct rtw_softc *); +void rtw_txdesc_blk_reset(struct rtw_txdesc_blk *); +void rtw_txdescs_reset(struct rtw_softc *); +void rtw_rxdescs_reset(struct rtw_softc *); +void rtw_rfmd_pwrstate(struct rtw_regs *, enum rtw_pwrstate, int, int); +int rtw_pwrstate(struct rtw_softc *, enum rtw_pwrstate); +int rtw_tune(struct rtw_softc *); +void rtw_set_nettype(struct rtw_softc *, enum ieee80211_opmode); +int rtw_init(struct ifnet *); +int rtw_ioctl(struct ifnet *, u_long, caddr_t); +int rtw_seg_too_short(bus_dmamap_t); +struct mbuf * rtw_dmamap_load_txbuf(bus_dma_tag_t, bus_dmamap_t, struct mbuf *, + u_int, short *, const char *); +int rtw_newstate(struct ieee80211com *, enum ieee80211_state, int); +int rtw_media_change(struct ifnet *); +int rtw_txctl_blk_setup_all(struct rtw_softc *); +struct rtw_rf * rtw_rf_attach(struct rtw_softc *, enum rtw_rfchipid, + rtw_rf_write_t, int); +u_int8_t rtw_check_phydelay(struct rtw_regs *, u_int32_t); + +#ifdef RTW_DEBUG +void rtw_print_txdesc(struct rtw_softc *, const char *, + struct rtw_txctl *, struct rtw_txdesc_blk *, int); +const char * rtw_access_string(enum rtw_access); +void rtw_dump_rings(struct rtw_softc *); +void rtw_print_txdesc(struct rtw_softc *, const char *, + struct rtw_txctl *, struct rtw_txdesc_blk *, int); +void rtw_print_regs(struct rtw_regs *, const char *, const char *); +#endif + +#ifdef RTW_DEBUG +void +rtw_print_regs(struct rtw_regs *regs, const char *dvname, const char *where) +{ +#define PRINTREG32(sc, reg) \ + RTW_DPRINTF(RTW_DEBUG_REGDUMP, \ + ("%s: reg[ " #reg " / %03x ] = %08x\n", \ + dvname, reg, RTW_READ(regs, reg))) + +#define PRINTREG16(sc, reg) \ + RTW_DPRINTF(RTW_DEBUG_REGDUMP, \ + ("%s: reg[ " #reg " / %03x ] = %04x\n", \ + dvname, reg, RTW_READ16(regs, reg))) + +#define PRINTREG8(sc, reg) \ + RTW_DPRINTF(RTW_DEBUG_REGDUMP, \ + ("%s: reg[ " #reg " / %03x ] = %02x\n", \ + dvname, reg, RTW_READ8(regs, reg))) + + RTW_DPRINTF(RTW_DEBUG_REGDUMP, ("%s: %s\n", dvname, where)); + + PRINTREG32(regs, RTW_IDR0); + PRINTREG32(regs, RTW_IDR1); + PRINTREG32(regs, RTW_MAR0); + PRINTREG32(regs, RTW_MAR1); + PRINTREG32(regs, RTW_TSFTRL); + PRINTREG32(regs, RTW_TSFTRH); + PRINTREG32(regs, RTW_TLPDA); + PRINTREG32(regs, RTW_TNPDA); + PRINTREG32(regs, RTW_THPDA); + PRINTREG32(regs, RTW_TCR); + PRINTREG32(regs, RTW_RCR); + PRINTREG32(regs, RTW_TINT); + PRINTREG32(regs, RTW_TBDA); + PRINTREG32(regs, RTW_ANAPARM); + PRINTREG32(regs, RTW_BB); + PRINTREG32(regs, RTW_PHYCFG); + PRINTREG32(regs, RTW_WAKEUP0L); + PRINTREG32(regs, RTW_WAKEUP0H); + PRINTREG32(regs, RTW_WAKEUP1L); + PRINTREG32(regs, RTW_WAKEUP1H); + PRINTREG32(regs, RTW_WAKEUP2LL); + PRINTREG32(regs, RTW_WAKEUP2LH); + PRINTREG32(regs, RTW_WAKEUP2HL); + PRINTREG32(regs, RTW_WAKEUP2HH); + PRINTREG32(regs, RTW_WAKEUP3LL); + PRINTREG32(regs, RTW_WAKEUP3LH); + PRINTREG32(regs, RTW_WAKEUP3HL); + PRINTREG32(regs, RTW_WAKEUP3HH); + PRINTREG32(regs, RTW_WAKEUP4LL); + PRINTREG32(regs, RTW_WAKEUP4LH); + PRINTREG32(regs, RTW_WAKEUP4HL); + PRINTREG32(regs, RTW_WAKEUP4HH); + PRINTREG32(regs, RTW_DK0); + PRINTREG32(regs, RTW_DK1); + PRINTREG32(regs, RTW_DK2); + PRINTREG32(regs, RTW_DK3); + PRINTREG32(regs, RTW_RETRYCTR); + PRINTREG32(regs, RTW_RDSAR); + PRINTREG32(regs, RTW_FER); + PRINTREG32(regs, RTW_FEMR); + PRINTREG32(regs, RTW_FPSR); + PRINTREG32(regs, RTW_FFER); + + /* 16-bit registers */ + PRINTREG16(regs, RTW_BRSR); + PRINTREG16(regs, RTW_IMR); + PRINTREG16(regs, RTW_ISR); + PRINTREG16(regs, RTW_BCNITV); + PRINTREG16(regs, RTW_ATIMWND); + PRINTREG16(regs, RTW_BINTRITV); + PRINTREG16(regs, RTW_ATIMTRITV); + PRINTREG16(regs, RTW_CRC16ERR); + PRINTREG16(regs, RTW_CRC0); + PRINTREG16(regs, RTW_CRC1); + PRINTREG16(regs, RTW_CRC2); + PRINTREG16(regs, RTW_CRC3); + PRINTREG16(regs, RTW_CRC4); + PRINTREG16(regs, RTW_CWR); + + /* 8-bit registers */ + PRINTREG8(regs, RTW_CR); + PRINTREG8(regs, RTW_9346CR); + PRINTREG8(regs, RTW_CONFIG0); + PRINTREG8(regs, RTW_CONFIG1); + PRINTREG8(regs, RTW_CONFIG2); + PRINTREG8(regs, RTW_MSR); + PRINTREG8(regs, RTW_CONFIG3); + PRINTREG8(regs, RTW_CONFIG4); + PRINTREG8(regs, RTW_TESTR); + PRINTREG8(regs, RTW_PSR); + PRINTREG8(regs, RTW_SCR); + PRINTREG8(regs, RTW_PHYDELAY); + PRINTREG8(regs, RTW_CRCOUNT); + PRINTREG8(regs, RTW_PHYADDR); + PRINTREG8(regs, RTW_PHYDATAW); + PRINTREG8(regs, RTW_PHYDATAR); + PRINTREG8(regs, RTW_CONFIG5); + PRINTREG8(regs, RTW_TPPOLL); + + PRINTREG16(regs, RTW_BSSID16); + PRINTREG32(regs, RTW_BSSID32); +#undef PRINTREG32 +#undef PRINTREG16 +#undef PRINTREG8 +} +#endif /* RTW_DEBUG */ + + +void +rtw_continuous_tx_enable(struct rtw_softc *sc, int enable) +{ + struct rtw_regs *regs = &sc->sc_regs; + + u_int32_t tcr; + tcr = RTW_READ(regs, RTW_TCR); + tcr &= ~RTW_TCR_LBK_MASK; + if (enable) + tcr |= RTW_TCR_LBK_CONT; + else + tcr |= RTW_TCR_LBK_NORMAL; + RTW_WRITE(regs, RTW_TCR, tcr); + RTW_SYNC(regs, RTW_TCR, RTW_TCR); + rtw_set_access(sc, RTW_ACCESS_ANAPARM); + rtw_txdac_enable(sc, !enable); + rtw_set_access(sc, RTW_ACCESS_ANAPARM); /* XXX Voodoo from Linux. */ + rtw_set_access(sc, RTW_ACCESS_NONE); +} + +#ifdef RTW_DEBUG +const char * +rtw_access_string(enum rtw_access access) +{ + switch (access) { + case RTW_ACCESS_NONE: + return "none"; + case RTW_ACCESS_CONFIG: + return "config"; + case RTW_ACCESS_ANAPARM: + return "anaparm"; + default: + return "unknown"; + } +} +#endif + +void +rtw_set_access1(struct rtw_regs *regs, + enum rtw_access oaccess, enum rtw_access naccess) +{ + KASSERT(naccess >= RTW_ACCESS_NONE && naccess <= RTW_ACCESS_ANAPARM); + KASSERT(oaccess >= RTW_ACCESS_NONE && oaccess <= RTW_ACCESS_ANAPARM); + + if (naccess == oaccess) + return; + + switch (naccess) { + case RTW_ACCESS_NONE: + switch (oaccess) { + case RTW_ACCESS_ANAPARM: + rtw_anaparm_enable(regs, 0); + /*FALLTHROUGH*/ + case RTW_ACCESS_CONFIG: + rtw_config0123_enable(regs, 0); + /*FALLTHROUGH*/ + case RTW_ACCESS_NONE: + break; + } + break; + case RTW_ACCESS_CONFIG: + switch (oaccess) { + case RTW_ACCESS_NONE: + rtw_config0123_enable(regs, 1); + /*FALLTHROUGH*/ + case RTW_ACCESS_CONFIG: + break; + case RTW_ACCESS_ANAPARM: + rtw_anaparm_enable(regs, 0); + break; + } + break; + case RTW_ACCESS_ANAPARM: + switch (oaccess) { + case RTW_ACCESS_NONE: + rtw_config0123_enable(regs, 1); + /*FALLTHROUGH*/ + case RTW_ACCESS_CONFIG: + rtw_anaparm_enable(regs, 1); + /*FALLTHROUGH*/ + case RTW_ACCESS_ANAPARM: + break; + } + break; + } +} + +void +rtw_set_access(struct rtw_softc *sc, enum rtw_access access) +{ + rtw_set_access1(&sc->sc_regs, sc->sc_access, access); + RTW_DPRINTF(RTW_DEBUG_ACCESS, + ("%s: access %s -> %s\n", sc->sc_dev.dv_xname, + rtw_access_string(sc->sc_access), + rtw_access_string(access))); + sc->sc_access = access; +} + +/* + * Enable registers, switch register banks. + */ +void +rtw_config0123_enable(struct rtw_regs *regs, int enable) +{ + u_int8_t ecr; + ecr = RTW_READ8(regs, RTW_9346CR); + ecr &= ~(RTW_9346CR_EEM_MASK | RTW_9346CR_EECS | RTW_9346CR_EESK); + if (enable) + ecr |= RTW_9346CR_EEM_CONFIG; + else { + RTW_WBW(regs, RTW_9346CR, MAX(RTW_CONFIG0, RTW_CONFIG3)); + ecr |= RTW_9346CR_EEM_NORMAL; + } + RTW_WRITE8(regs, RTW_9346CR, ecr); + RTW_SYNC(regs, RTW_9346CR, RTW_9346CR); +} + +/* requires rtw_config0123_enable(, 1) */ +void +rtw_anaparm_enable(struct rtw_regs *regs, int enable) +{ + u_int8_t cfg3; + + cfg3 = RTW_READ8(regs, RTW_CONFIG3); + cfg3 |= RTW_CONFIG3_CLKRUNEN; + if (enable) + cfg3 |= RTW_CONFIG3_PARMEN; + else + cfg3 &= ~RTW_CONFIG3_PARMEN; + RTW_WRITE8(regs, RTW_CONFIG3, cfg3); + RTW_SYNC(regs, RTW_CONFIG3, RTW_CONFIG3); +} + +/* requires rtw_anaparm_enable(, 1) */ +void +rtw_txdac_enable(struct rtw_softc *sc, int enable) +{ + u_int32_t anaparm; + struct rtw_regs *regs = &sc->sc_regs; + + anaparm = RTW_READ(regs, RTW_ANAPARM); + if (enable) + anaparm &= ~RTW_ANAPARM_TXDACOFF; + else + anaparm |= RTW_ANAPARM_TXDACOFF; + RTW_WRITE(regs, RTW_ANAPARM, anaparm); + RTW_SYNC(regs, RTW_ANAPARM, RTW_ANAPARM); +} + +static __inline int +rtw_chip_reset1(struct rtw_regs *regs, const char *dvname) +{ + u_int8_t cr; + int i; + + RTW_WRITE8(regs, RTW_CR, RTW_CR_RST); + + RTW_WBR(regs, RTW_CR, RTW_CR); + + for (i = 0; i < 1000; i++) { + if ((cr = RTW_READ8(regs, RTW_CR) & RTW_CR_RST) == 0) { + RTW_DPRINTF(RTW_DEBUG_RESET, + ("%s: reset in %dus\n", dvname, i)); + return 0; + } + RTW_RBR(regs, RTW_CR, RTW_CR); + DELAY(10); /* 10us */ + } + + printf("%s: reset failed\n", dvname); + return ETIMEDOUT; +} + +static __inline int +rtw_chip_reset(struct rtw_regs *regs, const char *dvname) +{ + uint32_t tcr; + + /* from Linux driver */ + tcr = RTW_TCR_CWMIN | RTW_TCR_MXDMA_2048 | + LSHIFT(7, RTW_TCR_SRL_MASK) | LSHIFT(7, RTW_TCR_LRL_MASK); + + RTW_WRITE(regs, RTW_TCR, tcr); + + RTW_WBW(regs, RTW_CR, RTW_TCR); + + return rtw_chip_reset1(regs, dvname); +} + +static __inline int +rtw_recall_eeprom(struct rtw_regs *regs, const char *dvname) +{ + int i; + u_int8_t ecr; + + ecr = RTW_READ8(regs, RTW_9346CR); + ecr = (ecr & ~RTW_9346CR_EEM_MASK) | RTW_9346CR_EEM_AUTOLOAD; + RTW_WRITE8(regs, RTW_9346CR, ecr); + + RTW_WBR(regs, RTW_9346CR, RTW_9346CR); + + /* wait 2.5ms for completion */ + for (i = 0; i < 25; i++) { + ecr = RTW_READ8(regs, RTW_9346CR); + if ((ecr & RTW_9346CR_EEM_MASK) == RTW_9346CR_EEM_NORMAL) { + RTW_DPRINTF(RTW_DEBUG_RESET, + ("%s: recall EEPROM in %dus\n", dvname, i * 100)); + return 0; + } + RTW_RBR(regs, RTW_9346CR, RTW_9346CR); + DELAY(100); + } + printf("%s: recall EEPROM failed\n", dvname); + return ETIMEDOUT; +} + +static __inline int +rtw_reset(struct rtw_softc *sc) +{ + int rc; + uint8_t config1; + + if ((rc = rtw_chip_reset(&sc->sc_regs, sc->sc_dev.dv_xname)) != 0) + return rc; + + if ((rc = rtw_recall_eeprom(&sc->sc_regs, sc->sc_dev.dv_xname)) != 0) + ; + + config1 = RTW_READ8(&sc->sc_regs, RTW_CONFIG1); + RTW_WRITE8(&sc->sc_regs, RTW_CONFIG1, config1 & ~RTW_CONFIG1_PMEN); + /* TBD turn off maximum power saving? */ + + return 0; +} + +static __inline int +rtw_txdesc_dmamaps_create(bus_dma_tag_t dmat, struct rtw_txctl *descs, + u_int ndescs) +{ + int i, rc = 0; + for (i = 0; i < ndescs; i++) { + rc = bus_dmamap_create(dmat, MCLBYTES, RTW_MAXPKTSEGS, MCLBYTES, + 0, 0, &descs[i].stx_dmamap); + if (rc != 0) + break; + } + return rc; +} + +static __inline int +rtw_rxdesc_dmamaps_create(bus_dma_tag_t dmat, struct rtw_rxctl *descs, + u_int ndescs) +{ + int i, rc = 0; + for (i = 0; i < ndescs; i++) { + rc = bus_dmamap_create(dmat, MCLBYTES, 1, MCLBYTES, 0, 0, + &descs[i].srx_dmamap); + if (rc != 0) + break; + } + return rc; +} + +static __inline void +rtw_rxctls_setup(struct rtw_rxctl *descs) +{ + int i; + for (i = 0; i < RTW_RXQLEN; i++) + descs[i].srx_mbuf = NULL; +} + +static __inline void +rtw_rxdesc_dmamaps_destroy(bus_dma_tag_t dmat, struct rtw_rxctl *descs, + u_int ndescs) +{ + int i; + for (i = 0; i < ndescs; i++) { + if (descs[i].srx_dmamap != NULL) + bus_dmamap_destroy(dmat, descs[i].srx_dmamap); + } +} + +static __inline void +rtw_txdesc_dmamaps_destroy(bus_dma_tag_t dmat, struct rtw_txctl *descs, + u_int ndescs) +{ + int i; + for (i = 0; i < ndescs; i++) { + if (descs[i].stx_dmamap != NULL) + bus_dmamap_destroy(dmat, descs[i].stx_dmamap); + } +} + +static __inline void +rtw_srom_free(struct rtw_srom *sr) +{ + sr->sr_size = 0; + if (sr->sr_content == NULL) + return; + free(sr->sr_content, M_DEVBUF); + sr->sr_content = NULL; +} + +void +rtw_srom_defaults(struct rtw_srom *sr, u_int32_t *flags, u_int8_t *cs_threshold, + enum rtw_rfchipid *rfchipid, u_int32_t *rcr) +{ + *flags |= (RTW_F_DIGPHY|RTW_F_ANTDIV); + *cs_threshold = RTW_SR_ENERGYDETTHR_DEFAULT; + *rcr |= RTW_RCR_ENCS1; + *rfchipid = RTW_RFCHIPID_PHILIPS; +} + +int +rtw_srom_parse(struct rtw_srom *sr, u_int32_t *flags, u_int8_t *cs_threshold, + enum rtw_rfchipid *rfchipid, u_int32_t *rcr, enum rtw_locale *locale, + const char *dvname) +{ + int i; + const char *rfname, *paname; + char scratch[sizeof("unknown 0xXX")]; + u_int16_t version; + u_int8_t mac[IEEE80211_ADDR_LEN]; + + *flags &= ~(RTW_F_DIGPHY|RTW_F_DFLANTB|RTW_F_ANTDIV); + *rcr &= ~(RTW_RCR_ENCS1 | RTW_RCR_ENCS2); + + version = RTW_SR_GET16(sr, RTW_SR_VERSION); + printf("%s: SROM version %d.%d", dvname, version >> 8, version & 0xff); + + if (version <= 0x0101) { + printf(" is not understood, limping along with defaults\n"); + rtw_srom_defaults(sr, flags, cs_threshold, rfchipid, rcr); + return 0; + } + printf("\n"); + + for (i = 0; i < IEEE80211_ADDR_LEN; i++) + mac[i] = RTW_SR_GET(sr, RTW_SR_MAC + i); + + RTW_DPRINTF(RTW_DEBUG_ATTACH, + ("%s: EEPROM MAC %s\n", dvname, ether_sprintf(mac))); + + *cs_threshold = RTW_SR_GET(sr, RTW_SR_ENERGYDETTHR); + + if ((RTW_SR_GET(sr, RTW_SR_CONFIG2) & RTW_CONFIG2_ANT) != 0) + *flags |= RTW_F_ANTDIV; + + /* Note well: the sense of the RTW_SR_RFPARM_DIGPHY bit seems + * to be reversed. + */ + if ((RTW_SR_GET(sr, RTW_SR_RFPARM) & RTW_SR_RFPARM_DIGPHY) == 0) + *flags |= RTW_F_DIGPHY; + if ((RTW_SR_GET(sr, RTW_SR_RFPARM) & RTW_SR_RFPARM_DFLANTB) != 0) + *flags |= RTW_F_DFLANTB; + + *rcr |= LSHIFT(MASK_AND_RSHIFT(RTW_SR_GET(sr, RTW_SR_RFPARM), + RTW_SR_RFPARM_CS_MASK), RTW_RCR_ENCS1); + + *rfchipid = RTW_SR_GET(sr, RTW_SR_RFCHIPID); + switch (*rfchipid) { + case RTW_RFCHIPID_GCT: /* this combo seen in the wild */ + rfname = "GCT GRF5101"; + paname = "Winspring WS9901"; + break; + case RTW_RFCHIPID_MAXIM: + rfname = "Maxim MAX2820"; /* guess */ + paname = "Maxim MAX2422"; /* guess */ + break; + case RTW_RFCHIPID_INTERSIL: + rfname = "Intersil HFA3873"; /* guess */ + paname = "Intersil "; + break; + case RTW_RFCHIPID_PHILIPS: /* this combo seen in the wild */ + rfname = "Philips SA2400A"; + paname = "Philips SA2411"; + break; + case RTW_RFCHIPID_RFMD: + /* this is the same front-end as an atw(4)! */ + rfname = "RFMD RF2948B, " /* mentioned in Realtek docs */ + "LNA: RFMD RF2494, " /* mentioned in Realtek docs */ + "SYN: Silicon Labs Si4126"; /* inferred from + * reference driver + */ + paname = "RFMD RF2189"; /* mentioned in Realtek docs */ + break; + case RTW_RFCHIPID_RESERVED: + rfname = paname = "reserved"; + break; + default: + snprintf(scratch, sizeof(scratch), "unknown 0x%02x", *rfchipid); + rfname = paname = scratch; + } + printf("%s: RF: %s, PA: %s\n", dvname, rfname, paname); + + switch (RTW_SR_GET(sr, RTW_SR_CONFIG0) & RTW_CONFIG0_GL_MASK) { + case RTW_CONFIG0_GL_USA: + *locale = RTW_LOCALE_USA; + break; + case RTW_CONFIG0_GL_EUROPE: + *locale = RTW_LOCALE_EUROPE; + break; + case RTW_CONFIG0_GL_JAPAN: + *locale = RTW_LOCALE_JAPAN; + break; + default: + *locale = RTW_LOCALE_UNKNOWN; + break; + } + return 0; +} + +/* Returns -1 on failure. */ +int +rtw_srom_read(struct rtw_regs *regs, u_int32_t flags, struct rtw_srom *sr, + const char *dvname) +{ + int rc; + struct seeprom_descriptor sd; + u_int8_t ecr; + + (void)memset(&sd, 0, sizeof(sd)); + + ecr = RTW_READ8(regs, RTW_9346CR); + + if ((flags & RTW_F_9356SROM) != 0) { + RTW_DPRINTF(RTW_DEBUG_ATTACH, ("%s: 93c56 SROM\n", dvname)); + sr->sr_size = 256; + sd.sd_chip = C56_66; + } else { + RTW_DPRINTF(RTW_DEBUG_ATTACH, ("%s: 93c46 SROM\n", dvname)); + sr->sr_size = 128; + sd.sd_chip = C46; + } + + ecr &= ~(RTW_9346CR_EEDI | RTW_9346CR_EEDO | RTW_9346CR_EESK | + RTW_9346CR_EEM_MASK); + ecr |= RTW_9346CR_EEM_PROGRAM; + + RTW_WRITE8(regs, RTW_9346CR, ecr); + + sr->sr_content = malloc(sr->sr_size, M_DEVBUF, M_NOWAIT); + + if (sr->sr_content == NULL) { + printf("%s: unable to allocate SROM buffer\n", dvname); + return ENOMEM; + } + + (void)memset(sr->sr_content, 0, sr->sr_size); + + /* RTL8180 has a single 8-bit register for controlling the + * 93cx6 SROM. There is no "ready" bit. The RTL8180 + * input/output sense is the reverse of read_seeprom's. + */ + sd.sd_tag = regs->r_bt; + sd.sd_bsh = regs->r_bh; + sd.sd_regsize = 1; + sd.sd_control_offset = RTW_9346CR; + sd.sd_status_offset = RTW_9346CR; + sd.sd_dataout_offset = RTW_9346CR; + sd.sd_CK = RTW_9346CR_EESK; + sd.sd_CS = RTW_9346CR_EECS; + sd.sd_DI = RTW_9346CR_EEDO; + sd.sd_DO = RTW_9346CR_EEDI; + /* make read_seeprom enter EEPROM read/write mode */ + sd.sd_MS = ecr; + sd.sd_RDY = 0; +#if 0 + sd.sd_clkdelay = 50; +#endif + + /* TBD bus barriers */ + if (!read_seeprom(&sd, sr->sr_content, 0, sr->sr_size/2)) { + printf("%s: could not read SROM\n", dvname); + free(sr->sr_content, M_DEVBUF); + sr->sr_content = NULL; + return -1; /* XXX */ + } + + /* end EEPROM read/write mode */ + RTW_WRITE8(regs, RTW_9346CR, + (ecr & ~RTW_9346CR_EEM_MASK) | RTW_9346CR_EEM_NORMAL); + RTW_WBRW(regs, RTW_9346CR, RTW_9346CR); + + if ((rc = rtw_recall_eeprom(regs, dvname)) != 0) + return rc; + +#ifdef RTW_DEBUG + { + int i; + RTW_DPRINTF(RTW_DEBUG_ATTACH, + ("\n%s: serial ROM:\n\t", dvname)); + for (i = 0; i < sr->sr_size/2; i++) { + if (((i % 8) == 0) && (i != 0)) + RTW_DPRINTF(RTW_DEBUG_ATTACH, ("\n\t")); + RTW_DPRINTF(RTW_DEBUG_ATTACH, + (" %04x", sr->sr_content[i])); + } + RTW_DPRINTF(RTW_DEBUG_ATTACH, ("\n")); + } +#endif /* RTW_DEBUG */ + return 0; +} + +void +rtw_set_rfprog(struct rtw_regs *regs, enum rtw_rfchipid rfchipid, + const char *dvname) +{ + u_int8_t cfg4; + const char *method; + + cfg4 = RTW_READ8(regs, RTW_CONFIG4) & ~RTW_CONFIG4_RFTYPE_MASK; + + switch (rfchipid) { + default: + cfg4 |= LSHIFT(rtw_rfprog_fallback, RTW_CONFIG4_RFTYPE_MASK); + method = "fallback"; + break; + case RTW_RFCHIPID_INTERSIL: + cfg4 |= RTW_CONFIG4_RFTYPE_INTERSIL; + method = "Intersil"; + break; + case RTW_RFCHIPID_PHILIPS: + cfg4 |= RTW_CONFIG4_RFTYPE_PHILIPS; + method = "Philips"; + break; + case RTW_RFCHIPID_RFMD: + cfg4 |= RTW_CONFIG4_RFTYPE_RFMD; + method = "RFMD"; + break; + } + + RTW_WRITE8(regs, RTW_CONFIG4, cfg4); + + RTW_WBR(regs, RTW_CONFIG4, RTW_CONFIG4); + + RTW_DPRINTF(RTW_DEBUG_INIT, + ("%s: %s RF programming method, %#02x\n", dvname, method, + RTW_READ8(regs, RTW_CONFIG4))); +} + +#if 0 +static __inline int +rtw_identify_rf(struct rtw_regs *regs, enum rtw_rftype *rftype, + const char *dvname) +{ + u_int8_t cfg4; + const char *name; + + cfg4 = RTW_READ8(regs, RTW_CONFIG4); + + switch (cfg4 & RTW_CONFIG4_RFTYPE_MASK) { + case RTW_CONFIG4_RFTYPE_PHILIPS: + *rftype = RTW_RFTYPE_PHILIPS; + name = "Philips"; + break; + case RTW_CONFIG4_RFTYPE_INTERSIL: + *rftype = RTW_RFTYPE_INTERSIL; + name = "Intersil"; + break; + case RTW_CONFIG4_RFTYPE_RFMD: + *rftype = RTW_RFTYPE_RFMD; + name = "RFMD"; + break; + default: + name = ""; + return ENXIO; + } + + printf("%s: RF prog type %s\n", dvname, name); + return 0; +} +#endif + +static __inline void +rtw_init_channels(enum rtw_locale locale, + struct ieee80211_channel (*chans)[IEEE80211_CHAN_MAX+1], + const char *dvname) +{ + int i; + const char *name = NULL; +#define ADD_CHANNEL(_chans, _chan) do { \ + (*_chans)[_chan].ic_flags = IEEE80211_CHAN_B; \ + (*_chans)[_chan].ic_freq = \ + ieee80211_ieee2mhz(_chan, (*_chans)[_chan].ic_flags);\ +} while (0) + + switch (locale) { + case RTW_LOCALE_USA: /* 1-11 */ + name = "USA"; + for (i = 1; i <= 11; i++) + ADD_CHANNEL(chans, i); + break; + case RTW_LOCALE_JAPAN: /* 1-14 */ + name = "Japan"; + ADD_CHANNEL(chans, 14); + for (i = 1; i <= 14; i++) + ADD_CHANNEL(chans, i); + break; + case RTW_LOCALE_EUROPE: /* 1-13 */ + name = "Europe"; + for (i = 1; i <= 13; i++) + ADD_CHANNEL(chans, i); + break; + default: /* 10-11 allowed by most countries */ + name = ""; + for (i = 10; i <= 11; i++) + ADD_CHANNEL(chans, i); + break; + } + printf("%s: Geographic Location %s\n", dvname, name); +#undef ADD_CHANNEL +} + +static __inline void +rtw_identify_country(struct rtw_regs *regs, enum rtw_locale *locale, + const char *dvname) +{ + u_int8_t cfg0 = RTW_READ8(regs, RTW_CONFIG0); + + switch (cfg0 & RTW_CONFIG0_GL_MASK) { + case RTW_CONFIG0_GL_USA: + *locale = RTW_LOCALE_USA; + break; + case RTW_CONFIG0_GL_JAPAN: + *locale = RTW_LOCALE_JAPAN; + break; + case RTW_CONFIG0_GL_EUROPE: + *locale = RTW_LOCALE_EUROPE; + break; + default: + *locale = RTW_LOCALE_UNKNOWN; + break; + } +} + +static __inline int +rtw_identify_sta(struct rtw_regs *regs, u_int8_t (*addr)[IEEE80211_ADDR_LEN], + const char *dvname) +{ + static const u_int8_t empty_macaddr[IEEE80211_ADDR_LEN] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + u_int32_t idr0 = RTW_READ(regs, RTW_IDR0), + idr1 = RTW_READ(regs, RTW_IDR1); + + (*addr)[0] = MASK_AND_RSHIFT(idr0, BITS(0, 7)); + (*addr)[1] = MASK_AND_RSHIFT(idr0, BITS(8, 15)); + (*addr)[2] = MASK_AND_RSHIFT(idr0, BITS(16, 23)); + (*addr)[3] = MASK_AND_RSHIFT(idr0, BITS(24 ,31)); + + (*addr)[4] = MASK_AND_RSHIFT(idr1, BITS(0, 7)); + (*addr)[5] = MASK_AND_RSHIFT(idr1, BITS(8, 15)); + + if (IEEE80211_ADDR_EQ(addr, empty_macaddr)) { + printf("%s: could not get mac address, attach failed\n", + dvname); + return ENXIO; + } + + printf("%s: 802.11 address %s\n", dvname, ether_sprintf(*addr)); + + return 0; +} + +u_int8_t +rtw_chan2txpower(struct rtw_srom *sr, struct ieee80211com *ic, + struct ieee80211_channel *chan) +{ + u_int idx = RTW_SR_TXPOWER1 + ieee80211_chan2ieee(ic, chan) - 1; + KASSERT2(idx >= RTW_SR_TXPOWER1 && idx <= RTW_SR_TXPOWER14, + ("%s: channel %d out of range", __func__, + idx - RTW_SR_TXPOWER1 + 1)); + return RTW_SR_GET(sr, idx); +} + +void +rtw_txdesc_blk_init_all(struct rtw_txdesc_blk *htcs) +{ + int pri; + u_int ndesc[RTW_NTXPRI] = + {RTW_NTXDESCLO, RTW_NTXDESCMD, RTW_NTXDESCHI, RTW_NTXDESCBCN}; + + for (pri = 0; pri < RTW_NTXPRI; pri++) { + htcs[pri].htc_nfree = ndesc[pri]; + htcs[pri].htc_next = 0; + } +} + +int +rtw_txctl_blk_init(struct rtw_txctl_blk *stc) +{ + int i; + struct rtw_txctl *stx; + + SIMPLEQ_INIT(&stc->stc_dirtyq); + SIMPLEQ_INIT(&stc->stc_freeq); + for (i = 0; i < stc->stc_ndesc; i++) { + stx = &stc->stc_desc[i]; + stx->stx_mbuf = NULL; + SIMPLEQ_INSERT_TAIL(&stc->stc_freeq, stx, stx_q); + } + return 0; +} + +void +rtw_txctl_blk_init_all(struct rtw_txctl_blk *stcs) +{ + int pri; + for (pri = 0; pri < RTW_NTXPRI; pri++) + rtw_txctl_blk_init(&stcs[pri]); +} + +static __inline void +rtw_rxdescs_sync(bus_dma_tag_t dmat, bus_dmamap_t dmap, u_int desc0, u_int + nsync, int ops) +{ + KASSERT(nsync <= RTW_RXQLEN); + /* sync to end of ring */ + if (desc0 + nsync > RTW_RXQLEN) { + bus_dmamap_sync(dmat, dmap, + offsetof(struct rtw_descs, hd_rx[desc0]), + sizeof(struct rtw_rxdesc) * (RTW_RXQLEN - desc0), ops); + nsync -= (RTW_RXQLEN - desc0); + desc0 = 0; + } + + KASSERT(desc0 < RTW_RXQLEN); + KASSERT(nsync <= RTW_RXQLEN); + KASSERT(desc0 + nsync <= RTW_RXQLEN); + + /* sync what remains */ + bus_dmamap_sync(dmat, dmap, + offsetof(struct rtw_descs, hd_rx[desc0]), + sizeof(struct rtw_rxdesc) * nsync, ops); +} + +void +rtw_txdescs_sync(bus_dma_tag_t dmat, bus_dmamap_t dmap, + struct rtw_txdesc_blk *htc, u_int desc0, u_int nsync, int ops) +{ + /* sync to end of ring */ + if (desc0 + nsync > htc->htc_ndesc) { + bus_dmamap_sync(dmat, dmap, + htc->htc_ofs + sizeof(struct rtw_txdesc) * desc0, + sizeof(struct rtw_txdesc) * (htc->htc_ndesc - desc0), + ops); + nsync -= (htc->htc_ndesc - desc0); + desc0 = 0; + } + + /* sync what remains */ + bus_dmamap_sync(dmat, dmap, + htc->htc_ofs + sizeof(struct rtw_txdesc) * desc0, + sizeof(struct rtw_txdesc) * nsync, ops); +} + +void +rtw_txdescs_sync_all(bus_dma_tag_t dmat, bus_dmamap_t dmap, + struct rtw_txdesc_blk *htcs) +{ + int pri; + for (pri = 0; pri < RTW_NTXPRI; pri++) { + rtw_txdescs_sync(dmat, dmap, + &htcs[pri], 0, htcs[pri].htc_ndesc, + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); + } +} + +void +rtw_rxbufs_release(bus_dma_tag_t dmat, struct rtw_rxctl *desc) +{ + int i; + struct rtw_rxctl *srx; + + for (i = 0; i < RTW_RXQLEN; i++) { + srx = &desc[i]; + bus_dmamap_sync(dmat, srx->srx_dmamap, 0, + srx->srx_dmamap->dm_mapsize, BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(dmat, srx->srx_dmamap); + m_freem(srx->srx_mbuf); + srx->srx_mbuf = NULL; + } +} + +static __inline int +rtw_rxbuf_alloc(bus_dma_tag_t dmat, struct rtw_rxctl *srx) +{ + int rc; + struct mbuf *m; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return ENOBUFS; + + MCLGET(m, M_DONTWAIT); + if (m == NULL) + return ENOBUFS; + + m->m_pkthdr.len = m->m_len = m->m_ext.ext_size; + + if (srx->srx_mbuf != NULL) + bus_dmamap_unload(dmat, srx->srx_dmamap); + + srx->srx_mbuf = NULL; + + rc = bus_dmamap_load_mbuf(dmat, srx->srx_dmamap, m, BUS_DMA_NOWAIT); + if (rc != 0) { + m_freem(m); + return -1; + } + + srx->srx_mbuf = m; + + return 0; +} + +int +rtw_rxctl_init_all(bus_dma_tag_t dmat, struct rtw_rxctl *desc, + u_int *next, const char *dvname) +{ + int i, rc; + struct rtw_rxctl *srx; + + for (i = 0; i < RTW_RXQLEN; i++) { + srx = &desc[i]; + if ((rc = rtw_rxbuf_alloc(dmat, srx)) == 0) + continue; + printf("%s: failed rtw_rxbuf_alloc after %d buffers, rc = %d\n", + dvname, i, rc); + if (i == 0) { + rtw_rxbufs_release(dmat, desc); + return rc; + } + } + *next = 0; + return 0; +} + +static __inline void +rtw_rxdesc_init(bus_dma_tag_t dmat, bus_dmamap_t dmam, + struct rtw_rxdesc *hrx, struct rtw_rxctl *srx, int idx, int kick) +{ + int is_last = (idx == RTW_RXQLEN - 1); + u_int32_t ctl, octl, obuf; + + obuf = hrx->hrx_buf; + hrx->hrx_buf = htole32(srx->srx_dmamap->dm_segs[0].ds_addr); + + ctl = LSHIFT(srx->srx_mbuf->m_len, RTW_RXCTL_LENGTH_MASK) | + RTW_RXCTL_OWN | RTW_RXCTL_FS | RTW_RXCTL_LS; + + if (is_last) + ctl |= RTW_RXCTL_EOR; + + octl = hrx->hrx_ctl; + hrx->hrx_ctl = htole32(ctl); + + RTW_DPRINTF( + kick ? (RTW_DEBUG_RECV_DESC | RTW_DEBUG_IO_KICK) + : RTW_DEBUG_RECV_DESC, + ("%s: hrx %p buf %08x -> %08x ctl %08x -> %08x\n", __func__, hrx, + letoh32(obuf), letoh32(hrx->hrx_buf), letoh32(octl), + letoh32(hrx->hrx_ctl))); + + /* sync the mbuf */ + bus_dmamap_sync(dmat, srx->srx_dmamap, 0, srx->srx_dmamap->dm_mapsize, + BUS_DMASYNC_PREREAD); + + /* sync the descriptor */ + bus_dmamap_sync(dmat, dmam, RTW_DESC_OFFSET(hd_rx, idx), + sizeof(struct rtw_rxdesc), + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); +} + +void +rtw_rxdesc_init_all(bus_dma_tag_t dmat, bus_dmamap_t dmam, + struct rtw_rxdesc *desc, struct rtw_rxctl *ctl, int kick) +{ + int i; + struct rtw_rxdesc *hrx; + struct rtw_rxctl *srx; + + for (i = 0; i < RTW_RXQLEN; i++) { + hrx = &desc[i]; + srx = &ctl[i]; + rtw_rxdesc_init(dmat, dmam, hrx, srx, i, kick); + } +} + +void +rtw_io_enable(struct rtw_regs *regs, u_int8_t flags, int enable) +{ + u_int8_t cr; + + RTW_DPRINTF(RTW_DEBUG_IOSTATE, ("%s: %s 0x%02x\n", __func__, + enable ? "enable" : "disable", flags)); + + cr = RTW_READ8(regs, RTW_CR); + + /* XXX reference source does not enable MULRW */ +#if 0 + /* enable PCI Read/Write Multiple */ + cr |= RTW_CR_MULRW; +#endif + + RTW_RBW(regs, RTW_CR, RTW_CR); /* XXX paranoia? */ + if (enable) + cr |= flags; + else + cr &= ~flags; + RTW_WRITE8(regs, RTW_CR, cr); + RTW_SYNC(regs, RTW_CR, RTW_CR); +} + +void +rtw_intr_rx(struct rtw_softc *sc, u_int16_t isr) +{ + static const int ratetbl[4] = {2, 4, 11, 22}; /* convert rates: + * hardware -> net80211 + */ + + u_int next, nproc = 0; + int hwrate, len, rate, rssi; + u_int32_t hrssi, hstat, htsfth, htsftl; + struct rtw_rxdesc *hrx; + struct rtw_rxctl *srx; + struct mbuf *m; + + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni; + struct ieee80211_frame *wh; + + KASSERT(sc->sc_rxnext < RTW_RXQLEN); + + for (next = sc->sc_rxnext; ; next = (next + 1) % RTW_RXQLEN) { + rtw_rxdescs_sync(sc->sc_dmat, sc->sc_desc_dmamap, + next, 1, BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); + hrx = &sc->sc_rxdesc[next]; + srx = &sc->sc_rxctl[next]; + + hstat = letoh32(hrx->hrx_stat); + hrssi = letoh32(hrx->hrx_rssi); + htsfth = letoh32(hrx->hrx_tsfth); + htsftl = letoh32(hrx->hrx_tsftl); + + RTW_DPRINTF(RTW_DEBUG_RECV_DESC, + ("%s: rxdesc[%d] hstat %08x hrssi %08x htsft %08x%08x\n", + __func__, next, hstat, hrssi, htsfth, htsftl)); + + KASSERT((hstat & (RTW_RXSTAT_FS|RTW_RXSTAT_LS)) == + (RTW_RXSTAT_FS|RTW_RXSTAT_LS)); + + ++nproc; + + /* still belongs to NIC */ + if ((hstat & RTW_RXSTAT_OWN) != 0) { + if (nproc > 1) + break; + + /* sometimes the NIC skips to the 0th descriptor */ + rtw_rxdescs_sync(sc->sc_dmat, sc->sc_desc_dmamap, + 0, 1, BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); + hrx = &sc->sc_rxdesc[0]; + if ((hrx->hrx_stat & htole32(RTW_RXSTAT_OWN)) != 0) + break; + RTW_DPRINTF(RTW_DEBUG_BUGS, + ("%s: NIC skipped to rxdesc[0]\n", + sc->sc_dev.dv_xname)); + next = 0; + continue; + } + + if ((hstat & RTW_RXSTAT_IOERROR) != 0) { + printf("%s: DMA error/FIFO overflow %08x, " + "rx descriptor %d\n", sc->sc_dev.dv_xname, + hstat & RTW_RXSTAT_IOERROR, next); + sc->sc_if.if_ierrors++; + goto next; + } + + len = MASK_AND_RSHIFT(hstat, RTW_RXSTAT_LENGTH_MASK); + if (len < IEEE80211_MIN_LEN) { + sc->sc_ic.ic_stats.is_rx_tooshort++; + goto next; + } + + hwrate = MASK_AND_RSHIFT(hstat, RTW_RXSTAT_RATE_MASK); + if (hwrate >= sizeof(ratetbl) / sizeof(ratetbl[0])) { + printf("%s: unknown rate #%d\n", sc->sc_dev.dv_xname, + MASK_AND_RSHIFT(hstat, RTW_RXSTAT_RATE_MASK)); + sc->sc_if.if_ierrors++; + goto next; + } + rate = ratetbl[hwrate]; + +#ifdef RTW_DEBUG +#define PRINTSTAT(flag) do { \ + if ((hstat & flag) != 0) { \ + printf("%s" #flag, delim); \ + delim = ","; \ + } \ +} while (0) + if ((rtw_debug & RTW_DEBUG_RECV_DESC) != 0) { + const char *delim = "<"; + printf("%s: ", sc->sc_dev.dv_xname); + if ((hstat & RTW_RXSTAT_DEBUG) != 0) { + printf("status %08x", hstat); + PRINTSTAT(RTW_RXSTAT_SPLCP); + PRINTSTAT(RTW_RXSTAT_MAR); + PRINTSTAT(RTW_RXSTAT_PAR); + PRINTSTAT(RTW_RXSTAT_BAR); + PRINTSTAT(RTW_RXSTAT_PWRMGT); + PRINTSTAT(RTW_RXSTAT_CRC32); + PRINTSTAT(RTW_RXSTAT_ICV); + printf(">, "); + } + printf("rate %d.%d Mb/s, time %08x%08x\n", + (rate * 5) / 10, (rate * 5) % 10, htsfth, htsftl); + } +#endif /* RTW_DEBUG */ + + if ((hstat & RTW_RXSTAT_RES) != 0 && + sc->sc_ic.ic_opmode != IEEE80211_M_MONITOR) + goto next; + + /* if bad flags, skip descriptor */ + if ((hstat & RTW_RXSTAT_ONESEG) != RTW_RXSTAT_ONESEG) { + printf("%s: too many rx segments\n", + sc->sc_dev.dv_xname); + goto next; + } + + bus_dmamap_sync(sc->sc_dmat, srx->srx_dmamap, 0, + srx->srx_dmamap->dm_mapsize, BUS_DMASYNC_POSTREAD); + + m = srx->srx_mbuf; + + /* if temporarily out of memory, re-use mbuf */ + switch (rtw_rxbuf_alloc(sc->sc_dmat, srx)) { + case 0: + break; + case ENOBUFS: + printf("%s: rtw_rxbuf_alloc(, %d) failed, " + "dropping this packet\n", sc->sc_dev.dv_xname, + next); + goto next; + default: + /* XXX shorten rx ring, instead? */ + panic("%s: could not load DMA map\n", + sc->sc_dev.dv_xname); + } + + if (sc->sc_rfchipid == RTW_RFCHIPID_PHILIPS) + rssi = MASK_AND_RSHIFT(hrssi, RTW_RXRSSI_RSSI); + else { + rssi = MASK_AND_RSHIFT(hrssi, RTW_RXRSSI_IMR_RSSI); + /* TBD find out each front-end's LNA gain in the + * front-end's units + */ + if ((hrssi & RTW_RXRSSI_IMR_LNA) == 0) + rssi |= 0x80; + } + + m->m_pkthdr.rcvif = &sc->sc_if; + m->m_pkthdr.len = m->m_len = len; + m->m_flags |= M_HASFCS; + + wh = mtod(m, struct ieee80211_frame *); + /* TBD use _MAR, _BAR, _PAR flags as hints to _find_rxnode? */ + ni = ieee80211_find_rxnode(&sc->sc_ic, wh); + + sc->sc_tsfth = htsfth; + +#ifdef RTW_DEBUG + if ((sc->sc_if.if_flags & (IFF_DEBUG|IFF_LINK2)) == + (IFF_DEBUG|IFF_LINK2)) { + ieee80211_dump_pkt(mtod(m, uint8_t *), m->m_pkthdr.len, + rate, rssi); + } +#endif /* RTW_DEBUG */ + + ieee80211_input(&sc->sc_if, m, ni, rssi, htsftl); + if (ni == ic->ic_bss) + ieee80211_unref_node(&ni); + else + ieee80211_free_node(&sc->sc_ic, ni); +next: + rtw_rxdesc_init(sc->sc_dmat, sc->sc_desc_dmamap, + hrx, srx, next, 0); + } + KASSERT(sc->sc_rxnext < RTW_RXQLEN); + + sc->sc_rxnext = next; + + return; +} + +void +rtw_txbuf_release(bus_dma_tag_t dmat, struct ieee80211com *ic, + struct rtw_txctl *stx) +{ + struct mbuf *m; + struct ieee80211_node *ni; + + m = stx->stx_mbuf; + ni = stx->stx_ni; + KASSERT(m != NULL); + KASSERT(ni != NULL); + stx->stx_mbuf = NULL; + stx->stx_ni = NULL; + + bus_dmamap_sync(dmat, stx->stx_dmamap, 0, stx->stx_dmamap->dm_mapsize, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(dmat, stx->stx_dmamap); + m_freem(m); + if (ni == ic->ic_bss) + ieee80211_unref_node(&ni); + else + ieee80211_free_node(ic, ni); +} + +void +rtw_txbufs_release(bus_dma_tag_t dmat, bus_dmamap_t desc_dmamap, + struct ieee80211com *ic, struct rtw_txctl_blk *stc) +{ + struct rtw_txctl *stx; + + while ((stx = SIMPLEQ_FIRST(&stc->stc_dirtyq)) != NULL) { + rtw_txbuf_release(dmat, ic, stx); + SIMPLEQ_REMOVE_HEAD(&stc->stc_dirtyq, stx_q); + SIMPLEQ_INSERT_TAIL(&stc->stc_freeq, stx, stx_q); + } +} + +static __inline void +rtw_collect_txpkt(struct rtw_softc *sc, struct rtw_txdesc_blk *htc, + struct rtw_txctl *stx, int ndesc) +{ + uint32_t hstat; + int data_retry, rts_retry; + struct rtw_txdesc *htxn; + const char *condstring; + + rtw_txbuf_release(sc->sc_dmat, &sc->sc_ic, stx); + + htc->htc_nfree += ndesc; + + htxn = &htc->htc_desc[stx->stx_last]; + + hstat = letoh32(htxn->htx_stat); + rts_retry = MASK_AND_RSHIFT(hstat, RTW_TXSTAT_RTSRETRY_MASK); + data_retry = MASK_AND_RSHIFT(hstat, RTW_TXSTAT_DRC_MASK); + + sc->sc_if.if_collisions += rts_retry + data_retry; + + if ((hstat & RTW_TXSTAT_TOK) != 0) + condstring = "ok"; + else { + sc->sc_if.if_oerrors++; + condstring = "error"; + } + + DPRINTF(sc, RTW_DEBUG_XMIT_DESC, + ("%s: stx %p txdesc[%d, %d] %s tries rts %u data %u\n", + sc->sc_dev.dv_xname, stx, stx->stx_first, stx->stx_last, + condstring, rts_retry, data_retry)); +} + +/* Collect transmitted packets. */ +static __inline void +rtw_collect_txring(struct rtw_softc *sc, struct rtw_txctl_blk *stc, + struct rtw_txdesc_blk *htc) +{ + int ndesc; + struct rtw_txctl *stx; + + while ((stx = SIMPLEQ_FIRST(&stc->stc_dirtyq)) != NULL) { + ndesc = 1 + stx->stx_last - stx->stx_first; + if (stx->stx_last < stx->stx_first) + ndesc += htc->htc_ndesc; + + KASSERT(ndesc > 0); + + rtw_txdescs_sync(sc->sc_dmat, sc->sc_desc_dmamap, htc, + stx->stx_first, ndesc, + BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); + + if ((htc->htc_desc[stx->stx_last].htx_stat & + htole32(RTW_TXSTAT_OWN)) != 0) + break; + + rtw_collect_txpkt(sc, htc, stx, ndesc); + SIMPLEQ_REMOVE_HEAD(&stc->stc_dirtyq, stx_q); + SIMPLEQ_INSERT_TAIL(&stc->stc_freeq, stx, stx_q); + sc->sc_if.if_flags &= ~IFF_OACTIVE; + } + if (stx == NULL) + stc->stc_tx_timer = 0; +} + +void +rtw_intr_tx(struct rtw_softc *sc, u_int16_t isr) +{ + int pri; + struct rtw_txctl_blk *stc; + struct rtw_txdesc_blk *htc; + + for (pri = 0; pri < RTW_NTXPRI; pri++) { + stc = &sc->sc_txctl_blk[pri]; + htc = &sc->sc_txdesc_blk[pri]; + + rtw_collect_txring(sc, stc, htc); + + if ((isr & RTW_INTR_TX) != 0) + rtw_start(&sc->sc_if); + } + + /* TBD */ + return; +} + +void +rtw_intr_beacon(struct rtw_softc *sc, u_int16_t isr) +{ + /* TBD */ + return; +} + +void +rtw_intr_atim(struct rtw_softc *sc) +{ + /* TBD */ + return; +} + +#ifdef RTW_DEBUG +void +rtw_dump_rings(struct rtw_softc *sc) +{ + struct rtw_txdesc_blk *htc; + struct rtw_rxdesc *hrx; + int desc, pri; + + if ((rtw_debug & RTW_DEBUG_IO_KICK) == 0) + return; + + for (pri = 0; pri < RTW_NTXPRI; pri++) { + htc = &sc->sc_txdesc_blk[pri]; + printf("%s: txpri %d ndesc %d nfree %d\n", __func__, pri, + htc->htc_ndesc, htc->htc_nfree); + for (desc = 0; desc < htc->htc_ndesc; desc++) + rtw_print_txdesc(sc, ".", NULL, htc, desc); + } + + for (desc = 0; desc < RTW_RXQLEN; desc++) { + hrx = &sc->sc_rxdesc[desc]; + printf("%s: ctl %08x rsvd0/rssi %08x buf/tsftl %08x " + "rsvd1/tsfth %08x\n", __func__, + letoh32(hrx->hrx_ctl), letoh32(hrx->hrx_rssi), + letoh32(hrx->hrx_buf), letoh32(hrx->hrx_tsfth)); + } +} +#endif /* RTW_DEBUG */ + +void +rtw_hwring_setup(struct rtw_softc *sc) +{ + struct rtw_regs *regs = &sc->sc_regs; + RTW_WRITE(regs, RTW_RDSAR, RTW_RING_BASE(sc, hd_rx)); + RTW_WRITE(regs, RTW_TLPDA, RTW_RING_BASE(sc, hd_txlo)); + RTW_WRITE(regs, RTW_TNPDA, RTW_RING_BASE(sc, hd_txmd)); + RTW_WRITE(regs, RTW_THPDA, RTW_RING_BASE(sc, hd_txhi)); + RTW_WRITE(regs, RTW_TBDA, RTW_RING_BASE(sc, hd_bcn)); + RTW_SYNC(regs, RTW_TLPDA, RTW_RDSAR); +#if 0 + RTW_DPRINTF(RTW_DEBUG_XMIT_DESC, + ("%s: reg[TLPDA] <- %" PRIxPTR "\n", __func__, + (uintptr_t)RTW_RING_BASE(sc, hd_txlo))); + RTW_DPRINTF(RTW_DEBUG_XMIT_DESC, + ("%s: reg[TNPDA] <- %" PRIxPTR "\n", __func__, + (uintptr_t)RTW_RING_BASE(sc, hd_txmd))); + RTW_DPRINTF(RTW_DEBUG_XMIT_DESC, + ("%s: reg[THPDA] <- %" PRIxPTR "\n", __func__, + (uintptr_t)RTW_RING_BASE(sc, hd_txhi))); + RTW_DPRINTF(RTW_DEBUG_XMIT_DESC, + ("%s: reg[TBDA] <- %" PRIxPTR "\n", __func__, + (uintptr_t)RTW_RING_BASE(sc, hd_bcn))); + RTW_DPRINTF(RTW_DEBUG_RECV_DESC, + ("%s: reg[RDSAR] <- %" PRIxPTR "\n", __func__, + (uintptr_t)RTW_RING_BASE(sc, hd_rx))); +#endif +} + +void +rtw_swring_setup(struct rtw_softc *sc) +{ + rtw_txdesc_blk_init_all(&sc->sc_txdesc_blk[0]); + + rtw_txctl_blk_init_all(&sc->sc_txctl_blk[0]); + + rtw_rxdescs_sync(sc->sc_dmat, sc->sc_desc_dmamap, + 0, RTW_RXQLEN, BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); + rtw_rxctl_init_all(sc->sc_dmat, sc->sc_rxctl, &sc->sc_rxnext, + sc->sc_dev.dv_xname); + rtw_rxdesc_init_all(sc->sc_dmat, sc->sc_desc_dmamap, + sc->sc_rxdesc, sc->sc_rxctl, 1); + + rtw_txdescs_sync_all(sc->sc_dmat, sc->sc_desc_dmamap, + &sc->sc_txdesc_blk[0]); +#if 0 /* redundant with rtw_rxdesc_init_all */ + rtw_rxdescs_sync(sc->sc_dmat, sc->sc_desc_dmamap, + 0, RTW_RXQLEN, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); +#endif +} + +void +rtw_txdesc_blk_reset(struct rtw_txdesc_blk *htc) +{ + int i; + + (void)memset(htc->htc_desc, 0, + sizeof(htc->htc_desc[0]) * htc->htc_ndesc); + for (i = 0; i < htc->htc_ndesc; i++) + htc->htc_desc[i].htx_next = htole32(RTW_NEXT_DESC(htc, i)); + htc->htc_nfree = htc->htc_ndesc; + htc->htc_next = 0; +} + +void +rtw_txdescs_reset(struct rtw_softc *sc) +{ + int pri; + struct rtw_txdesc_blk *htc; + + for (pri = 0; pri < RTW_NTXPRI; pri++) { + htc = &sc->sc_txdesc_blk[pri]; + rtw_txbufs_release(sc->sc_dmat, sc->sc_desc_dmamap, &sc->sc_ic, + &sc->sc_txctl_blk[pri]); + rtw_txdesc_blk_reset(htc); + rtw_txdescs_sync(sc->sc_dmat, sc->sc_desc_dmamap, htc, + 0, htc->htc_ndesc, + BUS_DMASYNC_PREWRITE|BUS_DMASYNC_PREREAD); + } +} + +void +rtw_rxdescs_reset(struct rtw_softc *sc) +{ + /* Re-initialize descriptors, just in case. */ + rtw_rxdesc_init_all(sc->sc_dmat, sc->sc_desc_dmamap, sc->sc_rxdesc, + &sc->sc_rxctl[0], 1); + + /* Reset to start of ring. */ + sc->sc_rxnext = 0; +} + +void +rtw_intr_ioerror(struct rtw_softc *sc, u_int16_t isr) +{ + struct rtw_regs *regs = &sc->sc_regs; + + if ((isr & RTW_INTR_TXFOVW) != 0) + printf("%s: tx fifo overflow\n", sc->sc_dev.dv_xname); + + if ((isr & (RTW_INTR_RDU|RTW_INTR_RXFOVW)) == 0) + return; + + RTW_DPRINTF(RTW_DEBUG_BUGS, ("%s: restarting xmit/recv\n", + sc->sc_dev.dv_xname)); + +#ifdef RTW_DEBUG + rtw_dump_rings(sc); +#endif /* RTW_DEBUG */ + + rtw_io_enable(regs, RTW_CR_RE | RTW_CR_TE, 0); + + /* Collect rx'd packets. Refresh rx buffers. */ + rtw_intr_rx(sc, 0); + /* Collect tx'd packets. */ + rtw_intr_tx(sc, 0); + + RTW_WRITE16(regs, RTW_IMR, 0); + RTW_SYNC(regs, RTW_IMR, RTW_IMR); + + rtw_chip_reset1(regs, sc->sc_dev.dv_xname); + + rtw_rxdescs_reset(sc); + rtw_txdescs_reset(sc); + + rtw_hwring_setup(sc); + +#ifdef RTW_DEBUG + rtw_dump_rings(sc); +#endif /* RTW_DEBUG */ + + RTW_WRITE16(regs, RTW_IMR, sc->sc_inten); + RTW_SYNC(regs, RTW_IMR, RTW_IMR); + rtw_io_enable(regs, RTW_CR_RE | RTW_CR_TE, 1); +} + +static __inline void +rtw_suspend_ticks(struct rtw_softc *sc) +{ + RTW_DPRINTF(RTW_DEBUG_TIMEOUT, + ("%s: suspending ticks\n", sc->sc_dev.dv_xname)); + sc->sc_do_tick = 0; +} + +static __inline void +rtw_resume_ticks(struct rtw_softc *sc) +{ + u_int32_t tsftrl0, tsftrl1, next_tick; + + tsftrl0 = RTW_READ(&sc->sc_regs, RTW_TSFTRL); + + tsftrl1 = RTW_READ(&sc->sc_regs, RTW_TSFTRL); + next_tick = tsftrl1 + 1000000; + RTW_WRITE(&sc->sc_regs, RTW_TINT, next_tick); + + sc->sc_do_tick = 1; + + RTW_DPRINTF(RTW_DEBUG_TIMEOUT, + ("%s: resume ticks delta %#08x now %#08x next %#08x\n", + sc->sc_dev.dv_xname, tsftrl1 - tsftrl0, tsftrl1, next_tick)); +} + +void +rtw_intr_timeout(struct rtw_softc *sc) +{ + RTW_DPRINTF(RTW_DEBUG_TIMEOUT, ("%s: timeout\n", sc->sc_dev.dv_xname)); + if (sc->sc_do_tick) + rtw_resume_ticks(sc); + return; +} + +int +rtw_intr(void *arg) +{ + int i; + struct rtw_softc *sc = arg; + struct rtw_regs *regs = &sc->sc_regs; + u_int16_t isr; + + /* + * If the interface isn't running, the interrupt couldn't + * possibly have come from us. + */ + if ((sc->sc_flags & RTW_F_ENABLED) == 0 || + (sc->sc_if.if_flags & IFF_RUNNING) == 0 || + (sc->sc_dev.dv_flags & DVF_ACTIVE) == 0) { + RTW_DPRINTF(RTW_DEBUG_INTR, ("%s: stray interrupt\n", + sc->sc_dev.dv_xname)); + return (0); + } + + for (i = 0; i < 10; i++) { + isr = RTW_READ16(regs, RTW_ISR); + + RTW_WRITE16(regs, RTW_ISR, isr); + RTW_WBR(regs, RTW_ISR, RTW_ISR); + + if (sc->sc_intr_ack != NULL) + (*sc->sc_intr_ack)(regs); + + if (isr == 0) + break; + +#ifdef RTW_DEBUG +#define PRINTINTR(flag) do { \ + if ((isr & flag) != 0) { \ + printf("%s" #flag, delim); \ + delim = ","; \ + } \ +} while (0) + + if ((rtw_debug & RTW_DEBUG_INTR) != 0 && isr != 0) { + const char *delim = "<"; + + printf("%s: reg[ISR] = %x", sc->sc_dev.dv_xname, isr); + + PRINTINTR(RTW_INTR_TXFOVW); + PRINTINTR(RTW_INTR_TIMEOUT); + PRINTINTR(RTW_INTR_BCNINT); + PRINTINTR(RTW_INTR_ATIMINT); + PRINTINTR(RTW_INTR_TBDER); + PRINTINTR(RTW_INTR_TBDOK); + PRINTINTR(RTW_INTR_THPDER); + PRINTINTR(RTW_INTR_THPDOK); + PRINTINTR(RTW_INTR_TNPDER); + PRINTINTR(RTW_INTR_TNPDOK); + PRINTINTR(RTW_INTR_RXFOVW); + PRINTINTR(RTW_INTR_RDU); + PRINTINTR(RTW_INTR_TLPDER); + PRINTINTR(RTW_INTR_TLPDOK); + PRINTINTR(RTW_INTR_RER); + PRINTINTR(RTW_INTR_ROK); + + printf(">\n"); + } +#undef PRINTINTR +#endif /* RTW_DEBUG */ + + if ((isr & RTW_INTR_RX) != 0) + rtw_intr_rx(sc, isr & RTW_INTR_RX); + if ((isr & RTW_INTR_TX) != 0) + rtw_intr_tx(sc, isr & RTW_INTR_TX); + if ((isr & RTW_INTR_BEACON) != 0) + rtw_intr_beacon(sc, isr & RTW_INTR_BEACON); + if ((isr & RTW_INTR_ATIMINT) != 0) + rtw_intr_atim(sc); + if ((isr & RTW_INTR_IOERROR) != 0) + rtw_intr_ioerror(sc, isr & RTW_INTR_IOERROR); + if ((isr & RTW_INTR_TIMEOUT) != 0) + rtw_intr_timeout(sc); + } + + return 1; +} + +/* Must be called at splnet. */ +void +rtw_stop(struct ifnet *ifp, int disable) +{ + int pri; + struct rtw_softc *sc = (struct rtw_softc *)ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + struct rtw_regs *regs = &sc->sc_regs; + + if ((sc->sc_flags & RTW_F_ENABLED) == 0) + return; + + rtw_suspend_ticks(sc); + + ieee80211_new_state(ic, IEEE80211_S_INIT, -1); + + if ((sc->sc_flags & RTW_F_INVALID) == 0) { + /* Disable interrupts. */ + RTW_WRITE16(regs, RTW_IMR, 0); + + RTW_WBW(regs, RTW_TPPOLL, RTW_IMR); + + /* Stop the transmit and receive processes. First stop DMA, + * then disable receiver and transmitter. + */ + RTW_WRITE8(regs, RTW_TPPOLL, + RTW_TPPOLL_SBQ|RTW_TPPOLL_SHPQ|RTW_TPPOLL_SNPQ| + RTW_TPPOLL_SLPQ); + + RTW_SYNC(regs, RTW_TPPOLL, RTW_IMR); + + rtw_io_enable(&sc->sc_regs, RTW_CR_RE|RTW_CR_TE, 0); + } + + for (pri = 0; pri < RTW_NTXPRI; pri++) { + rtw_txbufs_release(sc->sc_dmat, sc->sc_desc_dmamap, &sc->sc_ic, + &sc->sc_txctl_blk[pri]); + } + + if (disable) { + rtw_disable(sc); + rtw_rxbufs_release(sc->sc_dmat, &sc->sc_rxctl[0]); + } + + /* Mark the interface as not running. Cancel the watchdog timer. */ + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + ifp->if_timer = 0; + + return; +} + +const char * +rtw_pwrstate_string(enum rtw_pwrstate power) +{ + switch (power) { + case RTW_ON: + return "on"; + case RTW_SLEEP: + return "sleep"; + case RTW_OFF: + return "off"; + default: + return "unknown"; + } +} + +/* XXX For Maxim, I am using the RFMD settings gleaned from the + * reference driver, plus a magic Maxim "ON" value that comes from + * the Realtek document "Windows PG for Rtl8180." + */ +void +rtw_maxim_pwrstate(struct rtw_regs *regs, enum rtw_pwrstate power, + int before_rf, int digphy) +{ + u_int32_t anaparm; + + anaparm = RTW_READ(regs, RTW_ANAPARM); + anaparm &= ~(RTW_ANAPARM_RFPOW_MASK | RTW_ANAPARM_TXDACOFF); + + switch (power) { + case RTW_OFF: + if (before_rf) + return; + anaparm |= RTW_ANAPARM_RFPOW_MAXIM_OFF; + anaparm |= RTW_ANAPARM_TXDACOFF; + break; + case RTW_SLEEP: + if (!before_rf) + return; + anaparm |= RTW_ANAPARM_RFPOW_MAXIM_SLEEP; + anaparm |= RTW_ANAPARM_TXDACOFF; + break; + case RTW_ON: + if (!before_rf) + return; + anaparm |= RTW_ANAPARM_RFPOW_MAXIM_ON; + break; + } + RTW_DPRINTF(RTW_DEBUG_PWR, + ("%s: power state %s, %s RF, reg[ANAPARM] <- %08x\n", + __func__, rtw_pwrstate_string(power), + (before_rf) ? "before" : "after", anaparm)); + + RTW_WRITE(regs, RTW_ANAPARM, anaparm); + RTW_SYNC(regs, RTW_ANAPARM, RTW_ANAPARM); +} + +/* XXX I am using the RFMD settings gleaned from the reference + * driver. They agree + */ +void +rtw_rfmd_pwrstate(struct rtw_regs *regs, enum rtw_pwrstate power, + int before_rf, int digphy) +{ + u_int32_t anaparm; + + anaparm = RTW_READ(regs, RTW_ANAPARM); + anaparm &= ~(RTW_ANAPARM_RFPOW_MASK | RTW_ANAPARM_TXDACOFF); + + switch (power) { + case RTW_OFF: + if (before_rf) + return; + anaparm |= RTW_ANAPARM_RFPOW_RFMD_OFF; + anaparm |= RTW_ANAPARM_TXDACOFF; + break; + case RTW_SLEEP: + if (!before_rf) + return; + anaparm |= RTW_ANAPARM_RFPOW_RFMD_SLEEP; + anaparm |= RTW_ANAPARM_TXDACOFF; + break; + case RTW_ON: + if (!before_rf) + return; + anaparm |= RTW_ANAPARM_RFPOW_RFMD_ON; + break; + } + RTW_DPRINTF(RTW_DEBUG_PWR, + ("%s: power state %s, %s RF, reg[ANAPARM] <- %08x\n", + __func__, rtw_pwrstate_string(power), + (before_rf) ? "before" : "after", anaparm)); + + RTW_WRITE(regs, RTW_ANAPARM, anaparm); + RTW_SYNC(regs, RTW_ANAPARM, RTW_ANAPARM); +} + +void +rtw_philips_pwrstate(struct rtw_regs *regs, enum rtw_pwrstate power, + int before_rf, int digphy) +{ + u_int32_t anaparm; + + anaparm = RTW_READ(regs, RTW_ANAPARM); + anaparm &= ~(RTW_ANAPARM_RFPOW_MASK | RTW_ANAPARM_TXDACOFF); + + switch (power) { + case RTW_OFF: + if (before_rf) + return; + anaparm |= RTW_ANAPARM_RFPOW_PHILIPS_OFF; + anaparm |= RTW_ANAPARM_TXDACOFF; + break; + case RTW_SLEEP: + if (!before_rf) + return; + anaparm |= RTW_ANAPARM_RFPOW_PHILIPS_SLEEP; + anaparm |= RTW_ANAPARM_TXDACOFF; + break; + case RTW_ON: + if (!before_rf) + return; + if (digphy) { + anaparm |= RTW_ANAPARM_RFPOW_DIG_PHILIPS_ON; + /* XXX guess */ + anaparm |= RTW_ANAPARM_TXDACOFF; + } else + anaparm |= RTW_ANAPARM_RFPOW_ANA_PHILIPS_ON; + break; + } + RTW_DPRINTF(RTW_DEBUG_PWR, + ("%s: power state %s, %s RF, reg[ANAPARM] <- %08x\n", + __func__, rtw_pwrstate_string(power), + (before_rf) ? "before" : "after", anaparm)); + + RTW_WRITE(regs, RTW_ANAPARM, anaparm); + RTW_SYNC(regs, RTW_ANAPARM, RTW_ANAPARM); +} + +void +rtw_pwrstate0(struct rtw_softc *sc, enum rtw_pwrstate power, int before_rf, + int digphy) +{ + struct rtw_regs *regs = &sc->sc_regs; + + rtw_set_access(sc, RTW_ACCESS_ANAPARM); + + (*sc->sc_pwrstate_cb)(regs, power, before_rf, digphy); + + rtw_set_access(sc, RTW_ACCESS_NONE); + + return; +} + +int +rtw_pwrstate(struct rtw_softc *sc, enum rtw_pwrstate power) +{ + int rc; + + RTW_DPRINTF(RTW_DEBUG_PWR, + ("%s: %s->%s\n", __func__, + rtw_pwrstate_string(sc->sc_pwrstate), rtw_pwrstate_string(power))); + + if (sc->sc_pwrstate == power) + return 0; + + rtw_pwrstate0(sc, power, 1, sc->sc_flags & RTW_F_DIGPHY); + rc = rtw_rf_pwrstate(sc->sc_rf, power); + rtw_pwrstate0(sc, power, 0, sc->sc_flags & RTW_F_DIGPHY); + + switch (power) { + case RTW_ON: + /* TBD set LEDs */ + break; + case RTW_SLEEP: + /* TBD */ + break; + case RTW_OFF: + /* TBD */ + break; + } + if (rc == 0) + sc->sc_pwrstate = power; + else + sc->sc_pwrstate = RTW_OFF; + return rc; +} + +int +rtw_tune(struct rtw_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + u_int chan; + int rc; + int antdiv = sc->sc_flags & RTW_F_ANTDIV, + dflantb = sc->sc_flags & RTW_F_DFLANTB; + + KASSERT(ic->ic_bss->ni_chan != NULL); + + chan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan); + if (chan == IEEE80211_CHAN_ANY) + panic("%s: chan == IEEE80211_CHAN_ANY\n", __func__); + + if (chan == sc->sc_cur_chan) { + RTW_DPRINTF(RTW_DEBUG_TUNE, + ("%s: already tuned chan #%d\n", __func__, chan)); + return 0; + } + + rtw_suspend_ticks(sc); + + rtw_io_enable(&sc->sc_regs, RTW_CR_RE | RTW_CR_TE, 0); + + /* TBD wait for Tx to complete */ + + KASSERT((sc->sc_flags & RTW_F_ENABLED) != 0); + + if ((rc = rtw_phy_init(&sc->sc_regs, sc->sc_rf, + rtw_chan2txpower(&sc->sc_srom, ic, ic->ic_bss->ni_chan), + sc->sc_csthr, ic->ic_bss->ni_chan->ic_freq, antdiv, + dflantb, RTW_ON)) != 0) { + /* XXX condition on powersaving */ + printf("%s: phy init failed\n", sc->sc_dev.dv_xname); + } + + sc->sc_cur_chan = chan; + + rtw_io_enable(&sc->sc_regs, RTW_CR_RE | RTW_CR_TE, 1); + + rtw_resume_ticks(sc); + + return rc; +} + +void +rtw_disable(struct rtw_softc *sc) +{ + int rc; + + if ((sc->sc_flags & RTW_F_ENABLED) == 0) + return; + + /* turn off PHY */ + if ((rc = rtw_pwrstate(sc, RTW_OFF)) != 0) + printf("%s: failed to turn off PHY (%d)\n", + sc->sc_dev.dv_xname, rc); + + if (sc->sc_disable != NULL) + (*sc->sc_disable)(sc); + + sc->sc_flags &= ~RTW_F_ENABLED; +} + +int +rtw_enable(struct rtw_softc *sc) +{ + if ((sc->sc_flags & RTW_F_ENABLED) == 0) { + if (sc->sc_enable != NULL && (*sc->sc_enable)(sc) != 0) { + printf("%s: device enable failed\n", + sc->sc_dev.dv_xname); + return (EIO); + } + sc->sc_flags |= RTW_F_ENABLED; + } + return (0); +} + +void +rtw_transmit_config(struct rtw_regs *regs) +{ + u_int32_t tcr; + + tcr = RTW_READ(regs, RTW_TCR); + + tcr |= RTW_TCR_CWMIN; + tcr &= ~RTW_TCR_MXDMA_MASK; + tcr |= RTW_TCR_MXDMA_256; + tcr |= RTW_TCR_SAT; /* send ACK as fast as possible */ + tcr &= ~RTW_TCR_LBK_MASK; + tcr |= RTW_TCR_LBK_NORMAL; /* normal operating mode */ + + /* set short/long retry limits */ + tcr &= ~(RTW_TCR_SRL_MASK|RTW_TCR_LRL_MASK); + tcr |= LSHIFT(4, RTW_TCR_SRL_MASK) | LSHIFT(4, RTW_TCR_LRL_MASK); + + tcr &= ~RTW_TCR_CRC; /* NIC appends CRC32 */ + + RTW_WRITE(regs, RTW_TCR, tcr); + RTW_SYNC(regs, RTW_TCR, RTW_TCR); +} + +static __inline void +rtw_enable_interrupts(struct rtw_softc *sc) +{ + struct rtw_regs *regs = &sc->sc_regs; + + sc->sc_inten = RTW_INTR_RX|RTW_INTR_TX|RTW_INTR_BEACON|RTW_INTR_ATIMINT; + sc->sc_inten |= RTW_INTR_IOERROR|RTW_INTR_TIMEOUT; + + RTW_WRITE16(regs, RTW_IMR, sc->sc_inten); + RTW_WBW(regs, RTW_IMR, RTW_ISR); + RTW_WRITE16(regs, RTW_ISR, 0xffff); + RTW_SYNC(regs, RTW_IMR, RTW_ISR); + + /* XXX necessary? */ + if (sc->sc_intr_ack != NULL) + (*sc->sc_intr_ack)(regs); +} + +void +rtw_set_nettype(struct rtw_softc *sc, enum ieee80211_opmode opmode) +{ + uint8_t msr; + + /* I'm guessing that MSR is protected as CONFIG[0123] are. */ + rtw_set_access(sc, RTW_ACCESS_CONFIG); + + msr = RTW_READ8(&sc->sc_regs, RTW_MSR) & ~RTW_MSR_NETYPE_MASK; + + switch (opmode) { + case IEEE80211_M_AHDEMO: + case IEEE80211_M_IBSS: + msr |= RTW_MSR_NETYPE_ADHOC_OK; + break; + case IEEE80211_M_HOSTAP: + msr |= RTW_MSR_NETYPE_AP_OK; + break; + case IEEE80211_M_MONITOR: + /* XXX */ + msr |= RTW_MSR_NETYPE_NOLINK; + break; + case IEEE80211_M_STA: + msr |= RTW_MSR_NETYPE_INFRA_OK; + break; + } + RTW_WRITE8(&sc->sc_regs, RTW_MSR, msr); + + rtw_set_access(sc, RTW_ACCESS_NONE); +} + +/* XXX is the endianness correct? test. */ +#define rtw_calchash(addr) \ + (ether_crc32_le((addr), IEEE80211_ADDR_LEN) & BITS(5, 0)) + +void +rtw_pktfilt_load(struct rtw_softc *sc) +{ + struct rtw_regs *regs = &sc->sc_regs; + struct ieee80211com *ic = &sc->sc_ic; + struct arpcom *ec = &ic->ic_ac; + struct ifnet *ifp = &sc->sc_ic.ic_if; + int hash; + u_int32_t hashes[2] = { 0, 0 }; + struct ether_multi *enm; + struct ether_multistep step; + + /* XXX might be necessary to stop Rx/Tx engines while setting filters */ + +#define RTW_RCR_MONITOR (RTW_RCR_ACRC32|RTW_RCR_APM|RTW_RCR_AAP|RTW_RCR_AB|RTW_RCR_ACF | RTW_RCR_AICV | RTW_RCR_ACRC32) + + if (ic->ic_opmode == IEEE80211_M_MONITOR) + sc->sc_rcr |= RTW_RCR_MONITOR; + else + sc->sc_rcr &= ~RTW_RCR_MONITOR; + + /* XXX reference sources BEGIN */ + sc->sc_rcr |= RTW_RCR_ENMARP; + sc->sc_rcr |= RTW_RCR_AB | RTW_RCR_AM | RTW_RCR_APM; +#if 0 + /* receive broadcasts in our BSS */ + sc->sc_rcr |= RTW_RCR_ADD3; +#endif + /* XXX reference sources END */ + + /* receive pwrmgmt frames. */ + sc->sc_rcr |= RTW_RCR_APWRMGT; + /* receive mgmt/ctrl/data frames. */ + sc->sc_rcr |= RTW_RCR_ADF | RTW_RCR_AMF; + /* initialize Rx DMA threshold, Tx DMA burst size */ + sc->sc_rcr |= RTW_RCR_RXFTH_WHOLE | RTW_RCR_MXDMA_1024; + + ifp->if_flags &= ~IFF_ALLMULTI; + + if (ifp->if_flags & IFF_PROMISC) { + sc->sc_rcr |= RTW_RCR_AB; /* accept all broadcast */ +allmulti: + ifp->if_flags |= IFF_ALLMULTI; + goto setit; + } + + /* + * Program the 64-bit multicast hash filter. + */ + ETHER_FIRST_MULTI(step, ec, enm); + while (enm != NULL) { + /* XXX */ + if (memcmp(enm->enm_addrlo, enm->enm_addrhi, + ETHER_ADDR_LEN) != 0) + goto allmulti; + + hash = rtw_calchash(enm->enm_addrlo); + hashes[hash >> 5] |= 1 << (hash & 0x1f); + ETHER_NEXT_MULTI(step, enm); + } + + if (ifp->if_flags & IFF_BROADCAST) { + hash = rtw_calchash(etherbroadcastaddr); + hashes[hash >> 5] |= 1 << (hash & 0x1f); + } + + /* all bits set => hash is useless */ + if (~(hashes[0] & hashes[1]) == 0) + goto allmulti; + + setit: + if (ifp->if_flags & IFF_ALLMULTI) + sc->sc_rcr |= RTW_RCR_AM; /* accept all multicast */ + + if (ic->ic_state == IEEE80211_S_SCAN) + sc->sc_rcr |= RTW_RCR_AB; /* accept all broadcast */ + + hashes[0] = hashes[1] = 0xffffffff; + + RTW_WRITE(regs, RTW_MAR0, hashes[0]); + RTW_WRITE(regs, RTW_MAR1, hashes[1]); + RTW_WRITE(regs, RTW_RCR, sc->sc_rcr); + RTW_SYNC(regs, RTW_MAR0, RTW_RCR); /* RTW_MAR0 < RTW_MAR1 < RTW_RCR */ + + DPRINTF(sc, RTW_DEBUG_PKTFILT, + ("%s: RTW_MAR0 %08x RTW_MAR1 %08x RTW_RCR %08x\n", + sc->sc_dev.dv_xname, RTW_READ(regs, RTW_MAR0), + RTW_READ(regs, RTW_MAR1), RTW_READ(regs, RTW_RCR))); + + return; +} + +/* Must be called at splnet. */ +int +rtw_init(struct ifnet *ifp) +{ + struct rtw_softc *sc = (struct rtw_softc *)ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + struct rtw_regs *regs = &sc->sc_regs; + int rc = 0; + + if ((rc = rtw_enable(sc)) != 0) + goto out; + + /* Cancel pending I/O and reset. */ + rtw_stop(ifp, 0); + + ic->ic_bss->ni_chan = ic->ic_ibss_chan; + DPRINTF(sc, RTW_DEBUG_TUNE, ("%s: channel %d freq %d flags 0x%04x\n", + __func__, ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan), + ic->ic_bss->ni_chan->ic_freq, ic->ic_bss->ni_chan->ic_flags)); + + if ((rc = rtw_pwrstate(sc, RTW_OFF)) != 0) + goto out; + + rtw_swring_setup(sc); + + rtw_transmit_config(regs); + + rtw_set_access(sc, RTW_ACCESS_CONFIG); + + RTW_WRITE8(regs, RTW_MSR, 0x0); /* no link */ + RTW_WBW(regs, RTW_MSR, RTW_BRSR); + + /* long PLCP header, 1Mb/2Mb basic rate */ + RTW_WRITE16(regs, RTW_BRSR, RTW_BRSR_MBR8180_2MBPS); + RTW_SYNC(regs, RTW_BRSR, RTW_BRSR); + + rtw_set_access(sc, RTW_ACCESS_ANAPARM); + rtw_set_access(sc, RTW_ACCESS_NONE); + +#if 0 + RTW_WRITE(regs, RTW_FEMR, RTW_FEMR_GWAKE|RTW_FEMR_WKUP|RTW_FEMR_INTR); +#endif + /* XXX from reference sources */ + RTW_WRITE(regs, RTW_FEMR, 0xffff); + RTW_SYNC(regs, RTW_FEMR, RTW_FEMR); + + rtw_set_rfprog(regs, sc->sc_rfchipid, sc->sc_dev.dv_xname); + + RTW_WRITE8(regs, RTW_PHYDELAY, sc->sc_phydelay); + /* from Linux driver */ + RTW_WRITE8(regs, RTW_CRCOUNT, RTW_CRCOUNT_MAGIC); + + RTW_SYNC(regs, RTW_PHYDELAY, RTW_CRCOUNT); + + rtw_enable_interrupts(sc); + + rtw_pktfilt_load(sc); + + rtw_hwring_setup(sc); + + rtw_io_enable(regs, RTW_CR_RE|RTW_CR_TE, 1); + + ifp->if_flags |= IFF_RUNNING; + ic->ic_state = IEEE80211_S_INIT; + + RTW_WRITE16(regs, RTW_BSSID16, 0x0); + RTW_WRITE(regs, RTW_BSSID32, 0x0); + + rtw_resume_ticks(sc); + + rtw_set_nettype(sc, IEEE80211_M_MONITOR); + + if (ic->ic_opmode == IEEE80211_M_MONITOR) + return ieee80211_new_state(ic, IEEE80211_S_RUN, -1); + else + return ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + +out: + return rc; +} + +int +rtw_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + int rc = 0, s; + struct rtw_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + struct ifreq *ifr = (struct ifreq *)data; + struct ifaddr *ifa = (struct ifaddr *)data; + + s = splnet(); + switch (cmd) { + case SIOCSIFMTU: + if (ifr->ifr_mtu > ETHERMTU || ifr->ifr_mtu < ETHERMIN) { + rc = EINVAL; + } else if (ifp->if_mtu != ifr->ifr_mtu) { + ifp->if_mtu = ifr->ifr_mtu; + } + break; + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; +#ifdef INET + if (ifa->ifa_addr->sa_family == AF_INET) { + arp_ifinit(&ic->ic_ac, ifa); + } +#endif /* INET */ + /* FALLTHROUGH */ + + case SIOCSIFFLAGS: + if ((ifp->if_flags & IFF_UP) != 0) { + if (0 && (sc->sc_flags & RTW_F_ENABLED) != 0) { + rtw_pktfilt_load(sc); + } else + rc = rtw_init(ifp); +#ifdef RTW_DEBUG + rtw_print_regs(&sc->sc_regs, ifp->if_xname, __func__); +#endif /* RTW_DEBUG */ + } else if ((sc->sc_flags & RTW_F_ENABLED) != 0) { +#ifdef RTW_DEBUG + rtw_print_regs(&sc->sc_regs, ifp->if_xname, __func__); +#endif /* RTW_DEBUG */ + rtw_stop(ifp, 1); + } + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + if (cmd == SIOCADDMULTI) + rc = ether_addmulti(ifr, &sc->sc_ic.ic_ac); + else + rc = ether_delmulti(ifr, &sc->sc_ic.ic_ac); + if (rc == ENETRESET) { + if (sc->sc_flags & IFF_RUNNING) + rtw_pktfilt_load(sc); + rc = 0; + } + break; + default: + if ((rc = ieee80211_ioctl(ifp, cmd, data)) == ENETRESET) { + if ((sc->sc_flags & RTW_F_ENABLED) != 0) + rc = rtw_init(ifp); + else + rc = 0; + } + break; + } + splx(s); + return rc; +} + +/* Point *mp at the next 802.11 frame to transmit. Point *stcp + * at the driver's selection of transmit control block for the packet. + */ +static __inline int +rtw_dequeue(struct ifnet *ifp, struct rtw_txctl_blk **stcp, + struct rtw_txdesc_blk **htcp, struct mbuf **mp, + struct ieee80211_node **nip) +{ + struct rtw_txctl_blk *stc; + struct rtw_txdesc_blk *htc; + struct mbuf *m0; + struct rtw_softc *sc; + struct ieee80211com *ic; + + sc = (struct rtw_softc *)ifp->if_softc; + + DPRINTF(sc, RTW_DEBUG_XMIT, + ("%s: enter %s\n", sc->sc_dev.dv_xname, __func__)); + *mp = NULL; + + stc = &sc->sc_txctl_blk[RTW_TXPRIMD]; + htc = &sc->sc_txdesc_blk[RTW_TXPRIMD]; + + if (SIMPLEQ_EMPTY(&stc->stc_freeq) || htc->htc_nfree == 0) { + DPRINTF(sc, RTW_DEBUG_XMIT, + ("%s: out of descriptors\n", __func__)); + ifp->if_flags |= IFF_OACTIVE; + return 0; + } + + ic = &sc->sc_ic; + + if (!IF_IS_EMPTY(&ic->ic_mgtq)) { + IF_DEQUEUE(&ic->ic_mgtq, m0); + *nip = (struct ieee80211_node *)m0->m_pkthdr.rcvif; + m0->m_pkthdr.rcvif = NULL; + DPRINTF(sc, RTW_DEBUG_XMIT, + ("%s: dequeue mgt frame\n", __func__)); + } else if (ic->ic_state != IEEE80211_S_RUN) { + DPRINTF(sc, RTW_DEBUG_XMIT, ("%s: not running\n", __func__)); + return 0; + } else if (!IF_IS_EMPTY(&ic->ic_pwrsaveq)) { + IF_DEQUEUE(&ic->ic_pwrsaveq, m0); + *nip = (struct ieee80211_node *)m0->m_pkthdr.rcvif; + m0->m_pkthdr.rcvif = NULL; + DPRINTF(sc, RTW_DEBUG_XMIT, + ("%s: dequeue pwrsave frame\n", __func__)); + } else { + IFQ_POLL(&ifp->if_snd, m0); + if (m0 == NULL) { + DPRINTF(sc, RTW_DEBUG_XMIT, + ("%s: no frame\n", __func__)); + return 0; + } + DPRINTF(sc, RTW_DEBUG_XMIT, + ("%s: dequeue data frame\n", __func__)); + ifp->if_opackets++; +#if NBPFILTER > 0 + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m0); +#endif + if ((m0 = ieee80211_encap(ifp, m0, nip)) == NULL) { + DPRINTF(sc, RTW_DEBUG_XMIT, + ("%s: encap error\n", __func__)); + ifp->if_oerrors++; + return -1; + } + } + DPRINTF(sc, RTW_DEBUG_XMIT, ("%s: leave\n", __func__)); + *stcp = stc; + *htcp = htc; + *mp = m0; + return 0; +} + +int +rtw_seg_too_short(bus_dmamap_t dmamap) +{ + int i; + for (i = 0; i < dmamap->dm_nsegs; i++) { + if (dmamap->dm_segs[i].ds_len < 4) { + printf("%s: segment too short\n", __func__); + return 1; + } + } + return 0; +} + +/* TBD factor with atw_start */ +struct mbuf * +rtw_dmamap_load_txbuf(bus_dma_tag_t dmat, bus_dmamap_t dmam, struct mbuf *chain, + u_int ndescfree, short *ifflagsp, const char *dvname) +{ + int first, rc; + struct mbuf *m, *m0; + + m0 = chain; + + /* + * Load the DMA map. Copy and try (once) again if the packet + * didn't fit in the alloted number of segments. + */ + for (first = 1; + ((rc = bus_dmamap_load_mbuf(dmat, dmam, m0, + BUS_DMA_WRITE|BUS_DMA_NOWAIT)) != 0 || + dmam->dm_nsegs > ndescfree || rtw_seg_too_short(dmam)) && first; + first = 0) { + if (rc == 0) + bus_dmamap_unload(dmat, dmam); + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + printf("%s: unable to allocate Tx mbuf\n", + dvname); + break; + } + if (m0->m_pkthdr.len > MHLEN) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + printf("%s: cannot allocate Tx cluster\n", + dvname); + m_freem(m); + break; + } + } + m_copydata(m0, 0, m0->m_pkthdr.len, mtod(m, caddr_t)); + m->m_pkthdr.len = m->m_len = m0->m_pkthdr.len; + m_freem(m0); + m0 = m; + m = NULL; + } + if (rc != 0) { + printf("%s: cannot load Tx buffer, rc = %d\n", dvname, rc); + m_freem(m0); + return NULL; + } else if (rtw_seg_too_short(dmam)) { + printf("%s: cannot load Tx buffer, segment too short\n", + dvname); + bus_dmamap_unload(dmat, dmam); + m_freem(m0); + return NULL; + } else if (dmam->dm_nsegs > ndescfree) { + *ifflagsp |= IFF_OACTIVE; + bus_dmamap_unload(dmat, dmam); + m_freem(m0); + return NULL; + } + return m0; +} + +#ifdef RTW_DEBUG +void +rtw_print_txdesc(struct rtw_softc *sc, const char *action, + struct rtw_txctl *stx, struct rtw_txdesc_blk *htc, int desc) +{ + struct rtw_txdesc *htx = &htc->htc_desc[desc]; + DPRINTF(sc, RTW_DEBUG_XMIT_DESC, ("%s: %p %s txdesc[%d] ctl0 %#08x " + "ctl1 %#08x buf %#08x len %#08x\n", + sc->sc_dev.dv_xname, stx, action, desc, + letoh32(htx->htx_ctl0), + letoh32(htx->htx_ctl1), letoh32(htx->htx_buf), + letoh32(htx->htx_len))); +} +#endif /* RTW_DEBUG */ + +void +rtw_start(struct ifnet *ifp) +{ + uint8_t tppoll; + int desc, i, lastdesc, npkt, rate; + uint32_t proto_ctl0, ctl0, ctl1; + bus_dmamap_t dmamap; + struct ieee80211com *ic; + struct ieee80211_duration *d0; + struct ieee80211_frame *wh; + struct ieee80211_node *ni; + struct mbuf *m0; + struct rtw_softc *sc; + struct rtw_txctl_blk *stc; + struct rtw_txdesc_blk *htc; + struct rtw_txctl *stx; + struct rtw_txdesc *htx; + + sc = (struct rtw_softc *)ifp->if_softc; + ic = &sc->sc_ic; + + DPRINTF(sc, RTW_DEBUG_XMIT, + ("%s: enter %s\n", sc->sc_dev.dv_xname, __func__)); + + /* XXX do real rate control */ + proto_ctl0 = RTW_TXCTL0_RTSRATE_1MBPS; + + switch (rate = MAX(2, ieee80211_get_rate(ic))) { + case 2: + proto_ctl0 |= RTW_TXCTL0_RATE_1MBPS; + break; + case 4: + proto_ctl0 |= RTW_TXCTL0_RATE_2MBPS; + break; + case 11: + proto_ctl0 |= RTW_TXCTL0_RATE_5MBPS; + break; + case 22: + proto_ctl0 |= RTW_TXCTL0_RATE_11MBPS; + break; + } + + if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0) + proto_ctl0 |= RTW_TXCTL0_SPLCP; + + for (;;) { + if (rtw_dequeue(ifp, &stc, &htc, &m0, &ni) == -1) + continue; + if (m0 == NULL) + break; + stx = SIMPLEQ_FIRST(&stc->stc_freeq); + + dmamap = stx->stx_dmamap; + + m0 = rtw_dmamap_load_txbuf(sc->sc_dmat, dmamap, m0, + htc->htc_nfree, &ifp->if_flags, sc->sc_dev.dv_xname); + + if (m0 == NULL || dmamap->dm_nsegs == 0) { + DPRINTF(sc, RTW_DEBUG_XMIT, + ("%s: fail dmamap load\n", __func__)); + goto post_dequeue_err; + } + +#ifdef RTW_DEBUG + if ((sc->sc_if.if_flags & (IFF_DEBUG|IFF_LINK2)) == + (IFF_DEBUG|IFF_LINK2)) { + ieee80211_dump_pkt(mtod(m0, uint8_t *), + (dmamap->dm_nsegs == 1) ? m0->m_pkthdr.len + : sizeof(wh), + rate, 0); + } +#endif /* RTW_DEBUG */ + ctl0 = proto_ctl0 | + LSHIFT(m0->m_pkthdr.len, RTW_TXCTL0_TPKTSIZE_MASK); + + wh = mtod(m0, struct ieee80211_frame *); + + if (ieee80211_compute_duration(wh, m0->m_pkthdr.len, + ic->ic_flags, ic->ic_fragthreshold, + rate, &stx->stx_d0, &stx->stx_dn, &npkt, + (sc->sc_if.if_flags & (IFF_DEBUG|IFF_LINK2)) == + (IFF_DEBUG|IFF_LINK2)) == -1) { + DPRINTF(sc, RTW_DEBUG_XMIT, + ("%s: fail compute duration\n", __func__)); + goto post_load_err; + } + + /* XXX >= ? */ + if (m0->m_pkthdr.len > ic->ic_rtsthreshold) + ctl0 |= RTW_TXCTL0_RTSEN; + + d0 = &stx->stx_d0; + + *(uint16_t*)wh->i_dur = htole16(d0->d_data_dur); + + ctl1 = LSHIFT(d0->d_plcp_len, RTW_TXCTL1_LENGTH_MASK) | + LSHIFT(d0->d_rts_dur, RTW_TXCTL1_RTSDUR_MASK); + + if (d0->d_residue) + ctl1 |= RTW_TXCTL1_LENGEXT; + + /* TBD fragmentation */ + + stx->stx_first = htc->htc_next; + + rtw_txdescs_sync(sc->sc_dmat, sc->sc_desc_dmamap, + htc, stx->stx_first, dmamap->dm_nsegs, + BUS_DMASYNC_PREWRITE); + + KASSERT(stx->stx_first < htc->htc_ndesc); + + for (i = 0, lastdesc = desc = stx->stx_first; + i < dmamap->dm_nsegs; + i++, desc = RTW_NEXT_IDX(htc, desc)) { + if (dmamap->dm_segs[i].ds_len > RTW_TXLEN_LENGTH_MASK) { + DPRINTF(sc, RTW_DEBUG_XMIT_DESC, + ("%s: seg too long\n", __func__)); + goto post_load_err; + } + htx = &htc->htc_desc[desc]; + htx->htx_ctl0 = htole32(ctl0); + if (i != 0) + htx->htx_ctl0 |= htole32(RTW_TXCTL0_OWN); + htx->htx_ctl1 = htole32(ctl1); + htx->htx_buf = htole32(dmamap->dm_segs[i].ds_addr); + htx->htx_len = htole32(dmamap->dm_segs[i].ds_len); + lastdesc = desc; +#ifdef RTW_DEBUG + rtw_print_txdesc(sc, "load", stx, htc, desc); +#endif /* RTW_DEBUG */ + } + + KASSERT(desc < htc->htc_ndesc); + + stx->stx_ni = ni; + stx->stx_mbuf = m0; + stx->stx_last = lastdesc; + htc->htc_desc[stx->stx_last].htx_ctl0 |= htole32(RTW_TXCTL0_LS); + htc->htc_desc[stx->stx_first].htx_ctl0 |= + htole32(RTW_TXCTL0_FS); + +#ifdef RTW_DEBUG + rtw_print_txdesc(sc, "FS on", stx, htc, stx->stx_first); + rtw_print_txdesc(sc, "LS on", stx, htc, stx->stx_last); +#endif /* RTW_DEBUG */ + + htc->htc_nfree -= dmamap->dm_nsegs; + htc->htc_next = desc; + + rtw_txdescs_sync(sc->sc_dmat, sc->sc_desc_dmamap, + htc, stx->stx_first, dmamap->dm_nsegs, + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); + + htc->htc_desc[stx->stx_first].htx_ctl0 |= + htole32(RTW_TXCTL0_OWN); + +#ifdef RTW_DEBUG + rtw_print_txdesc(sc, "OWN on", stx, htc, stx->stx_first); +#endif /* RTW_DEBUG */ + + rtw_txdescs_sync(sc->sc_dmat, sc->sc_desc_dmamap, + htc, stx->stx_first, 1, + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); + + SIMPLEQ_REMOVE_HEAD(&stc->stc_freeq, stx_q); + SIMPLEQ_INSERT_TAIL(&stc->stc_dirtyq, stx, stx_q); + + stc->stc_tx_timer = 5; + ifp->if_timer = 1; + + tppoll = RTW_READ8(&sc->sc_regs, RTW_TPPOLL); + + /* TBD poke other queues. */ + RTW_WRITE8(&sc->sc_regs, RTW_TPPOLL, tppoll | RTW_TPPOLL_NPQ); + RTW_SYNC(&sc->sc_regs, RTW_TPPOLL, RTW_TPPOLL); + } + DPRINTF(sc, RTW_DEBUG_XMIT, ("%s: leave\n", __func__)); + return; +post_load_err: + bus_dmamap_unload(sc->sc_dmat, dmamap); + m_freem(m0); +post_dequeue_err: + if (ni != ic->ic_bss) + ieee80211_free_node(&sc->sc_ic, ni); + return; +} + +void +rtw_watchdog(struct ifnet *ifp) +{ + int pri; + struct rtw_softc *sc; + struct rtw_txctl_blk *stc; + + sc = ifp->if_softc; + + ifp->if_timer = 0; + + if ((sc->sc_flags & RTW_F_ENABLED) == 0) + return; + + for (pri = 0; pri < RTW_NTXPRI; pri++) { + stc = &sc->sc_txctl_blk[pri]; + + if (stc->stc_tx_timer == 0) + continue; + + if (--stc->stc_tx_timer == 0) { + if (SIMPLEQ_EMPTY(&stc->stc_dirtyq)) + continue; + printf("%s: transmit timeout, priority %d\n", + ifp->if_xname, pri); + ifp->if_oerrors++; + /* Stop Tx DMA, disable transmitter, clear + * Tx rings, and restart. + */ + RTW_WRITE8(&sc->sc_regs, RTW_TPPOLL, RTW_TPPOLL_SNPQ); + RTW_SYNC(&sc->sc_regs, RTW_TPPOLL, RTW_TPPOLL); + rtw_io_enable(&sc->sc_regs, RTW_CR_TE, 0); + rtw_txdescs_reset(sc); + rtw_io_enable(&sc->sc_regs, RTW_CR_TE, 1); + rtw_start(ifp); + } else + ifp->if_timer = 1; + } + ieee80211_watchdog(ifp); + return; +} + +void +rtw_start_beacon(struct rtw_softc *sc, int enable) +{ + /* TBD */ + return; +} + +void +rtw_next_scan(void *arg) +{ + struct rtw_softc *sc = arg; + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + int s; + + /* don't call rtw_start w/o network interrupts blocked */ + s = splnet(); + if (ic->ic_state == IEEE80211_S_SCAN) + ieee80211_next_scan(ifp); + splx(s); +} + +void +rtw_join_bss(struct rtw_softc *sc, uint8_t *bssid, enum ieee80211_opmode opmode, + uint16_t intval0) +{ + uint16_t bcnitv, intval; + int i; + struct rtw_regs *regs = &sc->sc_regs; + + for (i = 0; i < IEEE80211_ADDR_LEN; i++) + RTW_WRITE8(regs, RTW_BSSID + i, bssid[i]); + + RTW_SYNC(regs, RTW_BSSID16, RTW_BSSID32); + + rtw_set_access(sc, RTW_ACCESS_CONFIG); + + intval = MIN(intval0, PRESHIFT(RTW_BCNITV_BCNITV_MASK)); + + bcnitv = RTW_READ16(regs, RTW_BCNITV) & ~RTW_BCNITV_BCNITV_MASK; + bcnitv |= LSHIFT(intval, RTW_BCNITV_BCNITV_MASK); + RTW_WRITE16(regs, RTW_BCNITV, bcnitv); + /* magic from Linux */ + RTW_WRITE16(regs, RTW_ATIMWND, LSHIFT(1, RTW_ATIMWND_ATIMWND)); + RTW_WRITE16(regs, RTW_ATIMTRITV, LSHIFT(2, RTW_ATIMTRITV_ATIMTRITV)); + + rtw_set_nettype(sc, opmode); + + rtw_set_access(sc, RTW_ACCESS_NONE); + + /* TBD WEP */ + RTW_WRITE8(regs, RTW_SCR, 0); + + rtw_io_enable(regs, RTW_CR_RE | RTW_CR_TE, 1); +} + +/* Synchronize the hardware state with the software state. */ +int +rtw_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) +{ + struct ifnet *ifp = &ic->ic_if; + struct rtw_softc *sc = ifp->if_softc; + enum ieee80211_state ostate; + int error; + + ostate = ic->ic_state; + + if (nstate == IEEE80211_S_INIT) { + timeout_del(&sc->sc_scan_to); + sc->sc_cur_chan = IEEE80211_CHAN_ANY; + rtw_start_beacon(sc, 0); + return (*sc->sc_mtbl.mt_newstate)(ic, nstate, arg); + } + + if (ostate == IEEE80211_S_INIT && nstate != IEEE80211_S_INIT) + rtw_pwrstate(sc, RTW_ON); + + if ((error = rtw_tune(sc)) != 0) + return error; + + switch (nstate) { + case IEEE80211_S_ASSOC: + rtw_join_bss(sc, ic->ic_bss->ni_bssid, ic->ic_opmode, + ic->ic_bss->ni_intval); + break; + case IEEE80211_S_INIT: + panic("%s: unexpected state IEEE80211_S_INIT\n", __func__); + break; + case IEEE80211_S_SCAN: + if (ostate != IEEE80211_S_SCAN) { + (void)memset(ic->ic_bss->ni_bssid, 0, + IEEE80211_ADDR_LEN); + rtw_join_bss(sc, ic->ic_bss->ni_bssid, ic->ic_opmode, + ic->ic_bss->ni_intval); + } + + timeout_add(&sc->sc_scan_to, rtw_dwelltime * hz / 1000); + + break; + case IEEE80211_S_RUN: + if (ic->ic_opmode == IEEE80211_M_STA) + break; + /*FALLTHROUGH*/ + case IEEE80211_S_AUTH: +#if 0 + rtw_write_bcn_thresh(sc); + rtw_write_ssid(sc); + rtw_write_sup_rates(sc); +#endif + if (ic->ic_opmode == IEEE80211_M_AHDEMO || + ic->ic_opmode == IEEE80211_M_MONITOR) + break; + + /* TBD set listen interval */ + +#if 0 + rtw_tsf(sc); +#endif + break; + } + + if (nstate != IEEE80211_S_SCAN) + timeout_del(&sc->sc_scan_to); + + if (nstate == IEEE80211_S_RUN && + (ic->ic_opmode == IEEE80211_M_HOSTAP || + ic->ic_opmode == IEEE80211_M_IBSS)) + rtw_start_beacon(sc, 1); + else + rtw_start_beacon(sc, 0); + + return (*sc->sc_mtbl.mt_newstate)(ic, nstate, arg); +} + +void +rtw_recv_beacon(struct rtw_softc *sc, struct mbuf *m, + struct ieee80211_node *ni, int subtype, int rssi, u_int32_t rstamp) +{ + (*sc->sc_mtbl.mt_recv_mgmt)(&sc->sc_ic, m, ni, subtype, rssi, rstamp); + return; +} + +void +rtw_recv_mgmt(struct ieee80211com *ic, struct mbuf *m, + struct ieee80211_node *ni, int subtype, int rssi, u_int32_t rstamp) +{ + struct rtw_softc *sc = (struct rtw_softc*)ic->ic_softc; + + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_PROBE_REQ: + /* do nothing: hardware answers probe request XXX */ + break; + case IEEE80211_FC0_SUBTYPE_PROBE_RESP: + case IEEE80211_FC0_SUBTYPE_BEACON: + rtw_recv_beacon(sc, m, ni, subtype, rssi, rstamp); + break; + default: + (*sc->sc_mtbl.mt_recv_mgmt)(ic, m, ni, subtype, rssi, rstamp); + break; + } + return; +} + +struct ieee80211_node * +rtw_node_alloc(struct ieee80211com *ic) +{ + struct rtw_softc *sc = (struct rtw_softc *)ic->ic_if.if_softc; + struct ieee80211_node *ni = (*sc->sc_mtbl.mt_node_alloc)(ic); + + DPRINTF(sc, RTW_DEBUG_NODE, + ("%s: alloc node %p\n", sc->sc_dev.dv_xname, ni)); + return ni; +} + +void +rtw_node_free(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + struct rtw_softc *sc = (struct rtw_softc *)ic->ic_if.if_softc; + + DPRINTF(sc, RTW_DEBUG_NODE, + ("%s: freeing node %p %s\n", sc->sc_dev.dv_xname, ni, + ether_sprintf(ni->ni_bssid))); + (*sc->sc_mtbl.mt_node_free)(ic, ni); +} + +int +rtw_media_change(struct ifnet *ifp) +{ + int error; + + error = ieee80211_media_change(ifp); + if (error == ENETRESET) { + if ((ifp->if_flags & (IFF_RUNNING|IFF_UP)) == + (IFF_RUNNING|IFF_UP)) + rtw_init(ifp); /* XXX lose error */ + error = 0; + } + return error; +} + +void +rtw_media_status(struct ifnet *ifp, struct ifmediareq *imr) +{ + struct rtw_softc *sc = ifp->if_softc; + + if ((sc->sc_flags & RTW_F_ENABLED) == 0) { + imr->ifm_active = IFM_IEEE80211 | IFM_NONE; + imr->ifm_status = 0; + return; + } + ieee80211_media_status(ifp, imr); +} + +void +rtw_power(int why, void *arg) +{ + struct rtw_softc *sc = arg; + struct ifnet *ifp = &sc->sc_ic.ic_if; + int s; + + DPRINTF(sc, RTW_DEBUG_PWR, + ("%s: rtw_power(%d,)\n", sc->sc_dev.dv_xname, why)); + + s = splnet(); + switch (why) { + case PWR_STANDBY: + /* XXX do nothing. */ + break; + case PWR_SUSPEND: + rtw_stop(ifp, 0); + if (sc->sc_power != NULL) + (*sc->sc_power)(sc, why); + break; + case PWR_RESUME: + if (ifp->if_flags & IFF_UP) { + if (sc->sc_power != NULL) + (*sc->sc_power)(sc, why); + rtw_init(ifp); + } + break; + } + splx(s); +} + +/* rtw_shutdown: make sure the interface is stopped at reboot time. */ +void +rtw_shutdown(void *arg) +{ + struct rtw_softc *sc = arg; + + rtw_stop(&sc->sc_ic.ic_if, 1); +} + +static __inline void +rtw_setifprops(struct ifnet *ifp, const char *dvname, void *softc) +{ + (void)memcpy(ifp->if_xname, dvname, IFNAMSIZ); + ifp->if_softc = softc; + ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST | + IFF_NOTRAILERS; + ifp->if_ioctl = rtw_ioctl; + ifp->if_start = rtw_start; + ifp->if_watchdog = rtw_watchdog; +} + +static __inline void +rtw_set80211props(struct ieee80211com *ic) +{ + int nrate; + ic->ic_phytype = IEEE80211_T_DS; + ic->ic_opmode = IEEE80211_M_STA; + ic->ic_caps = IEEE80211_C_PMGT | IEEE80211_C_IBSS | + IEEE80211_C_HOSTAP | IEEE80211_C_MONITOR | IEEE80211_C_WEP; + + nrate = 0; + ic->ic_sup_rates[IEEE80211_MODE_11B].rs_rates[nrate++] = + IEEE80211_RATE_BASIC | 2; + ic->ic_sup_rates[IEEE80211_MODE_11B].rs_rates[nrate++] = + IEEE80211_RATE_BASIC | 4; + ic->ic_sup_rates[IEEE80211_MODE_11B].rs_rates[nrate++] = 11; + ic->ic_sup_rates[IEEE80211_MODE_11B].rs_rates[nrate++] = 22; + ic->ic_sup_rates[IEEE80211_MODE_11B].rs_nrates = nrate; +} + +static __inline void +rtw_set80211methods(struct rtw_mtbl *mtbl, struct ieee80211com *ic) +{ + mtbl->mt_newstate = ic->ic_newstate; + ic->ic_newstate = rtw_newstate; + + mtbl->mt_recv_mgmt = ic->ic_recv_mgmt; + ic->ic_recv_mgmt = rtw_recv_mgmt; + + mtbl->mt_node_free = ic->ic_node_free; + ic->ic_node_free = rtw_node_free; + + mtbl->mt_node_alloc = ic->ic_node_alloc; + ic->ic_node_alloc = rtw_node_alloc; +} + +static __inline void +rtw_establish_hooks(struct rtw_hooks *hooks, const char *dvname, + void *arg) +{ + /* + * Make sure the interface is shutdown during reboot. + */ + hooks->rh_shutdown = shutdownhook_establish(rtw_shutdown, arg); + if (hooks->rh_shutdown == NULL) + printf("%s: WARNING: unable to establish shutdown hook\n", + dvname); + + /* + * Add a suspend hook to make sure we come back up after a + * resume. + */ + hooks->rh_power = powerhook_establish(rtw_power, arg); + if (hooks->rh_power == NULL) + printf("%s: WARNING: unable to establish power hook\n", + dvname); +} + +static __inline void +rtw_disestablish_hooks(struct rtw_hooks *hooks, const char *dvname, + void *arg) +{ + if (hooks->rh_shutdown != NULL) + shutdownhook_disestablish(hooks->rh_shutdown); + + if (hooks->rh_power != NULL) + powerhook_disestablish(hooks->rh_power); +} + +static __inline void +rtw_init_radiotap(struct rtw_softc *sc) +{ + memset(&sc->sc_rxtapu, 0, sizeof(sc->sc_rxtapu)); + sc->sc_rxtap.rr_ihdr.it_len = sizeof(sc->sc_rxtapu); + sc->sc_rxtap.rr_ihdr.it_present = RTW_RX_RADIOTAP_PRESENT; + + memset(&sc->sc_txtapu, 0, sizeof(sc->sc_txtapu)); + sc->sc_txtap.rt_ihdr.it_len = sizeof(sc->sc_txtapu); + sc->sc_txtap.rt_ihdr.it_present = RTW_TX_RADIOTAP_PRESENT; +} + +static int +rtw_txctl_blk_setup(struct rtw_txctl_blk *stc, u_int qlen) +{ + SIMPLEQ_INIT(&stc->stc_dirtyq); + SIMPLEQ_INIT(&stc->stc_freeq); + stc->stc_ndesc = qlen; + stc->stc_desc = malloc(qlen * sizeof(*stc->stc_desc), M_DEVBUF, + M_NOWAIT); + if (stc->stc_desc == NULL) + return ENOMEM; + return 0; +} + +void +rtw_txctl_blk_cleanup_all(struct rtw_softc *sc) +{ + int pri; + struct rtw_txctl_blk *stc; + + for (pri = 0; pri < RTW_NTXPRI; pri++) { + stc = &sc->sc_txctl_blk[pri]; + free(stc->stc_desc, M_DEVBUF); + stc->stc_desc = NULL; + } +} + +int +rtw_txctl_blk_setup_all(struct rtw_softc *sc) +{ + int pri, rc = 0; + int qlen[RTW_NTXPRI] = + {RTW_TXQLENLO, RTW_TXQLENMD, RTW_TXQLENHI, RTW_TXQLENBCN}; + + for (pri = 0; pri < RTW_NTXPRI; pri++) { + rc = rtw_txctl_blk_setup(&sc->sc_txctl_blk[pri], qlen[pri]); + if (rc != 0) + break; + } + return rc; +} + +void +rtw_txdesc_blk_setup(struct rtw_txdesc_blk *htc, struct rtw_txdesc *desc, + u_int ndesc, bus_addr_t ofs, bus_addr_t physbase) +{ + htc->htc_ndesc = ndesc; + htc->htc_desc = desc; + htc->htc_physbase = physbase; + htc->htc_ofs = ofs; + + (void)memset(htc->htc_desc, 0, + sizeof(htc->htc_desc[0]) * htc->htc_ndesc); + + rtw_txdesc_blk_reset(htc); +} + +void +rtw_txdesc_blk_setup_all(struct rtw_softc *sc) +{ + rtw_txdesc_blk_setup(&sc->sc_txdesc_blk[RTW_TXPRILO], + &sc->sc_descs->hd_txlo[0], RTW_NTXDESCLO, + RTW_RING_OFFSET(hd_txlo), RTW_RING_BASE(sc, hd_txlo)); + + rtw_txdesc_blk_setup(&sc->sc_txdesc_blk[RTW_TXPRIMD], + &sc->sc_descs->hd_txmd[0], RTW_NTXDESCMD, + RTW_RING_OFFSET(hd_txmd), RTW_RING_BASE(sc, hd_txmd)); + + rtw_txdesc_blk_setup(&sc->sc_txdesc_blk[RTW_TXPRIHI], + &sc->sc_descs->hd_txhi[0], RTW_NTXDESCHI, + RTW_RING_OFFSET(hd_txhi), RTW_RING_BASE(sc, hd_txhi)); + + rtw_txdesc_blk_setup(&sc->sc_txdesc_blk[RTW_TXPRIBCN], + &sc->sc_descs->hd_bcn[0], RTW_NTXDESCBCN, + RTW_RING_OFFSET(hd_bcn), RTW_RING_BASE(sc, hd_bcn)); +} + +struct rtw_rf * +rtw_rf_attach(struct rtw_softc *sc, enum rtw_rfchipid rfchipid, + rtw_rf_write_t rf_write, int digphy) +{ + struct rtw_rf *rf; + + switch (rfchipid) { + case RTW_RFCHIPID_MAXIM: + rf = rtw_max2820_create(&sc->sc_regs, rf_write, 0); + sc->sc_pwrstate_cb = rtw_maxim_pwrstate; + break; + case RTW_RFCHIPID_PHILIPS: + rf = rtw_sa2400_create(&sc->sc_regs, rf_write, digphy); + sc->sc_pwrstate_cb = rtw_philips_pwrstate; + break; + case RTW_RFCHIPID_RFMD: + /* XXX RFMD has no RF constructor */ + sc->sc_pwrstate_cb = rtw_rfmd_pwrstate; + /*FALLTHROUGH*/ + default: + return NULL; + } + rf->rf_continuous_tx_cb = + (rtw_continuous_tx_cb_t)rtw_continuous_tx_enable; + rf->rf_continuous_tx_arg = (void *)sc; + return rf; +} + +/* Revision C and later use a different PHY delay setting than + * revisions A and B. + */ +u_int8_t +rtw_check_phydelay(struct rtw_regs *regs, u_int32_t rcr0) +{ +#define REVAB (RTW_RCR_MXDMA_UNLIMITED | RTW_RCR_AICV) +#define REVC (REVAB | RTW_RCR_RXFTH_WHOLE) + + u_int8_t phydelay = LSHIFT(0x6, RTW_PHYDELAY_PHYDELAY); + + RTW_WRITE(regs, RTW_RCR, REVAB); + RTW_WBW(regs, RTW_RCR, RTW_RCR); + RTW_WRITE(regs, RTW_RCR, REVC); + + RTW_WBR(regs, RTW_RCR, RTW_RCR); + if ((RTW_READ(regs, RTW_RCR) & REVC) == REVC) + phydelay |= RTW_PHYDELAY_REVC_MAGIC; + + RTW_WRITE(regs, RTW_RCR, rcr0); /* restore RCR */ + RTW_SYNC(regs, RTW_RCR, RTW_RCR); + + return phydelay; +#undef REVC +} + +void +rtw_attach(struct rtw_softc *sc) +{ + rtw_rf_write_t rf_write; + struct rtw_txctl_blk *stc; + int pri, rc, vers; + +#if 0 + CASSERT(RTW_DESC_ALIGNMENT % sizeof(struct rtw_txdesc) == 0, + "RTW_DESC_ALIGNMENT is not a multiple of " + "sizeof(struct rtw_txdesc)"); + + CASSERT(RTW_DESC_ALIGNMENT % sizeof(struct rtw_rxdesc) == 0, + "RTW_DESC_ALIGNMENT is not a multiple of " + "sizeof(struct rtw_rxdesc)"); + + CASSERT(RTW_DESC_ALIGNMENT % RTW_MAXPKTSEGS == 0, + "RTW_DESC_ALIGNMENT is not a multiple of RTW_MAXPKTSEGS"); +#endif + + NEXT_ATTACH_STATE(sc, DETACHED); + + switch (RTW_READ(&sc->sc_regs, RTW_TCR) & RTW_TCR_HWVERID_MASK) { + case RTW_TCR_HWVERID_F: + vers = 'F'; + rf_write = rtw_rf_hostwrite; + break; + case RTW_TCR_HWVERID_D: + vers = 'D'; + if (rtw_host_rfio) + rf_write = rtw_rf_hostwrite; + else + rf_write = rtw_rf_macwrite; + break; + default: + vers = '?'; + rf_write = rtw_rf_macwrite; + break; + } + printf("%s: hardware version %c\n", sc->sc_dev.dv_xname, vers); + + rc = bus_dmamem_alloc(sc->sc_dmat, sizeof(struct rtw_descs), + RTW_DESC_ALIGNMENT, 0, &sc->sc_desc_segs, 1, &sc->sc_desc_nsegs, + 0); + + if (rc != 0) { + printf("%s: could not allocate hw descriptors, error %d\n", + sc->sc_dev.dv_xname, rc); + goto err; + } + + NEXT_ATTACH_STATE(sc, FINISH_DESC_ALLOC); + + rc = bus_dmamem_map(sc->sc_dmat, &sc->sc_desc_segs, + sc->sc_desc_nsegs, sizeof(struct rtw_descs), + (caddr_t*)&sc->sc_descs, BUS_DMA_COHERENT); + + if (rc != 0) { + printf("%s: could not map hw descriptors, error %d\n", + sc->sc_dev.dv_xname, rc); + goto err; + } + NEXT_ATTACH_STATE(sc, FINISH_DESC_MAP); + + rc = bus_dmamap_create(sc->sc_dmat, sizeof(struct rtw_descs), 1, + sizeof(struct rtw_descs), 0, 0, &sc->sc_desc_dmamap); + + if (rc != 0) { + printf("%s: could not create DMA map for hw descriptors, " + "error %d\n", sc->sc_dev.dv_xname, rc); + goto err; + } + NEXT_ATTACH_STATE(sc, FINISH_DESCMAP_CREATE); + + rc = bus_dmamap_load(sc->sc_dmat, sc->sc_desc_dmamap, sc->sc_descs, + sizeof(struct rtw_descs), NULL, 0); + + if (rc != 0) { + printf("%s: could not load DMA map for hw descriptors, " + "error %d\n", sc->sc_dev.dv_xname, rc); + goto err; + } + NEXT_ATTACH_STATE(sc, FINISH_DESCMAP_LOAD); + + if (rtw_txctl_blk_setup_all(sc) != 0) + goto err; + NEXT_ATTACH_STATE(sc, FINISH_TXCTLBLK_SETUP); + + rtw_txdesc_blk_setup_all(sc); + + NEXT_ATTACH_STATE(sc, FINISH_TXDESCBLK_SETUP); + + sc->sc_rxdesc = &sc->sc_descs->hd_rx[0]; + + rtw_rxctls_setup(&sc->sc_rxctl[0]); + + for (pri = 0; pri < RTW_NTXPRI; pri++) { + stc = &sc->sc_txctl_blk[pri]; + + if ((rc = rtw_txdesc_dmamaps_create(sc->sc_dmat, + &stc->stc_desc[0], stc->stc_ndesc)) != 0) { + printf("%s: could not load DMA map for " + "hw tx descriptors, error %d\n", + sc->sc_dev.dv_xname, rc); + goto err; + } + } + + NEXT_ATTACH_STATE(sc, FINISH_TXMAPS_CREATE); + if ((rc = rtw_rxdesc_dmamaps_create(sc->sc_dmat, &sc->sc_rxctl[0], + RTW_RXQLEN)) != 0) { + printf("%s: could not load DMA map for hw rx descriptors, " + "error %d\n", sc->sc_dev.dv_xname, rc); + goto err; + } + NEXT_ATTACH_STATE(sc, FINISH_RXMAPS_CREATE); + + /* Reset the chip to a known state. */ + if (rtw_reset(sc) != 0) + goto err; + NEXT_ATTACH_STATE(sc, FINISH_RESET); + + sc->sc_rcr = RTW_READ(&sc->sc_regs, RTW_RCR); + + if ((sc->sc_rcr & RTW_RCR_9356SEL) != 0) + sc->sc_flags |= RTW_F_9356SROM; + + if (rtw_srom_read(&sc->sc_regs, sc->sc_flags, &sc->sc_srom, + sc->sc_dev.dv_xname) != 0) + goto err; + + NEXT_ATTACH_STATE(sc, FINISH_READ_SROM); + + if (rtw_srom_parse(&sc->sc_srom, &sc->sc_flags, &sc->sc_csthr, + &sc->sc_rfchipid, &sc->sc_rcr, &sc->sc_locale, + sc->sc_dev.dv_xname) != 0) { + printf("%s: attach failed, malformed serial ROM\n", + sc->sc_dev.dv_xname); + goto err; + } + + printf("%s: %s PHY\n", sc->sc_dev.dv_xname, + ((sc->sc_flags & RTW_F_DIGPHY) != 0) ? "digital" : "analog"); + + printf("%s: CS threshold %u\n", sc->sc_dev.dv_xname, sc->sc_csthr); + + NEXT_ATTACH_STATE(sc, FINISH_PARSE_SROM); + + sc->sc_rf = rtw_rf_attach(sc, sc->sc_rfchipid, rf_write, + sc->sc_flags & RTW_F_DIGPHY); + + if (sc->sc_rf == NULL) { + printf("%s: attach failed, could not attach RF\n", + sc->sc_dev.dv_xname); + goto err; + } + +#if 0 + if (rtw_identify_rf(&sc->sc_regs, &sc->sc_rftype, + sc->sc_dev.dv_xname) != 0) { + printf("%s: attach failed, unknown RF unidentified\n", + sc->sc_dev.dv_xname); + goto err; + } +#endif + + NEXT_ATTACH_STATE(sc, FINISH_RF_ATTACH); + + sc->sc_phydelay = rtw_check_phydelay(&sc->sc_regs, sc->sc_rcr); + + RTW_DPRINTF(RTW_DEBUG_ATTACH, + ("%s: PHY delay %d\n", sc->sc_dev.dv_xname, sc->sc_phydelay)); + + if (sc->sc_locale == RTW_LOCALE_UNKNOWN) + rtw_identify_country(&sc->sc_regs, &sc->sc_locale, + sc->sc_dev.dv_xname); + + rtw_init_channels(sc->sc_locale, &sc->sc_ic.ic_channels, + sc->sc_dev.dv_xname); + + if (rtw_identify_sta(&sc->sc_regs, &sc->sc_ic.ic_myaddr, + sc->sc_dev.dv_xname) != 0) + goto err; + NEXT_ATTACH_STATE(sc, FINISH_ID_STA); + + rtw_setifprops(&sc->sc_if, sc->sc_dev.dv_xname, (void*)sc); + + IFQ_SET_READY(&sc->sc_if.if_snd); + + rtw_set80211props(&sc->sc_ic); + + /* + * Call MI attach routines. + */ + if_attach(&sc->sc_if); + ieee80211_ifattach(&sc->sc_if); + + rtw_set80211methods(&sc->sc_mtbl, &sc->sc_ic); + + /* possibly we should fill in our own sc_send_prresp, since + * the RTL8180 is probably sending probe responses in ad hoc + * mode. + */ + + /* complete initialization */ + ieee80211_media_init(&sc->sc_if, rtw_media_change, rtw_media_status); + timeout_set(&sc->sc_scan_to, rtw_next_scan, sc); + +#if NBPFILTER > 0 + bpfattach(&sc->sc_radiobpf, &sc->sc_ic.ic_if, DLT_IEEE802_11_RADIO, + sizeof(struct ieee80211_frame) + 64); +#endif + + rtw_establish_hooks(&sc->sc_hooks, sc->sc_dev.dv_xname, (void*)sc); + + rtw_init_radiotap(sc); + + NEXT_ATTACH_STATE(sc, FINISHED); + + return; +err: + rtw_detach(sc); + return; +} + +int +rtw_detach(struct rtw_softc *sc) +{ + int pri; + + switch (sc->sc_attach_state) { + case FINISHED: + rtw_stop(&sc->sc_if, 1); + + rtw_disestablish_hooks(&sc->sc_hooks, sc->sc_dev.dv_xname, + (void*)sc); + timeout_del(&sc->sc_scan_to); + ieee80211_ifdetach(&sc->sc_if); + if_detach(&sc->sc_if); + break; + case FINISH_ID_STA: + case FINISH_RF_ATTACH: + rtw_rf_destroy(sc->sc_rf); + sc->sc_rf = NULL; + /*FALLTHROUGH*/ + case FINISH_PARSE_SROM: + case FINISH_READ_SROM: + rtw_srom_free(&sc->sc_srom); + /*FALLTHROUGH*/ + case FINISH_RESET: + case FINISH_RXMAPS_CREATE: + rtw_rxdesc_dmamaps_destroy(sc->sc_dmat, &sc->sc_rxctl[0], + RTW_RXQLEN); + /*FALLTHROUGH*/ + case FINISH_TXMAPS_CREATE: + for (pri = 0; pri < RTW_NTXPRI; pri++) { + rtw_txdesc_dmamaps_destroy(sc->sc_dmat, + sc->sc_txctl_blk[pri].stc_desc, + sc->sc_txctl_blk[pri].stc_ndesc); + } + /*FALLTHROUGH*/ + case FINISH_TXDESCBLK_SETUP: + case FINISH_TXCTLBLK_SETUP: + rtw_txctl_blk_cleanup_all(sc); + /*FALLTHROUGH*/ + case FINISH_DESCMAP_LOAD: + bus_dmamap_unload(sc->sc_dmat, sc->sc_desc_dmamap); + /*FALLTHROUGH*/ + case FINISH_DESCMAP_CREATE: + bus_dmamap_destroy(sc->sc_dmat, sc->sc_desc_dmamap); + /*FALLTHROUGH*/ + case FINISH_DESC_MAP: + bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_descs, + sizeof(struct rtw_descs)); + /*FALLTHROUGH*/ + case FINISH_DESC_ALLOC: + bus_dmamem_free(sc->sc_dmat, &sc->sc_desc_segs, + sc->sc_desc_nsegs); + /*FALLTHROUGH*/ + case DETACHED: + NEXT_ATTACH_STATE(sc, DETACHED); + break; + } + return 0; +} diff --git a/sys/dev/ic/rtwphy.c b/sys/dev/ic/rtwphy.c new file mode 100644 index 00000000000..d27fbbc23f1 --- /dev/null +++ b/sys/dev/ic/rtwphy.c @@ -0,0 +1,651 @@ +/* $OpenBSD: rtwphy.c,v 1.1 2004/12/29 01:02:31 jsg Exp $ */ +/* $NetBSD: rtwphy.c,v 1.2 2004/12/12 06:37:59 dyoung Exp $ */ +/*- + * Copyright (c) 2004, 2005 David Young. All rights reserved. + * + * Programmed for NetBSD by David Young. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of David Young may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David + * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ +/* + * Control the Philips SA2400 RF front-end and the baseband processor + * built into the Realtek RTL8180. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +int rtw_bbp_preinit(struct rtw_regs *, u_int, int, u_int); +int rtw_bbp_init(struct rtw_regs *, struct rtw_bbpset *, int, + int, u_int8_t, u_int); + +void verify_syna(u_int, u_int32_t); + +int rtw_sa2400_pwrstate(struct rtw_rf *, enum rtw_pwrstate); +int rtw_sa2400_txpower(struct rtw_rf *, u_int8_t); +int rtw_sa2400_tune(struct rtw_rf *, u_int); +int rtw_sa2400_manrx_init(struct rtw_sa2400 *); +int rtw_sa2400_vcocal_start(struct rtw_sa2400 *, int); +int rtw_sa2400_vco_calibration(struct rtw_sa2400 *); +int rtw_sa2400_filter_calibration(struct rtw_sa2400 *); +int rtw_sa2400_dc_calibration(struct rtw_sa2400 *); +int rtw_sa2400_agc_init(struct rtw_sa2400 *); +void rtw_sa2400_destroy(struct rtw_rf *); +int rtw_sa2400_calibrate(struct rtw_rf *, u_int); +int rtw_sa2400_init(struct rtw_rf *, u_int, u_int8_t, + enum rtw_pwrstate); + +int rtw_max2820_pwrstate(struct rtw_rf *, enum rtw_pwrstate); +void rtw_max2820_destroy(struct rtw_rf *); +int rtw_max2820_init(struct rtw_rf *, u_int, u_int8_t, + enum rtw_pwrstate); +int rtw_max2820_txpower(struct rtw_rf *, u_int8_t); +int rtw_max2820_tune(struct rtw_rf *, u_int); + +int +rtw_bbp_preinit(struct rtw_regs *regs, u_int antatten0, int dflantb, + u_int freq) +{ + u_int antatten = antatten0; + if (dflantb) + antatten |= RTW_BBP_ANTATTEN_DFLANTB; + if (freq == 2484) /* channel 14 */ + antatten |= RTW_BBP_ANTATTEN_CHAN14; + return rtw_bbp_write(regs, RTW_BBP_ANTATTEN, antatten); +} + +int +rtw_bbp_init(struct rtw_regs *regs, struct rtw_bbpset *bb, int antdiv, + int dflantb, u_int8_t cs_threshold, u_int freq) +{ + int rc; + u_int32_t sys2, sys3; + + sys2 = bb->bb_sys2; + if (antdiv) + sys2 |= RTW_BBP_SYS2_ANTDIV; + sys3 = bb->bb_sys3 | + LSHIFT(cs_threshold, RTW_BBP_SYS3_CSTHRESH_MASK); + +#define RTW_BBP_WRITE_OR_RETURN(reg, val) \ + if ((rc = rtw_bbp_write(regs, reg, val)) != 0) \ + return rc; + + RTW_BBP_WRITE_OR_RETURN(RTW_BBP_SYS1, bb->bb_sys1); + RTW_BBP_WRITE_OR_RETURN(RTW_BBP_TXAGC, bb->bb_txagc); + RTW_BBP_WRITE_OR_RETURN(RTW_BBP_LNADET, bb->bb_lnadet); + RTW_BBP_WRITE_OR_RETURN(RTW_BBP_IFAGCINI, bb->bb_ifagcini); + RTW_BBP_WRITE_OR_RETURN(RTW_BBP_IFAGCLIMIT, bb->bb_ifagclimit); + RTW_BBP_WRITE_OR_RETURN(RTW_BBP_IFAGCDET, bb->bb_ifagcdet); + + if ((rc = rtw_bbp_preinit(regs, bb->bb_antatten, dflantb, freq)) != 0) + return rc; + + RTW_BBP_WRITE_OR_RETURN(RTW_BBP_TRL, bb->bb_trl); + RTW_BBP_WRITE_OR_RETURN(RTW_BBP_SYS2, sys2); + RTW_BBP_WRITE_OR_RETURN(RTW_BBP_SYS3, sys3); + RTW_BBP_WRITE_OR_RETURN(RTW_BBP_CHESTLIM, bb->bb_chestlim); + RTW_BBP_WRITE_OR_RETURN(RTW_BBP_CHSQLIM, bb->bb_chsqlim); + return 0; +} + +int +rtw_sa2400_txpower(struct rtw_rf *rf, u_int8_t opaque_txpower) +{ + struct rtw_sa2400 *sa = (struct rtw_sa2400 *)rf; + struct rtw_rfbus *bus = &sa->sa_bus; + + return rtw_rfbus_write(bus, RTW_RFCHIPID_PHILIPS, SA2400_TX, + opaque_txpower); +} + +/* make sure we're using the same settings as the reference driver */ +void +verify_syna(u_int freq, u_int32_t val) +{ + u_int32_t expected_val = ~val; + + switch (freq) { + case 2412: + expected_val = 0x0000096c; /* ch 1 */ + break; + case 2417: + expected_val = 0x00080970; /* ch 2 */ + break; + case 2422: + expected_val = 0x00100974; /* ch 3 */ + break; + case 2427: + expected_val = 0x00180978; /* ch 4 */ + break; + case 2432: + expected_val = 0x00000980; /* ch 5 */ + break; + case 2437: + expected_val = 0x00080984; /* ch 6 */ + break; + case 2442: + expected_val = 0x00100988; /* ch 7 */ + break; + case 2447: + expected_val = 0x0018098c; /* ch 8 */ + break; + case 2452: + expected_val = 0x00000994; /* ch 9 */ + break; + case 2457: + expected_val = 0x00080998; /* ch 10 */ + break; + case 2462: + expected_val = 0x0010099c; /* ch 11 */ + break; + case 2467: + expected_val = 0x001809a0; /* ch 12 */ + break; + case 2472: + expected_val = 0x000009a8; /* ch 13 */ + break; + case 2484: + expected_val = 0x000009b4; /* ch 14 */ + break; + } + KASSERT(val == expected_val); +} + +/* freq is in MHz */ +int +rtw_sa2400_tune(struct rtw_rf *rf, u_int freq) +{ + struct rtw_sa2400 *sa = (struct rtw_sa2400 *)rf; + struct rtw_rfbus *bus = &sa->sa_bus; + int rc; + u_int32_t syna, synb, sync; + + /* XO = 44MHz, R = 11, hence N is in units of XO / R = 4MHz. + * + * The channel spacing (5MHz) is not divisible by 4MHz, so + * we set the fractional part of N to compensate. + */ + int n = freq / 4, nf = (freq % 4) * 2; + + syna = LSHIFT(nf, SA2400_SYNA_NF_MASK) | LSHIFT(n, SA2400_SYNA_N_MASK); + verify_syna(freq, syna); + + /* Divide the 44MHz crystal down to 4MHz. Set the fractional + * compensation charge pump value to agree with the fractional + * modulus. + */ + synb = LSHIFT(11, SA2400_SYNB_R_MASK) | SA2400_SYNB_L_NORMAL | + SA2400_SYNB_ON | SA2400_SYNB_ONE | + LSHIFT(80, SA2400_SYNB_FC_MASK); /* agrees w/ SA2400_SYNA_FM = 0 */ + + sync = SA2400_SYNC_CP_NORMAL; + + if ((rc = rtw_rfbus_write(bus, RTW_RFCHIPID_PHILIPS, SA2400_SYNA, + syna)) != 0) + return rc; + if ((rc = rtw_rfbus_write(bus, RTW_RFCHIPID_PHILIPS, SA2400_SYNB, + synb)) != 0) + return rc; + if ((rc = rtw_rfbus_write(bus, RTW_RFCHIPID_PHILIPS, SA2400_SYNC, + sync)) != 0) + return rc; + return rtw_rfbus_write(bus, RTW_RFCHIPID_PHILIPS, SA2400_SYND, 0x0); +} + +int +rtw_sa2400_pwrstate(struct rtw_rf *rf, enum rtw_pwrstate power) +{ + struct rtw_sa2400 *sa = (struct rtw_sa2400 *)rf; + struct rtw_rfbus *bus = &sa->sa_bus; + u_int32_t opmode; + opmode = SA2400_OPMODE_DEFAULTS; + switch (power) { + case RTW_ON: + opmode |= SA2400_OPMODE_MODE_TXRX; + break; + case RTW_SLEEP: + opmode |= SA2400_OPMODE_MODE_WAIT; + break; + case RTW_OFF: + opmode |= SA2400_OPMODE_MODE_SLEEP; + break; + } + + if (sa->sa_digphy) + opmode |= SA2400_OPMODE_DIGIN; + + return rtw_rfbus_write(bus, RTW_RFCHIPID_PHILIPS, SA2400_OPMODE, + opmode); +} + +int +rtw_sa2400_manrx_init(struct rtw_sa2400 *sa) +{ + u_int32_t manrx; + + /* XXX we are not supposed to be in RXMGC mode when we do + * this? + */ + manrx = SA2400_MANRX_AHSN; + manrx |= SA2400_MANRX_TEN; + manrx |= LSHIFT(1023, SA2400_MANRX_RXGAIN_MASK); + + return rtw_rfbus_write(&sa->sa_bus, RTW_RFCHIPID_PHILIPS, SA2400_MANRX, + manrx); +} + +int +rtw_sa2400_vcocal_start(struct rtw_sa2400 *sa, int start) +{ + u_int32_t opmode; + + opmode = SA2400_OPMODE_DEFAULTS; + if (start) + opmode |= SA2400_OPMODE_MODE_VCOCALIB; + else + opmode |= SA2400_OPMODE_MODE_SLEEP; + + if (sa->sa_digphy) + opmode |= SA2400_OPMODE_DIGIN; + + return rtw_rfbus_write(&sa->sa_bus, RTW_RFCHIPID_PHILIPS, SA2400_OPMODE, + opmode); +} + +int +rtw_sa2400_vco_calibration(struct rtw_sa2400 *sa) +{ + int rc; + /* calibrate VCO */ + if ((rc = rtw_sa2400_vcocal_start(sa, 1)) != 0) + return rc; + DELAY(2200); /* 2.2 milliseconds */ + /* XXX superfluous: SA2400 automatically entered SLEEP mode. */ + return rtw_sa2400_vcocal_start(sa, 0); +} + +int +rtw_sa2400_filter_calibration(struct rtw_sa2400 *sa) +{ + u_int32_t opmode; + + opmode = SA2400_OPMODE_DEFAULTS | SA2400_OPMODE_MODE_FCALIB; + if (sa->sa_digphy) + opmode |= SA2400_OPMODE_DIGIN; + + return rtw_rfbus_write(&sa->sa_bus, RTW_RFCHIPID_PHILIPS, SA2400_OPMODE, + opmode); +} + +int +rtw_sa2400_dc_calibration(struct rtw_sa2400 *sa) +{ + struct rtw_rf *rf = &sa->sa_rf; + int rc; + u_int32_t dccal; + + (*rf->rf_continuous_tx_cb)(rf->rf_continuous_tx_arg, 1); + + dccal = SA2400_OPMODE_DEFAULTS | SA2400_OPMODE_MODE_TXRX; + + rc = rtw_rfbus_write(&sa->sa_bus, RTW_RFCHIPID_PHILIPS, SA2400_OPMODE, + dccal); + if (rc != 0) + return rc; + + DELAY(5); /* DCALIB after being in Tx mode for 5 + * microseconds + */ + + dccal &= ~SA2400_OPMODE_MODE_MASK; + dccal |= SA2400_OPMODE_MODE_DCALIB; + + rc = rtw_rfbus_write(&sa->sa_bus, RTW_RFCHIPID_PHILIPS, SA2400_OPMODE, + dccal); + if (rc != 0) + return rc; + + DELAY(20); /* calibration takes at most 20 microseconds */ + + (*rf->rf_continuous_tx_cb)(rf->rf_continuous_tx_arg, 0); + + return 0; +} + +int +rtw_sa2400_agc_init(struct rtw_sa2400 *sa) +{ + u_int32_t agc; + + agc = LSHIFT(25, SA2400_AGC_MAXGAIN_MASK); + agc |= LSHIFT(7, SA2400_AGC_BBPDELAY_MASK); + agc |= LSHIFT(15, SA2400_AGC_LNADELAY_MASK); + agc |= LSHIFT(27, SA2400_AGC_RXONDELAY_MASK); + + return rtw_rfbus_write(&sa->sa_bus, RTW_RFCHIPID_PHILIPS, SA2400_AGC, + agc); +} + +void +rtw_sa2400_destroy(struct rtw_rf *rf) +{ + struct rtw_sa2400 *sa = (struct rtw_sa2400 *)rf; + memset(sa, 0, sizeof(*sa)); + free(sa, M_DEVBUF); +} + +int +rtw_sa2400_calibrate(struct rtw_rf *rf, u_int freq) +{ + struct rtw_sa2400 *sa = (struct rtw_sa2400 *)rf; + int i, rc; + + /* XXX reference driver calibrates VCO twice. Is it a bug? */ + for (i = 0; i < 2; i++) { + if ((rc = rtw_sa2400_vco_calibration(sa)) != 0) + return rc; + } + /* VCO calibration erases synthesizer registers, so re-tune */ + if ((rc = rtw_sa2400_tune(rf, freq)) != 0) + return rc; + if ((rc = rtw_sa2400_filter_calibration(sa)) != 0) + return rc; + /* analog PHY needs DC calibration */ + if (!sa->sa_digphy) + return rtw_sa2400_dc_calibration(sa); + return 0; +} + +int +rtw_sa2400_init(struct rtw_rf *rf, u_int freq, u_int8_t opaque_txpower, + enum rtw_pwrstate power) +{ + struct rtw_sa2400 *sa = (struct rtw_sa2400 *)rf; + int rc; + + if ((rc = rtw_sa2400_txpower(rf, opaque_txpower)) != 0) + return rc; + + /* skip configuration if it's time to sleep or to power-down. */ + if (power == RTW_SLEEP || power == RTW_OFF) + return rtw_sa2400_pwrstate(rf, power); + + /* go to sleep for configuration */ + if ((rc = rtw_sa2400_pwrstate(rf, RTW_SLEEP)) != 0) + return rc; + + if ((rc = rtw_sa2400_tune(rf, freq)) != 0) + return rc; + if ((rc = rtw_sa2400_agc_init(sa)) != 0) + return rc; + if ((rc = rtw_sa2400_manrx_init(sa)) != 0) + return rc; + + if ((rc = rtw_sa2400_calibrate(rf, freq)) != 0) + return rc; + + /* enter Tx/Rx mode */ + return rtw_sa2400_pwrstate(rf, power); +} + +struct rtw_rf * +rtw_sa2400_create(struct rtw_regs *regs, rtw_rf_write_t rf_write, int digphy) +{ + struct rtw_sa2400 *sa; + struct rtw_rfbus *bus; + struct rtw_rf *rf; + struct rtw_bbpset *bb; + + sa = malloc(sizeof(*sa), M_DEVBUF, M_NOWAIT); + if (sa == NULL) + return NULL; + bzero(sa, sizeof(struct rtw_sa2400)); + + sa->sa_digphy = digphy; + + rf = &sa->sa_rf; + bus = &sa->sa_bus; + + rf->rf_init = rtw_sa2400_init; + rf->rf_destroy = rtw_sa2400_destroy; + rf->rf_txpower = rtw_sa2400_txpower; + rf->rf_tune = rtw_sa2400_tune; + rf->rf_pwrstate = rtw_sa2400_pwrstate; + bb = &rf->rf_bbpset; + + /* XXX magic */ + bb->bb_antatten = RTW_BBP_ANTATTEN_PHILIPS_MAGIC; + bb->bb_chestlim = 0x00; + bb->bb_chsqlim = 0xa0; + bb->bb_ifagcdet = 0x64; + bb->bb_ifagcini = 0x90; + bb->bb_ifagclimit = 0x1a; + bb->bb_lnadet = 0xe0; + bb->bb_sys1 = 0x98; + bb->bb_sys2 = 0x47; + bb->bb_sys3 = 0x90; + bb->bb_trl = 0x88; + bb->bb_txagc = 0x38; + + bus->b_regs = regs; + bus->b_write = rf_write; + + return &sa->sa_rf; +} + +/* freq is in MHz */ +int +rtw_max2820_tune(struct rtw_rf *rf, u_int freq) +{ + struct rtw_max2820 *mx = (struct rtw_max2820 *)rf; + struct rtw_rfbus *bus = &mx->mx_bus; + + if (freq < 2400 || freq > 2499) + return -1; + + return rtw_rfbus_write(bus, RTW_RFCHIPID_MAXIM, MAX2820_CHANNEL, + LSHIFT(freq - 2400, MAX2820_CHANNEL_CF_MASK)); +} + +void +rtw_max2820_destroy(struct rtw_rf *rf) +{ + struct rtw_max2820 *mx = (struct rtw_max2820 *)rf; + memset(mx, 0, sizeof(*mx)); + free(mx, M_DEVBUF); +} + +int +rtw_max2820_init(struct rtw_rf *rf, u_int freq, u_int8_t opaque_txpower, + enum rtw_pwrstate power) +{ + struct rtw_max2820 *mx = (struct rtw_max2820 *)rf; + struct rtw_rfbus *bus = &mx->mx_bus; + int rc; + + if ((rc = rtw_rfbus_write(bus, RTW_RFCHIPID_MAXIM, MAX2820_TEST, + MAX2820_TEST_DEFAULT)) != 0) + return rc; + + if ((rc = rtw_rfbus_write(bus, RTW_RFCHIPID_MAXIM, MAX2820_ENABLE, + MAX2820_ENABLE_DEFAULT)) != 0) + return rc; + + /* skip configuration if it's time to sleep or to power-down. */ + if ((rc = rtw_max2820_pwrstate(rf, power)) != 0) + return rc; + else if (power == RTW_OFF || power == RTW_SLEEP) + return 0; + + if ((rc = rtw_rfbus_write(bus, RTW_RFCHIPID_MAXIM, MAX2820_SYNTH, + MAX2820_SYNTH_R_44MHZ)) != 0) + return rc; + + if ((rc = rtw_max2820_tune(rf, freq)) != 0) + return rc; + + /* XXX The MAX2820 datasheet indicates that 1C and 2C should not + * be changed from 7, however, the reference driver sets them + * to 4 and 1, respectively. + */ + if ((rc = rtw_rfbus_write(bus, RTW_RFCHIPID_MAXIM, MAX2820_RECEIVE, + MAX2820_RECEIVE_DL_DEFAULT | + LSHIFT(4, MAX2820A_RECEIVE_1C_MASK) | + LSHIFT(1, MAX2820A_RECEIVE_2C_MASK))) != 0) + return rc; + + return rtw_rfbus_write(bus, RTW_RFCHIPID_MAXIM, MAX2820_TRANSMIT, + MAX2820_TRANSMIT_PA_DEFAULT); +} + +int +rtw_max2820_txpower(struct rtw_rf *rf, u_int8_t opaque_txpower) +{ + /* TBD */ + return 0; +} + +int +rtw_max2820_pwrstate(struct rtw_rf *rf, enum rtw_pwrstate power) +{ + uint32_t enable; + struct rtw_max2820 *mx; + struct rtw_rfbus *bus; + + mx = (struct rtw_max2820 *)rf; + bus = &mx->mx_bus; + + switch (power) { + case RTW_OFF: + case RTW_SLEEP: + default: + enable = 0x0; + break; + case RTW_ON: + enable = MAX2820_ENABLE_DEFAULT; + break; + } + return rtw_rfbus_write(bus, RTW_RFCHIPID_MAXIM, MAX2820_ENABLE, enable); +} + +struct rtw_rf * +rtw_max2820_create(struct rtw_regs *regs, rtw_rf_write_t rf_write, int is_a) +{ + struct rtw_max2820 *mx; + struct rtw_rfbus *bus; + struct rtw_rf *rf; + struct rtw_bbpset *bb; + + mx = malloc(sizeof(*mx), M_DEVBUF, M_NOWAIT); + if (mx == NULL) + return NULL; + bzero(mx, sizeof(struct rtw_max2820)); + + mx->mx_is_a = is_a; + + rf = &mx->mx_rf; + bus = &mx->mx_bus; + + rf->rf_init = rtw_max2820_init; + rf->rf_destroy = rtw_max2820_destroy; + rf->rf_txpower = rtw_max2820_txpower; + rf->rf_tune = rtw_max2820_tune; + rf->rf_pwrstate = rtw_max2820_pwrstate; + bb = &rf->rf_bbpset; + + /* XXX magic */ + bb->bb_antatten = RTW_BBP_ANTATTEN_MAXIM_MAGIC; + bb->bb_chestlim = 0; + bb->bb_chsqlim = 159; + bb->bb_ifagcdet = 100; + bb->bb_ifagcini = 144; + bb->bb_ifagclimit = 26; + bb->bb_lnadet = 248; + bb->bb_sys1 = 136; + bb->bb_sys2 = 71; + bb->bb_sys3 = 155; + bb->bb_trl = 136; + bb->bb_txagc = 8; + + bus->b_regs = regs; + bus->b_write = rf_write; + + return &mx->mx_rf; +} + +/* freq is in MHz */ +int +rtw_phy_init(struct rtw_regs *regs, struct rtw_rf *rf, u_int8_t opaque_txpower, + u_int8_t cs_threshold, u_int freq, int antdiv, int dflantb, + enum rtw_pwrstate power) +{ + int rc; + RTW_DPRINTF(RTW_DEBUG_PHY, + ("%s: txpower %u csthresh %u freq %u antdiv %u dflantb %u " + "pwrstate %s\n", __func__, opaque_txpower, cs_threshold, freq, + antdiv, dflantb, rtw_pwrstate_string(power))); + + /* XXX is this really necessary? */ + if ((rc = rtw_rf_txpower(rf, opaque_txpower)) != 0) + return rc; + if ((rc = rtw_bbp_preinit(regs, rf->rf_bbpset.bb_antatten, dflantb, + freq)) != 0) + return rc; + if ((rc = rtw_rf_tune(rf, freq)) != 0) + return rc; + /* initialize RF */ + if ((rc = rtw_rf_init(rf, freq, opaque_txpower, power)) != 0) + return rc; +#if 0 /* what is this redundant tx power setting here for? */ + if ((rc = rtw_rf_txpower(rf, opaque_txpower)) != 0) + return rc; +#endif + return rtw_bbp_init(regs, &rf->rf_bbpset, antdiv, dflantb, + cs_threshold, freq); +} diff --git a/sys/dev/ic/rtwphy.h b/sys/dev/ic/rtwphy.h new file mode 100644 index 00000000000..01d759f7f7d --- /dev/null +++ b/sys/dev/ic/rtwphy.h @@ -0,0 +1,10 @@ +#ifndef _DEV_IC_RTWPHY_H +#define _DEV_IC_RTWPHY_H + +struct rtw_rf *rtw_sa2400_create(struct rtw_regs *, rtw_rf_write_t, int); +struct rtw_rf *rtw_max2820_create(struct rtw_regs *, rtw_rf_write_t, int); + +int rtw_phy_init(struct rtw_regs *, struct rtw_rf *, u_int8_t, u_int8_t, u_int, + int, int, enum rtw_pwrstate); + +#endif /* _DEV_IC_RTWPHY_H */ diff --git a/sys/dev/ic/rtwphyio.c b/sys/dev/ic/rtwphyio.c new file mode 100644 index 00000000000..0b4cf15dbc4 --- /dev/null +++ b/sys/dev/ic/rtwphyio.c @@ -0,0 +1,363 @@ +/* $OpenBSD: rtwphyio.c,v 1.1 2004/12/29 01:02:31 jsg Exp $ */ +/* $NetBSD: rtwphyio.c,v 1.4 2004/12/25 06:58:37 dyoung Exp $ */ +/*- + * Copyright (c) 2004, 2005 David Young. All rights reserved. + * + * Programmed for NetBSD by David Young. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of David Young may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David + * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ +/* + * Control input/output with the Philips SA2400 RF front-end and + * the baseband processor built into the Realtek RTL8180. + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +u_int32_t rtw_grf5101_host_crypt(u_int, u_int32_t); +u_int32_t rtw_maxim_swizzle(u_int, uint32_t); +u_int32_t rtw_grf5101_mac_crypt(u_int, u_int32_t); + +static int rtw_macbangbits_timeout = 100; + +u_int8_t +rtw_bbp_read(struct rtw_regs *regs, u_int addr) +{ + KASSERT((addr & ~PRESHIFT(RTW_BB_ADDR_MASK)) == 0); + RTW_WRITE(regs, RTW_BB, + LSHIFT(addr, RTW_BB_ADDR_MASK) | RTW_BB_RD_MASK | RTW_BB_WR_MASK); + delay(10); /* XXX */ + RTW_WBR(regs, RTW_BB, RTW_BB); + return MASK_AND_RSHIFT(RTW_READ(regs, RTW_BB), RTW_BB_RD_MASK); +} + +int +rtw_bbp_write(struct rtw_regs *regs, u_int addr, u_int val) +{ +#define BBP_WRITE_ITERS 50 +#define BBP_WRITE_DELAY 1 + int i; + u_int32_t wrbbp, rdbbp; + + RTW_DPRINTF(RTW_DEBUG_PHYIO, + ("%s: bbp[%u] <- %u\n", __func__, addr, val)); + + KASSERT((addr & ~PRESHIFT(RTW_BB_ADDR_MASK)) == 0); + KASSERT((val & ~PRESHIFT(RTW_BB_WR_MASK)) == 0); + + wrbbp = LSHIFT(addr, RTW_BB_ADDR_MASK) | RTW_BB_WREN | + LSHIFT(val, RTW_BB_WR_MASK) | RTW_BB_RD_MASK, + + rdbbp = LSHIFT(addr, RTW_BB_ADDR_MASK) | + RTW_BB_WR_MASK | RTW_BB_RD_MASK; + + RTW_DPRINTF(RTW_DEBUG_PHYIO, + ("%s: rdbbp = %#08x, wrbbp = %#08x\n", __func__, rdbbp, wrbbp)); + + for (i = BBP_WRITE_ITERS; --i >= 0; ) { + RTW_RBW(regs, RTW_BB, RTW_BB); + RTW_WRITE(regs, RTW_BB, wrbbp); + RTW_SYNC(regs, RTW_BB, RTW_BB); + RTW_WRITE(regs, RTW_BB, rdbbp); + RTW_SYNC(regs, RTW_BB, RTW_BB); + delay(BBP_WRITE_DELAY); /* 1 microsecond */ + if (MASK_AND_RSHIFT(RTW_READ(regs, RTW_BB), + RTW_BB_RD_MASK) == val) { + RTW_DPRINTF(RTW_DEBUG_PHYIO, + ("%s: finished in %dus\n", __func__, + BBP_WRITE_DELAY * (BBP_WRITE_ITERS - i))); + return 0; + } + delay(BBP_WRITE_DELAY); /* again */ + } + printf("%s: timeout\n", __func__); + return -1; +} + +/* Help rtw_rf_hostwrite bang bits to RF over 3-wire interface. */ +static __inline void +rtw_rf_hostbangbits(struct rtw_regs *regs, u_int32_t bits, int lo_to_hi, + u_int nbits) +{ + int i; + u_int32_t mask, reg; + + KASSERT(nbits <= 32); + + RTW_DPRINTF(RTW_DEBUG_PHYIO, + ("%s: %u bits, %#08x, %s\n", __func__, nbits, bits, + (lo_to_hi) ? "lo to hi" : "hi to lo")); + + reg = RTW_PHYCFG_HST; + RTW_WRITE(regs, RTW_PHYCFG, reg); + RTW_SYNC(regs, RTW_PHYCFG, RTW_PHYCFG); + + if (lo_to_hi) + mask = 0x1; + else + mask = 1 << (nbits - 1); + + for (i = 0; i < nbits; i++) { + RTW_DPRINTF(RTW_DEBUG_PHYBITIO, + ("%s: bits %#08x mask %#08x -> bit %#08x\n", + __func__, bits, mask, bits & mask)); + + if ((bits & mask) != 0) + reg |= RTW_PHYCFG_HST_DATA; + else + reg &= ~RTW_PHYCFG_HST_DATA; + + reg |= RTW_PHYCFG_HST_CLK; + RTW_WRITE(regs, RTW_PHYCFG, reg); + RTW_SYNC(regs, RTW_PHYCFG, RTW_PHYCFG); + + DELAY(2); /* arbitrary delay */ + + reg &= ~RTW_PHYCFG_HST_CLK; + RTW_WRITE(regs, RTW_PHYCFG, reg); + RTW_SYNC(regs, RTW_PHYCFG, RTW_PHYCFG); + + if (lo_to_hi) + mask <<= 1; + else + mask >>= 1; + } + + reg |= RTW_PHYCFG_HST_EN; + KASSERT((reg & RTW_PHYCFG_HST_CLK) == 0); + RTW_WRITE(regs, RTW_PHYCFG, reg); + RTW_SYNC(regs, RTW_PHYCFG, RTW_PHYCFG); +} + +/* Help rtw_rf_macwrite: tell MAC to bang bits to RF over the 3-wire + * interface. + */ +static __inline int +rtw_rf_macbangbits(struct rtw_regs *regs, u_int32_t reg) +{ + int i; + + RTW_DPRINTF(RTW_DEBUG_PHY, ("%s: %#08x\n", __func__, reg)); + + RTW_WRITE(regs, RTW_PHYCFG, RTW_PHYCFG_MAC_POLL | reg); + + RTW_WBR(regs, RTW_PHYCFG, RTW_PHYCFG); + + for (i = rtw_macbangbits_timeout; --i >= 0; delay(1)) { + if ((RTW_READ(regs, RTW_PHYCFG) & RTW_PHYCFG_MAC_POLL) == 0) { + RTW_DPRINTF(RTW_DEBUG_PHY, + ("%s: finished in %dus\n", __func__, + rtw_macbangbits_timeout - i)); + return 0; + } + RTW_RBR(regs, RTW_PHYCFG, RTW_PHYCFG); /* XXX paranoia? */ + } + + printf("%s: RTW_PHYCFG_MAC_POLL still set.\n", __func__); + return -1; +} + +u_int32_t +rtw_grf5101_host_crypt(u_int addr, u_int32_t val) +{ + /* TBD */ + return 0; +} + +u_int32_t +rtw_grf5101_mac_crypt(u_int addr, u_int32_t val) +{ + u_int32_t data_and_addr; +#define EXTRACT_NIBBLE(d, which) (((d) >> (4 * (which))) & 0xf) + static u_int8_t caesar[16] = {0x0, 0x8, 0x4, 0xc, + 0x2, 0xa, 0x6, 0xe, + 0x1, 0x9, 0x5, 0xd, + 0x3, 0xb, 0x7, 0xf}; + + data_and_addr = caesar[EXTRACT_NIBBLE(val, 2)] | + (caesar[EXTRACT_NIBBLE(val, 1)] << 4) | + (caesar[EXTRACT_NIBBLE(val, 0)] << 8) | + (caesar[(addr >> 1) & 0xf] << 12) | + ((addr & 0x1) << 16) | + (caesar[EXTRACT_NIBBLE(val, 3)] << 24); + return LSHIFT(data_and_addr, + RTW_PHYCFG_MAC_PHILIPS_ADDR_MASK|RTW_PHYCFG_MAC_PHILIPS_DATA_MASK); +#undef EXTRACT_NIBBLE +} + +static __inline const char * +rtw_rfchipid_string(enum rtw_rfchipid rfchipid) +{ + switch (rfchipid) { + case RTW_RFCHIPID_MAXIM: + return "Maxim"; + case RTW_RFCHIPID_PHILIPS: + return "Philips"; + case RTW_RFCHIPID_GCT: + return "GCT"; + case RTW_RFCHIPID_RFMD: + return "RFMD"; + case RTW_RFCHIPID_INTERSIL: + return "Intersil"; + default: + return "unknown"; + } +} + +/* Bang bits over the 3-wire interface. */ +int +rtw_rf_hostwrite(struct rtw_regs *regs, enum rtw_rfchipid rfchipid, + u_int addr, u_int32_t val) +{ + u_int nbits; + int lo_to_hi; + u_int32_t bits; + + RTW_DPRINTF(RTW_DEBUG_PHYIO, ("%s: %s[%u] <- %#08x\n", __func__, + rtw_rfchipid_string(rfchipid), addr, val)); + + switch (rfchipid) { + case RTW_RFCHIPID_MAXIM: + nbits = 16; + lo_to_hi = 0; + bits = LSHIFT(val, MAX2820_TWI_DATA_MASK) | + LSHIFT(addr, MAX2820_TWI_ADDR_MASK); + break; + case RTW_RFCHIPID_PHILIPS: + KASSERT((addr & ~PRESHIFT(SA2400_TWI_ADDR_MASK)) == 0); + KASSERT((val & ~PRESHIFT(SA2400_TWI_DATA_MASK)) == 0); + bits = LSHIFT(val, SA2400_TWI_DATA_MASK) | + LSHIFT(addr, SA2400_TWI_ADDR_MASK) | SA2400_TWI_WREN; + nbits = 32; + lo_to_hi = 1; + break; + case RTW_RFCHIPID_GCT: + case RTW_RFCHIPID_RFMD: + KASSERT((addr & ~PRESHIFT(SI4126_TWI_ADDR_MASK)) == 0); + KASSERT((val & ~PRESHIFT(SI4126_TWI_DATA_MASK)) == 0); + if (rfchipid == RTW_RFCHIPID_GCT) + bits = rtw_grf5101_host_crypt(addr, val); + else { + bits = LSHIFT(val, SI4126_TWI_DATA_MASK) | + LSHIFT(addr, SI4126_TWI_ADDR_MASK); + } + nbits = 22; + lo_to_hi = 0; + break; + case RTW_RFCHIPID_INTERSIL: + default: + printf("%s: unknown rfchipid %d\n", __func__, rfchipid); + return -1; + } + + rtw_rf_hostbangbits(regs, bits, lo_to_hi, nbits); + + return 0; +} + +u_int32_t +rtw_maxim_swizzle(u_int addr, u_int32_t val) +{ + u_int32_t hidata, lodata; + + KASSERT((val & ~(RTW_MAXIM_LODATA_MASK|RTW_MAXIM_HIDATA_MASK)) == 0); + lodata = MASK_AND_RSHIFT(val, RTW_MAXIM_LODATA_MASK); + hidata = MASK_AND_RSHIFT(val, RTW_MAXIM_HIDATA_MASK); + return LSHIFT(lodata, RTW_PHYCFG_MAC_MAXIM_LODATA_MASK) | + LSHIFT(hidata, RTW_PHYCFG_MAC_MAXIM_HIDATA_MASK) | + LSHIFT(addr, RTW_PHYCFG_MAC_MAXIM_ADDR_MASK); +} + +/* Tell the MAC what to bang over the 3-wire interface. */ +int +rtw_rf_macwrite(struct rtw_regs *regs, enum rtw_rfchipid rfchipid, + u_int addr, u_int32_t val) +{ + u_int32_t reg; + + RTW_DPRINTF(RTW_DEBUG_PHYIO, ("%s: %s[%u] <- %#08x\n", __func__, + rtw_rfchipid_string(rfchipid), addr, val)); + + switch (rfchipid) { + case RTW_RFCHIPID_GCT: + reg = rtw_grf5101_mac_crypt(addr, val); + break; + case RTW_RFCHIPID_MAXIM: + reg = rtw_maxim_swizzle(addr, val); + break; + default: /* XXX */ + case RTW_RFCHIPID_PHILIPS: + KASSERT( + (addr & ~PRESHIFT(RTW_PHYCFG_MAC_PHILIPS_ADDR_MASK)) == 0); + KASSERT( + (val & ~PRESHIFT(RTW_PHYCFG_MAC_PHILIPS_DATA_MASK)) == 0); + + reg = LSHIFT(addr, RTW_PHYCFG_MAC_PHILIPS_ADDR_MASK) | + LSHIFT(val, RTW_PHYCFG_MAC_PHILIPS_DATA_MASK); + } + + switch (rfchipid) { + case RTW_RFCHIPID_GCT: + case RTW_RFCHIPID_MAXIM: + case RTW_RFCHIPID_RFMD: + reg |= RTW_PHYCFG_MAC_RFTYPE_RFMD; + break; + case RTW_RFCHIPID_INTERSIL: + reg |= RTW_PHYCFG_MAC_RFTYPE_INTERSIL; + break; + case RTW_RFCHIPID_PHILIPS: + reg |= RTW_PHYCFG_MAC_RFTYPE_PHILIPS; + break; + default: + printf("%s: unknown rfchipid %d\n", __func__, rfchipid); + return -1; + } + + return rtw_rf_macbangbits(regs, reg); +} diff --git a/sys/dev/ic/rtwphyio.h b/sys/dev/ic/rtwphyio.h new file mode 100644 index 00000000000..90c44c072ff --- /dev/null +++ b/sys/dev/ic/rtwphyio.h @@ -0,0 +1,41 @@ +/* $OpenBSD: rtwphyio.h,v 1.1 2004/12/29 01:02:31 jsg Exp $ */ +/* $NetBSD: rtwphyio.h,v 1.1 2004/09/26 02:29:15 dyoung Exp $ */ +/*- + * Copyright (c) 2004, 2005 David Young. All rights reserved. + * + * Programmed for NetBSD by David Young. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of David Young may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David + * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ +#ifndef _DEV_IC_RTWPHYIO_H +#define _DEV_IC_RTWPHYIO_H + +int rtw_rf_hostwrite(struct rtw_regs *, enum rtw_rfchipid, u_int, u_int32_t); +int rtw_rf_macwrite(struct rtw_regs *, enum rtw_rfchipid, u_int, u_int32_t); +u_int8_t rtw_bbp_read(struct rtw_regs *, u_int); +int rtw_bbp_write(struct rtw_regs *, u_int, u_int); + +#endif /* _DEV_IC_RTWPHYIO_H */ diff --git a/sys/dev/ic/rtwreg.h b/sys/dev/ic/rtwreg.h new file mode 100644 index 00000000000..45144dc73ab --- /dev/null +++ b/sys/dev/ic/rtwreg.h @@ -0,0 +1,1100 @@ +/* $OpenBSD: rtwreg.h,v 1.1 2004/12/29 01:02:31 jsg Exp $ */ +/* $NetBSD: rtwreg.h,v 1.4 2004/12/21 09:07:23 dyoung Exp $ */ + +/* + * Copyright (c) 2003 The NetBSD Foundation, Inc. All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David Young. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY David Young AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL David Young + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Macros for bit twiddling. */ +/* TBD factor w/ dev/ic/atwreg.h. */ + +#ifndef _BIT_TWIDDLE +#define _BIT_TWIDDLE +/* nth bit, BIT(0) == 0x1. */ +#define BIT(n) (((n) == 32) ? 0 : ((u_int32_t)1 << (n))) + +/* bits m through n, m < n. */ +#define BITS(m, n) ((BIT(MAX((m), (n)) + 1) - 1) ^ (BIT(MIN((m), (n))) - 1)) + +/* find least significant bit that is set */ +#define LOWEST_SET_BIT(x) ((((x) - 1) & (x)) ^ (x)) + +/* for x a power of two and p a non-negative integer, is x a greater + * power than 2**p? + */ +#define GTEQ_POWER(x, p) (((u_long)(x) >> (p)) != 0) + +#define MASK_TO_SHIFT2(m) (GTEQ_POWER(LOWEST_SET_BIT((m)), 1) ? 1 : 0) + +#define MASK_TO_SHIFT4(m) \ + (GTEQ_POWER(LOWEST_SET_BIT((m)), 2) \ + ? 2 + MASK_TO_SHIFT2((m) >> 2) \ + : MASK_TO_SHIFT2((m))) + +#define MASK_TO_SHIFT8(m) \ + (GTEQ_POWER(LOWEST_SET_BIT((m)), 4) \ + ? 4 + MASK_TO_SHIFT4((m) >> 4) \ + : MASK_TO_SHIFT4((m))) + +#define MASK_TO_SHIFT16(m) \ + (GTEQ_POWER(LOWEST_SET_BIT((m)), 8) \ + ? 8 + MASK_TO_SHIFT8((m) >> 8) \ + : MASK_TO_SHIFT8((m))) + +#define MASK_TO_SHIFT(m) \ + (GTEQ_POWER(LOWEST_SET_BIT((m)), 16) \ + ? 16 + MASK_TO_SHIFT16((m) >> 16) \ + : MASK_TO_SHIFT16((m))) + +#define MASK_AND_RSHIFT(x, mask) (((x) & (mask)) >> MASK_TO_SHIFT(mask)) +#define LSHIFT(x, mask) ((x) << MASK_TO_SHIFT(mask)) +#define MASK_AND_REPLACE(reg, val, mask) ((reg & ~mask) | LSHIFT(val, mask)) +#define PRESHIFT(m) MASK_AND_RSHIFT((m), (m)) + +#endif /* _BIT_TWIDDLE */ + +/* RTL8180L Host Control and Status Registers */ + +#define RTW_IDR0 0x00 /* ID Register: MAC addr, 6 bytes. + * Auto-loaded from EEPROM. Read by byte, + * by word, or by double word, but write + * only by double word. + */ +#define RTW_IDR1 0x04 + +#define RTW_MAR0 0x08 /* Multicast filter, 64b. */ +#define RTW_MAR1 0x0c + +#define RTW_TSFTRL 0x18 /* Timing Synchronization Function Timer + * Register, low word, 32b, read-only. + */ +#define RTW_TSFTRH 0x1c /* High word, 32b, read-only. */ +#define RTW_TLPDA 0x20 /* Transmit Low Priority Descriptors Start + * Address, 32b, 256-byte alignment. + */ +#define RTW_TNPDA 0x24 /* Transmit Normal Priority Descriptors Start + * Address, 32b, 256-byte alignment. + */ +#define RTW_THPDA 0x28 /* Transmit High Priority Descriptors Start + * Address, 32b, 256-byte alignment. + */ + +#define RTW_BRSR 0x2c /* Basic Rate Set Register, 16b */ +#define RTW_BRSR_BPLCP BIT(8) /* 1: use short PLCP header for CTS/ACK packet, + * 0: use long PLCP header + */ +#define RTW_BRSR_MBR8180_MASK BITS(1,0) /* Maximum Basic Service Rate */ +#define RTW_BRSR_MBR8180_1MBPS LSHIFT(0, RTW_BRSR_MBR8180_MASK) +#define RTW_BRSR_MBR8180_2MBPS LSHIFT(1, RTW_BRSR_MBR8180_MASK) +#define RTW_BRSR_MBR8180_5MBPS LSHIFT(2, RTW_BRSR_MBR8180_MASK) +#define RTW_BRSR_MBR8180_11MBPS LSHIFT(3, RTW_BRSR_MBR8180_MASK) + +/* 8181 and 8180 docs conflict! */ +#define RTW_BRSR_MBR8181_1MBPS BIT(0) +#define RTW_BRSR_MBR8181_2MBPS BIT(1) +#define RTW_BRSR_MBR8181_5MBPS BIT(2) +#define RTW_BRSR_MBR8181_11MBPS BIT(3) + +#define RTW_BSSID 0x2e +/* BSSID, 6 bytes */ +#define RTW_BSSID16 0x2e /* first two bytes */ +#define RTW_BSSID32 (0x2e + 4) /* remaining four bytes */ +#define RTW_BSSID0 RTW_BSSID16 /* BSSID[0], 8b */ +#define RTW_BSSID1 (RTW_BSSID0 + 1) /* BSSID[1], 8b */ +#define RTW_BSSID2 (RTW_BSSID1 + 1) /* BSSID[2], 8b */ +#define RTW_BSSID3 (RTW_BSSID2 + 1) /* BSSID[3], 8b */ +#define RTW_BSSID4 (RTW_BSSID3 + 1) /* BSSID[4], 8b */ +#define RTW_BSSID5 (RTW_BSSID4 + 1) /* BSSID[5], 8b */ + +#define RTW_CR 0x37 /* Command Register, 8b */ +#define RTW_CR_RST BIT(4) /* Reset: host sets to 1 to disable + * transmitter & receiver, reinitialize FIFO. + * RTL8180L sets to 0 to signal completion. + */ +#define RTW_CR_RE BIT(3) /* Receiver Enable: host enables receiver + * by writing 1. RTL8180L indicates receiver + * is active with 1. After power-up, host + * must wait for reset before writing. + */ +#define RTW_CR_TE BIT(2) /* Transmitter Enable: host enables transmitter + * by writing 1. RTL8180L indicates transmitter + * is active with 1. After power-up, host + * must wait for reset before writing. + */ +#define RTW_CR_MULRW BIT(0) /* PCI Multiple Read/Write enable: 1 enables, + * 0 disables. XXX RTL8180, only? + */ + +#define RTW_IMR 0x3c /* Interrupt Mask Register, 16b */ +#define RTW_ISR 0x3e /* Interrupt status register, 16b */ + +#define RTW_INTR_TXFOVW BIT(15) /* Tx FIFO Overflow */ +#define RTW_INTR_TIMEOUT BIT(14) /* Time Out: 1 indicates + * RTW_TSFTR[0:31] = RTW_TINT + */ +#define RTW_INTR_BCNINT BIT(13) /* Beacon Time Out: time for host to + * prepare beacon: + * RTW_TSFTR % (RTW_BCNITV_BCNITV * TU) = + * (RTW_BCNITV_BCNITV * TU - RTW_BINTRITV) + */ +#define RTW_INTR_ATIMINT BIT(12) + /* ATIM Time Out: ATIM interval will pass, + * RTW_TSFTR % (RTW_BCNITV_BCNITV * TU) = + * (RTW_ATIMWND_ATIMWND * TU - RTW_ATIMTRITV) + */ +#define RTW_INTR_TBDER BIT(11) /* Tx Beacon Descriptor Error: + * beacon transmission aborted because + * frame Rx'd + */ +#define RTW_INTR_TBDOK BIT(10) /* Tx Beacon Descriptor OK */ +#define RTW_INTR_THPDER BIT(9) /* Tx High Priority Descriptor Error: + * reached short/long retry limit + */ +#define RTW_INTR_THPDOK BIT(8) /* Tx High Priority Descriptor OK */ +#define RTW_INTR_TNPDER BIT(7) /* Tx Normal Priority Descriptor Error: + * reached short/long retry limit + */ +#define RTW_INTR_TNPDOK BIT(6) /* Tx Normal Priority Descriptor OK */ +#define RTW_INTR_RXFOVW BIT(5) /* Rx FIFO Overflow: either RDU (see below) + * or PCI bus too slow/busy + */ +#define RTW_INTR_RDU BIT(4) /* Rx Descriptor Unavailable */ +#define RTW_INTR_TLPDER BIT(3) /* Tx Normal Priority Descriptor Error + * reached short/long retry limit + */ +#define RTW_INTR_TLPDOK BIT(2) /* Tx Normal Priority Descriptor OK */ +#define RTW_INTR_RER BIT(1) /* Rx Error: CRC32 or ICV error */ +#define RTW_INTR_ROK BIT(0) /* Rx OK */ + +/* Convenient interrupt conjunctions. */ +#define RTW_INTR_RX (RTW_INTR_RER|RTW_INTR_ROK) +#define RTW_INTR_TX (RTW_INTR_TLPDER|RTW_INTR_TLPDOK|RTW_INTR_THPDER|\ + RTW_INTR_THPDOK|RTW_INTR_TNPDER|RTW_INTR_TNPDOK) +#define RTW_INTR_BEACON (RTW_INTR_TBDER|RTW_INTR_TBDOK|RTW_INTR_BCNINT) +#define RTW_INTR_IOERROR (RTW_INTR_TXFOVW|RTW_INTR_RXFOVW|RTW_INTR_RDU) + +#define RTW_TCR 0x40 /* Transmit Configuration Register, 32b */ +#define RTW_TCR_CWMIN BIT(31) /* 1: CWmin = 8, 0: CWmin = 32. */ +#define RTW_TCR_SWSEQ BIT(30) /* 1: host assigns 802.11 sequence number, + * 0: hardware assigns sequence number + */ +/* Hardware version ID, read-only */ +#define RTW_TCR_HWVERID_MASK BITS(29, 25) +#define RTW_TCR_HWVERID_D LSHIFT(26, RTW_TCR_HWVERID_MASK) +#define RTW_TCR_HWVERID_F LSHIFT(27, RTW_TCR_HWVERID_MASK) +#define RTW_TCR_HWVERID_RTL8180 RTW_TCR_HWVERID_F + +/* Set ACK/CTS Timeout (EIFS). + * 1: ACK rate = max(RTW_BRSR_MBR, Rx rate) (XXX not min? typo in datasheet?) + * 0: ACK rate = 1Mbps + */ +#define RTW_TCR_SAT BIT(24) +/* Max DMA Burst Size per Tx DMA Burst */ +#define RTW_TCR_MXDMA_MASK BITS(23,21) +#define RTW_TCR_MXDMA_16 LSHIFT(0, RTW_TCR_MXDMA_MASK) +#define RTW_TCR_MXDMA_32 LSHIFT(1, RTW_TCR_MXDMA_MASK) +#define RTW_TCR_MXDMA_64 LSHIFT(2, RTW_TCR_MXDMA_MASK) +#define RTW_TCR_MXDMA_128 LSHIFT(3, RTW_TCR_MXDMA_MASK) +#define RTW_TCR_MXDMA_256 LSHIFT(4, RTW_TCR_MXDMA_MASK) +#define RTW_TCR_MXDMA_512 LSHIFT(5, RTW_TCR_MXDMA_MASK) +#define RTW_TCR_MXDMA_1024 LSHIFT(6, RTW_TCR_MXDMA_MASK) +#define RTW_TCR_MXDMA_2048 LSHIFT(7, RTW_TCR_MXDMA_MASK) + +#define RTW_TCR_DISCW BIT(20) /* disable 802.11 random backoff */ + +#define RTW_TCR_ICV BIT(19) /* host lets RTL8180 append ICV to + * WEP packets + */ + +/* Loopback Test: disables TXI/TXQ outputs. */ +#define RTW_TCR_LBK_MASK BITS(18,17) +#define RTW_TCR_LBK_NORMAL LSHIFT(0, RTW_TCR_LBK_MASK) /* normal ops */ +#define RTW_TCR_LBK_MAC LSHIFT(1, RTW_TCR_LBK_MASK) /* MAC loopback */ +#define RTW_TCR_LBK_BBP LSHIFT(2, RTW_TCR_LBK_MASK) /* baseband loop. */ +#define RTW_TCR_LBK_CONT LSHIFT(3, RTW_TCR_LBK_MASK) /* continuous Tx */ + +#define RTW_TCR_CRC BIT(16) /* 0: RTL8180 appends CRC32 + * 1: host appends CRC32 + * + * (I *think* this is right. + * The docs have a mysterious + * description in the + * passive voice.) + */ +#define RTW_TCR_SRL_MASK BITS(15,8) /* Short Retry Limit */ +#define RTW_TCR_LRL_MASK BITS(7,0) /* Long Retry Limit */ + +#define RTW_RCR 0x44 /* Receive Configuration Register, 32b */ +#define RTW_RCR_ONLYERLPKT BIT(31) /* only do Early Rx on packets + * longer than 1536 bytes + */ +#define RTW_RCR_ENCS2 BIT(30) /* enable carrier sense method 2 */ +#define RTW_RCR_ENCS1 BIT(29) /* enable carrier sense method 1 */ +#define RTW_RCR_ENMARP BIT(28) /* enable MAC auto-reset PHY */ +#define RTW_RCR_CBSSID BIT(23) /* Check BSSID/ToDS/FromDS: set + * "Link On" when received BSSID + * matches RTW_BSSID and received + * ToDS/FromDS are appropriate + * according to RTW_MSR_NETYPE. + */ +#define RTW_RCR_APWRMGT BIT(22) /* accept packets w/ PWRMGMT bit set */ +#define RTW_RCR_ADD3 BIT(21) /* when RTW_MSR_NETYPE == + * RTW_MSR_NETYPE_INFRA_OK, accept + * broadcast/multicast packets whose + * 3rd address matches RTL8180's MAC. + */ +#define RTW_RCR_AMF BIT(20) /* accept management frames */ +#define RTW_RCR_ACF BIT(19) /* accept control frames */ +#define RTW_RCR_ADF BIT(18) /* accept data frames */ +/* Rx FIFO Threshold: RTL8180 begins PCI transfer when this many data + * bytes are received + */ +#define RTW_RCR_RXFTH_MASK BITS(15,13) +#define RTW_RCR_RXFTH_64 LSHIFT(2, RTW_RCR_RXFTH_MASK) +#define RTW_RCR_RXFTH_128 LSHIFT(3, RTW_RCR_RXFTH_MASK) +#define RTW_RCR_RXFTH_256 LSHIFT(4, RTW_RCR_RXFTH_MASK) +#define RTW_RCR_RXFTH_512 LSHIFT(5, RTW_RCR_RXFTH_MASK) +#define RTW_RCR_RXFTH_1024 LSHIFT(6, RTW_RCR_RXFTH_MASK) +#define RTW_RCR_RXFTH_WHOLE LSHIFT(7, RTW_RCR_RXFTH_MASK) + +#define RTW_RCR_AICV BIT(12) /* accept frames w/ ICV errors */ + +/* Max DMA Burst Size per Rx DMA Burst */ +#define RTW_RCR_MXDMA_MASK BITS(10,8) +#define RTW_RCR_MXDMA_16 LSHIFT(0, RTW_RCR_MXDMA_MASK) +#define RTW_RCR_MXDMA_32 LSHIFT(1, RTW_RCR_MXDMA_MASK) +#define RTW_RCR_MXDMA_64 LSHIFT(2, RTW_RCR_MXDMA_MASK) +#define RTW_RCR_MXDMA_128 LSHIFT(3, RTW_RCR_MXDMA_MASK) +#define RTW_RCR_MXDMA_256 LSHIFT(4, RTW_RCR_MXDMA_MASK) +#define RTW_RCR_MXDMA_512 LSHIFT(5, RTW_RCR_MXDMA_MASK) +#define RTW_RCR_MXDMA_1024 LSHIFT(6, RTW_RCR_MXDMA_MASK) +#define RTW_RCR_MXDMA_UNLIMITED LSHIFT(7, RTW_RCR_MXDMA_MASK) + +/* EEPROM type, read-only. 1: EEPROM is 93c56, 0: 93c46 */ +#define RTW_RCR_9356SEL BIT(6) + +#define RTW_RCR_ACRC32 BIT(5) /* accept frames w/ CRC32 errors */ +#define RTW_RCR_AB BIT(3) /* accept broadcast frames */ +#define RTW_RCR_AM BIT(2) /* accept multicast frames */ +/* accept physical match frames. XXX means PLCP header ok? */ +#define RTW_RCR_APM BIT(1) +#define RTW_RCR_AAP BIT(0) /* accept frames w/ destination */ + +#define RTW_TINT 0x48 /* Timer Interrupt Register, 32b */ +#define RTW_TBDA 0x4c /* Transmit Beacon Descriptor Start Address, + * 32b, 256-byte alignment + */ +#define RTW_9346CR 0x50 /* 93c46/93c56 Command Register, 8b */ +#define RTW_9346CR_EEM_MASK BITS(7,6) /* Operating Mode */ +#define RTW_9346CR_EEM_NORMAL LSHIFT(0, RTW_9346CR_EEM_MASK) +/* Load the EEPROM. Reset registers to defaults. + * Takes ~2ms. RTL8180 indicates completion with RTW_9346CR_EEM_NORMAL. + * XXX RTL8180 only? + */ +#define RTW_9346CR_EEM_AUTOLOAD LSHIFT(1, RTW_9346CR_EEM_MASK) +/* Disable network & bus-master operations and enable + * _EECS, _EESK, _EEDI, _EEDO. + * XXX RTL8180 only? + */ +#define RTW_9346CR_EEM_PROGRAM LSHIFT(2, RTW_9346CR_EEM_MASK) +/* Enable RTW_CONFIG[0123] registers. */ +#define RTW_9346CR_EEM_CONFIG LSHIFT(3, RTW_9346CR_EEM_MASK) +/* EEPROM pin status/control in _EEM_CONFIG, _EEM_AUTOLOAD modes. + * XXX RTL8180 only? + */ +#define RTW_9346CR_EECS BIT(3) +#define RTW_9346CR_EESK BIT(2) +#define RTW_9346CR_EEDI BIT(1) +#define RTW_9346CR_EEDO BIT(0) /* read-only */ + +#define RTW_CONFIG0 0x51 /* Configuration Register 0, 8b */ +#define RTW_CONFIG0_WEP40 BIT(7) /* implements 40-bit WEP, + * XXX RTL8180 only? + */ +#define RTW_CONFIG0_WEP104 BIT(6) /* implements 104-bit WEP, + * from EEPROM, read-only + * XXX RTL8180 only? + */ +#define RTW_CONFIG0_LEDGPOEN BIT(4) /* 1: RTW_PSR_LEDGPO[01] control + * LED[01] pins. + * 0: LED behavior defined by + * RTW_CONFIG1_LEDS10_MASK + * XXX RTL8180 only? + */ +/* auxiliary power is present, read-only */ +#define RTW_CONFIG0_AUXPWR BIT(3) +/* Geographic Location, read-only */ +#define RTW_CONFIG0_GL_MASK BITS(1,0) +/* _RTW_CONFIG0_GL_* is what the datasheet says, but RTW_CONFIG0_GL_* + * work. + */ +#define _RTW_CONFIG0_GL_USA LSHIFT(3, RTW_CONFIG0_GL_MASK) +#define RTW_CONFIG0_GL_EUROPE LSHIFT(2, RTW_CONFIG0_GL_MASK) +#define RTW_CONFIG0_GL_JAPAN LSHIFT(1, RTW_CONFIG0_GL_MASK) +#define RTW_CONFIG0_GL_USA LSHIFT(0, RTW_CONFIG0_GL_MASK) +/* RTL8181 datasheet says RTW_CONFIG0_GL_JAPAN = 0. */ + +#define RTW_CONFIG1 0x52 /* Configuration Register 1, 8b */ + +/* LED configuration. From EEPROM. Read/write. + * + * Setting LED0 LED1 + * ------- ---- ---- + * RTW_CONFIG1_LEDS_ACT_INFRA Activity Infrastructure + * RTW_CONFIG1_LEDS_ACT_LINK Activity Link + * RTW_CONFIG1_LEDS_TX_RX Tx Rx + * RTW_CONFIG1_LEDS_LINKACT_INFRA Link/Activity Infrastructure + */ +#define RTW_CONFIG1_LEDS_MASK BITS(7,6) +#define RTW_CONFIG1_LEDS_ACT_INFRA LSHIFT(0, RTW_CONFIG1_LEDS_MASK) +#define RTW_CONFIG1_LEDS_ACT_LINK LSHIFT(1, RTW_CONFIG1_LEDS_MASK) +#define RTW_CONFIG1_LEDS_TX_RX LSHIFT(2, RTW_CONFIG1_LEDS_MASK) +#define RTW_CONFIG1_LEDS_LINKACT_INFRA LSHIFT(3, RTW_CONFIG1_LEDS_MASK) + +/* LWAKE Output Signal. Only applicable to Cardbus. Pulse width is 150ms. + * + * RTW_CONFIG1_LWACT + * 0 1 + * RTW_CONFIG4_LWPTN 0 active high active low + * 1 positive pulse negative pulse + */ +#define RTW_CONFIG1_LWACT BIT(4) + +#define RTW_CONFIG1_MEMMAP BIT(3) /* using PCI memory space, read-only */ +#define RTW_CONFIG1_IOMAP BIT(2) /* using PCI I/O space, read-only */ +#define RTW_CONFIG1_VPD BIT(1) /* if set, VPD from offsets + * 0x40-0x7f in EEPROM are at + * registers 0x60-0x67 of PCI + * Configuration Space (XXX huh?) + */ +#define RTW_CONFIG1_PMEN BIT(0) /* Power Management Enable: TBD */ + +#define RTW_CONFIG2 0x53 /* Configuration Register 2, 8b */ +#define RTW_CONFIG2_LCK BIT(7) /* clocks are locked, read-only: + * Tx frequency & symbol clocks + * are derived from the same OSC + */ +#define RTW_CONFIG2_ANT BIT(6) /* diversity enabled, read-only */ +#define RTW_CONFIG2_DPS BIT(3) /* Descriptor Polling State: enable + * test mode. + */ +#define RTW_CONFIG2_PAPESIGN BIT(2) /* TBD, from EEPROM */ +#define RTW_CONFIG2_PAPETIME_MASK BITS(1,0) /* TBD, from EEPROM */ + +#define RTW_ANAPARM 0x54 /* Analog parameter, 32b */ +#define RTW_ANAPARM_RFPOW0_MASK BITS(30,28) /* undocumented bits + * which appear to + * control the power + * state of the RF + * components + */ +#define RTW_ANAPARM_RFPOW_MASK \ + (RTW_ANAPARM_RFPOW0_MASK|RTW_ANAPARM_RFPOW1_MASK) + +#define RTW_ANAPARM_TXDACOFF BIT(27) /* 1: disable Tx DAC, + * 0: enable + */ +#define RTW_ANAPARM_RFPOW1_MASK BITS(26,20) /* undocumented bits + * which appear to + * control the power + * state of the RF + * components + */ + +/* + * Maxim On/Sleep/Off control + */ +#define RTW_ANAPARM_RFPOW_MAXIM_ON LSHIFT(0x8, RTW_ANAPARM_RFPOW1_MASK) + +/* reg[RTW_ANAPARM] |= RTW_ANAPARM_TXDACOFF; */ +#define RTW_ANAPARM_RFPOW_MAXIM_SLEEP LSHIFT(0x378, RTW_ANAPARM_RFPOW1_MASK) + +/* reg[RTW_ANAPARM] |= RTW_ANAPARM_TXDACOFF; */ +#define RTW_ANAPARM_RFPOW_MAXIM_OFF LSHIFT(0x379, RTW_ANAPARM_RFPOW1_MASK) + +/* + * RFMD On/Sleep/Off control + */ +#define RTW_ANAPARM_RFPOW_RFMD_ON LSHIFT(0x408, RTW_ANAPARM_RFPOW1_MASK) + +/* reg[RTW_ANAPARM] |= RTW_ANAPARM_TXDACOFF; */ +#define RTW_ANAPARM_RFPOW_RFMD_SLEEP LSHIFT(0x378, RTW_ANAPARM_RFPOW1_MASK) + +/* reg[RTW_ANAPARM] |= RTW_ANAPARM_TXDACOFF; */ +#define RTW_ANAPARM_RFPOW_RFMD_OFF LSHIFT(0x379, RTW_ANAPARM_RFPOW1_MASK) + +/* + * Philips On/Sleep/Off control + */ +#define RTW_ANAPARM_RFPOW_ANA_PHILIPS_ON \ + LSHIFT(0x328, RTW_ANAPARM_RFPOW1_MASK) +#define RTW_ANAPARM_RFPOW_DIG_PHILIPS_ON \ + LSHIFT(0x008, RTW_ANAPARM_RFPOW1_MASK) + +/* reg[RTW_ANAPARM] |= RTW_ANAPARM_TXDACOFF; */ +#define RTW_ANAPARM_RFPOW_PHILIPS_SLEEP\ + LSHIFT(0x378, RTW_ANAPARM_RFPOW1_MASK) + +/* reg[RTW_ANAPARM] |= RTW_ANAPARM_TXDACOFF; */ +#define RTW_ANAPARM_RFPOW_PHILIPS_OFF\ + LSHIFT(0x379, RTW_ANAPARM_RFPOW1_MASK) + +#define RTW_ANAPARM_RFPOW_PHILIPS_ON LSHIFT(0x328, RTW_ANAPARM_RFPOW1_MASK) + +#define RTW_ANAPARM_CARDSP_MASK BITS(19,0) /* undocumented + * card-specific + * bits from the + * EEPROM. + */ + +#define RTW_MSR 0x58 /* Media Status Register, 8b */ +/* Network Type and Link Status */ +#define RTW_MSR_NETYPE_MASK BITS(3,2) +/* AP, XXX RTL8181 only? */ +#define RTW_MSR_NETYPE_AP_OK LSHIFT(3, RTW_MSR_NETYPE_MASK) +/* infrastructure link ok */ +#define RTW_MSR_NETYPE_INFRA_OK LSHIFT(2, RTW_MSR_NETYPE_MASK) +/* ad-hoc link ok */ +#define RTW_MSR_NETYPE_ADHOC_OK LSHIFT(1, RTW_MSR_NETYPE_MASK) +/* no link */ +#define RTW_MSR_NETYPE_NOLINK LSHIFT(0, RTW_MSR_NETYPE_MASK) + +#define RTW_CONFIG3 0x59 /* Configuration Register 3, 8b */ +#define RTW_CONFIG3_GNTSEL BIT(7) /* Grant Select, read-only */ +#define RTW_CONFIG3_PARMEN BIT(6) /* Set RTW_CONFIG3_PARMEN and + * RTW_9346CR_EEM_CONFIG to + * allow RTW_ANAPARM writes. + */ +#define RTW_CONFIG3_MAGIC BIT(5) /* Valid when RTW_CONFIG1_PMEN is + * set. If set, RTL8180 wakes up + * OS when Magic Packet is Rx'd. + */ +#define RTW_CONFIG3_CARDBEN BIT(3) /* Cardbus-related registers + * and functions are enabled, + * read-only. XXX RTL8180 only. + */ +#define RTW_CONFIG3_CLKRUNEN BIT(2) /* CLKRUN enabled, read-only. + * XXX RTL8180 only. + */ +#define RTW_CONFIG3_FUNCREGEN BIT(1) /* Function Registers Enabled, + * read-only. XXX RTL8180 only. + */ +#define RTW_CONFIG3_FBTBEN BIT(0) /* Fast back-to-back enabled, + * read-only. + */ +#define RTW_CONFIG4 0x5A /* Configuration Register 4, 8b */ +#define RTW_CONFIG4_VCOPDN BIT(7) /* VCO Power Down + * 0: normal operation + * (power-on default) + * 1: power-down VCO, RF front-end, + * and most RTL8180 components. + */ +#define RTW_CONFIG4_PWROFF BIT(6) /* Power Off + * 0: normal operation + * (power-on default) + * 1: power-down RF front-end, + * and most RTL8180 components, + * but leave VCO on. + * + * XXX RFMD front-end only? + */ +#define RTW_CONFIG4_PWRMGT BIT(5) /* Power Management + * 0: normal operation + * (power-on default) + * 1: set Tx packet's PWRMGMT bit. + */ +#define RTW_CONFIG4_LWPME BIT(4) /* LANWAKE vs. PMEB: Cardbus-only + * 0: LWAKE & PMEB asserted + * simultaneously + * 1: LWAKE asserted only if + * both PMEB is asserted and + * ISOLATEB is low. + * XXX RTL8180 only. + */ +#define RTW_CONFIG4_LWPTN BIT(2) /* see RTW_CONFIG1_LWACT + * XXX RTL8180 only. + */ +/* Radio Front-End Programming Method */ +#define RTW_CONFIG4_RFTYPE_MASK BITS(1,0) +#define RTW_CONFIG4_RFTYPE_INTERSIL LSHIFT(1, RTW_CONFIG4_RFTYPE_MASK) +#define RTW_CONFIG4_RFTYPE_RFMD LSHIFT(2, RTW_CONFIG4_RFTYPE_MASK) +#define RTW_CONFIG4_RFTYPE_PHILIPS LSHIFT(3, RTW_CONFIG4_RFTYPE_MASK) + +#define RTW_TESTR 0x5B /* TEST mode register, 8b */ + +#define RTW_PSR 0x5e /* Page Select Register, 8b */ +#define RTW_PSR_GPO BIT(7) /* Control/status of pin 52. */ +#define RTW_PSR_GPI BIT(6) /* Status of pin 64. */ +#define RTW_PSR_LEDGPO1 BIT(5) /* Status/control of LED1 pin if + * RTW_CONFIG0_LEDGPOEN is set. + */ +#define RTW_PSR_LEDGPO0 BIT(4) /* Status/control of LED0 pin if + * RTW_CONFIG0_LEDGPOEN is set. + */ +#define RTW_PSR_UWF BIT(1) /* Enable Unicast Wakeup Frame */ +#define RTW_PSR_PSEN BIT(0) /* 1: page 1, 0: page 0 */ + +#define RTW_SCR 0x5f /* Security Configuration Register, 8b */ +#define RTW_SCR_KM_MASK BITS(5,4) /* Key Mode */ +#define RTW_SCR_KM_WEP104 LSHIFT(1, RTW_SCR_KM_MASK) +#define RTW_SCR_KM_WEP40 LSHIFT(0, RTW_SCR_KM_MASK) +#define RTW_SCR_TXSECON BIT(1) /* Enable Tx WEP. Invalid if + * neither RTW_CONFIG0_WEP40 nor + * RTW_CONFIG0_WEP104 is set. + */ +#define RTW_SCR_RXSECON BIT(0) /* Enable Rx WEP. Invalid if + * neither RTW_CONFIG0_WEP40 nor + * RTW_CONFIG0_WEP104 is set. + */ + +#define RTW_BCNITV 0x70 /* Beacon Interval Register, 16b */ +#define RTW_BCNITV_BCNITV_MASK BITS(9,0) /* TU between TBTT, written + * by host. + */ +#define RTW_ATIMWND 0x72 /* ATIM Window Register, 16b */ +#define RTW_ATIMWND_ATIMWND BITS(9,0) /* ATIM Window length in TU, + * written by host. + */ + +#define RTW_BINTRITV 0x74 /* Beacon Interrupt Interval Register, 16b */ +#define RTW_BINTRITV_BINTRITV BITS(9,0) /* RTL8180 wakes host with + * RTW_INTR_BCNINT at BINTRITV + * microseconds before TBTT + */ +#define RTW_ATIMTRITV 0x76 /* ATIM Interrupt Interval Register, 16b */ +#define RTW_ATIMTRITV_ATIMTRITV BITS(9,0) /* RTL8180 wakes host with + * RTW_INTR_ATIMINT at ATIMTRITV + * microseconds before end of + * ATIM Window + */ + +#define RTW_PHYDELAY 0x78 /* PHY Delay Register, 8b */ +#define RTW_PHYDELAY_REVC_MAGIC BIT(3) /* Rev. C magic from reference + * driver + */ +#define RTW_PHYDELAY_PHYDELAY BITS(2,0) /* microsecond Tx delay between + * MAC and RF front-end + */ +#define RTW_CRCOUNT 0x79 /* Carrier Sense Counter, 8b */ +#define RTW_CRCOUNT_MAGIC 0x4c + +#define RTW_CRC16ERR 0x7a /* CRC16 error count, 16b, XXX RTL8181 only? */ + +#define RTW_BB 0x7c /* Baseband interface, 32b */ +/* used for writing RTL8180's integrated baseband processor */ +#define RTW_BB_RD_MASK BITS(23,16) /* data to read */ +#define RTW_BB_WR_MASK BITS(15,8) /* data to write */ +#define RTW_BB_WREN BIT(7) /* write enable */ +#define RTW_BB_ADDR_MASK BITS(6,0) /* address */ + +#define RTW_PHYADDR 0x7c /* Address register for PHY interface, 8b */ +#define RTW_PHYDATAW 0x7d /* Write data to PHY, 8b, write-only */ +#define RTW_PHYDATAR 0x7e /* Read data from PHY, 8b (?), read-only */ + +#define RTW_PHYCFG 0x80 /* PHY Configuration Register, 32b */ +#define RTW_PHYCFG_MAC_POLL BIT(31) /* if !RTW_PHYCFG_HST, + * host sets. MAC clears + * after banging bits. + */ +#define RTW_PHYCFG_HST BIT(30) /* 1: host bangs bits + * 0: MAC bangs bits + */ +#define RTW_PHYCFG_MAC_RFTYPE_MASK BITS(29,28) +#define RTW_PHYCFG_MAC_RFTYPE_INTERSIL LSHIFT(0, RTW_PHYCFG_MAC_RFTYPE_MASK) +#define RTW_PHYCFG_MAC_RFTYPE_RFMD LSHIFT(1, RTW_PHYCFG_MAC_RFTYPE_MASK) +#define RTW_PHYCFG_MAC_RFTYPE_GCT RTW_PHYCFG_MAC_RFTYPE_RFMD +#define RTW_PHYCFG_MAC_RFTYPE_PHILIPS LSHIFT(3, RTW_PHYCFG_MAC_RFTYPE_MASK) +#define RTW_PHYCFG_MAC_PHILIPS_ADDR_MASK BITS(27,24) +#define RTW_PHYCFG_MAC_PHILIPS_DATA_MASK BITS(23,0) +#define RTW_PHYCFG_MAC_MAXIM_LODATA_MASK BITS(27,24) +#define RTW_PHYCFG_MAC_MAXIM_ADDR_MASK BITS(11,8) +#define RTW_PHYCFG_MAC_MAXIM_HIDATA_MASK BITS(7,0) +#define RTW_PHYCFG_HST_EN BIT(2) +#define RTW_PHYCFG_HST_CLK BIT(1) +#define RTW_PHYCFG_HST_DATA BIT(0) + +#define RTW_MAXIM_HIDATA_MASK BITS(11,4) +#define RTW_MAXIM_LODATA_MASK BITS(3,0) + +/** + ** 0x84 - 0xD3, page 1, selected when RTW_PSR[PSEN] == 1. + **/ + +#define RTW_WAKEUP0L 0x84 /* Power Management Wakeup Frame */ +#define RTW_WAKEUP0H 0x88 /* 32b */ + +#define RTW_WAKEUP1L 0x8c +#define RTW_WAKEUP1H 0x90 + +#define RTW_WAKEUP2LL 0x94 +#define RTW_WAKEUP2LH 0x98 + +#define RTW_WAKEUP2HL 0x9c +#define RTW_WAKEUP2HH 0xa0 + +#define RTW_WAKEUP3LL 0xa4 +#define RTW_WAKEUP3LH 0xa8 + +#define RTW_WAKEUP3HL 0xac +#define RTW_WAKEUP3HH 0xb0 + +#define RTW_WAKEUP4LL 0xb4 +#define RTW_WAKEUP4LH 0xb8 + +#define RTW_WAKEUP4HL 0xbc +#define RTW_WAKEUP4HH 0xc0 + +#define RTW_CRC0 0xc4 /* CRC of wakeup frame 0, 16b */ +#define RTW_CRC1 0xc6 /* CRC of wakeup frame 1, 16b */ +#define RTW_CRC2 0xc8 /* CRC of wakeup frame 2, 16b */ +#define RTW_CRC3 0xca /* CRC of wakeup frame 3, 16b */ +#define RTW_CRC4 0xcc /* CRC of wakeup frame 4, 16b */ + +/** + ** 0x84 - 0xD3, page 0, selected when RTW_PSR[PSEN] == 0. + **/ + +/* Default Key Registers, each 128b + * + * If RTW_SCR_KM_WEP104, 104 lsb are the key. + * If RTW_SCR_KM_WEP40, 40 lsb are the key. + */ +#define RTW_DK0 0x90 /* Default Key 0 Register, 128b */ +#define RTW_DK1 0xa0 /* Default Key 1 Register, 128b */ +#define RTW_DK2 0xb0 /* Default Key 2 Register, 128b */ +#define RTW_DK3 0xc0 /* Default Key 3 Register, 128b */ + +#define RTW_CONFIG5 0xd8 /* Configuration Register 5, 8b */ +#define RTW_CONFIG5_TXFIFOOK BIT(7) /* Tx FIFO self-test pass, read-only */ +#define RTW_CONFIG5_RXFIFOOK BIT(6) /* Rx FIFO self-test pass, read-only */ +#define RTW_CONFIG5_CALON BIT(5) /* 1: start calibration cycle + * and raise AGCRESET pin. + * 0: lower AGCRESET pin + */ +#define RTW_CONFIG5_EACPI BIT(2) /* Enable ACPI Wake up, default 0 */ +#define RTW_CONFIG5_LANWAKE BIT(1) /* Enable LAN Wake signal, + * from EEPROM + */ +#define RTW_CONFIG5_PMESTS BIT(0) /* 1: both software & PCI Reset + * reset PME_Status + * 0: only software resets PME_Status + * + * From EEPROM. + */ + +#define RTW_TPPOLL 0xd9 /* Transmit Priority Polling Register, 8b, + * write-only. + */ +#define RTW_TPPOLL_BQ BIT(7) /* RTL8180 clears to notify host of a beacon + * Tx. Host writes have no effect. + */ +#define RTW_TPPOLL_HPQ BIT(6) /* Host writes 1 to notify RTL8180 of + * high-priority Tx packets, RTL8180 clears + * to after high-priority Tx is complete. + */ +#define RTW_TPPOLL_NPQ BIT(5) /* If RTW_CONFIG2_DPS is set, + * host writes 1 to notify RTL8180 of + * normal-priority Tx packets, RTL8180 clears + * after normal-priority Tx is complete. + * + * If RTW_CONFIG2_DPS is clear, host writes + * have no effect. RTL8180 clears after + * normal-priority Tx is complete. + */ +#define RTW_TPPOLL_LPQ BIT(4) /* Host writes 1 to notify RTL8180 of + * low-priority Tx packets, RTL8180 clears + * after low-priority Tx is complete. + */ +#define RTW_TPPOLL_SBQ BIT(3) /* Host writes 1 to tell RTL8180 to + * stop beacon DMA. This bit is invalid + * when RTW_CONFIG2_DPS is set. + */ +#define RTW_TPPOLL_SHPQ BIT(2) /* Host writes 1 to tell RTL8180 to + * stop high-priority DMA. + */ +#define RTW_TPPOLL_SNPQ BIT(2) /* Host writes 1 to tell RTL8180 to + * stop normal-priority DMA. This bit is invalid + * when RTW_CONFIG2_DPS is set. + */ +#define RTW_TPPOLL_SLPQ BIT(2) /* Host writes 1 to tell RTL8180 to + * stop low-priority DMA. + */ +#define RTW_TPPOLL_FSWINT BIT(0) /* Force software interrupt. From + * reference driver. + */ + + +#define RTW_CWR 0xdc /* Contention Window Register, 16b, read-only */ +/* Contention Window: indicates number of contention windows before Tx + */ +#define RTW_CWR_CW BITS(9,0) + +/* Retry Count Register, 16b, read-only */ +#define RTW_RETRYCTR 0xde +/* Retry Count: indicates number of retries after Tx */ +#define RTW_RETRYCTR_RETRYCT BITS(7,0) + +#define RTW_RDSAR 0xe4 /* Receive descriptor Start Address Register, + * 32b, 256-byte alignment. + */ +/* Function Event Register, 32b, Cardbus only. Only valid when + * both RTW_CONFIG3_CARDBEN and RTW_CONFIG3_FUNCREGEN are set. + */ +#define RTW_FER 0xf0 +#define RTW_FER_INTR BIT(15) /* set when RTW_FFER_INTR is set */ +#define RTW_FER_GWAKE BIT(4) /* General Wakeup */ +/* Function Event Mask Register, 32b, Cardbus only. Only valid when + * both RTW_CONFIG3_CARDBEN and RTW_CONFIG3_FUNCREGEN are set. + */ +#define RTW_FEMR 0xf4 +#define RTW_FEMR_INTR BIT(15) /* set when RTW_FFER_INTR is set */ +#define RTW_FEMR_WKUP BIT(14) /* Wakeup Mask */ +#define RTW_FEMR_GWAKE BIT(4) /* General Wakeup */ +/* Function Present State Register, 32b, read-only, Cardbus only. + * Only valid when both RTW_CONFIG3_CARDBEN and RTW_CONFIG3_FUNCREGEN + * are set. + */ +#define RTW_FPSR 0xf8 +#define RTW_FPSR_INTR BIT(15) /* TBD */ +#define RTW_FPSR_GWAKE BIT(4) /* General Wakeup: TBD */ +/* Function Force Event Register, 32b, write-only, Cardbus only. + * Only valid when both RTW_CONFIG3_CARDBEN and RTW_CONFIG3_FUNCREGEN + * are set. + */ +#define RTW_FFER 0xfc +#define RTW_FFER_INTR BIT(15) /* TBD */ +#define RTW_FFER_GWAKE BIT(4) /* General Wakeup: TBD */ + +/* Serial EEPROM offsets */ +#define RTW_SR_ID 0x00 /* 16b */ +#define RTW_SR_VID 0x02 /* 16b */ +#define RTW_SR_DID 0x04 /* 16b */ +#define RTW_SR_SVID 0x06 /* 16b */ +#define RTW_SR_SMID 0x08 /* 16b */ +#define RTW_SR_MNGNT 0x0a +#define RTW_SR_MXLAT 0x0b +#define RTW_SR_RFCHIPID 0x0c +#define RTW_SR_CONFIG3 0x0d +#define RTW_SR_MAC 0x0e /* 6 bytes */ +#define RTW_SR_CONFIG0 0x14 +#define RTW_SR_CONFIG1 0x15 +#define RTW_SR_PMC 0x16 /* Power Management Capabilities, 16b */ +#define RTW_SR_CONFIG2 0x18 +#define RTW_SR_CONFIG4 0x19 +#define RTW_SR_ANAPARM 0x1a /* Analog Parameters, 32b */ +#define RTW_SR_TESTR 0x1e +#define RTW_SR_CONFIG5 0x1f +#define RTW_SR_TXPOWER1 0x20 +#define RTW_SR_TXPOWER2 0x21 +#define RTW_SR_TXPOWER3 0x22 +#define RTW_SR_TXPOWER4 0x23 +#define RTW_SR_TXPOWER5 0x24 +#define RTW_SR_TXPOWER6 0x25 +#define RTW_SR_TXPOWER7 0x26 +#define RTW_SR_TXPOWER8 0x27 +#define RTW_SR_TXPOWER9 0x28 +#define RTW_SR_TXPOWER10 0x29 +#define RTW_SR_TXPOWER11 0x2a +#define RTW_SR_TXPOWER12 0x2b +#define RTW_SR_TXPOWER13 0x2c +#define RTW_SR_TXPOWER14 0x2d +#define RTW_SR_CHANNELPLAN 0x2e /* bitmap of channels to scan */ +#define RTW_SR_ENERGYDETTHR 0x2f /* energy-detect threshold */ +#define RTW_SR_ENERGYDETTHR_DEFAULT 0x0c /* use this if old SROM */ +#define RTW_SR_CISPOINTER 0x30 /* 16b */ +#define RTW_SR_RFPARM 0x32 /* RF-specific parameter */ +#define RTW_SR_RFPARM_DIGPHY BIT(0) /* 1: digital PHY */ +#define RTW_SR_RFPARM_DFLANTB BIT(1) /* 1: antenna B is default */ +#define RTW_SR_RFPARM_CS_MASK BITS(2,3) /* carrier-sense type */ +#define RTW_SR_VERSION 0x3c /* EEPROM content version, 16b */ +#define RTW_SR_CRC 0x3e /* EEPROM content CRC, 16b */ +#define RTW_SR_VPD 0x40 /* Vital Product Data, 64 bytes */ +#define RTW_SR_CIS 0x80 /* CIS Data, 93c56 only, 128 bytes*/ + +/* + * RTL8180 Transmit/Receive Descriptors + */ + +/* the first descriptor in each ring must be on a 256-byte boundary */ +#define RTW_DESC_ALIGNMENT 256 + +/* Tx descriptor */ +struct rtw_txdesc { + u_int32_t htx_ctl0; + u_int32_t htx_ctl1; + u_int32_t htx_buf; + u_int32_t htx_len; + u_int32_t htx_next; + u_int32_t htx_rsvd[3]; +}; + +#define htx_stat htx_ctl0 + +#define RTW_TXCTL0_OWN BIT(31) /* 1: ready to Tx */ +#define RTW_TXCTL0_RSVD0 BIT(30) /* reserved */ +#define RTW_TXCTL0_FS BIT(29) /* first segment */ +#define RTW_TXCTL0_LS BIT(28) /* last segment */ + +#define RTW_TXCTL0_RATE_MASK BITS(27,24) /* Tx rate */ +#define RTW_TXCTL0_RATE_1MBPS LSHIFT(0, RTW_TXCTL0_RATE_MASK) +#define RTW_TXCTL0_RATE_2MBPS LSHIFT(1, RTW_TXCTL0_RATE_MASK) +#define RTW_TXCTL0_RATE_5MBPS LSHIFT(2, RTW_TXCTL0_RATE_MASK) +#define RTW_TXCTL0_RATE_11MBPS LSHIFT(3, RTW_TXCTL0_RATE_MASK) + +#define RTW_TXCTL0_RTSEN BIT(23) /* RTS Enable */ + +#define RTW_TXCTL0_RTSRATE_MASK BITS(22,19) /* Tx rate */ +#define RTW_TXCTL0_RTSRATE_1MBPS LSHIFT(0, RTW_TXCTL0_RTSRATE_MASK) +#define RTW_TXCTL0_RTSRATE_2MBPS LSHIFT(1, RTW_TXCTL0_RTSRATE_MASK) +#define RTW_TXCTL0_RTSRATE_5MBPS LSHIFT(2, RTW_TXCTL0_RTSRATE_MASK) +#define RTW_TXCTL0_RTSRATE_11MBPS LSHIFT(3, RTW_TXCTL0_RTSRATE_MASK) + +#define RTW_TXCTL0_BEACON BIT(18) /* packet is a beacon */ +#define RTW_TXCTL0_MOREFRAG BIT(17) /* another fragment follows */ +#define RTW_TXCTL0_SPLCP BIT(16) /* add short PLCP preamble + * and header + */ +#define RTW_TXCTL0_KEYID_MASK BITS(15,14) /* default key id */ +#define RTW_TXCTL0_RSVD1_MASK BITS(13,12) /* reserved */ +#define RTW_TXCTL0_TPKTSIZE_MASK BITS(11,0) /* Tx packet size + * in bytes + */ + +#define RTW_TXSTAT_OWN RTW_TXCTL0_OWN +#define RTW_TXSTAT_RSVD0 RTW_TXCTL0_RSVD0 +#define RTW_TXSTAT_FS RTW_TXCTL0_FS +#define RTW_TXSTAT_LS RTW_TXCTL0_LS +#define RTW_TXSTAT_RSVD1_MASK BITS(27,16) +#define RTW_TXSTAT_TOK BIT(15) +#define RTW_TXSTAT_RTSRETRY_MASK BITS(14,8) /* RTS retry count */ +#define RTW_TXSTAT_DRC_MASK BITS(7,0) /* Data retry count */ + +#define RTW_TXCTL1_LENGEXT BIT(31) /* supplements _LENGTH + * in packets sent 5.5Mb/s or + * faster + */ +#define RTW_TXCTL1_LENGTH_MASK BITS(30,16) /* PLCP length (microseconds) */ +#define RTW_TXCTL1_RTSDUR_MASK BITS(15,0) /* RTS Duration + * (microseconds) + */ + +#define RTW_TXLEN_LENGTH_MASK BITS(11,0) /* Tx buffer length in bytes */ + +/* Rx descriptor */ +struct rtw_rxdesc { + u_int32_t hrx_ctl; + u_int32_t hrx_rsvd0; + u_int32_t hrx_buf; + u_int32_t hrx_rsvd1; +}; + +#define hrx_stat hrx_ctl +#define hrx_rssi hrx_rsvd0 +#define hrx_tsftl hrx_buf /* valid only when RTW_RXSTAT_LS is set */ +#define hrx_tsfth hrx_rsvd1 /* valid only when RTW_RXSTAT_LS is set */ + +#define RTW_RXCTL_OWN BIT(31) /* 1: owned by NIC */ +#define RTW_RXCTL_EOR BIT(30) /* end of ring */ +#define RTW_RXCTL_FS BIT(29) /* first segment */ +#define RTW_RXCTL_LS BIT(28) /* last segment */ +#define RTW_RXCTL_RSVD0_MASK BITS(29,12) /* reserved */ +#define RTW_RXCTL_LENGTH_MASK BITS(11,0) /* Rx buffer length */ + +#define RTW_RXSTAT_OWN RTW_RXCTL_OWN +#define RTW_RXSTAT_EOR RTW_RXCTL_EOR +#define RTW_RXSTAT_FS RTW_RXCTL_FS /* first segment */ +#define RTW_RXSTAT_LS RTW_RXCTL_LS /* last segment */ +#define RTW_RXSTAT_DMAFAIL BIT(27) /* DMA failure on this pkt */ +#define RTW_RXSTAT_BOVF BIT(26) /* buffer overflow XXX means + * FIFO exhausted? + */ +#define RTW_RXSTAT_SPLCP BIT(25) /* Rx'd with short preamble + * and PLCP header + */ +#define RTW_RXSTAT_RSVD1 BIT(24) /* reserved */ +#define RTW_RXSTAT_RATE_MASK BITS(23,20) /* Rx rate */ +#define RTW_RXSTAT_RATE_1MBPS LSHIFT(0, RTW_RXSTAT_RATE_MASK) +#define RTW_RXSTAT_RATE_2MBPS LSHIFT(1, RTW_RXSTAT_RATE_MASK) +#define RTW_RXSTAT_RATE_5MBPS LSHIFT(2, RTW_RXSTAT_RATE_MASK) +#define RTW_RXSTAT_RATE_11MBPS LSHIFT(3, RTW_RXSTAT_RATE_MASK) +#define RTW_RXSTAT_MIC BIT(19) /* XXX from reference driver */ +#define RTW_RXSTAT_MAR BIT(18) /* is multicast */ +#define RTW_RXSTAT_PAR BIT(17) /* matches RTL8180's MAC */ +#define RTW_RXSTAT_BAR BIT(16) /* is broadcast */ +#define RTW_RXSTAT_RES BIT(15) /* error summary. valid when + * RTW_RXSTAT_LS set. indicates + * that either RTW_RXSTAT_CRC32 + * or RTW_RXSTAT_ICV is set. + */ +#define RTW_RXSTAT_PWRMGT BIT(14) /* 802.11 PWRMGMT bit is set */ +#define RTW_RXSTAT_CRC16 BIT(14) /* XXX CRC16 error, from + * reference driver + */ +#define RTW_RXSTAT_CRC32 BIT(13) /* CRC32 error */ +#define RTW_RXSTAT_ICV BIT(12) /* ICV error */ +#define RTW_RXSTAT_LENGTH_MASK BITS(11,0) /* frame length, including + * CRC32 + */ + +/* Convenient status conjunction. */ +#define RTW_RXSTAT_ONESEG (RTW_RXSTAT_FS|RTW_RXSTAT_LS) +/* Convenient status disjunctions. */ +#define RTW_RXSTAT_IOERROR (RTW_RXSTAT_DMAFAIL|RTW_RXSTAT_BOVF) +#define RTW_RXSTAT_DEBUG (RTW_RXSTAT_SPLCP|RTW_RXSTAT_MAR|\ + RTW_RXSTAT_PAR|RTW_RXSTAT_BAR|\ + RTW_RXSTAT_PWRMGT|RTW_RXSTAT_CRC32|\ + RTW_RXSTAT_ICV) + + +#define RTW_RXRSSI_VLAN BITS(32,16) /* XXX from reference driver */ +/* for Philips RF front-ends */ +#define RTW_RXRSSI_RSSI BITS(15,8) /* RF energy at the PHY */ +/* for RF front-ends by Intersil, Maxim, RFMD */ +#define RTW_RXRSSI_IMR_RSSI BITS(15,9) /* RF energy at the PHY */ +#define RTW_RXRSSI_IMR_LNA BIT(8) /* 1: LNA activated */ +#define RTW_RXRSSI_SQ BITS(7,0) /* Barker code-lock quality */ + +#define RTW_READ8(regs, ofs) \ + bus_space_read_1((regs)->r_bt, (regs)->r_bh, (ofs)) + +#define RTW_READ16(regs, ofs) \ + bus_space_read_2((regs)->r_bt, (regs)->r_bh, (ofs)) + +#define RTW_READ(regs, ofs) \ + bus_space_read_4((regs)->r_bt, (regs)->r_bh, (ofs)) + +#define RTW_WRITE8(regs, ofs, val) \ + bus_space_write_1((regs)->r_bt, (regs)->r_bh, (ofs), (val)) + +#define RTW_WRITE16(regs, ofs, val) \ + bus_space_write_2((regs)->r_bt, (regs)->r_bh, (ofs), (val)) + +#define RTW_WRITE(regs, ofs, val) \ + bus_space_write_4((regs)->r_bt, (regs)->r_bh, (ofs), (val)) + +#define RTW_ISSET(regs, reg, mask) \ + (RTW_READ((regs), (reg)) & (mask)) + +#define RTW_CLR(regs, reg, mask) \ + RTW_WRITE((regs), (reg), RTW_READ((regs), (reg)) & ~(mask)) + +/* bus_space(9) lied? */ +#ifndef BUS_SPACE_BARRIER_SYNC +#define BUS_SPACE_BARRIER_SYNC (BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE) +#endif + +#ifndef BUS_SPACE_BARRIER_READ_BEFORE_READ +#define BUS_SPACE_BARRIER_READ_BEFORE_READ BUS_SPACE_BARRIER_READ +#endif + +#ifndef BUS_SPACE_BARRIER_READ_BEFORE_WRITE +#define BUS_SPACE_BARRIER_READ_BEFORE_WRITE BUS_SPACE_BARRIER_READ +#endif + +#ifndef BUS_SPACE_BARRIER_WRITE_BEFORE_READ +#define BUS_SPACE_BARRIER_WRITE_BEFORE_READ BUS_SPACE_BARRIER_WRITE +#endif + +#ifndef BUS_SPACE_BARRIER_WRITE_BEFORE_WRITE +#define BUS_SPACE_BARRIER_WRITE_BEFORE_WRITE BUS_SPACE_BARRIER_WRITE +#endif + +/* + * Bus barrier + * + * Complete outstanding read and/or write ops on [reg0, reg1] + * ([reg1, reg0]) before starting new ops on the same region. See + * acceptable bus_space_barrier(9) for the flag definitions. + */ +#define RTW_BARRIER(regs, reg0, reg1, flags) \ + bus_space_barrier((regs)->r_bh, (regs)->r_bt, \ + MIN(reg0, reg1), MAX(reg0, reg1) - MIN(reg0, reg1) + 4, flags) + +/* + * Barrier convenience macros. + */ +/* sync */ +#define RTW_SYNC(regs, reg0, reg1) \ + RTW_BARRIER(regs, reg0, reg1, BUS_SPACE_BARRIER_SYNC) + +/* write-before-write */ +#define RTW_WBW(regs, reg0, reg1) \ + RTW_BARRIER(regs, reg0, reg1, BUS_SPACE_BARRIER_WRITE_BEFORE_WRITE) + +/* write-before-read */ +#define RTW_WBR(regs, reg0, reg1) \ + RTW_BARRIER(regs, reg0, reg1, BUS_SPACE_BARRIER_WRITE_BEFORE_READ) + +/* read-before-read */ +#define RTW_RBR(regs, reg0, reg1) \ + RTW_BARRIER(regs, reg0, reg1, BUS_SPACE_BARRIER_READ_BEFORE_READ) + +/* read-before-read */ +#define RTW_RBW(regs, reg0, reg1) \ + RTW_BARRIER(regs, reg0, reg1, BUS_SPACE_BARRIER_READ_BEFORE_WRITE) + +#define RTW_WBRW(regs, reg0, reg1) \ + RTW_BARRIER(regs, reg0, reg1, \ + BUS_SPACE_BARRIER_WRITE_BEFORE_READ | \ + BUS_SPACE_BARRIER_WRITE_BEFORE_WRITE) + +/* + * Registers for RTL8180L's built-in baseband modem. + */ +#define RTW_BBP_SYS1 0x00 +#define RTW_BBP_TXAGC 0x03 +#define RTW_BBP_LNADET 0x04 +#define RTW_BBP_IFAGCINI 0x05 +#define RTW_BBP_IFAGCLIMIT 0x06 +#define RTW_BBP_IFAGCDET 0x07 + +#define RTW_BBP_ANTATTEN 0x10 +#define RTW_BBP_ANTATTEN_PHILIPS_MAGIC 0x91 +#define RTW_BBP_ANTATTEN_INTERSIL_MAGIC 0x92 +#define RTW_BBP_ANTATTEN_RFMD_MAGIC 0x93 +#define RTW_BBP_ANTATTEN_MAXIM_MAGIC 0xb3 +#define RTW_BBP_ANTATTEN_DFLANTB 0x40 +#define RTW_BBP_ANTATTEN_CHAN14 0x0c + +#define RTW_BBP_TRL 0x11 +#define RTW_BBP_SYS2 0x12 +#define RTW_BBP_SYS2_ANTDIV 0x80 /* enable antenna diversity */ +#define RTW_BBP_SYS2_RATE_MASK BITS(5,4) /* loopback rate? + * 0: 1Mbps + * 1: 2Mbps + * 2: 5.5Mbps + * 3: 11Mbps + */ +#define RTW_BBP_SYS3 0x13 +/* carrier-sense threshold */ +#define RTW_BBP_SYS3_CSTHRESH_MASK BITS(0,3) +#define RTW_BBP_CHESTLIM 0x19 +#define RTW_BBP_CHSQLIM 0x1a + diff --git a/sys/dev/ic/rtwvar.h b/sys/dev/ic/rtwvar.h new file mode 100644 index 00000000000..fb91518560a --- /dev/null +++ b/sys/dev/ic/rtwvar.h @@ -0,0 +1,468 @@ +/* $OpenBSD: rtwvar.h,v 1.1 2004/12/29 01:02:31 jsg Exp $ */ +/* $NetBSD: rtwvar.h,v 1.10 2004/12/26 22:37:57 mycroft Exp $ */ +/*- + * Copyright (c) 2004, 2005 David Young. All rights reserved. + * + * Driver for the Realtek RTL8180 802.11 MAC/BBP by David Young. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of David Young may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David + * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#ifndef _DEV_IC_RTWVAR_H_ +#define _DEV_IC_RTWVAR_H_ + +#include +#include + +#ifdef RTW_DEBUG +#define RTW_DEBUG_TUNE 0x000001 +#define RTW_DEBUG_PKTFILT 0x000002 +#define RTW_DEBUG_XMIT 0x000004 +#define RTW_DEBUG_XMIT_DESC 0x000008 +#define RTW_DEBUG_NODE 0x000010 +#define RTW_DEBUG_PWR 0x000020 +#define RTW_DEBUG_ATTACH 0x000040 +#define RTW_DEBUG_REGDUMP 0x000080 +#define RTW_DEBUG_ACCESS 0x000100 +#define RTW_DEBUG_RESET 0x000200 +#define RTW_DEBUG_INIT 0x000400 +#define RTW_DEBUG_IOSTATE 0x000800 +#define RTW_DEBUG_RECV 0x001000 +#define RTW_DEBUG_RECV_DESC 0x002000 +#define RTW_DEBUG_IO_KICK 0x004000 +#define RTW_DEBUG_INTR 0x008000 +#define RTW_DEBUG_PHY 0x010000 +#define RTW_DEBUG_PHYIO 0x020000 +#define RTW_DEBUG_PHYBITIO 0x040000 +#define RTW_DEBUG_TIMEOUT 0x080000 +#define RTW_DEBUG_BUGS 0x100000 +#define RTW_DEBUG_MAX 0x1fffff + +extern int rtw_debug; +#define RTW_DPRINTF(__flags, __x) \ + if ((rtw_debug & (__flags)) != 0) printf __x +#define DPRINTF(__sc, __flags, __x) \ + if (((__sc)->sc_ic.ic_if.if_flags & IFF_DEBUG) != 0) \ + RTW_DPRINTF(__flags, __x) +#else /* RTW_DEBUG */ +#define RTW_DPRINTF(__flags, __x) +#define DPRINTF(__sc, __flags, __x) +#endif /* RTW_DEBUG */ + +#if 0 +enum rtw_rftype { + RTW_RFTYPE_INTERSIL = 0, + RTW_RFTYPE_RFMD, + RTW_RFTYPE_PHILIPS, + RTW_RFTYPE_MAXIM +}; +#endif + +enum rtw_locale { + RTW_LOCALE_USA = 0, + RTW_LOCALE_EUROPE, + RTW_LOCALE_JAPAN, + RTW_LOCALE_UNKNOWN +}; + +enum rtw_rfchipid { + RTW_RFCHIPID_RESERVED = 0, + RTW_RFCHIPID_INTERSIL = 1, + RTW_RFCHIPID_RFMD = 2, + RTW_RFCHIPID_PHILIPS = 3, + RTW_RFCHIPID_MAXIM = 4, + RTW_RFCHIPID_GCT = 5 +}; + +/* sc_flags */ +#define RTW_F_ENABLED 0x00000001 /* chip is enabled */ +#define RTW_F_DIGPHY 0x00000002 /* digital PHY */ +#define RTW_F_DFLANTB 0x00000004 /* B antenna is default */ +#define RTW_F_ANTDIV 0x00000010 /* h/w antenna diversity */ +#define RTW_F_9356SROM 0x00000020 /* 93c56 SROM */ +#define RTW_F_SLEEP 0x00000040 /* chip is asleep */ +#define RTW_F_INVALID 0x00000080 /* chip is absent */ + /* all PHY flags */ +#define RTW_F_ALLPHY (RTW_F_DIGPHY|RTW_F_DFLANTB|RTW_F_ANTDIV) + +struct rtw_regs { + bus_space_tag_t r_bt; + bus_space_handle_t r_bh; +}; + +#define RTW_SR_GET(sr, ofs) \ + (((sr)->sr_content[(ofs)/2] >> (((ofs) % 2 == 0) ? 0 : 8)) & 0xff) + +#define RTW_SR_GET16(sr, ofs) \ + (RTW_SR_GET((sr), (ofs)) | (RTW_SR_GET((sr), (ofs) + 1) << 8)) + +struct rtw_srom { + u_int16_t *sr_content; + u_int16_t sr_size; +}; + +struct rtw_rxctl { + struct mbuf *srx_mbuf; + bus_dmamap_t srx_dmamap; +}; + +struct rtw_txctl { + SIMPLEQ_ENTRY(rtw_txctl) stx_q; + struct mbuf *stx_mbuf; + bus_dmamap_t stx_dmamap; + struct ieee80211_node *stx_ni; /* destination node */ + u_int stx_first; /* 1st hw descriptor */ + u_int stx_last; /* last hw descriptor */ + struct ieee80211_duration stx_d0; + struct ieee80211_duration stx_dn; +}; + +#define RTW_NTXPRI 4 /* number of Tx priorities */ +#define RTW_TXPRILO 0 +#define RTW_TXPRIMD 1 +#define RTW_TXPRIHI 2 +#define RTW_TXPRIBCN 3 /* beacon priority */ + +#define RTW_MAXPKTSEGS 64 /* max 64 segments per Tx packet */ + +#define CASSERT(cond, complaint) complaint[(cond) ? 0 : -1] = complaint[(cond) ? 0 : -1] + +/* Note well: the descriptor rings must begin on RTW_DESC_ALIGNMENT + * boundaries. I allocate them consecutively from one buffer, so + * just round up. + */ +#define RTW_TXQLENLO 64 /* low-priority queue length */ +#define RTW_TXQLENMD 64 /* medium-priority */ +#define RTW_TXQLENHI 64 /* high-priority */ +#define RTW_TXQLENBCN 1 /* beacon */ + +#define RTW_NTXDESCLO RTW_TXQLENLO +#define RTW_NTXDESCMD RTW_TXQLENMD +#define RTW_NTXDESCHI RTW_TXQLENHI +#define RTW_NTXDESCBCN RTW_TXQLENBCN + +#define RTW_NTXDESCTOTAL (RTW_NTXDESCLO + RTW_NTXDESCMD + \ + RTW_NTXDESCHI + RTW_NTXDESCBCN) + +#define RTW_RXQLEN 64 + +struct rtw_txdesc_blk { + u_int htc_ndesc; + u_int htc_next; + u_int htc_nfree; + bus_addr_t htc_physbase; + bus_addr_t htc_ofs; + struct rtw_txdesc *htc_desc; +}; + +#define RTW_NEXT_IDX(__htc, __idx) (((__idx) + 1) % (__htc)->htc_ndesc) + +#define RTW_NEXT_DESC(__htc, __idx) \ + ((__htc)->htc_physbase + \ + sizeof(struct rtw_txdesc) * RTW_NEXT_IDX((__htc), (__idx))) + +SIMPLEQ_HEAD(rtw_txq, rtw_txctl); + +struct rtw_txctl_blk { + /* dirty/free s/w descriptors */ + struct rtw_txq stc_dirtyq; + struct rtw_txq stc_freeq; + u_int stc_ndesc; + int stc_tx_timer; + struct rtw_txctl *stc_desc; +}; + +struct rtw_descs { + struct rtw_txdesc hd_txlo[RTW_NTXDESCLO]; + struct rtw_txdesc hd_txmd[RTW_NTXDESCMD]; + struct rtw_txdesc hd_txhi[RTW_NTXDESCMD]; + struct rtw_rxdesc hd_rx[RTW_RXQLEN]; + struct rtw_txdesc hd_bcn[RTW_NTXDESCBCN]; +}; +#define RTW_DESC_OFFSET(ring, i) offsetof(struct rtw_descs, ring[i]) +#define RTW_RING_OFFSET(ring) RTW_DESC_OFFSET(ring, 0) +#define RTW_RING_BASE(sc, ring) ((sc)->sc_desc_physaddr + \ + RTW_RING_OFFSET(ring)) + +/* Radio capture format for RTL8180. */ + +#define RTW_RX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL) | \ + (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL)) + +struct rtw_rx_radiotap_header { + struct ieee80211_radiotap_header rr_ihdr; + u_int8_t rr_flags; + u_int8_t rr_rate; + u_int16_t rr_chan_freq; + u_int16_t rr_chan_flags; + u_int8_t rr_antsignal; +} __attribute__((__packed__)); + +#define RTW_TX_RADIOTAP_PRESENT ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL)) + +struct rtw_tx_radiotap_header { + struct ieee80211_radiotap_header rt_ihdr; + u_int8_t rt_flags; + u_int8_t rt_rate; + u_int16_t rt_chan_freq; + u_int16_t rt_chan_flags; +} __attribute__((__packed__)); + +enum rtw_attach_state {FINISHED, FINISH_DESCMAP_LOAD, FINISH_DESCMAP_CREATE, + FINISH_DESC_MAP, FINISH_DESC_ALLOC, FINISH_RXMAPS_CREATE, + FINISH_TXMAPS_CREATE, FINISH_RESET, FINISH_READ_SROM, FINISH_PARSE_SROM, + FINISH_RF_ATTACH, FINISH_ID_STA, FINISH_TXDESCBLK_SETUP, + FINISH_TXCTLBLK_SETUP, DETACHED}; + +struct rtw_hooks { + void *rh_shutdown; /* shutdown hook */ + void *rh_power; /* power management hook */ +}; + +struct rtw_mtbl { + int (*mt_newstate)(struct ieee80211com *, + enum ieee80211_state, int); + void (*mt_recv_mgmt)(struct ieee80211com *, + struct mbuf *, struct ieee80211_node *, + int, int, u_int32_t); + struct ieee80211_node *(*mt_node_alloc)(struct ieee80211com *); + void (*mt_node_free)(struct ieee80211com *, + struct ieee80211_node *); +}; + +enum rtw_pwrstate { RTW_OFF = 0, RTW_SLEEP, RTW_ON }; + +typedef void (*rtw_continuous_tx_cb_t)(void *arg, int); + +struct rtw_phy { + struct rtw_rf *p_rf; + struct rtw_regs *p_regs; +}; + +struct rtw_bbpset { + u_int bb_antatten; + u_int bb_chestlim; + u_int bb_chsqlim; + u_int bb_ifagcdet; + u_int bb_ifagcini; + u_int bb_ifagclimit; + u_int bb_lnadet; + u_int bb_sys1; + u_int bb_sys2; + u_int bb_sys3; + u_int bb_trl; + u_int bb_txagc; +}; + +struct rtw_rf { + void (*rf_destroy)(struct rtw_rf *); + /* args: frequency, txpower, power state */ + int (*rf_init)(struct rtw_rf *, u_int, u_int8_t, + enum rtw_pwrstate); + /* arg: power state */ + int (*rf_pwrstate)(struct rtw_rf *, enum rtw_pwrstate); + /* arg: frequency */ + int (*rf_tune)(struct rtw_rf *, u_int); + /* arg: txpower */ + int (*rf_txpower)(struct rtw_rf *, u_int8_t); + rtw_continuous_tx_cb_t rf_continuous_tx_cb; + void *rf_continuous_tx_arg; + struct rtw_bbpset rf_bbpset; +}; + +static __inline void +rtw_rf_destroy(struct rtw_rf *rf) +{ + (*rf->rf_destroy)(rf); +} + +static __inline int +rtw_rf_init(struct rtw_rf *rf, u_int freq, u_int8_t opaque_txpower, + enum rtw_pwrstate power) +{ + return (*rf->rf_init)(rf, freq, opaque_txpower, power); +} + +static __inline int +rtw_rf_pwrstate(struct rtw_rf *rf, enum rtw_pwrstate power) +{ + return (*rf->rf_pwrstate)(rf, power); +} + +static __inline int +rtw_rf_tune(struct rtw_rf *rf, u_int freq) +{ + return (*rf->rf_tune)(rf, freq); +} + +static __inline int +rtw_rf_txpower(struct rtw_rf *rf, u_int8_t opaque_txpower) +{ + return (*rf->rf_txpower)(rf, opaque_txpower); +} + +typedef int (*rtw_rf_write_t)(struct rtw_regs *, enum rtw_rfchipid, u_int, + u_int32_t); + +struct rtw_rfbus { + struct rtw_regs *b_regs; + rtw_rf_write_t b_write; +}; + +static __inline int +rtw_rfbus_write(struct rtw_rfbus *bus, enum rtw_rfchipid rfchipid, u_int addr, + u_int32_t val) +{ + return (*bus->b_write)(bus->b_regs, rfchipid, addr, val); +} + +struct rtw_max2820 { + struct rtw_rf mx_rf; + struct rtw_rfbus mx_bus; + int mx_is_a; /* 1: MAX2820A/MAX2821A */ +}; + +struct rtw_sa2400 { + struct rtw_rf sa_rf; + struct rtw_rfbus sa_bus; + int sa_digphy; /* 1: digital PHY */ +}; + +typedef void (*rtw_pwrstate_t)(struct rtw_regs *, enum rtw_pwrstate, int, int); + +enum rtw_access {RTW_ACCESS_NONE = 0, + RTW_ACCESS_CONFIG = 1, + RTW_ACCESS_ANAPARM = 2}; + +struct rtw_softc { + struct device sc_dev; + struct ieee80211com sc_ic; + struct rtw_regs sc_regs; + bus_dma_tag_t sc_dmat; + u_int32_t sc_flags; + +#if 0 + enum rtw_rftype sc_rftype; +#endif + enum rtw_attach_state sc_attach_state; + enum rtw_rfchipid sc_rfchipid; + enum rtw_locale sc_locale; + u_int8_t sc_phydelay; + + /* s/w Tx/Rx descriptors */ + struct rtw_txctl_blk sc_txctl_blk[RTW_NTXPRI]; + struct rtw_rxctl sc_rxctl[RTW_RXQLEN]; + u_int sc_txq; + u_int sc_txnext; + + struct rtw_txdesc_blk sc_txdesc_blk[RTW_NTXPRI]; + struct rtw_rxdesc *sc_rxdesc; + u_int sc_rxnext; + + struct rtw_descs *sc_descs; + + bus_dma_segment_t sc_desc_segs; + int sc_desc_nsegs; + bus_dmamap_t sc_desc_dmamap; +#define sc_desc_physaddr sc_desc_dmamap->dm_segs[0].ds_addr + + struct rtw_srom sc_srom; + + enum rtw_pwrstate sc_pwrstate; + + rtw_pwrstate_t sc_pwrstate_cb; + + struct rtw_rf *sc_rf; + + u_int16_t sc_inten; + + /* interrupt acknowledge hook */ + void (*sc_intr_ack) __P((struct rtw_regs *)); + + int (*sc_enable)(struct rtw_softc *); + void (*sc_disable)(struct rtw_softc *); + void (*sc_power)(struct rtw_softc *, int); + struct rtw_mtbl sc_mtbl; + struct rtw_hooks sc_hooks; + + caddr_t sc_radiobpf; + + struct timeval sc_last_beacon; + struct timeout sc_scan_to; + u_int sc_cur_chan; + + u_int32_t sc_tsfth; /* most significant TSFT bits */ + u_int32_t sc_rcr; /* RTW_RCR */ + u_int8_t sc_csthr; /* carrier-sense threshold */ + + int sc_do_tick; /* indicate 1s ticks */ + struct timeval sc_tick0; /* first tick */ + + u_int8_t sc_rev; /* PCI/Cardbus revision */ + + u_int32_t sc_anaparm; /* register RTW_ANAPARM */ + + union { + struct rtw_rx_radiotap_header tap; + u_int8_t pad[64]; + } sc_rxtapu; + union { + struct rtw_tx_radiotap_header tap; + u_int8_t pad[64]; + } sc_txtapu; + enum rtw_access sc_access; +}; + +#define sc_if sc_ic.ic_if +#define sc_rxtap sc_rxtapu.tap +#define sc_txtap sc_txtapu.tap + +extern int rtw_host_rfio; + +void rtw_txdac_enable(struct rtw_softc *, int); +void rtw_anaparm_enable(struct rtw_regs *, int); +void rtw_config0123_enable(struct rtw_regs *, int); +void rtw_continuous_tx_enable(struct rtw_softc *, int); +void rtw_set_access(struct rtw_softc *, enum rtw_access); + +void rtw_attach(struct rtw_softc *); +int rtw_detach(struct rtw_softc *); +int rtw_intr(void *); + +void rtw_disable(struct rtw_softc *); +int rtw_enable(struct rtw_softc *); + +int rtw_activate(struct device *, enum devact); +void rtw_power(int, void *); +void rtw_shutdown(void *); + +const char *rtw_pwrstate_string(enum rtw_pwrstate); + +#endif /* _DEV_IC_RTWVAR_H_ */ diff --git a/sys/dev/ic/sa2400reg.h b/sys/dev/ic/sa2400reg.h new file mode 100644 index 00000000000..5f382baceca --- /dev/null +++ b/sys/dev/ic/sa2400reg.h @@ -0,0 +1,263 @@ +/* $OpenBSD: sa2400reg.h,v 1.1 2004/12/29 01:02:31 jsg Exp $ */ +/* $NetBSD: sa2400reg.h,v 1.2 2004/12/12 06:37:59 dyoung Exp $ */ + +/* + * Copyright (c) 2005 David Young. All rights reserved. + * + * This code was written by David Young. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David + * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#ifndef _DEV_IC_SA2400REG_H_ +#define _DEV_IC_SA2400REG_H_ + +/* + * Serial bus format for Philips SA2400 Single-chip Transceiver. + */ +#define SA2400_TWI_DATA_MASK BITS(31,8) +#define SA2400_TWI_WREN BIT(7) /* enable write */ +#define SA2400_TWI_ADDR_MASK BITS(6,0) + +/* + * Registers for Philips SA2400 Single-chip Transceiver. + */ +#define SA2400_SYNA 0 /* Synthesizer Register A */ +#define SA2400_SYNA_FM BIT(21) /* fractional modulus select, + * 0: /8 (default) + * 1: /5 + */ +#define SA2400_SYNA_NF_MASK BITS(20,18) /* fractional increment value, + * 0 to 7, default 4 + */ +#define SA2400_SYNA_N_MASK BITS(17,2) /* main divider division ratio, + * 512 to 65535, default 615 + */ + +#define SA2400_SYNB 1 /* Synthesizer Register B */ +#define SA2400_SYNB_R_MASK BITS(21,12) /* reference divider ratio, + * 4 to 1023, default 11 + */ +#define SA2400_SYNB_L_MASK BITS(11,10) /* lock detect mode */ +#define SA2400_SYNB_L_INACTIVE0 LSHIFT(0, SA2400_SYNB_L_MASK) +#define SA2400_SYNB_L_INACTIVE1 LSHIFT(1, SA2400_SYNB_L_MASK) +#define SA2400_SYNB_L_NORMAL LSHIFT(2, SA2400_SYNB_L_MASK) +#define SA2400_SYNB_L_INACTIVE2 LSHIFT(3, SA2400_SYNB_L_MASK) + +#define SA2400_SYNB_ON BIT(9) /* power on/off, + * 0: inverted chip mode control + * 1: as defined by chip mode + * (see SA2400_OPMODE) + */ +#define SA2400_SYNB_ONE BIT(8) /* always 1 */ +#define SA2400_SYNB_FC_MASK BITS(7,0) /* fractional compensation + * charge pump current DAC, + * 0 to 255, default 80. + */ + +#define SA2400_SYNC 2 /* Synthesizer Register C */ +#define SA2400_SYNC_CP_MASK BITS(7,6) /* charge pump current + * setting + */ +#define SA2400_SYNC_CP_NORMAL_ LSHIFT(0, SA2400_SYNC_CP_MASK) +#define SA2400_SYNC_CP_THIRD_ LSHIFT(1, SA2400_SYNC_CP_MASK) +#define SA2400_SYNC_CP_NORMAL LSHIFT(2, SA2400_SYNC_CP_MASK) /* recommended */ +#define SA2400_SYNC_CP_THIRD LSHIFT(3, SA2400_SYNC_CP_MASK) + +#define SA2400_SYNC_SM_MASK BITS(5,3) /* comparison divider select, + * 0 to 4, extra division + * ratio is 2**SM. + */ +#define SA2400_SYNC_ZERO BIT(2) /* always 0 */ + +#define SA2400_SYND 3 /* Synthesizer Register D */ +#define SA2400_SYND_ZERO1_MASK BITS(21,17) /* always 0 */ +#define SA2400_SYND_TPHPSU BIT(16) /* T[phpsu], 1: disable + * PHP speedup pump, + * overrides SA2400_SYND_TSPU + */ +#define SA2400_SYND_TPSU BIT(15) /* T[spu], 1: speedup on, + * 0: speedup off + */ +#define SA2400_SYND_ZERO2_MASK BITS(14,3) /* always 0 */ + +#define SA2400_OPMODE 4 /* Operating mode, filter tuner, + * other controls + */ +#define SA2400_OPMODE_ADC BIT(19) /* 1: in Rx mode, RSSI-ADC always on + * 0: RSSI-ADC only on during AGC + */ +#define SA2400_OPMODE_FTERR BIT(18) /* read-only filter tuner error: + * 1 if tuner out of range + */ +/* Rx & Tx filter tuning, write tuning value (test mode only) or + * read tuner setting (in normal mode). + */ +#define SA2400_OPMODE_FILTTUNE_MASK BITS(17,15) + +#define SA2400_OPMODE_V2P5 BIT(14) /* external reference voltage + * (pad v2p5) on + */ +#define SA2400_OPMODE_I1M BIT(13) /* external reference current ... */ +#define SA2400_OPMODE_I0P3 BIT(12) /* external reference current ... */ +#define SA2400_OPMODE_IN22 BIT(10) /* xtal input frequency, + * 0: 44 MHz + * 1: 22 MHz + */ +#define SA2400_OPMODE_CLK BIT(9) /* reference clock output on */ +#define SA2400_OPMODE_XO BIT(8) /* xtal oscillator on */ +#define SA2400_OPMODE_DIGIN BIT(7) /* use digital Tx inputs (FIRDAC) */ +#define SA2400_OPMODE_RXLV BIT(6) /* Rx output common mode voltage, + * 0: V[DD]/2 + * 1: 1.25V + */ +#define SA2400_OPMODE_VEO BIT(5) /* make internal vco + * available at vco pads (vcoextout) + */ +#define SA2400_OPMODE_VEI BIT(4) /* use external vco input (vcoextin) */ +/* main operating mode */ +#define SA2400_OPMODE_MODE_MASK BITS(3,0) +#define SA2400_OPMODE_MODE_SLEEP LSHIFT(0, SA2400_OPMODE_MODE_MASK) +#define SA2400_OPMODE_MODE_TXRX LSHIFT(1, SA2400_OPMODE_MODE_MASK) +#define SA2400_OPMODE_MODE_WAIT LSHIFT(2, SA2400_OPMODE_MODE_MASK) +#define SA2400_OPMODE_MODE_RXMGC LSHIFT(3, SA2400_OPMODE_MODE_MASK) +#define SA2400_OPMODE_MODE_FCALIB LSHIFT(4, SA2400_OPMODE_MODE_MASK) +#define SA2400_OPMODE_MODE_DCALIB LSHIFT(5, SA2400_OPMODE_MODE_MASK) +#define SA2400_OPMODE_MODE_FASTTXRXMGC LSHIFT(6, SA2400_OPMODE_MODE_MASK) +#define SA2400_OPMODE_MODE_RESET LSHIFT(7, SA2400_OPMODE_MODE_MASK) +#define SA2400_OPMODE_MODE_VCOCALIB LSHIFT(8, SA2400_OPMODE_MODE_MASK) + +#define SA2400_OPMODE_DEFAULTS \ + (SA2400_OPMODE_XO | SA2400_OPMODE_RXLV | SA2400_OPMODE_CLK | \ + SA2400_OPMODE_I0P3 | LSHIFT(3, SA2400_OPMODE_FILTTUNE_MASK)) + +#define SA2400_AGC 5 /* AGC adjustment */ +#define SA2400_AGC_TARGETSIGN BIT(23) /* fine-tune AGC target: + * -7dB to 7dB, sign bit ... */ +#define SA2400_AGC_TARGET_MASK BITS(22,20) /* ... plus 0dB - 7dB */ +#define SA2400_AGC_MAXGAIN_MASK BITS(19,15) /* maximum AGC gain, 0 to 31, + * (yields 54dB to 85dB) + */ +/* write: settling time after baseband gain switching, units of + * 182 nanoseconds. + * read: output of RSSI/Tx-peak detector's ADC in 5-bit Gray code. + */ +#define SA2400_AGC_BBPDELAY_MASK BITS(14,10) +#define SA2400_AGC_ADCVAL_MASK SA2400_AGC_BBPDELAY_MASK + +/* write: settling time after LNA gain switching, units of + * 182 nanoseconds + * read: 2nd sample of RSSI in AGC cycle + */ +#define SA2400_AGC_LNADELAY_MASK BITS(9,5) +#define SA2400_AGC_SAMPLE2_MASK SA2400_AGC_LNADELAY_MASK + +/* write: time between turning on Rx and AGCSET, units of + * 182 nanoseconds + * read: 1st sample of RSSI in AGC cycle + */ +#define SA2400_AGC_RXONDELAY_MASK BITS(4,0) +#define SA2400_AGC_SAMPLE1_MASK SA2400_AGC_RXONDELAY_MASK + +#define SA2400_MANRX 6 /* Manual receiver control settings */ +#define SA2400_MANRX_AHSN BIT(23) /* 1: AGC w/ high S/N---switch LNA at + * step 52 (recommended) + * 0: switch LNA at step 60 + */ + +/* If _RXOSQON, Q offset is + * (_RXOSQSIGN ? -1 : 1) * (1 + _RXOSQ_MASK) * 8 millivolts, + * otherwise, Q offset is 0. + * + * Ditto I offset. + */ +#define SA2400_MANRX_RXOSQON BIT(22) /* Rx Q-channel correction. */ +#define SA2400_MANRX_RXOSQSIGN BIT(21) +#define SA2400_MANRX_RXOSQ_MASK BITS(20,18) + +#define SA2400_MANRX_RXOSION BIT(17) /* Rx I-channel correction. */ +#define SA2400_MANRX_RXOSISIGN BIT(16) +#define SA2400_MANRX_RXOSI_MASK BITS(15,13) +#define SA2400_MANRX_TEN BIT(12) /* use 10MHz offset cancellation + * cornerpoint for brief period + * after each gain change + */ + +/* DC offset cancellation cornerpoint select + * write: in RXMGC, set the cornerpoint + * read: in other modes, read AGC-controlled cornerpoint + */ +#define SA2400_MANRX_CORNERFREQ_MASK BITS(11,10) + +/* write: in RXMGC mode, sets receiver gain + * read: in other modes, read AGC-controlled gain + */ +#define SA2400_MANRX_RXGAIN_MASK BITS(9,0) + +#define SA2400_TX 7 /* Transmitter settings */ +/* Tx offsets + * + * write: in test mode, sets the offsets + * read: in normal mode, returns automatic settings + */ +#define SA2400_TX_TXOSQON BIT(19) +#define SA2400_TX_TXOSQSIGN BIT(18) +#define SA2400_TX_TXOSQ_MASK BITS(17,15) +#define SA2400_TX_TXOSION BIT(14) +#define SA2400_TX_TXOSISIGN BIT(13) +#define SA2400_TX_TXOSI_MASK BITS(12,10) + +#define SA2400_TX_RAMP_MASK BITS(9,8) /* Ramp-up delay, + * 0: 1us + * 1: 2us + * 2: 3us + * 3: 4us + * datasheet says, "ramp-up + * time always 1us". huh? + */ +#define SA2400_TX_HIGAIN_MASK BITS(7,4) /* Transmitter gain settings + * for TXHI output + */ +#define SA2400_TX_LOGAIN_MASK BITS(3,0) /* Transmitter gain settings + * for TXLO output + */ + +#define SA2400_VCO 8 /* VCO settings */ +#define SA2400_VCO_ZERO BITS(6,5) /* always zero */ +#define SA2400_VCO_VCERR BIT(4) /* VCO calibration error flag---no + * band with low enough frequency + * could be found + */ +#define SA2400_VCO_VCOBAND_MASK BITS(3,0) /* VCO band, + * write: in test mode, sets + * VCO band + * read: in normal mode, + * the result of + * calibration (VCOCAL). + * 0 = highest + * frequencies + */ +#endif /* _DEV_IC_SA2400REG_H_ */ diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index 8e9f2c0464d..38036a9377f 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1,4 +1,4 @@ -# $OpenBSD: files.pci,v 1.168 2004/12/19 16:06:23 deraadt Exp $ +# $OpenBSD: files.pci,v 1.169 2004/12/29 01:02:30 jsg Exp $ # $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $ # # Config file and device description for machine-independent PCI code. @@ -519,6 +519,10 @@ file dev/pci/if_ath_pci.c ath_pci attach atw at pci with atw_pci file dev/pci/if_atw_pci.c atw_pci +# Realtek RTL8180 PCI/Mini-PCI +attach rtw at pci with rtw_pci +file dev/pci/if_rtw_pci.c rtw_pci + # Sangoma PCI card device san: ifnet, ifmedia attach san at pci diff --git a/sys/dev/pci/if_rtw_pci.c b/sys/dev/pci/if_rtw_pci.c new file mode 100644 index 00000000000..b9b09bd9110 --- /dev/null +++ b/sys/dev/pci/if_rtw_pci.c @@ -0,0 +1,306 @@ +/* $OpenBSD: if_rtw_pci.c,v 1.1 2004/12/29 01:02:30 jsg Exp $ */ +/* $NetBSD: if_rtw_pci.c,v 1.1 2004/09/26 02:33:36 dyoung Exp $ */ + +/*- + * Copyright (c) 1998, 1999, 2000, 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center; Charles M. Hannum; and David Young. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * PCI bus front-end for the Realtek RTL8180 802.11 MAC/BBP chip. + * + * Derived from the ADMtek ADM8211 PCI bus front-end. + * + * Derived from the ``Tulip'' PCI bus front-end. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#ifdef INET +#include +#include +#endif + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +const struct rtw_pci_product * rtw_pci_lookup(const struct pci_attach_args *); +int rtw_pci_enable(struct rtw_softc *); +void rtw_pci_disable(struct rtw_softc *); + +/* + * PCI configuration space registers used by the ADM8211. + */ +#define RTW_PCI_IOBA 0x10 /* i/o mapped base */ +#define RTW_PCI_MMBA 0x14 /* memory mapped base */ + +struct rtw_pci_softc { + struct rtw_softc psc_rtw; /* real ADM8211 softc */ + + pci_intr_handle_t psc_ih; /* interrupt handle */ + void *psc_intrcookie; + + pci_chipset_tag_t psc_pc; /* our PCI chipset */ + pcitag_t psc_pcitag; /* our PCI tag */ +}; + +int rtw_pci_match(struct device *, void *, void *); +void rtw_pci_attach(struct device *, struct device *, void *); + +struct cfattach rtw_pci_ca = { + sizeof (struct rtw_pci_softc), rtw_pci_match, rtw_pci_attach +}; +struct cfdriver rtw_cd = { + 0, "rtw", DV_IFNET +}; + +const struct rtw_pci_product { + u_int32_t app_vendor; /* PCI vendor ID */ + u_int32_t app_product; /* PCI product ID */ +} rtw_pci_products[] = { + { PCI_VENDOR_REALTEK, PCI_PRODUCT_REALTEK_RT8180 }, + { PCI_VENDOR_BELKIN2, PCI_PRODUCT_BELKIN2_F5D6001 }, + + { 0, 0 }, +}; + +const struct rtw_pci_product * +rtw_pci_lookup(const struct pci_attach_args *pa) +{ + const struct rtw_pci_product *app; + + for (app = rtw_pci_products; + app->app_vendor != 0 && app->app_product != 0; + app++) { + if (PCI_VENDOR(pa->pa_id) == app->app_vendor && + PCI_PRODUCT(pa->pa_id) == app->app_product) + return (app); + } + return (NULL); +} + +int +rtw_pci_match(struct device *parent, void *match, void *aux) +{ + struct pci_attach_args *pa = aux; + + if (rtw_pci_lookup(pa) != NULL) + return (1); + + return (0); +} + +int +rtw_pci_enable(struct rtw_softc *sc) +{ + struct rtw_pci_softc *psc = (void *)sc; + + /* Establish the interrupt. */ + psc->psc_intrcookie = pci_intr_establish(psc->psc_pc, psc->psc_ih, + IPL_NET, rtw_intr, sc, sc->sc_dev.dv_xname); + if (psc->psc_intrcookie == NULL) { + printf("%s: unable to establish interrupt\n", + sc->sc_dev.dv_xname); + return (1); + } + + return (0); +} + +void +rtw_pci_disable(struct rtw_softc *sc) +{ + struct rtw_pci_softc *psc = (void *)sc; + + /* Unhook the interrupt handler. */ + pci_intr_disestablish(psc->psc_pc, psc->psc_intrcookie); + psc->psc_intrcookie = NULL; +} + +void +rtw_pci_attach(struct device *parent, struct device *self, void *aux) +{ + struct rtw_pci_softc *psc = (void *) self; + struct rtw_softc *sc = &psc->psc_rtw; + struct rtw_regs *regs = &sc->sc_regs; + struct pci_attach_args *pa = aux; + pci_chipset_tag_t pc = pa->pa_pc; + const char *intrstr = NULL; + bus_space_tag_t iot, memt; + bus_space_handle_t ioh, memh; + int ioh_valid, memh_valid; + const struct rtw_pci_product *app; + pcireg_t reg; + int pmreg; + + psc->psc_pc = pa->pa_pc; + psc->psc_pcitag = pa->pa_tag; + + app = rtw_pci_lookup(pa); + if (app == NULL) { + printf("\n"); + panic("rtw_pci_attach: impossible"); + } + + /* + * No power management hooks. + * XXX Maybe we should add some! + */ + sc->sc_flags |= RTW_F_ENABLED; + + /* + * Get revision info, and set some chip-specific variables. + */ + sc->sc_rev = PCI_REVISION(pa->pa_class); + + /* + * Check to see if the device is in power-save mode, and + * being it out if necessary. + * + * XXX This code comes almost verbatim from if_tlp_pci.c. I do + * not understand it. Tulip clears the "sleep mode" bit in the + * CFDA register, first. There is an equivalent (?) register at the + * same place in the ADM8211, but the docs do not assign its bits + * any meanings. -dcy + */ + if (pci_get_capability(pc, pa->pa_tag, PCI_CAP_PWRMGMT, &pmreg, 0)) { + reg = pci_conf_read(pc, pa->pa_tag, pmreg + PCI_PMCSR); + switch (reg & PCI_PMCSR_STATE_MASK) { + case PCI_PMCSR_STATE_D1: + case PCI_PMCSR_STATE_D2: + printf(": waking up from power state D%d\n", + reg & PCI_PMCSR_STATE_MASK); + pci_conf_write(pc, pa->pa_tag, pmreg + PCI_PMCSR, + (reg & ~PCI_PMCSR_STATE_MASK) | + PCI_PMCSR_STATE_D0); + break; + case PCI_PMCSR_STATE_D3: + /* + * The card has lost all configuration data in + * this state, so punt. + */ + printf(": unable to wake up from power state D3, " + "reboot required.\n"); + pci_conf_write(pc, pa->pa_tag, pmreg + PCI_PMCSR, + (reg & ~PCI_PMCSR_STATE_MASK) | + PCI_PMCSR_STATE_D0); + return; + } + } + + /* + * Map the device. + */ + ioh_valid = (pci_mapreg_map(pa, RTW_PCI_IOBA, + PCI_MAPREG_TYPE_IO, 0, + &iot, &ioh, NULL, NULL, 0) == 0); + memh_valid = (pci_mapreg_map(pa, RTW_PCI_MMBA, + PCI_MAPREG_TYPE_MEM|PCI_MAPREG_MEM_TYPE_32BIT, 0, + &memt, &memh, NULL, NULL, 0) == 0); + + if (memh_valid) { + regs->r_bt = memt; + regs->r_bh = memh; + } else if (ioh_valid) { + regs->r_bt = iot; + regs->r_bh = ioh; + } else { + printf(": unable to map device registers\n"); + return; + } + + sc->sc_dmat = pa->pa_dmat; + + /* + * Make sure bus mastering is enabled. + */ + pci_conf_write(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, + pci_conf_read(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG) | + PCI_COMMAND_MASTER_ENABLE); + + /* + * Map and establish our interrupt. + */ + if (pci_intr_map(pa, &psc->psc_ih)) { + printf(": unable to map interrupt\n"); + return; + } + intrstr = pci_intr_string(pc, psc->psc_ih); + psc->psc_intrcookie = pci_intr_establish(pc, psc->psc_ih, IPL_NET, + rtw_intr, sc, sc->sc_dev.dv_xname); + if (psc->psc_intrcookie == NULL) { + printf(": unable to establish interrupt"); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + return; + } + + printf(": %s\n", intrstr); + + sc->sc_enable = rtw_pci_enable; + sc->sc_disable = rtw_pci_disable; + + /* + * Finish off the attach. + */ + rtw_attach(sc); +} -- cgit v1.2.3