// SPDX-License-Identifier: GPL-2.0 /* Copyright(c) 2007 - 2011 Realtek Corporation. */ #include "../include/HalPwrSeqCmd.h" #define PWR_CMD_WRITE 0x01 /* offset: the read register offset */ /* msk: the mask of the write bits */ /* value: write value */ /* note: driver shall implement this cmd by read & msk after write */ #define PWR_CMD_POLLING 0x02 /* offset: the read register offset */ /* msk: the mask of the polled value */ /* value: the value to be polled, masked by the msd field. */ /* note: driver shall implement this cmd by */ /* do{ */ /* if ( (Read(offset) & msk) == (value & msk) ) */ /* break; */ /* } while (not timeout); */ #define PWR_CMD_DELAY 0x03 /* offset: the value to delay (in us) */ /* msk: N/A */ /* value: N/A */ struct wl_pwr_cfg { u16 offset; u8 cmd:4; u8 msk; u8 value; }; #define GET_PWR_CFG_OFFSET(__PWR_CMD) __PWR_CMD.offset #define GET_PWR_CFG_CMD(__PWR_CMD) __PWR_CMD.cmd #define GET_PWR_CFG_MASK(__PWR_CMD) __PWR_CMD.msk #define GET_PWR_CFG_VALUE(__PWR_CMD) __PWR_CMD.value static struct wl_pwr_cfg rtl8188E_power_on_flow[] = { { 0x0006, PWR_CMD_POLLING, BIT(1), BIT(1) }, { 0x0002, PWR_CMD_WRITE, BIT(0) | BIT(1), 0 }, /* reset BB */ { 0x0026, PWR_CMD_WRITE, BIT(7), BIT(7) }, /* schmitt trigger */ { 0x0005, PWR_CMD_WRITE, BIT(7), 0 }, /* disable HWPDN (control by DRV)*/ { 0x0005, PWR_CMD_WRITE, BIT(4) | BIT(3), 0 }, /* disable WL suspend*/ { 0x0005, PWR_CMD_WRITE, BIT(0), BIT(0) }, { 0x0005, PWR_CMD_POLLING, BIT(0), 0 }, { 0x0023, PWR_CMD_WRITE, BIT(4), 0 }, }; static struct wl_pwr_cfg rtl8188E_card_disable_flow[] = { { 0x001F, PWR_CMD_WRITE, 0xFF, 0 }, /* turn off RF */ { 0x0023, PWR_CMD_WRITE, BIT(4), BIT(4) }, /* LDO Sleep mode */ { 0x0005, PWR_CMD_WRITE, BIT(1), BIT(1) }, /* turn off MAC by HW state machine */ { 0x0005, PWR_CMD_POLLING, BIT(1), 0 }, { 0x0026, PWR_CMD_WRITE, BIT(7), BIT(7) }, /* schmitt trigger */ { 0x0005, PWR_CMD_WRITE, BIT(3) | BIT(4), BIT(3) }, /* enable WL suspend */ { 0x0007, PWR_CMD_WRITE, 0xFF, 0 }, /* enable bandgap mbias in suspend */ { 0x0041, PWR_CMD_WRITE, BIT(4), 0 }, /* Clear SIC_EN register */ { 0xfe10, PWR_CMD_WRITE, BIT(4), BIT(4) }, /* Set USB suspend enable local register */ }; /* This is used by driver for LPSRadioOff Procedure, not for FW LPS Step */ static struct wl_pwr_cfg rtl8188E_enter_lps_flow[] = { { 0x0522, PWR_CMD_WRITE, 0xFF, 0x7F },/* Tx Pause */ { 0x05F8, PWR_CMD_POLLING, 0xFF, 0 }, /* Should be zero if no packet is transmitted */ { 0x05F9, PWR_CMD_POLLING, 0xFF, 0 }, /* Should be zero if no packet is transmitted */ { 0x05FA, PWR_CMD_POLLING, 0xFF, 0 }, /* Should be zero if no packet is transmitted */ { 0x05FB, PWR_CMD_POLLING, 0xFF, 0 }, /* Should be zero if no packet is transmitted */ { 0x0002, PWR_CMD_WRITE, BIT(0), 0 }, /* CCK and OFDM are disabled, clocks are gated */ { 0x0002, PWR_CMD_DELAY, 0, 0 }, { 0x0100, PWR_CMD_WRITE, 0xFF, 0x3F }, /* Reset MAC TRX */ { 0x0101, PWR_CMD_WRITE, BIT(1), 0 }, /* check if removed later */ { 0x0553, PWR_CMD_WRITE, BIT(5), BIT(5) }, /* Respond TxOK to scheduler */ }; u8 HalPwrSeqCmdParsing(struct adapter *padapter, enum r8188eu_pwr_seq seq) { struct wl_pwr_cfg pwrcfgcmd = {0}; struct wl_pwr_cfg *pwrseqcmd; u8 poll_bit = false; u8 idx, num_steps; u8 value = 0; u32 offset = 0; u32 poll_count = 0; /* polling autoload done. */ u32 max_poll_count = 5000; int res; switch (seq) { case PWR_ON_FLOW: pwrseqcmd = rtl8188E_power_on_flow; num_steps = ARRAY_SIZE(rtl8188E_power_on_flow); break; case DISABLE_FLOW: pwrseqcmd = rtl8188E_card_disable_flow; num_steps = ARRAY_SIZE(rtl8188E_card_disable_flow); break; case LPS_ENTER_FLOW: pwrseqcmd = rtl8188E_enter_lps_flow; num_steps = ARRAY_SIZE(rtl8188E_enter_lps_flow); break; default: return false; } for (idx = 0; idx < num_steps; idx++) { pwrcfgcmd = pwrseqcmd[idx]; switch (GET_PWR_CFG_CMD(pwrcfgcmd)) { case PWR_CMD_WRITE: offset = GET_PWR_CFG_OFFSET(pwrcfgcmd); /* Read the value from system register */ res = rtw_read8(padapter, offset, &value); if (res) return false; value &= ~(GET_PWR_CFG_MASK(pwrcfgcmd)); value |= (GET_PWR_CFG_VALUE(pwrcfgcmd) & GET_PWR_CFG_MASK(pwrcfgcmd)); /* Write the value back to system register */ rtw_write8(padapter, offset, value); break; case PWR_CMD_POLLING: poll_bit = false; offset = GET_PWR_CFG_OFFSET(pwrcfgcmd); do { res = rtw_read8(padapter, offset, &value); if (res) return false; value &= GET_PWR_CFG_MASK(pwrcfgcmd); if (value == (GET_PWR_CFG_VALUE(pwrcfgcmd) & GET_PWR_CFG_MASK(pwrcfgcmd))) poll_bit = true; else udelay(10); if (poll_count++ > max_poll_count) return false; } while (!poll_bit); break; case PWR_CMD_DELAY: udelay(GET_PWR_CFG_OFFSET(pwrcfgcmd)); break; default: break; } } return true; }