From 7a5ae42852a1e25a2c929a8bcfd6d0aaebc5500e Mon Sep 17 00:00:00 2001 From: avos Date: Mon, 22 Feb 2016 01:15:02 +0000 Subject: urtwn: shutdown the device properly - R92C path: NetBSD (mostly) - R88E path: TP-Link driver Tested with RTL8188EU and RTL8188CUS. Reviewed by: kevlo Approved by: adrian (mentor) Differential Revision: https://reviews.freebsd.org/D5198 --- sys/dev/usb/wlan/if_urtwn.c | 272 ++++++++++++++++++++++++++++++++++++++++- sys/dev/usb/wlan/if_urtwnreg.h | 25 ++++ sys/dev/usb/wlan/if_urtwnvar.h | 1 + 3 files changed, 296 insertions(+), 2 deletions(-) diff --git a/sys/dev/usb/wlan/if_urtwn.c b/sys/dev/usb/wlan/if_urtwn.c index fa93210..a793ba9 100644 --- a/sys/dev/usb/wlan/if_urtwn.c +++ b/sys/dev/usb/wlan/if_urtwn.c @@ -308,6 +308,8 @@ static void urtwn_start(struct urtwn_softc *); static void urtwn_parent(struct ieee80211com *); static int urtwn_r92c_power_on(struct urtwn_softc *); static int urtwn_r88e_power_on(struct urtwn_softc *); +static void urtwn_r92c_power_off(struct urtwn_softc *); +static void urtwn_r88e_power_off(struct urtwn_softc *); static int urtwn_llt_init(struct urtwn_softc *); #ifndef URTWN_WITHOUT_UCODE static void urtwn_fw_reset(struct urtwn_softc *); @@ -1782,6 +1784,7 @@ urtwn_read_rom(struct urtwn_softc *sc) sc->sc_rf_write = urtwn_r92c_rf_write; sc->sc_power_on = urtwn_r92c_power_on; + sc->sc_power_off = urtwn_r92c_power_off; return (0); } @@ -1809,6 +1812,7 @@ urtwn_r88e_read_rom(struct urtwn_softc *sc) sc->sc_rf_write = urtwn_r88e_rf_write; sc->sc_power_on = urtwn_r88e_power_on; + sc->sc_power_off = urtwn_r88e_power_off; return (0); } @@ -3235,7 +3239,7 @@ urtwn_r88e_power_on(struct urtwn_softc *sc) /* Enable LDO normal mode. */ error = urtwn_write_1(sc, R92C_LPLDO_CTRL, - urtwn_read_1(sc, R92C_LPLDO_CTRL) & ~0x10); + urtwn_read_1(sc, R92C_LPLDO_CTRL) & ~R92C_LPLDO_CTRL_SLEEP); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); @@ -3254,6 +3258,269 @@ urtwn_r88e_power_on(struct urtwn_softc *sc) return (0); } +static __inline void +urtwn_power_off(struct urtwn_softc *sc) +{ + + return sc->sc_power_off(sc); +} + +static void +urtwn_r92c_power_off(struct urtwn_softc *sc) +{ + uint32_t reg; + + /* Block all Tx queues. */ + urtwn_write_1(sc, R92C_TXPAUSE, R92C_TX_QUEUE_ALL); + + /* Disable RF */ + urtwn_rf_write(sc, 0, 0, 0); + + urtwn_write_1(sc, R92C_APSD_CTRL, R92C_APSD_CTRL_OFF); + + /* Reset BB state machine */ + urtwn_write_1(sc, R92C_SYS_FUNC_EN, + R92C_SYS_FUNC_EN_USBD | R92C_SYS_FUNC_EN_USBA | + R92C_SYS_FUNC_EN_BB_GLB_RST); + urtwn_write_1(sc, R92C_SYS_FUNC_EN, + R92C_SYS_FUNC_EN_USBD | R92C_SYS_FUNC_EN_USBA); + + /* + * Reset digital sequence + */ +#ifndef URTWN_WITHOUT_UCODE + if (urtwn_read_1(sc, R92C_MCUFWDL) & R92C_MCUFWDL_RDY) { + /* Reset MCU ready status */ + urtwn_write_1(sc, R92C_MCUFWDL, 0); + + /* If firmware in ram code, do reset */ + urtwn_fw_reset(sc); + } +#endif + + /* Reset MAC and Enable 8051 */ + urtwn_write_1(sc, R92C_SYS_FUNC_EN + 1, + (R92C_SYS_FUNC_EN_CPUEN | + R92C_SYS_FUNC_EN_ELDR | + R92C_SYS_FUNC_EN_HWPDN) >> 8); + + /* Reset MCU ready status */ + urtwn_write_1(sc, R92C_MCUFWDL, 0); + + /* Disable MAC clock */ + urtwn_write_2(sc, R92C_SYS_CLKR, + R92C_SYS_CLKR_ANAD16V_EN | + R92C_SYS_CLKR_ANA8M | + R92C_SYS_CLKR_LOADER_EN | + R92C_SYS_CLKR_80M_SSC_DIS | + R92C_SYS_CLKR_SYS_EN | + R92C_SYS_CLKR_RING_EN | + 0x4000); + + /* Disable AFE PLL */ + urtwn_write_1(sc, R92C_AFE_PLL_CTRL, 0x80); + + /* Gated AFE DIG_CLOCK */ + urtwn_write_2(sc, R92C_AFE_XTAL_CTRL, 0x880F); + + /* Isolated digital to PON */ + urtwn_write_1(sc, R92C_SYS_ISO_CTRL, + R92C_SYS_ISO_CTRL_MD2PP | + R92C_SYS_ISO_CTRL_PA2PCIE | + R92C_SYS_ISO_CTRL_PD2CORE | + R92C_SYS_ISO_CTRL_IP2MAC | + R92C_SYS_ISO_CTRL_DIOP | + R92C_SYS_ISO_CTRL_DIOE); + + /* + * Pull GPIO PIN to balance level and LED control + */ + /* 1. Disable GPIO[7:0] */ + urtwn_write_2(sc, R92C_GPIO_IOSEL, 0x0000); + + reg = urtwn_read_4(sc, R92C_GPIO_PIN_CTRL) & ~0x0000ff00; + reg |= ((reg << 8) & 0x0000ff00) | 0x00ff0000; + urtwn_write_4(sc, R92C_GPIO_PIN_CTRL, reg); + + /* Disable GPIO[10:8] */ + urtwn_write_1(sc, R92C_MAC_PINMUX_CFG, 0x00); + + reg = urtwn_read_2(sc, R92C_GPIO_IO_SEL) & ~0x00f0; + reg |= (((reg & 0x000f) << 4) | 0x0780); + urtwn_write_2(sc, R92C_GPIO_IO_SEL, reg); + + /* Disable LED0 & 1 */ + urtwn_write_2(sc, R92C_LEDCFG0, 0x8080); + + /* + * Reset digital sequence + */ + /* Disable ELDR clock */ + urtwn_write_2(sc, R92C_SYS_CLKR, + R92C_SYS_CLKR_ANAD16V_EN | + R92C_SYS_CLKR_ANA8M | + R92C_SYS_CLKR_LOADER_EN | + R92C_SYS_CLKR_80M_SSC_DIS | + R92C_SYS_CLKR_SYS_EN | + R92C_SYS_CLKR_RING_EN | + 0x4000); + + /* Isolated ELDR to PON */ + urtwn_write_1(sc, R92C_SYS_ISO_CTRL + 1, + (R92C_SYS_ISO_CTRL_DIOR | + R92C_SYS_ISO_CTRL_PWC_EV12V) >> 8); + + /* + * Disable analog sequence + */ + /* Disable A15 power */ + urtwn_write_1(sc, R92C_LDOA15_CTRL, R92C_LDOA15_CTRL_OBUF); + /* Disable digital core power */ + urtwn_write_1(sc, R92C_LDOV12D_CTRL, + urtwn_read_1(sc, R92C_LDOV12D_CTRL) & + ~R92C_LDOV12D_CTRL_LDV12_EN); + + /* Enter PFM mode */ + urtwn_write_1(sc, R92C_SPS0_CTRL, 0x23); + + /* Set USB suspend */ + urtwn_write_2(sc, R92C_APS_FSMCO, + R92C_APS_FSMCO_APDM_HOST | + R92C_APS_FSMCO_AFSM_HSUS | + R92C_APS_FSMCO_PFM_ALDN); + + /* Lock ISO/CLK/Power control register. */ + urtwn_write_1(sc, R92C_RSV_CTRL, 0x0E); +} + +static void +urtwn_r88e_power_off(struct urtwn_softc *sc) +{ + uint8_t reg; + int ntries; + + /* Disable any kind of TX reports. */ + urtwn_write_1(sc, R88E_TX_RPT_CTRL, + urtwn_read_1(sc, R88E_TX_RPT_CTRL) & + ~(R88E_TX_RPT1_ENA | R88E_TX_RPT2_ENA)); + + /* Stop Rx. */ + urtwn_write_1(sc, R92C_CR, 0); + + /* Move card to Low Power State. */ + /* Block all Tx queues. */ + urtwn_write_1(sc, R92C_TXPAUSE, R92C_TX_QUEUE_ALL); + + for (ntries = 0; ntries < 20; ntries++) { + /* Should be zero if no packet is transmitting. */ + if (urtwn_read_4(sc, R88E_SCH_TXCMD) == 0) + break; + + urtwn_ms_delay(sc); + } + if (ntries == 20) { + device_printf(sc->sc_dev, "%s: failed to block Tx queues\n", + __func__); + return; + } + + /* CCK and OFDM are disabled, and clock are gated. */ + urtwn_write_1(sc, R92C_SYS_FUNC_EN, + urtwn_read_1(sc, R92C_SYS_FUNC_EN) & ~R92C_SYS_FUNC_EN_BBRSTB); + + urtwn_ms_delay(sc); + + /* Reset MAC TRX */ + urtwn_write_1(sc, R92C_CR, + R92C_CR_HCI_TXDMA_EN | R92C_CR_HCI_RXDMA_EN | + R92C_CR_TXDMA_EN | R92C_CR_RXDMA_EN | + R92C_CR_PROTOCOL_EN | R92C_CR_SCHEDULE_EN); + + /* check if removed later */ + urtwn_write_1(sc, R92C_CR + 1, + urtwn_read_1(sc, R92C_CR + 1) & ~(R92C_CR_ENSEC >> 8)); + + /* Respond TxOK to scheduler */ + urtwn_write_1(sc, R92C_DUAL_TSF_RST, + urtwn_read_1(sc, R92C_DUAL_TSF_RST) | 0x20); + + /* If firmware in ram code, do reset. */ +#ifndef URTWN_WITHOUT_UCODE + if (urtwn_read_1(sc, R92C_MCUFWDL) & R92C_MCUFWDL_RDY) + urtwn_r88e_fw_reset(sc); +#endif + + /* Reset MCU ready status. */ + urtwn_write_1(sc, R92C_MCUFWDL, 0x00); + + /* Disable 32k. */ + urtwn_write_1(sc, R88E_32K_CTRL, + urtwn_read_1(sc, R88E_32K_CTRL) & ~0x01); + + /* Move card to Disabled state. */ + /* Turn off RF. */ + urtwn_write_1(sc, R92C_RF_CTRL, 0); + + /* LDO Sleep mode. */ + urtwn_write_1(sc, R92C_LPLDO_CTRL, + urtwn_read_1(sc, R92C_LPLDO_CTRL) | R92C_LPLDO_CTRL_SLEEP); + + /* Turn off MAC by HW state machine */ + urtwn_write_1(sc, R92C_APS_FSMCO + 1, + urtwn_read_1(sc, R92C_APS_FSMCO + 1) | + (R92C_APS_FSMCO_APFM_OFF >> 8)); + + for (ntries = 0; ntries < 20; ntries++) { + /* Wait until it will be disabled. */ + if ((urtwn_read_1(sc, R92C_APS_FSMCO + 1) & + (R92C_APS_FSMCO_APFM_OFF >> 8)) == 0) + break; + + urtwn_ms_delay(sc); + } + if (ntries == 20) { + device_printf(sc->sc_dev, "%s: could not turn off MAC\n", + __func__); + return; + } + + /* schmit trigger */ + urtwn_write_1(sc, R92C_AFE_XTAL_CTRL + 2, + urtwn_read_1(sc, R92C_AFE_XTAL_CTRL + 2) | 0x80); + + /* Enable WL suspend. */ + urtwn_write_1(sc, R92C_APS_FSMCO + 1, + (urtwn_read_1(sc, R92C_APS_FSMCO + 1) & ~0x10) | 0x08); + + /* Enable bandgap mbias in suspend. */ + urtwn_write_1(sc, R92C_APS_FSMCO + 3, 0); + + /* Clear SIC_EN register. */ + urtwn_write_1(sc, R92C_GPIO_MUXCFG + 1, + urtwn_read_1(sc, R92C_GPIO_MUXCFG + 1) & ~0x10); + + /* Set USB suspend enable local register */ + urtwn_write_1(sc, R92C_USB_SUSPEND, + urtwn_read_1(sc, R92C_USB_SUSPEND) | 0x10); + + /* Reset MCU IO Wrapper. */ + reg = urtwn_read_1(sc, R92C_RSV_CTRL + 1); + urtwn_write_1(sc, R92C_RSV_CTRL + 1, reg & ~0x08); + urtwn_write_1(sc, R92C_RSV_CTRL + 1, reg | 0x08); + + /* marked as 'For Power Consumption' code. */ + urtwn_write_1(sc, R92C_GPIO_OUT, urtwn_read_1(sc, R92C_GPIO_IN)); + urtwn_write_1(sc, R92C_GPIO_IOSEL, 0xff); + + urtwn_write_1(sc, R92C_GPIO_IO_SEL, + urtwn_read_1(sc, R92C_GPIO_IO_SEL) << 4); + urtwn_write_1(sc, R92C_GPIO_MOD, + urtwn_read_1(sc, R92C_GPIO_MOD) | 0x0f); + + /* Set LNA, TRSW, EX_PA Pin to output mode. */ + urtwn_write_4(sc, R88E_BB_PAD_CTRL, 0x00080808); +} + static int urtwn_llt_init(struct urtwn_softc *sc) { @@ -4910,9 +5177,10 @@ urtwn_stop(struct urtwn_softc *sc) URTWN_TEMP_MEASURED); sc->thcal_lctemp = 0; callout_stop(&sc->sc_watchdog_ch); - urtwn_abort_xfers(sc); + urtwn_abort_xfers(sc); urtwn_drain_mbufq(sc); + urtwn_power_off(sc); URTWN_UNLOCK(sc); } diff --git a/sys/dev/usb/wlan/if_urtwnreg.h b/sys/dev/usb/wlan/if_urtwnreg.h index 5b8b4f5..72835f3 100644 --- a/sys/dev/usb/wlan/if_urtwnreg.h +++ b/sys/dev/usb/wlan/if_urtwnreg.h @@ -70,6 +70,10 @@ #define R92C_GPIO_IO_SEL 0x042 #define R92C_MAC_PINMUX_CFG 0x043 #define R92C_GPIO_PIN_CTRL 0x044 +#define R92C_GPIO_IN 0x044 +#define R92C_GPIO_OUT 0x045 +#define R92C_GPIO_IOSEL 0x046 +#define R92C_GPIO_MOD 0x047 #define R92C_GPIO_INTM 0x048 #define R92C_LEDCFG0 0x04c #define R92C_LEDCFG1 0x04d @@ -79,6 +83,7 @@ #define R92C_FSISR 0x054 #define R92C_HSIMR 0x058 #define R92C_HSISR 0x05c +#define R88E_BB_PAD_CTRL 0x064 #define R92C_MCUFWDL 0x080 #define R92C_HMEBOX_EXT(idx) (0x088 + (idx) * 2) #define R88E_HIMR 0x0b0 @@ -117,6 +122,7 @@ #define R92C_MBIST_START 0x174 #define R92C_MBIST_DONE 0x178 #define R92C_MBIST_FAIL 0x17c +#define R88E_32K_CTRL 0x194 #define R92C_C2HEVT_MSG_NORMAL 0x1a0 #define R92C_C2HEVT_MSG_TEST 0x1b8 #define R92C_C2HEVT_CLEAR 0x1bf @@ -204,6 +210,7 @@ #define R92C_BE_ADMTIME 0x5c8 #define R92C_EDCA_RANDOM_GEN 0x5cc #define R92C_SCH_TXCMD 0x5d0 +#define R88E_SCH_TXCMD 0x5f8 /* WMAC Configuration. */ #define R92C_APSD_CTRL 0x600 #define R92C_BWOPMODE 0x603 @@ -303,13 +310,30 @@ #define R92C_RF_CTRL_RSTB 0x02 #define R92C_RF_CTRL_SDMRSTB 0x04 +/* Bits for R92C_LDOA15_CTRL. */ +#define R92C_LDOA15_CTRL_EN 0x01 +#define R92C_LDOA15_CTRL_STBY 0x02 +#define R92C_LDOA15_CTRL_OBUF 0x04 +#define R92C_LDOA15_CTRL_REG_VOS 0x08 + /* Bits for R92C_LDOV12D_CTRL. */ #define R92C_LDOV12D_CTRL_LDV12_EN 0x01 +/* Bits for R92C_LPLDO_CTRL. */ +#define R92C_LPLDO_CTRL_SLEEP 0x10 + /* Bits for R92C_AFE_XTAL_CTRL. */ #define R92C_AFE_XTAL_CTRL_ADDR_M 0x007ff800 #define R92C_AFE_XTAL_CTRL_ADDR_S 11 +/* Bits for R92C_AFE_PLL_CTRL. */ +#define R92C_AFE_PLL_CTRL_EN 0x0001 +#define R92C_AFE_PLL_CTRL_320_EN 0x0002 +#define R92C_AFE_PLL_CTRL_FREF_SEL 0x0004 +#define R92C_AFE_PLL_CTRL_EDGE_SEL 0x0008 +#define R92C_AFE_PLL_CTRL_WDOGB 0x0010 +#define R92C_AFE_PLL_CTRL_LPFEN 0x0020 + /* Bits for R92C_EFUSE_CTRL. */ #define R92C_EFUSE_CTRL_DATA_M 0x000000ff #define R92C_EFUSE_CTRL_DATA_S 0 @@ -748,6 +772,7 @@ /* * USB registers. */ +#define R92C_USB_SUSPEND 0xfe10 #define R92C_USB_INFO 0xfe17 #define R92C_USB_SPECIAL_OPTION 0xfe55 #define R92C_USB_HCPWM 0xfe57 diff --git a/sys/dev/usb/wlan/if_urtwnvar.h b/sys/dev/usb/wlan/if_urtwnvar.h index ce388d2..ba8ca81 100644 --- a/sys/dev/usb/wlan/if_urtwnvar.h +++ b/sys/dev/usb/wlan/if_urtwnvar.h @@ -172,6 +172,7 @@ struct urtwn_softc { void (*sc_rf_write)(struct urtwn_softc *, int, uint8_t, uint32_t); int (*sc_power_on)(struct urtwn_softc *); + void (*sc_power_off)(struct urtwn_softc *); struct ieee80211_node *node_list[R88E_MACID_MAX + 1]; struct mtx nt_mtx; -- cgit v1.1