// 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 ========\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 * 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 * 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)); }