/* SPDX-License-Identifier: GPL-2.0 */
|
/******************************************************************************
|
*
|
* Copyright(c) 2016 - 2017 Realtek Corporation.
|
*
|
* 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.
|
*
|
*****************************************************************************/
|
#define _RTL8821CS_OPS_C_
|
|
#include <drv_types.h> /* PADAPTER, basic_types.h and etc. */
|
#include <hal_data.h> /* HAL_DATA_TYPE, GET_HAL_DATA() and etc. */
|
#include <hal_intf.h> /* struct hal_ops */
|
#include "../rtl8821c.h"
|
#include "rtl8821cs.h" /* rtl8821cs_hal_init() */
|
#include "rtl8821cs_xmit.h"
|
#include "rtl8821cs_recv.h"
|
#include "rtl8821cs_io.h"
|
#include "rtl8821cs_led.h"
|
#include "rtl8821cs_halmac.h"
|
|
|
static void intf_chip_configure(PADAPTER adapter)
|
{
|
#if 0
|
u8 try_cnt = 0;
|
|
/*adjust SDIO output driving -output delay time about 8ns (SDIO SPEC must last than 7.5ns),offset 0x74[18]*/
|
if (rtw_is_sdio30(adapter)) {
|
rtw_write8(adapter, REG_SDIO_HSUS_CTRL, rtw_read8(adapter, REG_SDIO_HSUS_CTRL) & ~BIT(0));
|
|
do {
|
rtw_mdelay_os(2);
|
if ((rtw_read8(adapter, REG_SDIO_HSUS_CTRL) & BIT(1)))
|
break;
|
try_cnt++;
|
} while (try_cnt <= 50);
|
|
if (try_cnt == 10)
|
RTW_ERR("%s: SDIO active state change failed!!\n", __func__);
|
|
rtw_write32(adapter, REG_HCI_OPT_CTRL,
|
rtw_read32(adapter, REG_HCI_OPT_CTRL) | BIT(18));
|
}
|
#endif
|
}
|
|
u32 rtl8821cs_get_interrupt(PADAPTER adapter)
|
{
|
return rtw_read32(adapter, REG_SDIO_HISR_8821C);
|
}
|
|
void rtl8821cs_clear_interrupt(PADAPTER adapter, u32 hisr)
|
{
|
/* Perform write one clear operation */
|
if (hisr)
|
rtw_write32(adapter, REG_SDIO_HISR_8821C, hisr);
|
}
|
|
u32 rtl8821cs_get_himr(PADAPTER adapter)
|
{
|
return rtw_read32(adapter, REG_SDIO_HIMR_8821C);
|
}
|
|
void rtl8821cs_update_himr(PADAPTER adapter, u32 himr)
|
{
|
rtw_write32(adapter, REG_SDIO_HIMR_8821C, himr);
|
}
|
|
void rtl8821cs_update_interrupt_mask(PADAPTER padapter,u32 AddMSR, u32 RemoveMSR)
|
{
|
HAL_DATA_TYPE *pHalData;
|
pHalData = GET_HAL_DATA(padapter);
|
|
if (AddMSR)
|
pHalData->sdio_himr |= AddMSR;
|
|
if (RemoveMSR)
|
pHalData->sdio_himr &= (~RemoveMSR);
|
|
rtl8821cs_update_himr(padapter,pHalData->sdio_himr);
|
}
|
|
/*
|
* Description:
|
* Initialize SDIO Host Interrupt Mask configuration variables for future use.
|
*
|
* Assumption:
|
* Using SDIO Local register ONLY for configuration.
|
*/
|
void rtl8821cs_init_interrupt(PADAPTER adapter)
|
{
|
PHAL_DATA_TYPE hal;
|
|
|
hal = GET_HAL_DATA(adapter);
|
hal->sdio_himr = (u32)(\
|
BIT_RX_REQUEST_MSK_8821C |
|
#ifdef CONFIG_SDIO_TX_ENABLE_AVAL_INT
|
BIT_SDIO_AVAL_MSK_8821C |
|
#endif /* CONFIG_SDIO_TX_ENABLE_AVAL_INT */
|
|
#ifdef CONFIG_ERROR_STATE_MONITOR
|
BIT_SDIO_TXERR_MSK_8821C |
|
BIT_SDIO_RXERR_MSK_8821C |
|
#endif
|
#ifdef CONFIG_MONITOR_OVERFLOW
|
BIT_SDIO_TXFOVW_MSK_8821C |
|
BIT_SDIO_RXFOVW_MSK_8821C |
|
#endif
|
#ifdef CONFIG_INTERRUPT_BASED_TXBCN
|
|
#ifdef CONFIG_INTERRUPT_BASED_TXBCN_BCN_OK_ERR
|
BIT_SDIO_TXBCNOK_MSK_8821C |
|
BIT_SDIO_TXBCNERR_MSK_8821C |
|
#endif
|
|
#ifdef CONFIG_INTERRUPT_BASED_TXBCN_EARLY_INT
|
BIT_SDIO_BCNERLY_INT_MSK_8821C |
|
#endif
|
#endif
|
#if 0
|
BIT_SDIO_C2HCMD_INT_MSK_8821C |
|
#endif
|
#if defined(CONFIG_LPS_LCLK) && !defined(CONFIG_DETECT_CPWM_BY_POLLING)
|
BIT_SDIO_CPWM1_MSK_8821C |
|
#if 0
|
BIT_SDIO_CPWM2_MSK_8821C |
|
#endif
|
#endif /* CONFIG_LPS_LCLK && !CONFIG_DETECT_CPWM_BY_POLLING */
|
#if 0
|
BIT_SDIO_HSISR_IND_MSK_8821C |
|
BIT_SDIO_GTINT3_MSK_8821C |
|
BIT_SDIO_GTINT4_MSK_8821C |
|
BIT_SDIO_PSTIMEOUT_MSK_8821C |
|
BIT_SDIO_OCPINT_MSK_8821C |
|
BIT_SDIIO_ATIMend_MSK_8821C |
|
BIT_SDIO_ATIMend_E_MSK_8821C |
|
BIT_SDIO_CTWend_MSK_8821C |
|
BIT_SDIO_CRCERR_MSK_8821C |
|
#endif
|
0);
|
|
}
|
|
/*
|
* Description:
|
* Clear corresponding SDIO Host ISR interrupt service.
|
*
|
* Assumption:
|
* Using SDIO Local register ONLY for configuration.
|
*/
|
#if defined(CONFIG_WOWLAN) || defined(CONFIG_AP_WOWLAN)
|
static void clear_interrupt_all(PADAPTER adapter)
|
{
|
PHAL_DATA_TYPE hal;
|
|
|
if (rtw_is_surprise_removed(adapter))
|
return;
|
|
hal = GET_HAL_DATA(adapter);
|
rtl8821cs_clear_interrupt(adapter, 0xFFFFFFFF);
|
}
|
#endif /*#if defined(CONFIG_WOWLAN) || defined(CONFIG_AP_WOWLAN)*/
|
/*
|
* Description:
|
* Enalbe SDIO Host Interrupt Mask configuration on SDIO local domain.
|
*
|
* Assumption:
|
* 1. Using SDIO Local register ONLY for configuration.
|
* 2. PASSIVE LEVEL
|
*/
|
static void enable_interrupt(PADAPTER adapter)
|
{
|
PHAL_DATA_TYPE hal;
|
|
|
hal = GET_HAL_DATA(adapter);
|
|
rtl8821cs_update_himr(adapter, hal->sdio_himr);
|
RTW_INFO(FUNC_ADPT_FMT ": update SDIO HIMR=0x%08X\n",
|
FUNC_ADPT_ARG(adapter), hal->sdio_himr);
|
}
|
|
/*
|
* Description:
|
* Disable SDIO Host IMR configuration to mask unnecessary interrupt service.
|
*
|
* Assumption:
|
* Using SDIO Local register ONLY for configuration.
|
*/
|
static void disable_interrupt(PADAPTER adapter)
|
{
|
PHAL_DATA_TYPE hal;
|
|
|
hal = GET_HAL_DATA(adapter);
|
|
rtl8821cs_update_himr(adapter, 0);
|
RTW_INFO("%s: update SDIO HIMR=0\n", __FUNCTION__);
|
}
|
|
#ifdef CONFIG_WOWLAN
|
void rtl8821cs_disable_interrupt_but_cpwm2(PADAPTER adapter)
|
{
|
u32 himr, tmp;
|
|
tmp = rtw_read32(adapter, REG_SDIO_HIMR);
|
RTW_INFO("%s: Read SDIO_REG_HIMR: 0x%08x\n", __FUNCTION__, tmp);
|
|
himr = BIT_SDIO_CPWM2_MSK;
|
rtl8821cs_update_himr(adapter, himr);
|
|
tmp = rtw_read32(adapter, REG_SDIO_HIMR);
|
RTW_INFO("%s: Read again SDIO_REG_HIMR: 0x%08x\n", __FUNCTION__, tmp);
|
}
|
#endif /* CONFIG_WOWLAN */
|
|
static void _run_thread(PADAPTER adapter)
|
{
|
#ifndef CONFIG_SDIO_TX_TASKLET
|
struct xmit_priv *xmitpriv = &adapter->xmitpriv;
|
|
if (xmitpriv->SdioXmitThread == NULL) {
|
RTW_INFO(FUNC_ADPT_FMT " start RTWHALXT\n", FUNC_ADPT_ARG(adapter));
|
xmitpriv->SdioXmitThread = kthread_run(rtl8821cs_xmit_thread, adapter, "RTWHALXT");
|
if (IS_ERR(xmitpriv->SdioXmitThread)) {
|
RTW_ERR("%s: start rtl8821cs_xmit_thread FAIL!!\n", __FUNCTION__);
|
xmitpriv->SdioXmitThread = NULL;
|
}
|
}
|
#endif /* !CONFIG_SDIO_TX_TASKLET */
|
}
|
|
static void run_thread(PADAPTER adapter)
|
{
|
_run_thread(adapter);
|
rtl8821c_run_thread(adapter);
|
}
|
|
static void _cancel_thread(PADAPTER adapter)
|
{
|
#ifndef CONFIG_SDIO_TX_TASKLET
|
struct xmit_priv *xmitpriv = &adapter->xmitpriv;
|
|
/* stop xmit_buf_thread */
|
if (xmitpriv->SdioXmitThread) {
|
_rtw_up_sema(&xmitpriv->SdioXmitSema);
|
rtw_thread_stop(xmitpriv->SdioXmitThread);
|
xmitpriv->SdioXmitThread = NULL;
|
}
|
#endif /* !CONFIG_SDIO_TX_TASKLET */
|
}
|
|
static void cancel_thread(PADAPTER adapter)
|
{
|
rtl8821c_cancel_thread(adapter);
|
_cancel_thread(adapter);
|
}
|
|
#ifdef CONFIG_SDIO_OOB
|
/*
|
REG_SYS_SDIO_CTRL_8821C
|
BIT_SDIO_INT - 1: Enabled (GPIO4:SDIO_INT); 0 : Disabled
|
BIT_SDIO_INT_POLARITY - 0: Low Active 1: High Active
|
*/
|
|
void rtl8821cs_gpio4_sdio_int_enable(_adapter *adapter, u8 enable, u8 polarity_high)
|
{
|
u32 sdio_ctrl = 0;
|
|
sdio_ctrl = rtw_read32(adapter, REG_SYS_SDIO_CTRL_8821C);
|
if (enable) {
|
sdio_ctrl |= BIT_SDIO_INT;
|
if (polarity_high == _TRUE)
|
sdio_ctrl |= BIT_SDIO_INT_POLARITY;
|
else
|
sdio_ctrl &= (~BIT_SDIO_INT_POLARITY);
|
} else
|
sdio_ctrl &= (~BIT_SDIO_INT);
|
rtw_write32(adapter, REG_SYS_SDIO_CTRL_8821C, sdio_ctrl);
|
}
|
#endif
|
|
/*
|
* If variable not handled here,
|
* some variables will be processed in rtl8821c_sethwreg()
|
*/
|
static u8 sethwreg(PADAPTER adapter, u8 variable, u8 *val)
|
{
|
u8 ret = _SUCCESS;
|
|
switch (variable) {
|
case HW_VAR_SET_RPWM:
|
{
|
/*
|
* rpwm value only use BIT0(clock bit), and BIT7(Toggle bit)
|
* BIT0 value - 1: 32k, 0:40MHz.
|
* BIT4 value - 1: enable power gated, 0: disable power gated
|
* BIT6 value - 1: report cpwm value after success set, 0:do not report.
|
* BIT7 value - Toggling Bit.
|
*/
|
u8 val8 = 0;
|
struct pwrctrl_priv *pwrpriv = adapter_to_pwrctl(adapter);
|
|
val8 = (*val) & 0xC1;
|
|
#ifdef CONFIG_LPS_PG
|
if ((val8 & BIT(0)) && (LPS_PG == pwrpriv->lps_level))
|
val8 |= BIT(4);
|
#endif
|
rtw_write8(adapter, REG_SDIO_HRPWM1_8821C, val8);
|
}
|
break;
|
|
#ifdef CONFIG_GPIO_WAKEUP
|
case HW_SET_GPIO_WL_CTRL:
|
{
|
}
|
break;
|
#endif
|
/*
|
#ifdef CONFIG_SDIO_OOB
|
case HW_SET_SDIO_OOB:
|
{
|
u8 sdio_ctrl = *val;
|
u8 enable, is_high_active;
|
|
enable = (sdio_ctrl & BIT(0)) ? _TRUE : _FALSE;
|
is_high_active = (sdio_ctrl & BIT(4)) ? _TRUE : _FALSE;
|
rtl8821cs_gpio4_sdio_int_enable(adapter, enable, is_high_active);
|
}
|
break;
|
#endif
|
*/
|
default:
|
ret = rtl8821c_sethwreg(adapter, variable, val);
|
break;
|
}
|
|
return ret;
|
}
|
|
/*
|
* If variable not handled here,
|
* some variables will be processed in GetHwReg8723B()
|
*/
|
static void gethwreg(PADAPTER adapter, u8 variable, u8 *val)
|
{
|
PHAL_DATA_TYPE hal;
|
|
|
hal = GET_HAL_DATA(adapter);
|
|
switch (variable) {
|
case HW_VAR_CPWM:
|
*val = rtw_read8(adapter, REG_SDIO_HCPWM1_V2_8821C);
|
break;
|
|
case HW_VAR_RPWM_TOG:
|
*val = rtw_read8(adapter, REG_SDIO_HRPWM1_8821C);
|
*val &= BIT_TOGGLE_8821C;
|
break;
|
|
#ifdef CONFIG_GPIO_WAKEUP
|
case HW_SET_GPIO_WL_CTRL:
|
|
break;
|
#endif
|
|
default:
|
rtl8821c_gethwreg(adapter, variable, val);
|
break;
|
}
|
}
|
|
/*
|
* Description:
|
* Query setting of specified variable.
|
*/
|
static u8 gethaldefvar(PADAPTER adapter, HAL_DEF_VARIABLE eVariable, void *pval)
|
{
|
u8 bResult = _SUCCESS;
|
struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
|
PSDIO_DATA psdio_data = &dvobj->intf_data;
|
PHAL_DATA_TYPE hal = GET_HAL_DATA(adapter);
|
|
switch (eVariable) {
|
|
case HW_VAR_MAX_RX_AMPDU_FACTOR:
|
{
|
if (psdio_data->clock > RTW_SDIO_CLK_40M) {
|
*(HT_CAP_AMPDU_FACTOR *)pval = MAX_AMPDU_FACTOR_32K;
|
RTW_INFO("AMPDU FACTOR - 32K\n");
|
} else if (psdio_data->clock > RTW_SDIO_CLK_33M) {
|
*(HT_CAP_AMPDU_FACTOR *)pval = MAX_AMPDU_FACTOR_16K;
|
RTW_INFO("AMPDU FACTOR - 16K\n");
|
} else {
|
*(HT_CAP_AMPDU_FACTOR *)pval = MAX_AMPDU_FACTOR_8K;
|
RTW_INFO("AMPDU FACTOR - 8K\n");
|
}
|
}
|
break;
|
|
default:
|
bResult = rtl8821c_gethaldefvar(adapter, eVariable, pval);
|
break;
|
}
|
|
return bResult;
|
}
|
|
/*
|
* Description:
|
* Change default setting of specified variable.
|
*/
|
static u8 sethaldefvar(PADAPTER adapter, HAL_DEF_VARIABLE eVariable, void *pval)
|
{
|
PHAL_DATA_TYPE hal = GET_HAL_DATA(adapter);
|
u8 bResult = _SUCCESS;
|
|
switch (eVariable) {
|
default:
|
bResult = rtl8821c_sethaldefvar(adapter, eVariable, pval);
|
break;
|
}
|
|
return bResult;
|
}
|
|
u8 rtl8821cs_set_hal_ops(PADAPTER adapter)
|
{
|
struct hal_ops *ops_func = &adapter->hal_func;
|
|
u8 err;
|
|
err = rtl8821cs_halmac_init_adapter(adapter);
|
if (err) {
|
RTW_INFO("%s: [ERROR]HALMAC initialize FAIL!\n", __FUNCTION__);
|
return _FAIL;
|
}
|
|
rtl8821c_set_hal_ops(adapter);
|
rtl8821cs_init_interrupt(adapter);
|
|
ops_func->init_default_value = rtl8821cs_init_default_value;
|
ops_func->intf_chip_configure = intf_chip_configure;
|
|
ops_func->hal_init = rtl8821cs_hal_init;
|
ops_func->hal_deinit = rtl8821cs_hal_deinit;
|
|
ops_func->init_xmit_priv = rtl8821cs_init_xmit_priv;
|
ops_func->free_xmit_priv = rtl8821cs_free_xmit_priv;
|
ops_func->hal_xmit = rtl8821cs_hal_xmit;
|
ops_func->mgnt_xmit = rtl8821cs_mgnt_xmit;
|
#ifdef CONFIG_RTW_MGMT_QUEUE
|
ops_func->hal_mgmt_xmitframe_enqueue = rtl8821cs_hal_mgmt_xmit_enqueue;
|
#endif
|
ops_func->hal_xmitframe_enqueue = rtl8821cs_hal_xmit_enqueue;
|
#ifdef CONFIG_XMIT_THREAD_MODE
|
ops_func->xmit_thread_handler = rtl8821cs_xmit_buf_handler;
|
#endif
|
ops_func->run_thread = run_thread;
|
ops_func->cancel_thread = cancel_thread;
|
|
ops_func->init_recv_priv = rtl8821cs_init_recv_priv;
|
ops_func->free_recv_priv = rtl8821cs_free_recv_priv;
|
#ifdef CONFIG_RECV_THREAD_MODE
|
ops_func->recv_hdl = rtl8821cs_recv_hdl;
|
#endif
|
|
ops_func->enable_interrupt = enable_interrupt;
|
ops_func->disable_interrupt = disable_interrupt;
|
#if defined(CONFIG_WOWLAN) || defined(CONFIG_AP_WOWLAN)
|
ops_func->clear_interrupt = clear_interrupt_all;
|
#endif
|
#ifdef CONFIG_RTW_SW_LED
|
ops_func->InitSwLeds = rtl8821cs_initswleds;
|
ops_func->DeInitSwLeds = rtl8821cs_deinitswleds;
|
#endif
|
ops_func->set_hw_reg_handler = sethwreg;
|
ops_func->GetHwRegHandler = gethwreg;
|
ops_func->get_hal_def_var_handler = gethaldefvar;
|
ops_func->SetHalDefVarHandler = sethaldefvar;
|
return _TRUE;
|
}
|