/****************************************************************************** * * Copyright(c) 2019 Realtek Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * ******************************************************************************/ #include "pwr.h" #include "coex.h" static void restore_flr_lps(struct mac_ax_adapter *adapter) { struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); MAC_REG_W32(R_AX_WCPU_FW_CTRL, 0); MAC_REG_W32(R_AX_AFE_CTRL1, MAC_REG_R32(R_AX_AFE_CTRL1) & ~B_AX_CMAC_CLK_SEL); MAC_REG_W32(R_AX_GPIO0_15_EECS_EESK_LED1_PULL_LOW_EN, MAC_REG_R32(R_AX_GPIO0_15_EECS_EESK_LED1_PULL_LOW_EN) & ~(B_AX_GPIO8_PULL_LOW_EN | B_AX_LED1_PULL_LOW_EN)); } static void clr_aon_int(struct mac_ax_adapter *adapter) { struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); u32 val32; if (adapter->hw_info->intf != MAC_AX_INTF_PCIE) return; val32 = MAC_REG_R32(R_AX_FWS0IMR); val32 &= ~B_AX_FS_GPIOA_INT_EN; MAC_REG_W32(R_AX_FWS0IMR, val32); val32 = MAC_REG_R32(R_AX_FWS0ISR); val32 |= B_AX_FS_GPIOA_INT; MAC_REG_W32(R_AX_FWS0ISR, val32); } static u32 _patch_aon_int_leave_lps(struct mac_ax_adapter *adapter) { struct mac_ax_ops *mac_ops = adapter_to_mac_ops(adapter); struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); u32 val32, cnt, ret = MACSUCCESS; if (adapter->hw_info->intf != MAC_AX_INTF_PCIE) return MACSUCCESS; restore_flr_lps(adapter); val32 = MAC_REG_R32(R_AX_FWS0IMR); val32 |= B_AX_FS_GPIOA_INT_EN; MAC_REG_W32(R_AX_FWS0IMR, val32); ret = mac_ops->set_gpio_func(adapter, RTW_MAC_GPIO_SW_IO, LPS_LEAVE_GPIO); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("[ERR]set gpio fail %d\n", ret); return ret; } val32 = MAC_REG_R32(R_AX_GPIO0_15_EECS_EESK_LED1_PULL_HIGH_EN); val32 |= B_AX_GPIO10_PULL_HIGH_EN; MAC_REG_W32(R_AX_GPIO0_15_EECS_EESK_LED1_PULL_HIGH_EN, val32); val32 = MAC_REG_R32(R_AX_GPIO_EXT_CTRL); val32 |= (BIT10 | BIT18 | BIT26); MAC_REG_W32(R_AX_GPIO_EXT_CTRL, val32); val32 &= ~BIT10; MAC_REG_W32(R_AX_GPIO_EXT_CTRL, val32); val32 |= BIT10; MAC_REG_W32(R_AX_GPIO_EXT_CTRL, val32); cnt = LPS_POLL_CNT; while (cnt && (GET_FIELD(MAC_REG_R32(R_AX_IC_PWR_STATE), B_AX_WLMAC_PWR_STE) == MAC_AX_MAC_LPS)) { cnt--; PLTFM_DELAY_US(LPS_POLL_DLY_US); } if (!cnt) { PLTFM_MSG_ERR("[ERR]Polling MAC state timeout! 0x3F0 = %X\n", MAC_REG_R32(R_AX_IC_PWR_STATE)); return MACPOLLTO; } return MACSUCCESS; } static u32 pwr_cmd_poll(struct mac_ax_adapter *adapter, struct mac_pwr_cfg *seq) { u8 val = 0; u32 addr; u32 cnt; struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); cnt = PWR_POLL_CNT; addr = seq->addr; while (cnt--) { val = MAC_REG_R8(addr); val &= seq->msk; if (val == (seq->val & seq->msk)) return MACSUCCESS; PLTFM_DELAY_US(PWR_POLL_DLY_US); } PLTFM_MSG_ERR("[ERR] Polling timeout\n"); PLTFM_MSG_ERR("[ERR] addr: %X, %X\n", addr, seq->addr); PLTFM_MSG_ERR("[ERR] val: %X, %X\n", val, seq->val); return MACPOLLTO; } static u32 sub_pwr_seq_start(struct mac_ax_adapter *adapter, u8 cv_msk, u8 intf_msk, struct mac_pwr_cfg *seq) { u8 val; u32 addr; u32 ret; struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); while (seq->cmd != PWR_CMD_END) { if (!(seq->intf_msk & intf_msk) || !(seq->cut_msk & cv_msk)) goto next_seq; switch (seq->cmd) { case PWR_CMD_WRITE: addr = seq->addr; val = MAC_REG_R8(addr); val &= ~(seq->msk); val |= (seq->val & seq->msk); MAC_REG_W8(addr, val); break; case PWR_CMD_POLL: ret = pwr_cmd_poll(adapter, seq); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("[ERR]pwr cmd poll %d\n", ret); return ret; } break; case PWR_CMD_DELAY: if (seq->val == PWR_DELAY_US) PLTFM_DELAY_US(seq->addr); else PLTFM_DELAY_US(seq->addr * 1000); break; default: PLTFM_MSG_ERR("[ERR]unknown pwr seq cmd %d\n", seq->cmd); return MACNOITEM; } next_seq: seq++; } return MACSUCCESS; } u32 pwr_seq_start(struct mac_ax_adapter *adapter, struct mac_pwr_cfg **seq) { u8 cv; u8 intf; u32 ret; struct mac_ax_hw_info *hw_info = adapter->hw_info; struct mac_pwr_cfg *sub_seq = *seq; #if MAC_AX_USB_SUPPORT u32 val = 0; #endif switch (hw_info->cv) { case CAV: cv = PWR_CAV_MSK; break; case CBV: cv = PWR_CBV_MSK; break; case CCV: cv = PWR_CCV_MSK; break; case CDV: cv = PWR_CDV_MSK; break; case CEV: cv = PWR_CEV_MSK; break; default: PLTFM_MSG_ERR("[ERR]cut version\n"); return MACNOITEM; } switch (hw_info->intf) { case MAC_AX_INTF_SDIO: intf = PWR_INTF_MSK_SDIO; break; case MAC_AX_INTF_USB: #if MAC_AX_USB_SUPPORT val = get_usb_mode(adapter); if (val == MAC_AX_USB3) intf = PWR_INTF_MSK_USB3; else intf = PWR_INTF_MSK_USB2; break; #endif case MAC_AX_INTF_PCIE: intf = PWR_INTF_MSK_PCIE; break; default: PLTFM_MSG_ERR("[ERR]interface\n"); return MACNOITEM; } while (sub_seq) { ret = sub_pwr_seq_start(adapter, cv, intf, sub_seq); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("[ERR]sub pwr seq %d\n", ret); return ret; } seq++; sub_seq = *seq; } return MACSUCCESS; } u32 mac_pwr_switch(struct mac_ax_adapter *adapter, u8 on) { u32 ret = MACSUCCESS; u32 ret_end; struct mac_pwr_cfg **pwr_seq; struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter); u32 (*intf_pwr_switch)(void *vadapter, u8 pre_switch, u8 on); u32 (*pwr_func)(void *vadapter); u32 val32; val32 = MAC_REG_R32(R_AX_GPIO_MUXCFG) & B_AX_BOOT_MODE; if (val32 == B_AX_BOOT_MODE) { val32 = MAC_REG_R32(R_AX_SYS_PW_CTRL) & ~B_AX_APFN_ONMAC; MAC_REG_W32(R_AX_SYS_PW_CTRL, val32); val32 = MAC_REG_R32(R_AX_SYS_STATUS1) & ~B_AX_AUTO_WLPON; MAC_REG_W32(R_AX_SYS_STATUS1, val32); val32 = MAC_REG_R32(R_AX_GPIO_MUXCFG) & ~B_AX_BOOT_MODE; MAC_REG_W32(R_AX_GPIO_MUXCFG, val32); val32 = MAC_REG_R32(R_AX_RSV_CTRL) & ~B_AX_R_DIS_PRST; MAC_REG_W32(R_AX_RSV_CTRL, val32); } val32 = MAC_REG_R32(R_AX_IC_PWR_STATE); val32 = GET_FIELD(val32, B_AX_WLMAC_PWR_STE); if (val32 == MAC_AX_MAC_OFF && on == MAC_AX_MAC_OFF) { PLTFM_MSG_WARN("MAC has already powered off\n"); return MACSUCCESS; } intf_pwr_switch = adapter->mac_pwr_info.intf_pwr_switch; if (!intf_pwr_switch) { PLTFM_MSG_ERR("interface power switch func is NULL\n"); ret = MACNPTR; return ret; } if (on) { pwr_seq = adapter->hw_info->pwr_on_seq; pwr_func = adapter->hw_info->pwr_on; } else { pwr_seq = adapter->hw_info->pwr_off_seq; pwr_func = adapter->hw_info->pwr_off; adapter->sm.pwr = MAC_AX_PWR_PRE_OFF; adapter->sm.dmac_func = MAC_AX_FUNC_OFF; adapter->sm.cmac0_func = MAC_AX_FUNC_OFF; adapter->sm.cmac1_func = MAC_AX_FUNC_OFF; adapter->sm.bb0_func = MAC_AX_FUNC_OFF; adapter->sm.bb1_func = MAC_AX_FUNC_OFF; adapter->sm.plat = MAC_AX_PLAT_OFF; } ret = intf_pwr_switch(adapter, PWR_PRE_SWITCH, on); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("interface pre pwr switch fails %d\n", ret); goto END; } if (on) { ret = _patch_aon_int_leave_lps(adapter); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("AON interrupt leave LPS fail %X\n", ret); goto END; } val32 = MAC_REG_R32(R_AX_IC_PWR_STATE); val32 = GET_FIELD(val32, B_AX_WLMAC_PWR_STE); if (val32 == MAC_AX_MAC_ON) { PLTFM_MSG_WARN("MAC has already powered on %d\n", val32); ret = MACALRDYON; goto END; } else if (val32 == MAC_AX_MAC_LPS) { PLTFM_MSG_ERR("MAC leave LPS fail %d\n", val32); ret = MACPWRSTAT; goto END; } } if (!pwr_func) { ret = pwr_seq_start(adapter, pwr_seq); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("[ERR]pwr seq start %d\n", ret); adapter->sm.pwr = MAC_AX_PWR_ERR; goto END; } } else { ret = pwr_func(adapter); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("[ERR]pwr func %d\n", ret); adapter->sm.pwr = MAC_AX_PWR_ERR; goto END; } } ret = intf_pwr_switch(adapter, PWR_POST_SWITCH, on); if (ret != MACSUCCESS) { PLTFM_MSG_ERR("interface post pwr switch fails %d\n", ret); adapter->sm.pwr = MAC_AX_PWR_ERR; goto END; } mac_ax_init_state(adapter); if (on) { adapter->sm.pwr = MAC_AX_PWR_ON; adapter->sm.plat = MAC_AX_PLAT_ON; adapter->sm.io_st = MAC_AX_IO_ST_NORM; adapter->sm.fw_rst = MAC_AX_FW_RESET_IDLE; adapter->sm.l2_st = MAC_AX_L2_DIS; adapter->mac_pwr_info.pwr_in_lps = 0; /* patch form BT BG/LDO issue, ONLY FOR 52B CAV */ if (is_chip_id(adapter, MAC_AX_CHIP_ID_8852B) && is_cv(adapter, CAV)) MAC_REG_W32(R_AX_SCOREBOARD, (u32)(MAC_AX_NOTIFY_TP_MAJOR << MAC_AX_NOTIFY_SH)); } else { adapter->sm.pwr = MAC_AX_PWR_OFF; adapter->sm.l2_st = MAC_AX_L2_DIS; /* patch form BT BG/LDO issue, ONLY FOR 52B CAV */ if (is_chip_id(adapter, MAC_AX_CHIP_ID_8852B) && is_cv(adapter, CAV)) MAC_REG_W32(R_AX_SCOREBOARD, (u32)(MAC_AX_NOTIFY_PWR_MAJOR << MAC_AX_NOTIFY_SH)); } END: if (on) clr_aon_int(adapter); ret_end = intf_pwr_switch(adapter, PWR_END_SWITCH, on); if (ret_end != MACSUCCESS) { PLTFM_MSG_ERR("interface end pwr switch fails %d\n", ret_end); adapter->sm.pwr = MAC_AX_PWR_ERR; return ret_end; } return ret; }