| // SPDX-License-Identifier: GPL-2.0 |
| /****************************************************************************** |
| * |
| * Copyright(c) 2016 Realtek Corporation. |
| * |
| * Contact Information: |
| * wlanfae <wlanfae@realtek.com> |
| * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, |
| * Hsinchu 300, Taiwan. |
| * |
| * Larry Finger <Larry.Finger@lwfinger.net> |
| * |
| *****************************************************************************/ |
| |
| #include "halmac_api.h" |
| #include "rtl_halmac.h" |
| #include <linux/module.h> |
| #include <linux/vmalloc.h> |
| |
| #define DEFAULT_INDICATOR_TIMELMT msecs_to_jiffies(1000) /* ms */ |
| #define FIRMWARE_MAX_SIZE HALMAC_FW_SIZE_MAX_88XX |
| |
| static struct rtl_halmac_ops rtl_halmac_operation = { |
| .halmac_init_adapter = rtl_halmac_init_adapter, |
| .halmac_deinit_adapter = rtl_halmac_deinit_adapter, |
| .halmac_init_hal = rtl_halmac_init_hal, |
| .halmac_deinit_hal = rtl_halmac_deinit_hal, |
| .halmac_poweron = rtl_halmac_poweron, |
| .halmac_poweroff = rtl_halmac_poweroff, |
| |
| .halmac_phy_power_switch = rtl_halmac_phy_power_switch, |
| .halmac_set_mac_address = rtl_halmac_set_mac_address, |
| .halmac_set_bssid = rtl_halmac_set_bssid, |
| |
| .halmac_get_physical_efuse_size = rtl_halmac_get_physical_efuse_size, |
| .halmac_read_physical_efuse_map = rtl_halmac_read_physical_efuse_map, |
| .halmac_get_logical_efuse_size = rtl_halmac_get_logical_efuse_size, |
| .halmac_read_logical_efuse_map = rtl_halmac_read_logical_efuse_map, |
| |
| .halmac_set_bandwidth = rtl_halmac_set_bandwidth, |
| |
| .halmac_c2h_handle = rtl_halmac_c2h_handle, |
| |
| .halmac_chk_txdesc = rtl_halmac_chk_txdesc, |
| }; |
| |
| struct rtl_halmac_ops *rtl_halmac_get_ops_pointer(void) |
| { |
| return &rtl_halmac_operation; |
| } |
| EXPORT_SYMBOL(rtl_halmac_get_ops_pointer); |
| |
| /* |
| * Driver API for HALMAC operations |
| */ |
| |
| static u8 _halmac_reg_read_8(void *p, u32 offset) |
| { |
| struct rtl_priv *rtlpriv = (struct rtl_priv *)p; |
| |
| return rtl_read_byte(rtlpriv, offset); |
| } |
| |
| static u16 _halmac_reg_read_16(void *p, u32 offset) |
| { |
| struct rtl_priv *rtlpriv = (struct rtl_priv *)p; |
| |
| return rtl_read_word(rtlpriv, offset); |
| } |
| |
| static u32 _halmac_reg_read_32(void *p, u32 offset) |
| { |
| struct rtl_priv *rtlpriv = (struct rtl_priv *)p; |
| |
| return rtl_read_dword(rtlpriv, offset); |
| } |
| |
| static void _halmac_reg_write_8(void *p, u32 offset, u8 val) |
| { |
| struct rtl_priv *rtlpriv = (struct rtl_priv *)p; |
| |
| rtl_write_byte(rtlpriv, offset, val); |
| } |
| |
| static void _halmac_reg_write_16(void *p, u32 offset, u16 val) |
| { |
| struct rtl_priv *rtlpriv = (struct rtl_priv *)p; |
| |
| rtl_write_word(rtlpriv, offset, val); |
| } |
| |
| static void _halmac_reg_write_32(void *p, u32 offset, u32 val) |
| { |
| struct rtl_priv *rtlpriv = (struct rtl_priv *)p; |
| |
| rtl_write_dword(rtlpriv, offset, val); |
| } |
| |
| static bool _halmac_write_data_rsvd_page(void *p, u8 *buf, u32 size) |
| { |
| struct rtl_priv *rtlpriv = (struct rtl_priv *)p; |
| |
| if (rtlpriv->cfg->ops->halmac_cb_write_data_rsvd_page && |
| rtlpriv->cfg->ops->halmac_cb_write_data_rsvd_page(rtlpriv, buf, |
| size)) |
| return true; |
| |
| return false; |
| } |
| |
| static bool _halmac_write_data_h2c(void *p, u8 *buf, u32 size) |
| { |
| struct rtl_priv *rtlpriv = (struct rtl_priv *)p; |
| |
| if (rtlpriv->cfg->ops->halmac_cb_write_data_h2c && |
| rtlpriv->cfg->ops->halmac_cb_write_data_h2c(rtlpriv, buf, size)) |
| return true; |
| |
| return false; |
| } |
| |
| static const char *const RTL_HALMAC_FEATURE_NAME[] = { |
| "HALMAC_FEATURE_CFG_PARA", |
| "HALMAC_FEATURE_DUMP_PHYSICAL_EFUSE", |
| "HALMAC_FEATURE_DUMP_LOGICAL_EFUSE", |
| "HALMAC_FEATURE_UPDATE_PACKET", |
| "HALMAC_FEATURE_UPDATE_DATAPACK", |
| "HALMAC_FEATURE_RUN_DATAPACK", |
| "HALMAC_FEATURE_CHANNEL_SWITCH", |
| "HALMAC_FEATURE_IQK", |
| "HALMAC_FEATURE_POWER_TRACKING", |
| "HALMAC_FEATURE_PSD", |
| "HALMAC_FEATURE_ALL"}; |
| |
| static inline bool is_valid_id_status(struct rtl_priv *rtlpriv, |
| enum halmac_feature_id id, |
| enum halmac_cmd_process_status status) |
| { |
| switch (id) { |
| case HALMAC_FEATURE_CFG_PARA: |
| RT_TRACE(rtlpriv, COMP_HALMAC, DBG_LOUD, "%s: %s\n", __func__, |
| RTL_HALMAC_FEATURE_NAME[id]); |
| break; |
| case HALMAC_FEATURE_DUMP_PHYSICAL_EFUSE: |
| RT_TRACE(rtlpriv, COMP_HALMAC, DBG_LOUD, "%s: %s\n", __func__, |
| RTL_HALMAC_FEATURE_NAME[id]); |
| if (status != HALMAC_CMD_PROCESS_DONE) { |
| RT_TRACE(rtlpriv, COMP_HALMAC, DBG_LOUD, |
| "%s: <WARN> id(%d) unspecified status(%d)!\n", |
| __func__, id, status); |
| } |
| break; |
| case HALMAC_FEATURE_DUMP_LOGICAL_EFUSE: |
| RT_TRACE(rtlpriv, COMP_HALMAC, DBG_LOUD, "%s: %s\n", __func__, |
| RTL_HALMAC_FEATURE_NAME[id]); |
| if (status != HALMAC_CMD_PROCESS_DONE) { |
| RT_TRACE(rtlpriv, COMP_HALMAC, DBG_LOUD, |
| "%s: <WARN> id(%d) unspecified status(%d)!\n", |
| __func__, id, status); |
| } |
| break; |
| case HALMAC_FEATURE_UPDATE_PACKET: |
| RT_TRACE(rtlpriv, COMP_HALMAC, DBG_LOUD, "%s: %s\n", __func__, |
| RTL_HALMAC_FEATURE_NAME[id]); |
| break; |
| case HALMAC_FEATURE_UPDATE_DATAPACK: |
| RT_TRACE(rtlpriv, COMP_HALMAC, DBG_LOUD, "%s: %s\n", __func__, |
| RTL_HALMAC_FEATURE_NAME[id]); |
| break; |
| case HALMAC_FEATURE_RUN_DATAPACK: |
| RT_TRACE(rtlpriv, COMP_HALMAC, DBG_LOUD, "%s: %s\n", __func__, |
| RTL_HALMAC_FEATURE_NAME[id]); |
| break; |
| case HALMAC_FEATURE_CHANNEL_SWITCH: |
| RT_TRACE(rtlpriv, COMP_HALMAC, DBG_LOUD, "%s: %s\n", __func__, |
| RTL_HALMAC_FEATURE_NAME[id]); |
| break; |
| case HALMAC_FEATURE_IQK: |
| RT_TRACE(rtlpriv, COMP_HALMAC, DBG_LOUD, "%s: %s\n", __func__, |
| RTL_HALMAC_FEATURE_NAME[id]); |
| break; |
| case HALMAC_FEATURE_POWER_TRACKING: |
| RT_TRACE(rtlpriv, COMP_HALMAC, DBG_LOUD, "%s: %s\n", __func__, |
| RTL_HALMAC_FEATURE_NAME[id]); |
| break; |
| case HALMAC_FEATURE_PSD: |
| RT_TRACE(rtlpriv, COMP_HALMAC, DBG_LOUD, "%s: %s\n", __func__, |
| RTL_HALMAC_FEATURE_NAME[id]); |
| break; |
| case HALMAC_FEATURE_ALL: |
| RT_TRACE(rtlpriv, COMP_HALMAC, DBG_LOUD, "%s: %s\n", __func__, |
| RTL_HALMAC_FEATURE_NAME[id]); |
| break; |
| default: |
| RT_TRACE(rtlpriv, COMP_HALMAC, DBG_LOUD, |
| "%s: unknown feature id(%d)\n", __func__, id); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static int init_halmac_event_with_waittime(struct rtl_priv *rtlpriv, |
| enum halmac_feature_id id, u8 *buf, |
| u32 size, u32 time) |
| { |
| struct completion *comp; |
| |
| if (!rtlpriv->halmac.indicator[id].comp) { |
| comp = kzalloc(sizeof(*comp), GFP_KERNEL); |
| if (!comp) |
| return -1; |
| } else { |
| RT_TRACE(rtlpriv, COMP_HALMAC, DBG_LOUD, |
| "%s: <WARN> id(%d) sctx is not NULL!!\n", __func__, |
| id); |
| comp = rtlpriv->halmac.indicator[id].comp; |
| rtlpriv->halmac.indicator[id].comp = NULL; |
| } |
| |
| init_completion(comp); |
| rtlpriv->halmac.indicator[id].wait_ms = time; |
| |
| rtlpriv->halmac.indicator[id].buffer = buf; |
| rtlpriv->halmac.indicator[id].buf_size = size; |
| rtlpriv->halmac.indicator[id].ret_size = 0; |
| rtlpriv->halmac.indicator[id].status = 0; |
| /* fill sctx at least to sure other variables are all ready! */ |
| rtlpriv->halmac.indicator[id].comp = comp; |
| |
| return 0; |
| } |
| |
| static inline int init_halmac_event(struct rtl_priv *rtlpriv, |
| enum halmac_feature_id id, u8 *buf, |
| u32 size) |
| { |
| return init_halmac_event_with_waittime(rtlpriv, id, buf, size, |
| DEFAULT_INDICATOR_TIMELMT); |
| } |
| |
| static void free_halmac_event(struct rtl_priv *rtlpriv, |
| enum halmac_feature_id id) |
| { |
| struct completion *comp; |
| |
| if (!rtlpriv->halmac.indicator[id].comp) |
| return; |
| |
| comp = rtlpriv->halmac.indicator[id].comp; |
| rtlpriv->halmac.indicator[id].comp = NULL; |
| kfree(comp); |
| } |
| |
| static int wait_halmac_event(struct rtl_priv *rtlpriv, |
| enum halmac_feature_id id) |
| { |
| struct completion *comp; |
| int ret; |
| |
| comp = rtlpriv->halmac.indicator[id].comp; |
| if (!comp) |
| return -1; |
| |
| ret = wait_for_completion_timeout( |
| comp, rtlpriv->halmac.indicator[id].wait_ms); |
| free_halmac_event(rtlpriv, id); |
| if (ret > 0) |
| return 0; |
| |
| return -1; |
| } |
| |
| /* |
| * Return: |
| * Always return true, HALMAC don't care the return value. |
| */ |
| static bool |
| _halmac_event_indication(void *p, enum halmac_feature_id feature_id, |
| enum halmac_cmd_process_status process_status, u8 *buf, |
| u32 size) |
| { |
| struct rtl_priv *rtlpriv; |
| struct rtl_halmac_indicator *tbl, *indicator; |
| struct completion *comp; |
| u32 cpsz; |
| bool ret; |
| |
| rtlpriv = (struct rtl_priv *)p; |
| tbl = rtlpriv->halmac.indicator; |
| |
| ret = is_valid_id_status(rtlpriv, feature_id, process_status); |
| if (!ret) |
| goto exit; |
| |
| indicator = &tbl[feature_id]; |
| indicator->status = process_status; |
| indicator->ret_size = size; |
| if (!indicator->comp) { |
| RT_TRACE(rtlpriv, COMP_HALMAC, DBG_LOUD, |
| "%s: No feature id(%d) waiting!!\n", __func__, |
| feature_id); |
| goto exit; |
| } |
| comp = indicator->comp; |
| |
| if (process_status == HALMAC_CMD_PROCESS_ERROR) { |
| RT_TRACE(rtlpriv, COMP_HALMAC, DBG_LOUD, |
| "%s: Something wrong id(%d)!!\n", __func__, |
| feature_id); |
| complete(comp); /* may provide error code */ |
| goto exit; |
| } |
| |
| if (size > indicator->buf_size) { |
| RT_TRACE( |
| rtlpriv, COMP_HALMAC, DBG_LOUD, |
| "%s: <WARN> id(%d) buffer is not enough(%d<%d), data will be truncated!\n", |
| __func__, feature_id, indicator->buf_size, size); |
| cpsz = indicator->buf_size; |
| } else { |
| cpsz = size; |
| } |
| |
| if (cpsz && indicator->buffer) |
| memcpy(indicator->buffer, buf, cpsz); |
| |
| complete(comp); |
| |
| exit: |
| return true; |
| } |
| |
| static struct halmac_platform_api rtl_halmac_platform_api = { |
| /* R/W register */ |
| .REG_READ_8 = _halmac_reg_read_8, |
| .REG_READ_16 = _halmac_reg_read_16, |
| .REG_READ_32 = _halmac_reg_read_32, |
| .REG_WRITE_8 = _halmac_reg_write_8, |
| .REG_WRITE_16 = _halmac_reg_write_16, |
| .REG_WRITE_32 = _halmac_reg_write_32, |
| |
| /* Write data */ |
| /* impletement in HAL-IC level */ |
| .SEND_RSVD_PAGE = _halmac_write_data_rsvd_page, |
| .SEND_H2C_PKT = _halmac_write_data_h2c, |
| |
| .EVENT_INDICATION = _halmac_event_indication, |
| }; |
| |
| static int init_priv(struct rtl_halmac *halmac) |
| { |
| struct rtl_halmac_indicator *indicator; |
| u32 count, size; |
| |
| halmac->send_general_info = 0; |
| |
| count = HALMAC_FEATURE_ALL + 1; |
| size = sizeof(*indicator) * count; |
| indicator = kzalloc(size, GFP_KERNEL); |
| if (!indicator) |
| return -1; |
| halmac->indicator = indicator; |
| |
| return 0; |
| } |
| |
| static void deinit_priv(struct rtl_halmac *halmac) |
| { |
| struct rtl_halmac_indicator *indicator; |
| |
| indicator = halmac->indicator; |
| halmac->indicator = NULL; |
| kfree(indicator); |
| } |
| |
| int rtl_halmac_init_adapter(struct rtl_priv *rtlpriv) |
| { |
| struct halmac_adapter *halmac; |
| struct halmac_api *api; |
| enum halmac_interface intf; |
| enum halmac_ret_status status; |
| int err = 0; |
| struct halmac_platform_api *pf_api = &rtl_halmac_platform_api; |
| |
| halmac = rtlpriv_to_halmac(rtlpriv); |
| if (halmac) { |
| err = 0; |
| goto out; |
| } |
| |
| err = init_priv(&rtlpriv->halmac); |
| if (err) |
| goto out; |
| |
| intf = HALMAC_INTERFACE_PCIE; |
| status = halmac_init_adapter(rtlpriv, pf_api, intf, &halmac, &api); |
| if (status != HALMAC_RET_SUCCESS) { |
| RT_TRACE(rtlpriv, COMP_HALMAC, DBG_LOUD, |
| "%s: halmac_init_adapter fail!(status=%d)\n", __func__, |
| status); |
| err = -1; |
| goto out; |
| } |
| |
| rtlpriv->halmac.internal = halmac; |
| |
| out: |
| if (err) |
| rtl_halmac_deinit_adapter(rtlpriv); |
| |
| return err; |
| } |
| |
| int rtl_halmac_deinit_adapter(struct rtl_priv *rtlpriv) |
| { |
| struct halmac_adapter *halmac; |
| enum halmac_ret_status status; |
| int err = 0; |
| |
| halmac = rtlpriv_to_halmac(rtlpriv); |
| if (!halmac) { |
| err = 0; |
| goto out; |
| } |
| |
| deinit_priv(&rtlpriv->halmac); |
| |
| halmac_halt_api(halmac); |
| |
| status = halmac_deinit_adapter(halmac); |
| rtlpriv->halmac.internal = NULL; |
| if (status != HALMAC_RET_SUCCESS) { |
| err = -1; |
| goto out; |
| } |
| |
| out: |
| return err; |
| } |
| |
| int rtl_halmac_poweron(struct rtl_priv *rtlpriv) |
| { |
| struct halmac_adapter *halmac; |
| struct halmac_api *api; |
| enum halmac_ret_status status; |
| int err = -1; |
| |
| halmac = rtlpriv_to_halmac(rtlpriv); |
| if (!halmac) |
| goto out; |
| |
| api = HALMAC_GET_API(halmac); |
| |
| status = api->halmac_pre_init_system_cfg(halmac); |
| if (status != HALMAC_RET_SUCCESS) |
| goto out; |
| |
| status = api->halmac_mac_power_switch(halmac, HALMAC_MAC_POWER_ON); |
| if (status != HALMAC_RET_SUCCESS) |
| goto out; |
| |
| status = api->halmac_init_system_cfg(halmac); |
| if (status != HALMAC_RET_SUCCESS) |
| goto out; |
| |
| err = 0; |
| out: |
| return err; |
| } |
| |
| int rtl_halmac_poweroff(struct rtl_priv *rtlpriv) |
| { |
| struct halmac_adapter *halmac; |
| struct halmac_api *api; |
| enum halmac_ret_status status; |
| int err = -1; |
| |
| halmac = rtlpriv_to_halmac(rtlpriv); |
| if (!halmac) |
| goto out; |
| |
| api = HALMAC_GET_API(halmac); |
| |
| status = api->halmac_mac_power_switch(halmac, HALMAC_MAC_POWER_OFF); |
| if (status != HALMAC_RET_SUCCESS) |
| goto out; |
| |
| err = 0; |
| out: |
| return err; |
| } |
| |
| /* |
| * Note: |
| * When this function return, the register REG_RCR may be changed. |
| */ |
| int rtl_halmac_config_rx_info(struct rtl_priv *rtlpriv, |
| enum halmac_drv_info info) |
| { |
| struct halmac_adapter *halmac; |
| struct halmac_api *api; |
| enum halmac_ret_status status; |
| int err = -1; |
| |
| halmac = rtlpriv_to_halmac(rtlpriv); |
| api = HALMAC_GET_API(halmac); |
| |
| status = api->halmac_cfg_drv_info(halmac, info); |
| if (status != HALMAC_RET_SUCCESS) |
| goto out; |
| |
| err = 0; |
| out: |
| return err; |
| } |
| |
| static enum halmac_ret_status init_mac_flow(struct rtl_priv *rtlpriv) |
| { |
| struct halmac_adapter *halmac; |
| struct halmac_api *api; |
| enum halmac_ret_status status; |
| u8 wifi_test = 0; |
| int err; |
| |
| halmac = rtlpriv_to_halmac(rtlpriv); |
| api = HALMAC_GET_API(halmac); |
| |
| if (wifi_test) |
| status = api->halmac_init_mac_cfg(halmac, HALMAC_TRX_MODE_WMM); |
| else |
| status = api->halmac_init_mac_cfg(halmac, |
| HALMAC_TRX_MODE_NORMAL); |
| if (status != HALMAC_RET_SUCCESS) |
| goto out; |
| |
| err = rtl_halmac_rx_agg_switch(rtlpriv, true); |
| if (err) |
| goto out; |
| |
| if (rtlpriv->cfg->maps[RTL_RC_VHT_RATE_1SS_MCS7]) |
| status = api->halmac_cfg_operation_mode( |
| halmac, HALMAC_WIRELESS_MODE_AC); |
| else if (rtlpriv->cfg->maps[RTL_RC_HT_RATEMCS7]) |
| status = api->halmac_cfg_operation_mode(halmac, |
| HALMAC_WIRELESS_MODE_N); |
| else if (rtlpriv->cfg->maps[RTL_RC_OFDM_RATE6M]) |
| status = api->halmac_cfg_operation_mode(halmac, |
| HALMAC_WIRELESS_MODE_G); |
| else |
| status = api->halmac_cfg_operation_mode(halmac, |
| HALMAC_WIRELESS_MODE_B); |
| if (status != HALMAC_RET_SUCCESS) |
| goto out; |
| |
| out: |
| return status; |
| } |
| |
| static inline enum halmac_rf_type _rf_type_drv2halmac(enum rf_type rf_drv) |
| { |
| enum halmac_rf_type rf_mac; |
| |
| switch (rf_drv) { |
| case RF_1T2R: |
| rf_mac = HALMAC_RF_1T2R; |
| break; |
| case RF_2T2R: |
| rf_mac = HALMAC_RF_2T2R; |
| break; |
| case RF_1T1R: |
| rf_mac = HALMAC_RF_1T1R; |
| break; |
| case RF_2T2R_GREEN: |
| rf_mac = HALMAC_RF_2T2R_GREEN; |
| break; |
| default: |
| rf_mac = (enum halmac_rf_type)rf_drv; |
| break; |
| } |
| |
| return rf_mac; |
| } |
| |
| static int _send_general_info(struct rtl_priv *rtlpriv) |
| { |
| struct halmac_adapter *halmac; |
| struct halmac_api *api; |
| struct halmac_general_info info; |
| enum halmac_ret_status status; |
| |
| halmac = rtlpriv_to_halmac(rtlpriv); |
| if (!halmac) |
| return -1; |
| api = HALMAC_GET_API(halmac); |
| |
| memset(&info, 0, sizeof(info)); |
| info.rfe_type = rtlpriv->rtlhal.rfe_type; |
| info.rf_type = _rf_type_drv2halmac(rtlpriv->phy.rf_type); |
| |
| status = api->halmac_send_general_info(halmac, &info); |
| switch (status) { |
| case HALMAC_RET_SUCCESS: |
| break; |
| case HALMAC_RET_NO_DLFW: |
| RT_TRACE(rtlpriv, COMP_HALMAC, DBG_WARNING, |
| "%s: halmac_send_general_info() fail because fw not dl!\n", |
| __func__); |
| /* fall through */ |
| default: |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * Notices: |
| * Make sure |
| * 1. rtl_hal_get_hwreg(HW_VAR_RF_TYPE) |
| * 2. HAL_DATA_TYPE.rfe_type |
| * already ready for use before calling this function. |
| */ |
| static int _halmac_init_hal(struct rtl_priv *rtlpriv, u8 *fw, u32 fwsize) |
| { |
| struct halmac_adapter *halmac; |
| struct halmac_api *api; |
| enum halmac_ret_status status; |
| bool ok; |
| bool fw_ok = false; |
| int err, err_ret = -1; |
| |
| halmac = rtlpriv_to_halmac(rtlpriv); |
| if (!halmac) |
| goto out; |
| api = HALMAC_GET_API(halmac); |
| |
| /* StatePowerOff */ |
| |
| /* SKIP: halmac_init_adapter (Already done before) */ |
| |
| /* halmac_pre_Init_system_cfg */ |
| /* halmac_mac_power_switch(on) */ |
| /* halmac_Init_system_cfg */ |
| err = rtl_halmac_poweron(rtlpriv); |
| if (err) |
| goto out; |
| |
| /* StatePowerOn */ |
| |
| /* DownloadFW */ |
| rtlpriv->halmac.send_general_info = 0; |
| if (fw && fwsize) { |
| err = rtl_halmac_dlfw(rtlpriv, fw, fwsize); |
| if (err) |
| goto out; |
| fw_ok = true; |
| } |
| |
| /* InitMACFlow */ |
| status = init_mac_flow(rtlpriv); |
| if (status != HALMAC_RET_SUCCESS) |
| goto out; |
| |
| /* halmac_send_general_info */ |
| if (fw_ok) { |
| rtlpriv->halmac.send_general_info = 0; |
| err = _send_general_info(rtlpriv); |
| if (err) |
| goto out; |
| } else { |
| rtlpriv->halmac.send_general_info = 1; |
| } |
| |
| /* Init Phy parameter-MAC */ |
| if (rtlpriv->cfg->ops->halmac_cb_init_mac_register) |
| ok = rtlpriv->cfg->ops->halmac_cb_init_mac_register(rtlpriv); |
| else |
| ok = false; |
| |
| if (!ok) |
| goto out; |
| |
| /* StateMacInitialized */ |
| |
| /* halmac_cfg_drv_info */ |
| err = rtl_halmac_config_rx_info(rtlpriv, HALMAC_DRV_INFO_PHY_STATUS); |
| if (err) |
| goto out; |
| |
| /* halmac_set_hw_value(HALMAC_HW_EN_BB_RF) */ |
| /* Init BB, RF */ |
| if (rtlpriv->cfg->ops->halmac_cb_init_bb_rf_register) |
| ok = rtlpriv->cfg->ops->halmac_cb_init_bb_rf_register(rtlpriv); |
| else |
| ok = false; |
| |
| if (!ok) |
| goto out; |
| |
| status = api->halmac_init_interface_cfg(halmac); |
| if (status != HALMAC_RET_SUCCESS) |
| goto out; |
| |
| /* SKIP: halmac_verify_platform_api */ |
| /* SKIP: halmac_h2c_lb */ |
| |
| /* StateRxIdle */ |
| |
| err_ret = 0; |
| out: |
| return err_ret; |
| } |
| |
| int rtl_halmac_init_hal(struct rtl_priv *rtlpriv) |
| { |
| if (!rtlpriv->rtlhal.pfirmware || rtlpriv->rtlhal.fwsize == 0) |
| return -1; |
| |
| return _halmac_init_hal(rtlpriv, rtlpriv->rtlhal.pfirmware, |
| rtlpriv->rtlhal.fwsize); |
| } |
| |
| int rtl_halmac_deinit_hal(struct rtl_priv *rtlpriv) |
| { |
| struct halmac_adapter *halmac; |
| struct halmac_api *api; |
| enum halmac_ret_status status; |
| int err = -1; |
| |
| halmac = rtlpriv_to_halmac(rtlpriv); |
| if (!halmac) |
| goto out; |
| api = HALMAC_GET_API(halmac); |
| |
| status = api->halmac_deinit_interface_cfg(halmac); |
| if (status != HALMAC_RET_SUCCESS) |
| goto out; |
| |
| /* rtw_hal_power_off(adapter); */ |
| status = api->halmac_mac_power_switch(halmac, HALMAC_MAC_POWER_OFF); |
| if (status != HALMAC_RET_SUCCESS) |
| goto out; |
| |
| err = 0; |
| out: |
| return err; |
| } |
| |
| int rtl_halmac_self_verify(struct rtl_priv *rtlpriv) |
| { |
| struct halmac_adapter *mac; |
| struct halmac_api *api; |
| enum halmac_ret_status status; |
| int err = -1; |
| |
| mac = rtlpriv_to_halmac(rtlpriv); |
| api = HALMAC_GET_API(mac); |
| |
| status = api->halmac_verify_platform_api(mac); |
| if (status != HALMAC_RET_SUCCESS) |
| goto out; |
| |
| status = api->halmac_h2c_lb(mac); |
| if (status != HALMAC_RET_SUCCESS) |
| goto out; |
| |
| err = 0; |
| out: |
| return err; |
| } |
| |
| int rtl_halmac_dlfw(struct rtl_priv *rtlpriv, u8 *fw, u32 fwsize) |
| { |
| struct halmac_adapter *mac; |
| struct halmac_api *api; |
| enum halmac_ret_status status; |
| struct halmac_fw_version fw_version; |
| int err = 0; |
| |
| mac = rtlpriv_to_halmac(rtlpriv); |
| api = HALMAC_GET_API(mac); |
| |
| if ((!fw) || (!fwsize)) |
| return -1; |
| |
| /* 1. Driver Stop Tx */ |
| /* ToDo */ |
| |
| /* 2. Driver Check Tx FIFO is empty */ |
| /* ToDo */ |
| |
| /* 3. Config MAX download size */ |
| api->halmac_cfg_max_dl_size(mac, 0x1000); |
| |
| /* 4. Download Firmware */ |
| mac->h2c_packet_seq = 0; |
| status = api->halmac_download_firmware(mac, fw, fwsize); |
| if (status != HALMAC_RET_SUCCESS) |
| return -1; |
| |
| status = api->halmac_get_fw_version(mac, &fw_version); |
| if (status == HALMAC_RET_SUCCESS) { |
| rtlpriv->rtlhal.fw_version = fw_version.version; |
| rtlpriv->rtlhal.fw_subversion = |
| (fw_version.sub_version << 8) | (fw_version.sub_index); |
| |
| RT_TRACE( |
| rtlpriv, COMP_HALMAC, DBG_DMESG, |
| "halmac report firmware version %04X.%04X\n", |
| rtlpriv->rtlhal.fw_version, |
| rtlpriv->rtlhal.fw_subversion); |
| } |
| |
| if (rtlpriv->halmac.send_general_info) { |
| rtlpriv->halmac.send_general_info = 0; |
| err = _send_general_info(rtlpriv); |
| } |
| |
| /* 5. Driver resume TX if needed */ |
| /* ToDo */ |
| |
| /* 6. Reset driver variables if needed */ |
| /*hal->LastHMEBoxNum = 0;*/ |
| |
| return err; |
| } |
| |
| /* |
| * Description: |
| * Power on/off BB/RF domain. |
| * |
| * Parameters: |
| * enable true/false for power on/off |
| * |
| * Return: |
| * 0 Success |
| * others Fail |
| */ |
| int rtl_halmac_phy_power_switch(struct rtl_priv *rtlpriv, u8 enable) |
| { |
| struct halmac_adapter *halmac; |
| struct halmac_api *api; |
| enum halmac_ret_status status; |
| |
| halmac = rtlpriv_to_halmac(rtlpriv); |
| if (!halmac) |
| return -1; |
| api = HALMAC_GET_API(halmac); |
| |
| status = api->halmac_set_hw_value(halmac, HALMAC_HW_EN_BB_RF, &enable); |
| if (status != HALMAC_RET_SUCCESS) |
| return -1; |
| |
| return 0; |
| } |
| |
| static bool _is_fw_read_cmd_down(struct rtl_priv *rtlpriv, u8 msgbox_num) |
| { |
| bool read_down = false; |
| int retry_cnts = 100; |
| u8 valid; |
| |
| RT_TRACE(rtlpriv, COMP_HALMAC, DBG_LOUD, |
| "%s, reg_1cc(%x), msg_box(%d)...\n", __func__, |
| rtl_read_byte(rtlpriv, REG_HMETFR), msgbox_num); |
| |
| do { |
| valid = rtl_read_byte(rtlpriv, REG_HMETFR) & BIT(msgbox_num); |
| if (valid == 0) |
| read_down = true; |
| else |
| schedule(); |
| } while ((!read_down) && (retry_cnts--)); |
| |
| return read_down; |
| } |
| |
| int rtl_halmac_send_h2c(struct rtl_priv *rtlpriv, u8 *h2c) |
| { |
| u8 h2c_box_num = 0; |
| u32 msgbox_addr = 0; |
| u32 msgbox_ex_addr = 0; |
| __le32 h2c_cmd = 0; |
| __le32 h2c_cmd_ex = 0; |
| s32 ret = -1; |
| unsigned long flag = 0; |
| struct rtl_hal *rtlhal = rtl_hal(rtlpriv); |
| |
| if (!h2c) { |
| RT_TRACE(rtlpriv, COMP_HALMAC, DBG_LOUD, "%s: pbuf is NULL\n", |
| __func__); |
| return ret; |
| } |
| |
| spin_lock_irqsave(&rtlpriv->locks.h2c_lock, flag); |
| |
| /* pay attention to if race condition happened in H2C cmd setting */ |
| h2c_box_num = rtlhal->last_hmeboxnum; |
| |
| if (!_is_fw_read_cmd_down(rtlpriv, h2c_box_num)) { |
| RT_TRACE(rtlpriv, COMP_HALMAC, DBG_LOUD, |
| " fw read cmd failed...\n"); |
| goto exit; |
| } |
| |
| /* Write Ext command(byte 4 -7) */ |
| msgbox_ex_addr = REG_HMEBOX_E0 + (h2c_box_num * EX_MESSAGE_BOX_SIZE); |
| memcpy((u8 *)(&h2c_cmd_ex), h2c + 4, EX_MESSAGE_BOX_SIZE); |
| rtl_write_dword(rtlpriv, msgbox_ex_addr, le32_to_cpu(h2c_cmd_ex)); |
| |
| /* Write command (byte 0 -3 ) */ |
| msgbox_addr = REG_HMEBOX0 + (h2c_box_num * MESSAGE_BOX_SIZE); |
| memcpy((u8 *)(&h2c_cmd), h2c, 4); |
| rtl_write_dword(rtlpriv, msgbox_addr, le32_to_cpu(h2c_cmd)); |
| |
| /* update last msg box number */ |
| rtlhal->last_hmeboxnum = (h2c_box_num + 1) % MAX_H2C_BOX_NUMS; |
| ret = 0; |
| |
| exit: |
| spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag); |
| return ret; |
| } |
| |
| int rtl_halmac_c2h_handle(struct rtl_priv *rtlpriv, u8 *c2h, u32 size) |
| { |
| struct halmac_adapter *mac; |
| struct halmac_api *api; |
| enum halmac_ret_status status; |
| |
| mac = rtlpriv_to_halmac(rtlpriv); |
| api = HALMAC_GET_API(mac); |
| |
| status = api->halmac_get_c2h_info(mac, c2h, size); |
| if (status != HALMAC_RET_SUCCESS) |
| return -1; |
| |
| return 0; |
| } |
| |
| int rtl_halmac_get_physical_efuse_size(struct rtl_priv *rtlpriv, u32 *size) |
| { |
| struct halmac_adapter *mac; |
| struct halmac_api *api; |
| enum halmac_ret_status status; |
| u32 val; |
| |
| mac = rtlpriv_to_halmac(rtlpriv); |
| api = HALMAC_GET_API(mac); |
| |
| status = api->halmac_get_efuse_size(mac, &val); |
| if (status != HALMAC_RET_SUCCESS) |
| return -1; |
| |
| *size = val; |
| return 0; |
| } |
| |
| int rtl_halmac_read_physical_efuse_map(struct rtl_priv *rtlpriv, u8 *map, |
| u32 size) |
| { |
| struct halmac_adapter *mac; |
| struct halmac_api *api; |
| enum halmac_ret_status status; |
| enum halmac_feature_id id; |
| int ret; |
| |
| mac = rtlpriv_to_halmac(rtlpriv); |
| api = HALMAC_GET_API(mac); |
| id = HALMAC_FEATURE_DUMP_PHYSICAL_EFUSE; |
| |
| ret = init_halmac_event(rtlpriv, id, map, size); |
| if (ret) |
| return -1; |
| |
| status = api->halmac_dump_efuse_map(mac, HALMAC_EFUSE_R_DRV); |
| if (status != HALMAC_RET_SUCCESS) { |
| free_halmac_event(rtlpriv, id); |
| return -1; |
| } |
| |
| ret = wait_halmac_event(rtlpriv, id); |
| if (ret) |
| return -1; |
| |
| return 0; |
| } |
| |
| int rtl_halmac_read_physical_efuse(struct rtl_priv *rtlpriv, u32 offset, |
| u32 cnt, u8 *data) |
| { |
| struct halmac_adapter *mac; |
| struct halmac_api *api; |
| enum halmac_ret_status status; |
| u8 v; |
| u32 i; |
| |
| mac = rtlpriv_to_halmac(rtlpriv); |
| api = HALMAC_GET_API(mac); |
| |
| for (i = 0; i < cnt; i++) { |
| status = api->halmac_read_efuse(mac, offset + i, &v); |
| if (status != HALMAC_RET_SUCCESS) |
| return -1; |
| data[i] = v; |
| } |
| |
| return 0; |
| } |
| |
| int rtl_halmac_write_physical_efuse(struct rtl_priv *rtlpriv, u32 offset, |
| u32 cnt, u8 *data) |
| { |
| struct halmac_adapter *mac; |
| struct halmac_api *api; |
| enum halmac_ret_status status; |
| u32 i; |
| |
| mac = rtlpriv_to_halmac(rtlpriv); |
| api = HALMAC_GET_API(mac); |
| |
| for (i = 0; i < cnt; i++) { |
| status = api->halmac_write_efuse(mac, offset + i, data[i]); |
| if (status != HALMAC_RET_SUCCESS) |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| int rtl_halmac_get_logical_efuse_size(struct rtl_priv *rtlpriv, u32 *size) |
| { |
| struct halmac_adapter *mac; |
| struct halmac_api *api; |
| enum halmac_ret_status status; |
| u32 val; |
| |
| mac = rtlpriv_to_halmac(rtlpriv); |
| api = HALMAC_GET_API(mac); |
| |
| status = api->halmac_get_logical_efuse_size(mac, &val); |
| if (status != HALMAC_RET_SUCCESS) |
| return -1; |
| |
| *size = val; |
| return 0; |
| } |
| |
| int rtl_halmac_read_logical_efuse_map(struct rtl_priv *rtlpriv, u8 *map, |
| u32 size) |
| { |
| struct halmac_adapter *mac; |
| struct halmac_api *api; |
| enum halmac_ret_status status; |
| enum halmac_feature_id id; |
| int ret; |
| |
| mac = rtlpriv_to_halmac(rtlpriv); |
| api = HALMAC_GET_API(mac); |
| id = HALMAC_FEATURE_DUMP_LOGICAL_EFUSE; |
| |
| ret = init_halmac_event(rtlpriv, id, map, size); |
| if (ret) |
| return -1; |
| |
| status = api->halmac_dump_logical_efuse_map(mac, HALMAC_EFUSE_R_AUTO); |
| if (status != HALMAC_RET_SUCCESS) { |
| free_halmac_event(rtlpriv, id); |
| return -1; |
| } |
| |
| ret = wait_halmac_event(rtlpriv, id); |
| if (ret) |
| return -1; |
| |
| return 0; |
| } |
| |
| int rtl_halmac_write_logical_efuse_map(struct rtl_priv *rtlpriv, u8 *map, |
| u32 size, u8 *maskmap, u32 masksize) |
| { |
| struct halmac_adapter *mac; |
| struct halmac_api *api; |
| struct halmac_pg_efuse_info pginfo; |
| enum halmac_ret_status status; |
| |
| mac = rtlpriv_to_halmac(rtlpriv); |
| api = HALMAC_GET_API(mac); |
| |
| pginfo.efuse_map = map; |
| pginfo.efuse_map_size = size; |
| pginfo.efuse_mask = maskmap; |
| pginfo.efuse_mask_size = masksize; |
| |
| status = api->halmac_pg_efuse_by_map(mac, &pginfo, HALMAC_EFUSE_R_AUTO); |
| if (status != HALMAC_RET_SUCCESS) |
| return -1; |
| |
| return 0; |
| } |
| |
| int rtl_halmac_read_logical_efuse(struct rtl_priv *rtlpriv, u32 offset, u32 cnt, |
| u8 *data) |
| { |
| struct halmac_adapter *mac; |
| struct halmac_api *api; |
| enum halmac_ret_status status; |
| u8 v; |
| u32 i; |
| |
| mac = rtlpriv_to_halmac(rtlpriv); |
| api = HALMAC_GET_API(mac); |
| |
| for (i = 0; i < cnt; i++) { |
| status = api->halmac_read_logical_efuse(mac, offset + i, &v); |
| if (status != HALMAC_RET_SUCCESS) |
| return -1; |
| data[i] = v; |
| } |
| |
| return 0; |
| } |
| |
| int rtl_halmac_write_logical_efuse(struct rtl_priv *rtlpriv, u32 offset, |
| u32 cnt, u8 *data) |
| { |
| struct halmac_adapter *mac; |
| struct halmac_api *api; |
| enum halmac_ret_status status; |
| u32 i; |
| |
| mac = rtlpriv_to_halmac(rtlpriv); |
| api = HALMAC_GET_API(mac); |
| |
| for (i = 0; i < cnt; i++) { |
| status = api->halmac_write_logical_efuse(mac, offset + i, |
| data[i]); |
| if (status != HALMAC_RET_SUCCESS) |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| int rtl_halmac_set_mac_address(struct rtl_priv *rtlpriv, u8 hwport, u8 *addr) |
| { |
| struct halmac_adapter *halmac; |
| struct halmac_api *api; |
| u8 port; |
| union halmac_wlan_addr hwa; |
| enum halmac_ret_status status; |
| int err = -1; |
| |
| halmac = rtlpriv_to_halmac(rtlpriv); |
| api = HALMAC_GET_API(halmac); |
| |
| port = hwport; |
| memset(&hwa, 0, sizeof(hwa)); |
| memcpy(hwa.address, addr, 6); |
| |
| status = api->halmac_cfg_mac_addr(halmac, port, &hwa); |
| if (status != HALMAC_RET_SUCCESS) |
| goto out; |
| |
| err = 0; |
| out: |
| return err; |
| } |
| |
| int rtl_halmac_set_bssid(struct rtl_priv *rtlpriv, u8 hwport, u8 *addr) |
| { |
| struct halmac_adapter *halmac; |
| struct halmac_api *api; |
| u8 port; |
| union halmac_wlan_addr hwa; |
| enum halmac_ret_status status; |
| int err = -1; |
| |
| halmac = rtlpriv_to_halmac(rtlpriv); |
| api = HALMAC_GET_API(halmac); |
| port = hwport; |
| |
| memset(&hwa, 0, sizeof(union halmac_wlan_addr)); |
| memcpy(hwa.address, addr, 6); |
| status = api->halmac_cfg_bssid(halmac, port, &hwa); |
| if (status != HALMAC_RET_SUCCESS) |
| goto out; |
| |
| err = 0; |
| out: |
| return err; |
| } |
| |
| int rtl_halmac_set_bandwidth(struct rtl_priv *rtlpriv, u8 channel, |
| u8 pri_ch_idx, u8 bw) |
| { |
| struct halmac_adapter *mac; |
| struct halmac_api *api; |
| enum halmac_ret_status status; |
| |
| mac = rtlpriv_to_halmac(rtlpriv); |
| api = HALMAC_GET_API(mac); |
| |
| status = api->halmac_cfg_ch_bw(mac, channel, pri_ch_idx, bw); |
| if (status != HALMAC_RET_SUCCESS) |
| return -1; |
| |
| return 0; |
| } |
| |
| int rtl_halmac_get_hw_value(struct rtl_priv *rtlpriv, enum halmac_hw_id hw_id, |
| void *pvalue) |
| { |
| struct halmac_adapter *mac; |
| struct halmac_api *api; |
| enum halmac_ret_status status; |
| |
| mac = rtlpriv_to_halmac(rtlpriv); |
| api = HALMAC_GET_API(mac); |
| |
| status = api->halmac_get_hw_value(mac, hw_id, pvalue); |
| if (status != HALMAC_RET_SUCCESS) |
| return -1; |
| |
| return 0; |
| } |
| |
| int rtl_halmac_dump_fifo(struct rtl_priv *rtlpriv, |
| enum hal_fifo_sel halmac_fifo_sel) |
| { |
| struct halmac_adapter *mac; |
| struct halmac_api *api; |
| enum halmac_ret_status status; |
| u8 *pfifo_map = NULL; |
| u32 fifo_size = 0; |
| s8 ret = 0; |
| |
| mac = rtlpriv_to_halmac(rtlpriv); |
| api = HALMAC_GET_API(mac); |
| |
| fifo_size = api->halmac_get_fifo_size(mac, halmac_fifo_sel); |
| if (fifo_size) |
| pfifo_map = vmalloc(fifo_size); |
| if (!pfifo_map) |
| return -1; |
| |
| status = api->halmac_dump_fifo(mac, halmac_fifo_sel, 0, fifo_size, |
| pfifo_map); |
| |
| if (status != HALMAC_RET_SUCCESS) { |
| ret = -1; |
| goto _exit; |
| } |
| |
| _exit: |
| if (pfifo_map) |
| vfree(pfifo_map); |
| return ret; |
| } |
| |
| int rtl_halmac_rx_agg_switch(struct rtl_priv *rtlpriv, bool enable) |
| { |
| struct halmac_adapter *halmac; |
| struct halmac_api *api; |
| struct halmac_rxagg_cfg rxaggcfg; |
| enum halmac_ret_status status; |
| int err = -1; |
| |
| halmac = rtlpriv_to_halmac(rtlpriv); |
| api = HALMAC_GET_API(halmac); |
| memset((void *)&rxaggcfg, 0, sizeof(rxaggcfg)); |
| |
| if (enable) { |
| /* enable RX agg. */ |
| /* PCIE do nothing */ |
| } else { |
| /* disable RX agg. */ |
| rxaggcfg.mode = HALMAC_RX_AGG_MODE_NONE; |
| } |
| |
| status = api->halmac_cfg_rx_aggregation(halmac, &rxaggcfg); |
| if (status != HALMAC_RET_SUCCESS) |
| goto out; |
| |
| err = 0; |
| out: |
| return err; |
| } |
| |
| int rtl_halmac_get_wow_reason(struct rtl_priv *rtlpriv, u8 *reason) |
| { |
| u8 val8; |
| int err = -1; |
| |
| val8 = rtl_read_byte(rtlpriv, 0x1C7); |
| if (val8 == 0xEA) |
| goto out; |
| |
| *reason = val8; |
| err = 0; |
| out: |
| return err; |
| } |
| |
| /* |
| * Description: |
| * Get RX driver info size. RX driver info is a small memory space between |
| * scriptor and RX payload. |
| * |
| * +-------------------------+ |
| * | RX descriptor | |
| * | usually 24 bytes | |
| * +-------------------------+ |
| * | RX driver info | |
| * | depends on driver cfg | |
| * +-------------------------+ |
| * | RX paylad | |
| * | | |
| * +-------------------------+ |
| * |
| * Parameter: |
| * d pointer to struct dvobj_priv of driver |
| * sz rx driver info size in bytes. |
| * |
| * Rteurn: |
| * 0 Success |
| * other Fail |
| */ |
| int rtl_halmac_get_drv_info_sz(struct rtl_priv *rtlpriv, u8 *sz) |
| { |
| /* enum halmac_ret_status status; */ |
| u8 dw = 6; /* max number */ |
| |
| *sz = dw * 8; |
| return 0; |
| } |
| |
| int rtl_halmac_get_rsvd_drv_pg_bndy(struct rtl_priv *rtlpriv, u16 *drv_pg) |
| { |
| enum halmac_ret_status status; |
| struct halmac_adapter *halmac = rtlpriv_to_halmac(rtlpriv); |
| struct halmac_api *api = HALMAC_GET_API(halmac); |
| |
| status = api->halmac_get_hw_value(halmac, HALMAC_HW_RSVD_PG_BNDY, |
| drv_pg); |
| if (status != HALMAC_RET_SUCCESS) |
| return -1; |
| |
| return 0; |
| } |
| |
| int rtl_halmac_chk_txdesc(struct rtl_priv *rtlpriv, u8 *txdesc, u32 size) |
| { |
| struct halmac_adapter *mac; |
| struct halmac_api *api; |
| enum halmac_ret_status status; |
| |
| mac = rtlpriv_to_halmac(rtlpriv); |
| api = HALMAC_GET_API(mac); |
| |
| status = api->halmac_chk_txdesc(mac, txdesc, size); |
| |
| if (status != HALMAC_RET_SUCCESS) |
| return -1; |
| |
| return 0; |
| } |
| |
| MODULE_AUTHOR("Realtek WlanFAE <wlanfae@realtek.com>"); |
| MODULE_AUTHOR("Larry Finger <Larry.FInger@lwfinger.net>"); |
| MODULE_LICENSE("GPL"); |
| MODULE_DESCRIPTION("Realtek 802.11n PCI wireless core"); |