6db4831e98
Android 14
1386 lines
38 KiB
C
1386 lines
38 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2019 MediaTek Inc.
|
|
*/
|
|
|
|
#ifdef DFT_TAG
|
|
#undef DFT_TAG
|
|
#endif
|
|
#define DFT_TAG "MTK-BTIF"
|
|
|
|
#define NEW_TX_HANDLING_SUPPORT 1
|
|
|
|
#include "btif_pub.h"
|
|
#include "btif_priv.h"
|
|
#include "mtk_btif.h"
|
|
|
|
#define BTIF_USER_ID "btif_driver"
|
|
|
|
static spinlock_t g_clk_cg_spinlock; /*BTIF clock's spinlock */
|
|
|
|
/*----------------BTIF Module Clock and Power Control Defination--------------*/
|
|
|
|
struct _MTK_BTIF_IRQ_STR_ mtk_btif_irq = {
|
|
.name = "mtk btif irq",
|
|
.is_irq_sup = true,
|
|
.reg_flag = false,
|
|
#ifdef CONFIG_OF
|
|
.irq_flags = IRQF_TRIGGER_NONE,
|
|
#else
|
|
.irq_id = MT_BTIF_IRQ_ID,
|
|
.sens_type = IRQ_SENS_LVL,
|
|
.lvl_type = IRQ_LVL_LOW,
|
|
#endif
|
|
.p_irq_handler = NULL,
|
|
};
|
|
|
|
/*
|
|
* will call clock manager's API export by WCP to control BTIF's clock,
|
|
* but we may need to access these registers in case of
|
|
* btif clock control logic is wrong in clock manager
|
|
*/
|
|
|
|
struct _MTK_BTIF_INFO_STR_ mtk_btif_info = {
|
|
#ifndef CONFIG_OF
|
|
.base = MTK_BTIF_REG_BASE,
|
|
#endif
|
|
.p_irq = &mtk_btif_irq,
|
|
.tx_fifo_size = BTIF_TX_FIFO_SIZE,
|
|
.rx_fifo_size = BTIF_RX_FIFO_SIZE,
|
|
.tx_tri_lvl = BTIF_TX_FIFO_THRE,
|
|
.rx_tri_lvl = BTIF_RX_FIFO_THRE,
|
|
.rx_data_len = 0,
|
|
.p_tx_fifo = NULL,
|
|
};
|
|
#if !(NEW_TX_HANDLING_SUPPORT)
|
|
static bool _btif_is_tx_allow(struct _MTK_BTIF_INFO_STR_ *p_btif);
|
|
#endif
|
|
|
|
static int btif_rx_irq_handler(struct _MTK_BTIF_INFO_STR_ *p_btif_info,
|
|
unsigned char *p_buf,
|
|
const unsigned int max_len);
|
|
static int btif_tx_irq_handler(struct _MTK_BTIF_INFO_STR_ *p_btif);
|
|
|
|
/*****************************************************************************
|
|
* FUNCTION
|
|
* hal_btif_rx_ier_ctrl
|
|
* DESCRIPTION
|
|
* BTIF Rx interrupt enable/disable
|
|
* PARAMETERS
|
|
* p_base [IN] BTIF module's base address
|
|
* enable [IN] control if rx interrupt enabled or not
|
|
* RETURNS
|
|
* 0 means success, negative means fail
|
|
*****************************************************************************/
|
|
static int hal_btif_rx_ier_ctrl(struct _MTK_BTIF_INFO_STR_ *p_btif, bool en);
|
|
|
|
/*****************************************************************************
|
|
* FUNCTION
|
|
* hal_btif_tx_ier_ctrl
|
|
* DESCRIPTION
|
|
* BTIF Tx interrupt enable/disable
|
|
* PARAMETERS
|
|
* p_base [IN] BTIF module's base address
|
|
* enable [IN] control if tx interrupt enabled or not
|
|
* RETURNS
|
|
* 0 means success, negative means fail
|
|
*****************************************************************************/
|
|
static int hal_btif_tx_ier_ctrl(struct _MTK_BTIF_INFO_STR_ *p_btif, bool en);
|
|
|
|
#ifndef MTK_BTIF_MARK_UNUSED_API
|
|
static int btif_sleep_ctrl(struct _MTK_BTIF_INFO_STR_ *p_btif, bool en);
|
|
|
|
/*****************************************************************************
|
|
* FUNCTION
|
|
* _btif_receive_data
|
|
* DESCRIPTION
|
|
* receive data from btif module in FIFO polling mode
|
|
* PARAMETERS
|
|
* p_base [IN] BTIF module's base address
|
|
* p_buf [IN/OUT] pointer to rx data buffer
|
|
* max_len [IN] max length of rx buffer
|
|
* RETURNS
|
|
* positive means data is available, 0 means no data available
|
|
*****************************************************************************/
|
|
static int _btif_receive_data(struct _MTK_BTIF_INFO_STR_ *p_btif,
|
|
unsigned char *p_buf, const unsigned int max_len);
|
|
static int btif_tx_thr_set(struct _MTK_BTIF_INFO_STR_ *p_btif,
|
|
unsigned int thr_count);
|
|
#endif
|
|
|
|
int btif_dump_array(const char *string, const char *p_buf, int len)
|
|
{
|
|
#define BTIF_LENGTH_PER_LINE 32
|
|
unsigned int idx = 0;
|
|
unsigned char str[BTIF_LENGTH_PER_LINE * 3 + 2];
|
|
unsigned char *p_str = NULL;
|
|
|
|
pr_info("========dump %s start <length:%d>========\n", string, len);
|
|
p_str = &str[0];
|
|
for (idx = 0; idx < len; idx++, p_buf++) {
|
|
if (sprintf(p_str, "%02x ", *p_buf) < 0)
|
|
return -1;
|
|
p_str += 3;
|
|
if ((BTIF_LENGTH_PER_LINE - 1) == (idx % BTIF_LENGTH_PER_LINE)) {
|
|
*p_str++ = '\n';
|
|
*p_str = '\0';
|
|
pr_info("%s", str);
|
|
p_str = &str[0];
|
|
}
|
|
}
|
|
if (len % BTIF_LENGTH_PER_LINE) {
|
|
*p_str++ = '\n';
|
|
*p_str = '\0';
|
|
pr_info("%s", str);
|
|
}
|
|
pr_info("========dump %s end========\n", string);
|
|
return 0;
|
|
}
|
|
|
|
#if NEW_TX_HANDLING_SUPPORT
|
|
static int _btif_tx_fifo_init(struct _MTK_BTIF_INFO_STR_ *p_btif_info)
|
|
{
|
|
int i_ret = -1;
|
|
|
|
spin_lock_init(&(p_btif_info->tx_fifo_spinlock));
|
|
|
|
if (p_btif_info->p_tx_fifo == NULL) {
|
|
p_btif_info->p_tx_fifo = kzalloc(sizeof(struct kfifo),
|
|
GFP_ATOMIC);
|
|
if (p_btif_info->p_tx_fifo == NULL) {
|
|
i_ret = -ENOMEM;
|
|
BTIF_ERR_FUNC("kzalloc for p_btif->p_tx_fifo failed\n");
|
|
goto ret;
|
|
}
|
|
|
|
i_ret = kfifo_alloc(p_btif_info->p_tx_fifo,
|
|
BTIF_HAL_TX_FIFO_SIZE, GFP_ATOMIC);
|
|
if (i_ret != 0) {
|
|
BTIF_ERR_FUNC("kfifo_alloc failed, errno(%d)\n", i_ret);
|
|
i_ret = -ENOMEM;
|
|
goto ret;
|
|
}
|
|
i_ret = 0;
|
|
} else {
|
|
BTIF_WARN_FUNC
|
|
("p_tx_fifo is already init p_btif_info->p_tx_fifo(0x%p)\n",
|
|
p_btif_info->p_tx_fifo);
|
|
i_ret = 0;
|
|
}
|
|
ret:
|
|
return i_ret;
|
|
}
|
|
|
|
static int _get_btif_tx_fifo_room(struct _MTK_BTIF_INFO_STR_ *p_btif_info)
|
|
{
|
|
int i_ret = 0;
|
|
unsigned long flag = 0;
|
|
|
|
spin_lock_irqsave(&(p_btif_info->tx_fifo_spinlock), flag);
|
|
if (p_btif_info->p_tx_fifo == NULL)
|
|
i_ret = 0;
|
|
else
|
|
i_ret = kfifo_avail(p_btif_info->p_tx_fifo);
|
|
spin_unlock_irqrestore(&(p_btif_info->tx_fifo_spinlock), flag);
|
|
BTIF_DBG_FUNC("tx kfifo:0x%p, available room:%d\n",
|
|
p_btif_info->p_tx_fifo, i_ret);
|
|
return i_ret;
|
|
}
|
|
|
|
static int _btif_tx_fifo_reset(struct _MTK_BTIF_INFO_STR_ *p_btif_info)
|
|
{
|
|
int i_ret = 0;
|
|
|
|
if (p_btif_info->p_tx_fifo != NULL)
|
|
kfifo_reset(p_btif_info->p_tx_fifo);
|
|
return i_ret;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_OF
|
|
static void _btif_set_default_setting(void)
|
|
{
|
|
struct device_node *node = NULL;
|
|
unsigned int irq_info[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
unsigned int phy_base;
|
|
|
|
if (!g_btif[0].private_data) {
|
|
BTIF_INFO_FUNC("g_btif[0].private_data is NULL");
|
|
return;
|
|
}
|
|
|
|
node = ((struct device *)(g_btif[0].private_data))->of_node;
|
|
if (node) {
|
|
mtk_btif_info.p_irq->irq_id = irq_of_parse_and_map(node, 0);
|
|
/*fixme, be compitable arch 64bits*/
|
|
mtk_btif_info.base = (unsigned long)of_iomap(node, 0);
|
|
BTIF_INFO_FUNC("get btif irq(%d),register base(0x%lx)\n",
|
|
mtk_btif_info.p_irq->irq_id, mtk_btif_info.base);
|
|
} else {
|
|
BTIF_ERR_FUNC("get btif device node fail\n");
|
|
}
|
|
|
|
/* get the interrupt line behaviour */
|
|
if (of_property_read_u32_array(node, "interrupts", irq_info,
|
|
ARRAY_SIZE(irq_info))) {
|
|
BTIF_ERR_FUNC("get interrupt flag from DTS fail\n");
|
|
} else {
|
|
mtk_btif_info.p_irq->irq_flags = irq_info[2];
|
|
BTIF_INFO_FUNC("get interrupt flag(0x%x)\n",
|
|
mtk_btif_info.p_irq->irq_flags);
|
|
}
|
|
|
|
if (of_property_read_u32_index(node, "reg", 1, &phy_base))
|
|
BTIF_ERR_FUNC("get register phy base from DTS fail\n");
|
|
else
|
|
BTIF_INFO_FUNC("get register phy base(0x%x)\n",
|
|
(unsigned int)phy_base);
|
|
}
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
* FUNCTION
|
|
* hal_btif_info_get
|
|
* DESCRIPTION
|
|
* get btif's information included base address , irq related information
|
|
* PARAMETERS
|
|
* RETURNS
|
|
* BTIF's information
|
|
*****************************************************************************/
|
|
struct _MTK_BTIF_INFO_STR_ *hal_btif_info_get(void)
|
|
{
|
|
#if NEW_TX_HANDLING_SUPPORT
|
|
int i_ret = 0;
|
|
/*tx fifo and fifo lock init*/
|
|
i_ret = _btif_tx_fifo_init(&mtk_btif_info);
|
|
if (i_ret == 0)
|
|
BTIF_INFO_FUNC("_btif_tx_fifo_init succeed\n");
|
|
else
|
|
BTIF_ERR_FUNC("_btif_tx_fifo_init failed, i_ret:%d\n", i_ret);
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_OF
|
|
_btif_set_default_setting();
|
|
#endif
|
|
|
|
spin_lock_init(&g_clk_cg_spinlock);
|
|
|
|
return &mtk_btif_info;
|
|
}
|
|
/*****************************************************************************
|
|
* FUNCTION
|
|
* hal_btif_clk_get_and_prepare
|
|
* DESCRIPTION
|
|
* get clock from device tree and prepare for enable/disable control
|
|
* PARAMETERS
|
|
* pdev device pointer
|
|
* RETURNS
|
|
* 0 means success, negative means fail
|
|
*****************************************************************************/
|
|
#if !defined(CONFIG_MTK_CLKMGR)
|
|
int hal_btif_clk_get_and_prepare(struct platform_device *pdev)
|
|
{
|
|
int i_ret = -1;
|
|
|
|
clk_btif = devm_clk_get(&pdev->dev, "btifc");
|
|
if (IS_ERR(clk_btif)) {
|
|
BTIF_ERR_FUNC("[CCF]cannot get clk_btif clock.\n");
|
|
return PTR_ERR(clk_btif);
|
|
}
|
|
BTIF_ERR_FUNC("[CCF]clk_btif=%p\n", clk_btif);
|
|
clk_btif_apdma = devm_clk_get(&pdev->dev, "apdmac");
|
|
if (IS_ERR(clk_btif_apdma)) {
|
|
BTIF_ERR_FUNC("[CCF]can't get clk_btif_apdma clock.\n");
|
|
return PTR_ERR(clk_btif_apdma);
|
|
}
|
|
BTIF_ERR_FUNC("[CCF]clk_btif_apdma=%p\n", clk_btif_apdma);
|
|
|
|
i_ret = clk_prepare(clk_btif);
|
|
if (i_ret != 0) {
|
|
BTIF_ERR_FUNC("clk_prepare failed! ret:%d\n", i_ret);
|
|
return i_ret;
|
|
}
|
|
|
|
i_ret = clk_prepare(clk_btif_apdma);
|
|
if (i_ret != 0) {
|
|
BTIF_ERR_FUNC("clk_prepare failed! ret:%d\n", i_ret);
|
|
return i_ret;
|
|
}
|
|
return i_ret;
|
|
}
|
|
/*****************************************************************************
|
|
* FUNCTION
|
|
* hal_btif_clk_unprepare
|
|
* DESCRIPTION
|
|
* unprepare btif clock and apdma clock
|
|
* PARAMETERS
|
|
* none
|
|
* RETURNS
|
|
* 0 means success, negative means fail
|
|
*****************************************************************************/
|
|
int hal_btif_clk_unprepare(void)
|
|
{
|
|
clk_unprepare(clk_btif);
|
|
clk_unprepare(clk_btif_apdma);
|
|
return 0;
|
|
}
|
|
#endif
|
|
/*****************************************************************************
|
|
* FUNCTION
|
|
* hal_btif_clk_ctrl
|
|
* DESCRIPTION
|
|
* control clock output enable/disable of BTIF module
|
|
* PARAMETERS
|
|
* p_base [IN] BTIF module's base address
|
|
* RETURNS
|
|
* 0 means success, negative means fail
|
|
*****************************************************************************/
|
|
int hal_btif_clk_ctrl(struct _MTK_BTIF_INFO_STR_ *p_btif,
|
|
enum _ENUM_CLOCK_CTRL_ flag)
|
|
{
|
|
/*In MTK BTIF, there's only one global CG on AP_DMA, no sub channel's CG bit*/
|
|
/*according to Artis's comment, clock of DMA and BTIF is default off,*/
|
|
/*so we assume it to be off by default*/
|
|
int i_ret = 0;
|
|
unsigned long irq_flag = 0;
|
|
|
|
#if MTK_BTIF_ENABLE_CLK_REF_COUNTER
|
|
static atomic_t s_clk_ref = ATOMIC_INIT(0);
|
|
#else
|
|
static enum _ENUM_CLOCK_CTRL_ status = CLK_OUT_DISABLE;
|
|
#endif
|
|
|
|
spin_lock_irqsave(&(g_clk_cg_spinlock), irq_flag);
|
|
|
|
#if MTK_BTIF_ENABLE_CLK_CTL
|
|
|
|
#if MTK_BTIF_ENABLE_CLK_REF_COUNTER
|
|
|
|
if (flag == CLK_OUT_ENABLE) {
|
|
if (atomic_inc_return(&s_clk_ref) == 1) {
|
|
#if defined(CONFIG_MTK_CLKMGR)
|
|
i_ret = enable_clock(MTK_BTIF_CG_BIT, BTIF_USER_ID);
|
|
#else
|
|
BTIF_DBG_FUNC("[CCF]enable clk_btif\n");
|
|
i_ret = clk_enable(clk_btif);
|
|
#endif /* defined(CONFIG_MTK_CLKMGR) */
|
|
if (i_ret) {
|
|
BTIF_WARN_FUNC
|
|
("enable_clock failed, ret:%d", i_ret);
|
|
}
|
|
}
|
|
} else if (flag == CLK_OUT_DISABLE) {
|
|
if (atomic_dec_return(&s_clk_ref) == 0) {
|
|
#if defined(CONFIG_MTK_CLKMGR)
|
|
i_ret = disable_clock(MTK_BTIF_CG_BIT, BTIF_USER_ID);
|
|
if (i_ret) {
|
|
BTIF_WARN_FUNC
|
|
("disable_clock failed, ret:%d", i_ret);
|
|
}
|
|
#else
|
|
BTIF_DBG_FUNC("[CCF] clk_disable(clk_btif) calling\n");
|
|
clk_disable(clk_btif);
|
|
#endif /* defined(CONFIG_MTK_CLKMGR) */
|
|
}
|
|
} else {
|
|
i_ret = ERR_INVALID_PAR;
|
|
BTIF_ERR_FUNC("invalid clock ctrl flag (%d)\n", flag);
|
|
}
|
|
|
|
#else
|
|
|
|
if (status == flag) {
|
|
i_ret = 0;
|
|
BTIF_DBG_FUNC("btif clock already %s\n",
|
|
CLK_OUT_ENABLE ==
|
|
status ? "enabled" : "disabled");
|
|
} else {
|
|
if (flag == CLK_OUT_ENABLE) {
|
|
#if defined(CONFIG_MTK_CLKMGR)
|
|
i_ret = enable_clock(MTK_BTIF_CG_BIT, BTIF_USER_ID);
|
|
#else
|
|
BTIF_DBG_FUNC("[CCF]enable clk_btif\n");
|
|
i_ret = clk_enable(clk_btif);
|
|
#endif /* defined(CONFIG_MTK_CLKMGR) */
|
|
status = (i_ret == 0) ? flag : status;
|
|
if (i_ret) {
|
|
BTIF_WARN_FUNC
|
|
("enable_clock failed, ret:%d", i_ret);
|
|
}
|
|
} else if (flag == CLK_OUT_DISABLE) {
|
|
#if defined(CONFIG_MTK_CLKMGR)
|
|
i_ret = disable_clock(MTK_BTIF_CG_BIT, BTIF_USER_ID);
|
|
status = (i_ret == 0) ? flag : status;
|
|
if (i_ret) {
|
|
BTIF_WARN_FUNC
|
|
("disable_clock failed, ret:%d", i_ret);
|
|
}
|
|
#else
|
|
BTIF_DBG_FUNC("[CCF] clk_disable(clk_btif) calling\n");
|
|
clk_disable(clk_btif);
|
|
#endif /* defined(CONFIG_MTK_CLKMGR) */
|
|
} else {
|
|
i_ret = ERR_INVALID_PAR;
|
|
BTIF_ERR_FUNC("invalid clock ctrl flag (%d)\n", flag);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#else
|
|
|
|
#if MTK_BTIF_ENABLE_CLK_REF_COUNTER
|
|
|
|
#else
|
|
|
|
status = flag;
|
|
#endif
|
|
|
|
i_ret = 0;
|
|
#endif
|
|
|
|
spin_unlock_irqrestore(&(g_clk_cg_spinlock), irq_flag);
|
|
|
|
#if MTK_BTIF_ENABLE_CLK_REF_COUNTER
|
|
if (i_ret == 0) {
|
|
BTIF_DBG_FUNC("btif clock %s\n", flag == CLK_OUT_ENABLE ?
|
|
"enabled" : "disabled");
|
|
} else {
|
|
BTIF_ERR_FUNC("%s btif clock failed, ret(%d)\n",
|
|
flag == CLK_OUT_ENABLE ? "enable" : "disable",
|
|
i_ret);
|
|
}
|
|
#else
|
|
|
|
if (i_ret == 0) {
|
|
BTIF_DBG_FUNC("btif clock %s\n", flag == CLK_OUT_ENABLE ?
|
|
"enabled" : "disabled");
|
|
} else {
|
|
BTIF_ERR_FUNC("%s btif clock failed, ret(%d)\n",
|
|
flag == CLK_OUT_ENABLE ? "enable" : "disable",
|
|
i_ret);
|
|
}
|
|
#endif
|
|
#if defined(CONFIG_MTK_CLKMGR)
|
|
BTIF_DBG_FUNC("BTIF's clock is %s\n",
|
|
(clock_is_on(MTK_BTIF_CG_BIT) == 0) ? "off" : "on");
|
|
#endif
|
|
return i_ret;
|
|
}
|
|
|
|
static int btif_new_handshake_ctrl(struct _MTK_BTIF_INFO_STR_ *p_btif,
|
|
bool enable)
|
|
{
|
|
unsigned long base = p_btif->base;
|
|
|
|
if (enable == true)
|
|
BTIF_SET_BIT(BTIF_HANDSHAKE(base), BTIF_HANDSHAKE_EN_HANDSHAKE);
|
|
else
|
|
BTIF_CLR_BIT(BTIF_HANDSHAKE(base), BTIF_HANDSHAKE_EN_HANDSHAKE);
|
|
return true;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* FUNCTION
|
|
* hal_btif_hw_init
|
|
* DESCRIPTION
|
|
* BTIF hardware init
|
|
* PARAMETERS
|
|
* p_base [IN] BTIF module's base address
|
|
* RETURNS
|
|
* 0 means success, negative means fail
|
|
*****************************************************************************/
|
|
int hal_btif_hw_init(struct _MTK_BTIF_INFO_STR_ *p_btif)
|
|
{
|
|
/*Chaozhong: To be implement*/
|
|
int i_ret = -1;
|
|
unsigned long base = p_btif->base;
|
|
|
|
#if NEW_TX_HANDLING_SUPPORT
|
|
_btif_tx_fifo_reset(p_btif);
|
|
#endif
|
|
|
|
/*set to normal mode*/
|
|
btif_reg_sync_writel(BTIF_FAKELCR_NORMAL_MODE, BTIF_FAKELCR(base));
|
|
/*set to newhandshake mode*/
|
|
btif_new_handshake_ctrl(p_btif, true);
|
|
/*No need to access: enable sleep mode*/
|
|
/*No need to access: set Rx timeout count*/
|
|
/*set Tx threshold*/
|
|
/*set Rx threshold*/
|
|
/*disable internal loopback test*/
|
|
|
|
/*set Rx FIFO clear bit to 1*/
|
|
BTIF_SET_BIT(BTIF_FIFOCTRL(base), BTIF_FIFOCTRL_CLR_RX);
|
|
/*clear Rx FIFO clear bit to 0*/
|
|
BTIF_CLR_BIT(BTIF_FIFOCTRL(base), BTIF_FIFOCTRL_CLR_RX);
|
|
/*set Tx FIFO clear bit to 1*/
|
|
BTIF_SET_BIT(BTIF_FIFOCTRL(base), BTIF_FIFOCTRL_CLR_TX);
|
|
/*clear Tx FIFO clear bit to 0*/
|
|
BTIF_CLR_BIT(BTIF_FIFOCTRL(base), BTIF_FIFOCTRL_CLR_TX);
|
|
|
|
btif_reg_sync_writel(BTIF_TRI_LVL_TX(p_btif->tx_tri_lvl)
|
|
| BTIF_TRI_LVL_RX(p_btif->rx_tri_lvl)
|
|
| BTIF_TRI_LOOP_DIS, BTIF_TRI_LVL(base));
|
|
hal_btif_loopback_ctrl(p_btif, false);
|
|
/*disable BTIF Tx DMA mode*/
|
|
hal_btif_tx_mode_ctrl(p_btif, false);
|
|
/*disable BTIF Rx DMA mode*/
|
|
hal_btif_rx_mode_ctrl(p_btif, false);
|
|
/*auto reset*/
|
|
BTIF_SET_BIT(BTIF_DMA_EN(base), BTIF_DMA_EN_AUTORST_EN);
|
|
/*disable Tx IER*/
|
|
hal_btif_tx_ier_ctrl(p_btif, false);
|
|
/*enable Rx IER by default*/
|
|
hal_btif_rx_ier_ctrl(p_btif, true);
|
|
|
|
i_ret = 0;
|
|
return i_ret;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* FUNCTION
|
|
* hal_btif_rx_ier_ctrl
|
|
* DESCRIPTION
|
|
* BTIF Rx interrupt enable/disable
|
|
* PARAMETERS
|
|
* p_base [IN] BTIF module's base address
|
|
* enable [IN] control if rx interrupt enabled or not
|
|
* RETURNS
|
|
* 0 means success, negative means fail
|
|
*****************************************************************************/
|
|
int hal_btif_rx_ier_ctrl(struct _MTK_BTIF_INFO_STR_ *p_btif, bool en)
|
|
{
|
|
int i_ret = -1;
|
|
unsigned long base = p_btif->base;
|
|
|
|
if (en == false)
|
|
BTIF_CLR_BIT(BTIF_IER(base), BTIF_IER_RXFEN);
|
|
else
|
|
BTIF_SET_BIT(BTIF_IER(base), BTIF_IER_RXFEN);
|
|
|
|
/*TODO:do we need to read back ? Answer: no*/
|
|
i_ret = 0;
|
|
return i_ret;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* FUNCTION
|
|
* hal_btif_tx_ier_ctrl
|
|
* DESCRIPTION
|
|
* BTIF Tx interrupt enable/disable
|
|
* PARAMETERS
|
|
* p_base [IN] BTIF module's base address
|
|
* enable [IN] control if tx interrupt enabled or not
|
|
* RETURNS
|
|
* 0 means success, negative means fail
|
|
*****************************************************************************/
|
|
int hal_btif_tx_ier_ctrl(struct _MTK_BTIF_INFO_STR_ *p_btif, bool en)
|
|
{
|
|
int i_ret = -1;
|
|
unsigned long base = p_btif->base;
|
|
|
|
if (en == false)
|
|
BTIF_CLR_BIT(BTIF_IER(base), BTIF_IER_TXEEN);
|
|
else
|
|
BTIF_SET_BIT(BTIF_IER(base), BTIF_IER_TXEEN);
|
|
|
|
/*TODO:do we need to read back ? Answer: no*/
|
|
i_ret = 0;
|
|
|
|
return i_ret;
|
|
}
|
|
|
|
#ifndef MTK_BTIF_MARK_UNUSED_API
|
|
|
|
/*****************************************************************************
|
|
* FUNCTION
|
|
* _btif_receive_data
|
|
* DESCRIPTION
|
|
* receive data from btif module in FIFO polling mode
|
|
* PARAMETERS
|
|
* p_base [IN] BTIF module's base address
|
|
* p_buf [IN/OUT] pointer to rx data buffer
|
|
* max_len [IN] max length of rx buffer
|
|
* RETURNS
|
|
* positive means data is available, 0 means no data available
|
|
*****************************************************************************/
|
|
int _btif_receive_data(struct _MTK_BTIF_INFO_STR_ *p_btif,
|
|
unsigned char *p_buf, const unsigned int max_len)
|
|
{
|
|
/*Chaozhong: To be implement*/
|
|
int i_ret = -1;
|
|
|
|
/*check parameter valid or not*/
|
|
if ((p_buf == NULL) || (max_len == 0)) {
|
|
i_ret = ERR_INVALID_PAR;
|
|
return i_ret;
|
|
}
|
|
i_ret = btif_rx_irq_handler(p_btif, p_buf, max_len);
|
|
|
|
return i_ret;
|
|
}
|
|
|
|
int btif_sleep_ctrl(struct _MTK_BTIF_INFO_STR_ *p_btif, bool en)
|
|
{
|
|
int i_ret = -1;
|
|
unsigned long base = p_btif->base;
|
|
|
|
if (en == false)
|
|
BTIF_CLR_BIT(BTIF_SLEEP_EN(base), BTIF_SLEEP_EN_BIT);
|
|
else
|
|
BTIF_SET_BIT(BTIF_SLEEP_EN(base), BTIF_SLEEP_EN_BIT);
|
|
|
|
/*TODO:do we need to read back ? Answer: no*/
|
|
/*TODO:do we need to dsb?*/
|
|
i_ret = 0;
|
|
return i_ret;
|
|
}
|
|
|
|
static int btif_tx_thr_set(struct _MTK_BTIF_INFO_STR_ *p_btif,
|
|
unsigned int thr_count)
|
|
{
|
|
int i_ret = -1;
|
|
unsigned long base = p_btif->base;
|
|
unsigned int value = 0;
|
|
|
|
/*read BTIF_TRI_LVL*/
|
|
value = BTIF_READ32(BTIF_TRI_LVL(base));
|
|
/*clear Tx threshold bits*/
|
|
value &= (~BTIF_TRI_LVL_TX_MASK);
|
|
/*set tx threshold bits*/
|
|
value |= BTIF_TRI_LVL_TX(BTIF_TX_FIFO_THRE);
|
|
/*write back to BTIF_TRI_LVL*/
|
|
btif_reg_sync_writel(value, BTIF_TRI_LVL(base));
|
|
|
|
return i_ret;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* FUNCTION
|
|
* btif_rx_fifo_reset
|
|
* DESCRIPTION
|
|
* reset BTIF's rx fifo
|
|
* PARAMETERS
|
|
* p_base [IN] BTIF module's base address
|
|
* ec [IN] control if loopback mode is enabled or not
|
|
* RETURNS
|
|
* 0 means success, negative means fail
|
|
*****************************************************************************/
|
|
static int btif_rx_fifo_reset(struct _MTK_BTIF_INFO_STR_ *p_btif)
|
|
{
|
|
/*Chaozhong: To be implement*/
|
|
int i_ret = -1;
|
|
unsigned long base = p_btif->base;
|
|
|
|
/*set Rx FIFO clear bit to 1*/
|
|
BTIF_SET_BIT(BTIF_FIFOCTRL(base), BTIF_FIFOCTRL_CLR_RX);
|
|
|
|
/*clear Rx FIFO clear bit to 0*/
|
|
BTIF_CLR_BIT(BTIF_FIFOCTRL(base), BTIF_FIFOCTRL_CLR_RX);
|
|
|
|
/*TODO:do we need to read back ? Answer: no*/
|
|
/*TODO:do we need to dsb?*/
|
|
i_ret = 0;
|
|
return i_ret;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* FUNCTION
|
|
* btif_tx_fifo_reset
|
|
* DESCRIPTION
|
|
* reset BTIF's tx fifo
|
|
* PARAMETERS
|
|
* p_base [IN] BTIF module's base address
|
|
* RETURNS
|
|
* 0 means success, negative means fail
|
|
*****************************************************************************/
|
|
static int btif_tx_fifo_reset(struct _MTK_BTIF_INFO_STR_ *p_btif)
|
|
{
|
|
int i_ret = -1;
|
|
unsigned long base = p_btif->base;
|
|
|
|
/*set Tx FIFO clear bit to 1*/
|
|
BTIF_SET_BIT(BTIF_FIFOCTRL(base), BTIF_FIFOCTRL_CLR_TX);
|
|
|
|
/*clear Tx FIFO clear bit to 0*/
|
|
BTIF_CLR_BIT(BTIF_FIFOCTRL(base), BTIF_FIFOCTRL_CLR_TX);
|
|
|
|
/*TODO:do we need to read back ? Answer: no*/
|
|
/*TODO:do we need to dsb?*/
|
|
i_ret = 0;
|
|
return i_ret;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
* FUNCTION
|
|
* hal_btif_loopback_ctrl
|
|
* DESCRIPTION
|
|
* BTIF Tx/Rx loopback mode set, this operation can only be done
|
|
* after set BTIF to normal mode
|
|
* PARAMETERS
|
|
* RETURNS
|
|
* 0 means success, negative means fail
|
|
*****************************************************************************/
|
|
int hal_btif_loopback_ctrl(struct _MTK_BTIF_INFO_STR_ *p_btif, bool en)
|
|
{
|
|
int i_ret = -1;
|
|
unsigned long base = p_btif->base;
|
|
|
|
if (en == false)
|
|
BTIF_CLR_BIT(BTIF_TRI_LVL(base), BTIF_TRI_LOOP_EN);
|
|
else
|
|
BTIF_SET_BIT(BTIF_TRI_LVL(base), BTIF_TRI_LOOP_EN);
|
|
|
|
/*TODO:do we need to read back ? Answer: no*/
|
|
/*TODO:do we need to dsb?*/
|
|
i_ret = 0;
|
|
return i_ret;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* FUNCTION
|
|
* hal_btif_rx_handler
|
|
* DESCRIPTION
|
|
* lower level interrupt handler
|
|
* PARAMETERS
|
|
* p_base [IN] BTIF module's base address
|
|
* p_buf [IN/OUT] pointer to rx data buffer
|
|
* max_len [IN] max length of rx buffer
|
|
* RETURNS
|
|
* 0 means success; negative means fail; positive means rx data length
|
|
*****************************************************************************/
|
|
int hal_btif_irq_handler(struct _MTK_BTIF_INFO_STR_ *p_btif,
|
|
unsigned char *p_buf, const unsigned int max_len)
|
|
{
|
|
/*Chaozhong: To be implement*/
|
|
int i_ret = -1;
|
|
unsigned int iir = 0;
|
|
unsigned int rx_len = 0;
|
|
unsigned long base = p_btif->base;
|
|
unsigned long irq_flag = 0;
|
|
|
|
spin_lock_irqsave(&(g_clk_cg_spinlock), irq_flag);
|
|
|
|
#if defined(CONFIG_MTK_CLKMGR)
|
|
if (clock_is_on(MTK_BTIF_CG_BIT) == 0) {
|
|
spin_unlock_irqrestore(&(g_clk_cg_spinlock), irq_flag);
|
|
BTIF_ERR_FUNC("%s: clock is off before irq handle done!!!\n",
|
|
__FILE__);
|
|
return i_ret;
|
|
}
|
|
#endif
|
|
/*read interrupt identifier register*/
|
|
iir = BTIF_READ32(BTIF_IIR(base));
|
|
|
|
while (iir & (BTIF_IIR_RX | BTIF_IIR_RX_TIMEOUT)) {
|
|
rx_len += btif_rx_irq_handler(p_btif, p_buf, max_len);
|
|
|
|
/*update IIR*/
|
|
iir = BTIF_READ32(BTIF_IIR(base));
|
|
}
|
|
|
|
/*is tx interrupt exist?*/
|
|
if (iir & BTIF_IIR_TX_EMPTY)
|
|
i_ret = btif_tx_irq_handler(p_btif);
|
|
spin_unlock_irqrestore(&(g_clk_cg_spinlock), irq_flag);
|
|
|
|
i_ret = rx_len != 0 ? rx_len : i_ret;
|
|
return i_ret;
|
|
}
|
|
|
|
int hal_btif_rx_cb_reg(struct _MTK_BTIF_INFO_STR_ *p_btif_info,
|
|
btif_rx_buf_write rx_cb)
|
|
{
|
|
if (p_btif_info->rx_cb != NULL)
|
|
BTIF_DBG_FUNC("already registered, replace 0x%p with 0x%p\n",
|
|
p_btif_info->rx_cb, rx_cb);
|
|
p_btif_info->rx_cb = rx_cb;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* FUNCTION
|
|
* btif_rx_irq_handler
|
|
* DESCRIPTION
|
|
* lower level rx interrupt handler
|
|
* PARAMETERS
|
|
* p_base [IN] BTIF module's base address
|
|
* RETURNS
|
|
* positive means length of rx data , negative means fail
|
|
*****************************************************************************/
|
|
static int btif_rx_irq_handler(struct _MTK_BTIF_INFO_STR_ *p_btif_info,
|
|
unsigned char *p_buf, const unsigned int max_len)
|
|
{
|
|
/*Chaozhong: To be implement*/
|
|
int i_ret = 0;
|
|
unsigned int iir = 0;
|
|
unsigned int rx_len = 0;
|
|
unsigned long base = p_btif_info->base;
|
|
unsigned char rx_buf[256];
|
|
unsigned int local_buf_len = 256;
|
|
btif_rx_buf_write rx_cb = p_btif_info->rx_cb;
|
|
unsigned int total_len = 0;
|
|
|
|
/*read interrupt identifier register*/
|
|
iir = BTIF_READ32(BTIF_IIR(base));
|
|
while ((iir & (BTIF_IIR_RX | BTIF_IIR_RX_TIMEOUT)) &&
|
|
(rx_len < local_buf_len)) {
|
|
rx_buf[rx_len] = BTIF_READ8(base);
|
|
rx_len++;
|
|
/*need to consult CC Hwang for advice */
|
|
/*
|
|
* whether we need to do memory barrier here
|
|
* Ans: no
|
|
*/
|
|
/*
|
|
* whether we need to d memory barrier when call BTIF_SET_BIT or BTIF_CLR_BIT
|
|
* Ans: no
|
|
*/
|
|
if (rx_len == local_buf_len) {
|
|
if (rx_cb)
|
|
(*rx_cb) (p_btif_info, rx_buf, rx_len);
|
|
rx_len = 0;
|
|
total_len += rx_len;
|
|
}
|
|
iir = BTIF_READ32(BTIF_IIR(base));
|
|
}
|
|
total_len += rx_len;
|
|
if (rx_len && rx_cb)
|
|
(*rx_cb) (p_btif_info, rx_buf, rx_len);
|
|
|
|
/*
|
|
* make sure all data write back to memory, mb or dsb?
|
|
* need to consult CC Hwang for advice
|
|
* Ans: no need here
|
|
*/
|
|
i_ret = total_len;
|
|
return i_ret;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* FUNCTION
|
|
* btif_tx_irq_handler
|
|
* DESCRIPTION
|
|
* lower level tx interrupt handler
|
|
* PARAMETERS
|
|
* p_base [IN] BTIF module's base address
|
|
* p_buf [IN/OUT] pointer to rx data buffer
|
|
* max_len [IN] max length of rx buffer
|
|
* RETURNS
|
|
* 0 means success, negative means fail
|
|
*****************************************************************************/
|
|
static int btif_tx_irq_handler(struct _MTK_BTIF_INFO_STR_ *p_btif)
|
|
{
|
|
int i_ret = -1;
|
|
|
|
#if NEW_TX_HANDLING_SUPPORT
|
|
int how_many = 0;
|
|
unsigned int lsr;
|
|
unsigned int ava_len = 0;
|
|
unsigned long base = p_btif->base;
|
|
char local_buf[BTIF_TX_FIFO_SIZE];
|
|
char *p_data = local_buf;
|
|
unsigned long flag = 0;
|
|
|
|
struct kfifo *p_tx_fifo = p_btif->p_tx_fifo;
|
|
|
|
/*read LSR and check THER or TEMT, either one is 1 means can accept tx data*/
|
|
lsr = BTIF_READ32(BTIF_LSR(base));
|
|
|
|
if (lsr & BTIF_LSR_TEMT_BIT)
|
|
/*Tx Holding Register if empty,*/
|
|
/*we can write tx FIFO count to BTIF*/
|
|
ava_len = BTIF_TX_FIFO_SIZE;
|
|
else if (lsr & BTIF_LSR_THRE_BIT)
|
|
/*Tx Holding Register if empty,*/
|
|
/*we can write (Tx FIFO count - Tx threshold)to BTIF*/
|
|
ava_len = BTIF_TX_FIFO_SIZE - BTIF_TX_FIFO_THRE;
|
|
else {
|
|
/*data size in tx FIFO is more than Tx threshold,*/
|
|
/*we will not write data to THR*/
|
|
ava_len = 0;
|
|
goto ret;
|
|
}
|
|
spin_lock_irqsave(&(p_btif->tx_fifo_spinlock), flag);
|
|
how_many = kfifo_out(p_tx_fifo, local_buf, ava_len);
|
|
spin_unlock_irqrestore(&(p_btif->tx_fifo_spinlock), flag);
|
|
BTIF_DBG_FUNC("BTIF tx size %d done, left:%d\n", how_many,
|
|
kfifo_avail(p_tx_fifo));
|
|
while (how_many--)
|
|
btif_reg_sync_writeb(*(p_data++), BTIF_THR(base));
|
|
|
|
spin_lock_irqsave(&(p_btif->tx_fifo_spinlock), flag);
|
|
/*clear Tx enable flag if necessary*/
|
|
if (kfifo_is_empty(p_tx_fifo)) {
|
|
hal_btif_tx_ier_ctrl(p_btif, false);
|
|
BTIF_DBG_FUNC("BTIF tx FIFO is empty\n");
|
|
}
|
|
spin_unlock_irqrestore(&(p_btif->tx_fifo_spinlock), flag);
|
|
ret:
|
|
#else
|
|
/*clear Tx enable flag*/
|
|
hal_btif_tx_ier_ctrl(p_btif, false);
|
|
#endif
|
|
i_ret = 0;
|
|
return i_ret;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* FUNCTION
|
|
* hal_btif_tx_mode_ctrl
|
|
* DESCRIPTION
|
|
* set BTIF tx to corresponding mode (PIO/DMA)
|
|
* PARAMETERS
|
|
* p_base [IN] BTIF module's base address
|
|
* mode [IN] rx mode <PIO/DMA>
|
|
* RETURNS
|
|
* 0 means success, negative means fail
|
|
*****************************************************************************/
|
|
int hal_btif_tx_mode_ctrl(struct _MTK_BTIF_INFO_STR_ *p_btif,
|
|
enum _ENUM_BTIF_MODE_ mode)
|
|
{
|
|
int i_ret = -1;
|
|
unsigned long base = p_btif->base;
|
|
|
|
if (mode == BTIF_MODE_DMA)
|
|
/*set to DMA mode*/
|
|
BTIF_SET_BIT(BTIF_DMA_EN(base), BTIF_DMA_EN_TX);
|
|
else
|
|
/*set to PIO mode*/
|
|
BTIF_CLR_BIT(BTIF_DMA_EN(base), BTIF_DMA_EN_TX);
|
|
|
|
i_ret = 0;
|
|
return i_ret;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* FUNCTION
|
|
* hal_btif_rx_mode_ctrl
|
|
* DESCRIPTION
|
|
* set BTIF rx to corresponding mode (PIO/DMA)
|
|
* PARAMETERS
|
|
* p_base [IN] BTIF module's base address
|
|
* mode [IN] rx mode <PIO/DMA>
|
|
* RETURNS
|
|
* 0 means success, negative means fail
|
|
*****************************************************************************/
|
|
int hal_btif_rx_mode_ctrl(struct _MTK_BTIF_INFO_STR_ *p_btif,
|
|
enum _ENUM_BTIF_MODE_ mode)
|
|
{
|
|
int i_ret = -1;
|
|
unsigned long base = p_btif->base;
|
|
|
|
if (mode == BTIF_MODE_DMA)
|
|
/*set to DMA mode*/
|
|
BTIF_SET_BIT(BTIF_DMA_EN(base), BTIF_DMA_EN_RX);
|
|
else
|
|
/*set to PIO mode*/
|
|
BTIF_CLR_BIT(BTIF_DMA_EN(base), BTIF_DMA_EN_RX);
|
|
|
|
i_ret = 0;
|
|
return i_ret;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* FUNCTION
|
|
* hal_btif_send_data
|
|
* DESCRIPTION
|
|
* send data through btif in FIFO mode
|
|
* PARAMETERS
|
|
* p_base [IN] BTIF module's base address
|
|
* p_buf [IN] pointer to rx data buffer
|
|
* max_len [IN] tx buffer length
|
|
* RETURNS
|
|
* positive means number of data sent; 0 means no data put to FIFO;
|
|
* negative means error happens
|
|
*****************************************************************************/
|
|
int hal_btif_send_data(struct _MTK_BTIF_INFO_STR_ *p_btif,
|
|
const unsigned char *p_buf, const unsigned int buf_len)
|
|
{
|
|
/*Chaozhong: To be implement*/
|
|
int i_ret = -1;
|
|
|
|
unsigned int ava_len = 0;
|
|
unsigned int sent_len = 0;
|
|
|
|
#if !(NEW_TX_HANDLING_SUPPORT)
|
|
unsigned long base = p_btif->base;
|
|
unsigned int lsr = 0;
|
|
unsigned int left_len = 0;
|
|
unsigned char *p_data = (unsigned char *)p_buf;
|
|
#endif
|
|
|
|
/*check parameter valid or not*/
|
|
if ((p_buf == NULL) || (buf_len == 0)) {
|
|
i_ret = ERR_INVALID_PAR;
|
|
return i_ret;
|
|
}
|
|
#if NEW_TX_HANDLING_SUPPORT
|
|
ava_len = _get_btif_tx_fifo_room(p_btif);
|
|
sent_len = buf_len <= ava_len ? buf_len : ava_len;
|
|
if (sent_len > 0) {
|
|
int enqueue_len = 0;
|
|
unsigned long flag = 0;
|
|
|
|
spin_lock_irqsave(&(p_btif->tx_fifo_spinlock), flag);
|
|
enqueue_len = kfifo_in(p_btif->p_tx_fifo,
|
|
(unsigned char *)p_buf, sent_len);
|
|
if (sent_len != enqueue_len) {
|
|
BTIF_ERR_FUNC("target tx len:%d, len sent:%d\n",
|
|
sent_len, enqueue_len);
|
|
}
|
|
i_ret = enqueue_len;
|
|
mb(); /* for btif send data */
|
|
/*enable BTIF Tx IRQ*/
|
|
hal_btif_tx_ier_ctrl(p_btif, true);
|
|
spin_unlock_irqrestore(&(p_btif->tx_fifo_spinlock), flag);
|
|
BTIF_DBG_FUNC("enqueue len:%d\n", enqueue_len);
|
|
} else {
|
|
i_ret = 0;
|
|
}
|
|
#else
|
|
while ((_btif_is_tx_allow(p_btif)) && (sent_len < buf_len)) {
|
|
/*read LSR and check THER or TEMT,*/
|
|
/*either one is 1 means can accept tx data*/
|
|
lsr = BTIF_READ32(BTIF_LSR(base));
|
|
|
|
if (lsr & BTIF_LSR_TEMT_BIT)
|
|
/*Tx Holding Register if empty,*/
|
|
/*we can write tx FIFO count to BTIF*/
|
|
ava_len = BTIF_TX_FIFO_SIZE;
|
|
else if (lsr & BTIF_LSR_THRE_BIT)
|
|
/*Tx Holding Register if empty,*/
|
|
/*we can write (Tx FIFO count - Tx threshold)*/
|
|
/*to BTIF*/
|
|
ava_len = BTIF_TX_FIFO_SIZE - BTIF_TX_FIFO_THRE;
|
|
else {
|
|
/*data size in tx FIFO is more than Tx threshold,*/
|
|
/*we will not write data to THR*/
|
|
ava_len = 0;
|
|
break;
|
|
}
|
|
|
|
left_len = buf_len - sent_len;
|
|
/*ava_len will be real length will write to BTIF THR*/
|
|
ava_len = ava_len > left_len ? left_len : ava_len;
|
|
/*update sent length valud after this operation*/
|
|
sent_len += ava_len;
|
|
/*
|
|
* whether we need memory barrier here?
|
|
* Ans: No, no memory ordering issue exist,
|
|
* CPU will make sure logically right
|
|
*/
|
|
while (ava_len--)
|
|
btif_reg_sync_writeb(*(p_data++), BTIF_THR(base));
|
|
|
|
}
|
|
/* while ((hal_btif_is_tx_allow()) && (sent_len < buf_len)); */
|
|
|
|
i_ret = sent_len;
|
|
|
|
/*enable BTIF Tx IRQ*/
|
|
hal_btif_tx_ier_ctrl(p_btif, true);
|
|
#endif
|
|
return i_ret;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* FUNCTION
|
|
* hal_btif_raise_wak_sig
|
|
* DESCRIPTION
|
|
* raise wakeup signal to counterpart
|
|
* PARAMETERS
|
|
* p_base [IN] BTIF module's base address
|
|
* RETURNS
|
|
* 0 means success, negative means fail
|
|
*****************************************************************************/
|
|
int hal_btif_raise_wak_sig(struct _MTK_BTIF_INFO_STR_ *p_btif)
|
|
{
|
|
int i_ret = -1;
|
|
unsigned long base = p_btif->base;
|
|
#if defined(CONFIG_MTK_CLKMGR)
|
|
if (clock_is_on(MTK_BTIF_CG_BIT) == 0) {
|
|
BTIF_ERR_FUNC("%s: clock is off before send wakeup signal!!!\n",
|
|
__FILE__);
|
|
return i_ret;
|
|
}
|
|
#endif
|
|
/*write 0 to BTIF_WAK to pull ap_wakeup_consyss low */
|
|
BTIF_CLR_BIT(BTIF_WAK(base), BTIF_WAK_BIT);
|
|
|
|
/*wait for a period for longer than 1/32k period, here we use 40us*/
|
|
set_current_state(TASK_UNINTERRUPTIBLE);
|
|
usleep_range(128, 160);
|
|
/*
|
|
* according to linux/documentation/timers/timers-how-to, we choose usleep_range
|
|
* SLEEPING FOR ~USECS OR SMALL MSECS ( 10us - 20ms): * Use usleep_range
|
|
*/
|
|
/*write 1 to pull ap_wakeup_consyss high*/
|
|
BTIF_SET_BIT(BTIF_WAK(base), BTIF_WAK_BIT);
|
|
i_ret = 0;
|
|
return i_ret;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* FUNCTION
|
|
* hal_btif_dump_reg
|
|
* DESCRIPTION
|
|
* dump BTIF module's information when needed
|
|
* PARAMETERS
|
|
* p_base [IN] BTIF module's base address
|
|
* flag [IN] register id flag
|
|
* RETURNS
|
|
* 0 means success, negative means fail
|
|
*****************************************************************************/
|
|
int hal_btif_dump_reg(struct _MTK_BTIF_INFO_STR_ *p_btif,
|
|
enum _ENUM_BTIF_REG_ID_ flag)
|
|
{
|
|
/*Chaozhong: To be implement*/
|
|
int i_ret = -1;
|
|
int idx = 0;
|
|
/*unsigned long irq_flag = 0;*/
|
|
unsigned long base = p_btif->base;
|
|
unsigned char reg_map[0xE0 / 4] = { 0 };
|
|
unsigned int lsr = 0x0;
|
|
unsigned int dma_en = 0;
|
|
|
|
/*spin_lock_irqsave(&(g_clk_cg_spinlock), irq_flag);*/
|
|
#if defined(CONFIG_MTK_CLKMGR)
|
|
if (clock_is_on(MTK_BTIF_CG_BIT) == 0) {
|
|
/*spin_unlock_irqrestore(&(g_clk_cg_spinlock), irq_flag);*/
|
|
BTIF_ERR_FUNC("%s: clock is off, this should never happen!!!\n",
|
|
__FILE__);
|
|
return i_ret;
|
|
}
|
|
#endif
|
|
lsr = BTIF_READ32(BTIF_LSR(base));
|
|
dma_en = BTIF_READ32(BTIF_DMA_EN(base));
|
|
|
|
switch (flag) {
|
|
case REG_ALL:
|
|
/*
|
|
* here we omit 1st register which is THR/RBR register to avoid
|
|
* Rx data read by this debug information accidently
|
|
*/
|
|
for (idx = 1; idx < sizeof(reg_map); idx++)
|
|
reg_map[idx] = BTIF_READ8(p_btif->base + (4 * idx));
|
|
|
|
btif_dump_array("BTIF register", reg_map, sizeof(reg_map));
|
|
break;
|
|
case REG_IRQ:
|
|
BTIF_INFO_FUNC("IER:0x%x, IIR:0x%x, LSR:0x%x\n",
|
|
BTIF_READ32(BTIF_IER(base)),
|
|
BTIF_READ32(BTIF_IIR(base)),
|
|
lsr);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
BTIF_INFO_FUNC("Tx DMA %s, Rx DMA %s, Rx data is %s, Tx data is %s\n",
|
|
(dma_en & BTIF_DMA_EN_TX) ? "enabled" : "disabled",
|
|
(dma_en & BTIF_DMA_EN_RX) ? "enabled" : "disabled",
|
|
(lsr & BTIF_LSR_DR_BIT) ? "not empty" : "empty",
|
|
(lsr & BTIF_LSR_TEMT_BIT) ? "empty" : "not empty");
|
|
|
|
return i_ret;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* FUNCTION
|
|
* hal_btif_is_tx_complete
|
|
* DESCRIPTION
|
|
* get tx complete flag
|
|
* PARAMETERS
|
|
* p_base [IN] BTIF module's base address
|
|
* RETURNS
|
|
* true means tx complete, false means tx in process
|
|
*****************************************************************************/
|
|
bool hal_btif_is_tx_complete(struct _MTK_BTIF_INFO_STR_ *p_btif)
|
|
{
|
|
/*Chaozhong: To be implement*/
|
|
bool b_ret = false;
|
|
unsigned int lsr = 0;
|
|
unsigned long flags = 0;
|
|
unsigned long base = p_btif->base;
|
|
unsigned int tx_empty = 0;
|
|
unsigned int rx_dr = 0;
|
|
unsigned int tx_irq_disable = 0;
|
|
|
|
/*
|
|
* 3 conditions allow clock to be disable
|
|
* 1. if TEMT is set or not
|
|
* 2. if DR is set or not
|
|
* 3. Tx IRQ is disabled or not
|
|
*/
|
|
lsr = BTIF_READ32(BTIF_LSR(base));
|
|
tx_empty = lsr & BTIF_LSR_TEMT_BIT;
|
|
rx_dr = lsr & BTIF_LSR_DR_BIT;
|
|
tx_irq_disable = BTIF_READ32(BTIF_IER(base)) & BTIF_IER_TXEEN;
|
|
|
|
b_ret =
|
|
(tx_empty && (tx_irq_disable == 0) && (rx_dr == 0)) ? true : false;
|
|
if (!b_ret) {
|
|
BTIF_DBG_FUNC
|
|
("BTIF flag, tx_empty:%d, rx_dr:%d, tx_irq_disable:%d\n",
|
|
tx_empty, rx_dr, tx_irq_disable);
|
|
}
|
|
#if NEW_TX_HANDLING_SUPPORT
|
|
spin_lock_irqsave(&(p_btif->tx_fifo_spinlock), flags);
|
|
/*clear Tx enable flag if necessary*/
|
|
if (!(kfifo_is_empty(p_btif->p_tx_fifo))) {
|
|
BTIF_DBG_FUNC("BTIF tx FIFO is not empty\n");
|
|
b_ret = false;
|
|
}
|
|
spin_unlock_irqrestore(&(p_btif->tx_fifo_spinlock), flags);
|
|
#endif
|
|
return b_ret;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* FUNCTION
|
|
* hal_btif_is_tx_allow
|
|
* DESCRIPTION
|
|
* whether tx is allowed
|
|
* PARAMETERS
|
|
* p_base [IN] BTIF module's base address
|
|
* RETURNS
|
|
* true if tx operation is allowed; false if tx is not allowed
|
|
*****************************************************************************/
|
|
bool hal_btif_is_tx_allow(struct _MTK_BTIF_INFO_STR_ *p_btif)
|
|
{
|
|
#define MIN_TX_MB ((26 * 1000000 / 13) / 1000000)
|
|
#define AVE_TX_MB ((26 * 1000000 / 8) / 1000000)
|
|
|
|
/*Chaozhong: To be implement*/
|
|
bool b_ret = false;
|
|
|
|
#if NEW_TX_HANDLING_SUPPORT
|
|
unsigned long flags = 0;
|
|
|
|
spin_lock_irqsave(&(p_btif->tx_fifo_spinlock), flags);
|
|
/*clear Tx enable flag if necessary*/
|
|
if (kfifo_is_full(p_btif->p_tx_fifo)) {
|
|
BTIF_WARN_FUNC("BTIF tx FIFO is full\n");
|
|
b_ret = false;
|
|
} else {
|
|
b_ret = true;
|
|
}
|
|
spin_unlock_irqrestore(&(p_btif->tx_fifo_spinlock), flags);
|
|
#else
|
|
unsigned int lsr = 0;
|
|
unsigned long base = p_btif->base;
|
|
unsigned int wait_us = (BTIF_TX_FIFO_SIZE - BTIF_TX_FIFO_THRE) /
|
|
MIN_TX_MB; /*only ava length */
|
|
|
|
/*read LSR and check THER or TEMT, either one is 1 means can accept tx data*/
|
|
lsr = BTIF_READ32(BTIF_LSR(base));
|
|
|
|
if (!(lsr & (BTIF_LSR_TEMT_BIT | BTIF_LSR_THRE_BIT))) {
|
|
BTIF_DBG_FUNC("wait for %d ~ %d us\n", wait_us, 3 * wait_us);
|
|
/* usleep_range(wait_us, 3 * 10 * wait_us); */
|
|
usleep_range(wait_us, 3 * wait_us);
|
|
}
|
|
lsr = BTIF_READ32(BTIF_LSR(base));
|
|
b_ret = (lsr & (BTIF_LSR_TEMT_BIT | BTIF_LSR_THRE_BIT)) ? true : false;
|
|
if (!b_ret)
|
|
BTIF_DBG_FUNC(" tx is not allowed for the moment\n");
|
|
else
|
|
BTIF_DBG_FUNC(" tx is allowed\n");
|
|
#endif
|
|
return b_ret;
|
|
}
|
|
|
|
#if !(NEW_TX_HANDLING_SUPPORT)
|
|
|
|
static bool _btif_is_tx_allow(struct _MTK_BTIF_INFO_STR_ *p_btif)
|
|
{
|
|
/*Chaozhong: To be implement*/
|
|
bool b_ret = false;
|
|
unsigned long base = p_btif->base;
|
|
unsigned int lsr = 0;
|
|
|
|
/*read LSR and check THER or TEMT, either one is 1 means can accept tx data*/
|
|
lsr = BTIF_READ32(BTIF_LSR(base));
|
|
b_ret = (lsr & (BTIF_LSR_TEMT_BIT | BTIF_LSR_THRE_BIT)) ? true : false;
|
|
return b_ret;
|
|
}
|
|
#endif
|
|
|
|
int hal_btif_pm_ops(struct _MTK_BTIF_INFO_STR_ *p_btif_info,
|
|
enum _MTK_BTIF_PM_OPID_ opid)
|
|
{
|
|
int i_ret = -1;
|
|
|
|
BTIF_DBG_FUNC("op id: %d\n", opid);
|
|
switch (opid) {
|
|
case BTIF_PM_DPIDLE_EN:
|
|
i_ret = 0;
|
|
break;
|
|
case BTIF_PM_DPIDLE_DIS:
|
|
i_ret = 0;
|
|
break;
|
|
case BTIF_PM_SUSPEND:
|
|
i_ret = 0;
|
|
break;
|
|
case BTIF_PM_RESUME:
|
|
i_ret = 0;
|
|
break;
|
|
case BTIF_PM_RESTORE_NOIRQ:{
|
|
unsigned int flag = 0;
|
|
struct _MTK_BTIF_IRQ_STR_ *p_irq = p_btif_info->p_irq;
|
|
|
|
#ifdef CONFIG_OF
|
|
flag = p_irq->irq_flags;
|
|
#else
|
|
switch (p_irq->sens_type) {
|
|
case IRQ_SENS_EDGE:
|
|
if (p_irq->edge_type == IRQ_EDGE_FALL)
|
|
flag = IRQF_TRIGGER_FALLING;
|
|
else if (p_irq->edge_type == IRQ_EDGE_RAISE)
|
|
flag = IRQF_TRIGGER_RISING;
|
|
else if (p_irq->edge_type == IRQ_EDGE_BOTH)
|
|
flag = IRQF_TRIGGER_RISING |
|
|
IRQF_TRIGGER_FALLING;
|
|
else
|
|
flag = IRQF_TRIGGER_FALLING;
|
|
break;
|
|
case IRQ_SENS_LVL:
|
|
if (p_irq->lvl_type == IRQ_LVL_LOW)
|
|
flag = IRQF_TRIGGER_LOW;
|
|
else if (p_irq->lvl_type == IRQ_LVL_HIGH)
|
|
flag = IRQF_TRIGGER_HIGH;
|
|
else
|
|
flag = IRQF_TRIGGER_LOW;
|
|
break;
|
|
default:
|
|
flag = IRQF_TRIGGER_LOW;
|
|
break;
|
|
}
|
|
#endif
|
|
/* irq_set_irq_type(p_irq->irq_id, flag); */
|
|
i_ret = 0;
|
|
}
|
|
break;
|
|
default:
|
|
i_ret = ERR_INVALID_PAR;
|
|
break;
|
|
}
|
|
|
|
return i_ret;
|
|
}
|
|
void mtk_btif_read_cpu_sw_rst_debug_plat(void)
|
|
{
|
|
#define CONSYS_AP2CONN_WAKEUP_OFFSET 0x00000064
|
|
BTIF_WARN_FUNC("+CONSYS_AP2CONN_WAKEUP_OFFSET(0x%x)\n",
|
|
BTIF_READ32(mtk_btif_info.base + CONSYS_AP2CONN_WAKEUP_OFFSET));
|
|
}
|
|
|