// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. */ /*-----------linux system header files----------------*/ #include #include #include #include #include #include #include #include #include /*#include */ /*-----------driver own header files----------------*/ #ifdef CONFIG_COMPAT #include #endif #ifdef DFT_TAG #undef DFT_TAG #endif #define DFT_TAG "MTK-BTIF" #define BTIF_CDEV_SUPPORT 0 #include "btif_pub.h" #include "btif_dma_pub.h" #include "mtk_btif_exp.h" #include "mtk_btif.h" #define DRIVER_ATTR(_name, _mode, _show, _store) \ struct driver_attribute driver_attr_##_name = \ __ATTR(_name, _mode, _show, _store) /*-----------static function declearation----------------*/ static int mtk_btif_probe(struct platform_device *pdev); static int mtk_btif_remove(struct platform_device *pdev); static int mtk_btif_suspend(struct platform_device *pdev, pm_message_t state); static int mtk_btif_resume(struct platform_device *pdev); static int mtk_btif_drv_resume(struct device *dev); static int mtk_btif_drv_suspend(struct device *pdev); static int mtk_btif_restore_noirq(struct device *device); #if BTIF_CDEV_SUPPORT static int btif_file_open(struct inode *pinode, struct file *pfile); static int btif_file_release(struct inode *pinode, struct file *pfile); static ssize_t btif_file_read(struct file *pfile, char __user *buf, size_t count, loff_t *f_ops); static unsigned int btif_poll(struct file *filp, poll_table *wait); #endif static int _btif_irq_reg(struct _MTK_BTIF_IRQ_STR_ *p_irq, irq_handler_t irq_handler, void *data); static int _btif_irq_free(struct _MTK_BTIF_IRQ_STR_ *p_irq, void *data); static int _btif_irq_ctrl(struct _MTK_BTIF_IRQ_STR_ *p_irq, bool en); static int _btif_irq_ctrl_sync(struct _MTK_BTIF_IRQ_STR_ *p_irq, bool en); static irqreturn_t btif_irq_handler(int irq, void *data); static unsigned int btif_pio_rx_data_receiver( struct _MTK_BTIF_INFO_STR_ *p_btif_info, unsigned char *p_buf, unsigned int buf_len); static irqreturn_t btif_tx_dma_irq_handler(int irq, void *data); static irqreturn_t btif_rx_dma_irq_handler(int irq, void *data); static unsigned int btif_dma_rx_data_receiver( struct _MTK_DMA_INFO_STR_ *p_dma_info, unsigned char *p_buf, unsigned int buf_len); static int _btif_controller_tx_setup(struct _mtk_btif_ *p_btif); static int _btif_controller_tx_free(struct _mtk_btif_ *p_btif); static int _btif_controller_rx_setup(struct _mtk_btif_ *p_btif); static int _btif_controller_rx_free(struct _mtk_btif_ *p_btif); static int _btif_tx_pio_setup(struct _mtk_btif_ *p_btif); static int _btif_rx_pio_setup(struct _mtk_btif_ *p_btif); static int _btif_rx_dma_setup(struct _mtk_btif_ *p_btif); static int _btif_rx_dma_free(struct _mtk_btif_ *p_btif); static int _btif_tx_dma_setup(struct _mtk_btif_ *p_btif); static int _btif_tx_dma_free(struct _mtk_btif_ *p_btif); static int _btif_controller_setup(struct _mtk_btif_ *p_btif); static int _btif_controller_free(struct _mtk_btif_ *p_btif); static int _btif_pio_write(struct _mtk_btif_ *p_btif, const unsigned char *p_buf, unsigned int buf_len); static int _btif_dma_write(struct _mtk_btif_ *p_btif, const unsigned char *p_buf, unsigned int buf_len); static unsigned int btif_bbs_wr_direct(struct _btif_buf_str_ *p_bbs, unsigned char *p_buf, unsigned int buf_len); static unsigned int btif_bbs_write(struct _btif_buf_str_ *p_bbs, unsigned char *p_buf, unsigned int buf_len); static void btif_dump_bbs_str(unsigned char *p_str, struct _btif_buf_str_ *p_bbs); static int _btif_dump_memory(char *str, unsigned char *p_buf, unsigned int buf_len); static int _btif_rx_btm_deinit(struct _mtk_btif_ *p_btif); static int _btif_rx_btm_sched(struct _mtk_btif_ *p_btif); static int _btif_rx_btm_init(struct _mtk_btif_ *p_btif); static void btif_rx_tasklet(unsigned long func_data); static void btif_rx_worker(struct work_struct *p_work); static int btif_rx_thread(void *p_data); static int btif_rx_data_consummer(struct _mtk_btif_ *p_btif); static int _btif_tx_ctx_init(struct _mtk_btif_ *p_btif); static int _btif_tx_ctx_deinit(struct _mtk_btif_ *p_btif); static void btif_tx_worker(struct work_struct *p_work); static int _btif_state_deinit(struct _mtk_btif_ *p_btif); static int _btif_state_release(struct _mtk_btif_ *p_btif); static enum _ENUM_BTIF_STATE_ _btif_state_get(struct _mtk_btif_ *p_btif); static int _btif_state_set(struct _mtk_btif_ *p_btif, enum _ENUM_BTIF_STATE_ state); static int _btif_state_hold(struct _mtk_btif_ *p_btif); static int _btif_state_init(struct _mtk_btif_ *p_btif); static int _btif_dpidle_notify_ctrl(struct _mtk_btif_ *p_btif, enum _ENUM_BTIF_DPIDLE_ en_flag); static int _btif_enter_dpidle(struct _mtk_btif_ *p_btif); static int _btif_exit_dpidle(struct _mtk_btif_ *p_btif); static int _btif_exit_dpidle_from_sus(struct _mtk_btif_ *p_btif); static int _btif_exit_dpidle_from_dpidle(struct _mtk_btif_ *p_btif); static int _btif_enter_dpidle_from_on(struct _mtk_btif_ *p_btif); static int _btif_enter_dpidle_from_sus(struct _mtk_btif_ *p_btif); #if ENABLE_BTIF_TX_DMA static int _btif_vfifo_deinit(struct _mtk_btif_dma_ *p_dma); static int _btif_vfifo_init(struct _mtk_btif_dma_ *p_dma); #endif static int _btif_init(struct _mtk_btif_ *p_btif); static int _btif_lpbk_ctrl(struct _mtk_btif_ *p_btif, bool flag); #if BTIF_CDEV_SUPPORT static int btif_rx_dma_mode_set(int en); static int btif_tx_dma_mode_set(int en); #endif static int _btif_send_data(struct _mtk_btif_ *p_btif, const unsigned char *p_buf, unsigned int buf_len); static int _btif_rx_thread_lock(struct _mtk_btif_ *p_btif, bool enable); #ifdef BTIF_FLAG_SET_ENABLE_ALL_FUNC #if BTIF_DBG_SUPPORT static void btif_rx_test_handler(struct work_struct *work); static int btif_block_rx_dma_irq_test(void); #endif #endif /*-----------end of static function declearation----------------*/ static const char *g_state[B_S_MAX] = { "OFF", "SUSPEND", "DPIDLE", "ON", }; /*-----------BTIF setting--------------*/ struct _mtk_btif_setting_ g_btif_setting[BTIF_PORT_NR] = { { .tx_mode = BTIF_TX_MODE, .rx_mode = BTIF_RX_MODE, .rx_type = BTIF_RX_BTM_CTX, .tx_type = BTIF_TX_CTX, }, }; struct _mtk_btif_ g_btif[BTIF_PORT_NR] = { { .open_counter = 0, .state = B_S_OFF, .setting = &g_btif_setting[0], .p_tx_dma = NULL, .p_rx_dma = NULL, .rx_cb = NULL, .p_btif_info = NULL, }, }; struct _mtk_btif_dma_ g_dma[BTIF_PORT_NR][BTIF_DIR_MAX] = { { { .p_btif = NULL, .dir = BTIF_TX, .p_dma_info = NULL, .entry = ATOMIC_INIT(0), }, { .p_btif = NULL, .dir = BTIF_RX, .p_dma_info = NULL, .entry = ATOMIC_INIT(0), }, }, }; #define G_MAX_PKG_LEN (7 * 1024) static int g_max_pkg_len = G_MAX_PKG_LEN; /*DMA vFIFO is set to 8 * 1024, we set this to 7/8 * vFIFO size*/ static int g_max_pding_data_size = BTIF_RX_BUFFER_SIZE * 3 / 4; #ifdef BTIF_FLAG_SET_ENABLE_ALL_FUNC #if BTIF_DBG_SUPPORT int g_enable_btif_rxd_test; #endif #endif static int mtk_btif_dbg_lvl = BTIF_LOG_INFO; #if BTIF_RXD_BE_BLOCKED_DETECT static struct timespec64 btif_rxd_time_stamp[MAX_BTIF_RXD_TIME_REC]; #endif /*-----------Platform bus related structures----------------*/ #define DRV_NAME "mtk_btif" #ifdef CONFIG_OF const struct of_device_id apbtif_of_ids[] = { { .compatible = "mediatek,btif", }, {} }; #endif const struct dev_pm_ops mtk_btif_drv_pm_ops = { .restore_noirq = mtk_btif_restore_noirq, .suspend = mtk_btif_drv_suspend, .resume = mtk_btif_drv_resume, }; struct platform_driver mtk_btif_dev_drv = { .probe = mtk_btif_probe, .remove = mtk_btif_remove, #ifdef CONFIG_PM .suspend = mtk_btif_suspend, .resume = mtk_btif_resume, #endif .driver = { .name = DRV_NAME, .owner = THIS_MODULE, #ifdef CONFIG_PM .pm = &mtk_btif_drv_pm_ops, #endif #ifdef CONFIG_OF .of_match_table = apbtif_of_ids, #endif } }; static int btif_probed; #define BTIF_STATE_RELEASE(x) _btif_state_release(x) /*-----------End of Platform bus related structures----------------*/ /*-----------platform bus related operation APIs----------------*/ static int mtk_btif_probe(struct platform_device *pdev) { /*Chaozhong: ToDo: to be implement*/ /*register IRQ for BTIF and Tx Rx DMA and disable them by default*/ BTIF_INFO_FUNC("DO BTIF PROBE\n"); platform_set_drvdata(pdev, &g_btif[0]); g_btif[0].private_data = (struct device *)&pdev->dev; #if !defined(CONFIG_MTK_CLKMGR) hal_btif_clk_get_and_prepare(pdev); #endif btif_probed = 1; return 0; } static int mtk_btif_remove(struct platform_device *pdev) { /*Chaozhong: ToDo: to be implement*/ BTIF_INFO_FUNC("DO BTIF REMOVE\n"); platform_set_drvdata(pdev, NULL); g_btif[0].private_data = NULL; return 0; } int _btif_suspend(struct _mtk_btif_ *p_btif) { int i_ret; if (p_btif != NULL) { if (_btif_state_hold(p_btif)) return E_BTIF_INTR; if (!(p_btif->enable)) i_ret = 0; else { if (_btif_state_get(p_btif) == B_S_ON) { BTIF_ERR_FUNC("BTIF in ON state, %s%s", "there are data need to be send", " or recev,suspend fail\n"); i_ret = -1; } else { /* * before disable BTIF controller and DMA * controller, we need to set BTIF to ON state */ i_ret = _btif_exit_dpidle(p_btif); if (i_ret == 0) { i_ret += _btif_controller_free(p_btif); i_ret = _btif_controller_tx_free (p_btif); i_ret += _btif_controller_rx_free (p_btif); } if (i_ret != 0) { BTIF_INFO_FUNC("failed\n"); /*Chaozhong: what if failed*/ } else { BTIF_INFO_FUNC("succeed\n"); i_ret = _btif_state_set(p_btif, B_S_SUSPEND); if (i_ret && _btif_init(p_btif)) { /* Chaozhong: BTIF re-init*/ /*failed? what to do */ i_ret = _btif_state_set(p_btif, B_S_OFF); } } } } BTIF_STATE_RELEASE(p_btif); } else i_ret = -1; return i_ret; } static int mtk_btif_drv_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); pm_message_t state = PMSG_SUSPEND; return mtk_btif_suspend(pdev, state); } static int mtk_btif_drv_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); return mtk_btif_resume(pdev); } static int mtk_btif_suspend(struct platform_device *pdev, pm_message_t state) { int i_ret = 0; struct _mtk_btif_ *p_btif = NULL; /*Chaozhong: ToDo: to be implement*/ BTIF_DBG_FUNC("++\n"); p_btif = platform_get_drvdata(pdev); i_ret = _btif_suspend(p_btif); BTIF_DBG_FUNC("--, i_ret:%d\n", i_ret); return i_ret; } int _btif_restore_noirq(struct _mtk_btif_ *p_btif) { int i_ret = 0; /*BTIF IRQ restore no irq*/ i_ret = hal_btif_pm_ops(p_btif->p_btif_info, BTIF_PM_RESTORE_NOIRQ); if (i_ret == 0) { BTIF_INFO_FUNC("BTIF HW IRQ restore succeed\n"); } else { BTIF_INFO_FUNC("BTIF HW IRQ restore failed, i_ret:%d\n", i_ret); return i_ret; } /*BTIF DMA restore no irq*/ if (p_btif->tx_mode == BTIF_MODE_DMA) { i_ret = hal_dma_pm_ops(p_btif->p_tx_dma->p_dma_info, BTIF_PM_RESTORE_NOIRQ); if (i_ret == 0) { BTIF_INFO_FUNC("BTIF Tx DMA IRQ restore succeed\n"); } else { BTIF_INFO_FUNC ("BTIF Tx DMA IRQ restore failed, i_ret:%d\n", i_ret); return i_ret; } } if (p_btif->rx_mode == BTIF_MODE_DMA) { i_ret = hal_dma_pm_ops(p_btif->p_rx_dma->p_dma_info, BTIF_PM_RESTORE_NOIRQ); if (i_ret == 0) { BTIF_INFO_FUNC("BTIF Rx DMA IRQ restore succeed\n"); } else { BTIF_INFO_FUNC ("BTIF Rx DMA IRQ restore failed, i_ret:%d\n", i_ret); return i_ret; } } return i_ret; } static int mtk_btif_restore_noirq(struct device *dev) { int i_ret = 0; struct platform_device *pdev = to_platform_device(dev); struct _mtk_btif_ *p_btif = platform_get_drvdata(pdev); BTIF_INFO_FUNC("++\n"); if (_btif_state_hold(p_btif)) return E_BTIF_INTR; if (p_btif->enable) BTIF_ERR_FUNC("BTIF is not closed before IPOH shutdown!!!\n"); WARN_ON(p_btif->enable); i_ret = _btif_restore_noirq(p_btif); BTIF_STATE_RELEASE(p_btif); BTIF_INFO_FUNC("--\n"); return i_ret; } int _btif_resume(struct _mtk_btif_ *p_btif) { int i_ret = 0; enum _ENUM_BTIF_STATE_ state = B_S_MAX; if (p_btif != NULL) { if (_btif_state_hold(p_btif)) return E_BTIF_INTR; state = _btif_state_get(p_btif); if (!(p_btif->enable)) i_ret = 0; else if (state == B_S_SUSPEND) i_ret = _btif_enter_dpidle(p_btif); else if (state >= B_S_OFF && state < B_S_MAX) BTIF_INFO_FUNC ("BTIF state: %s before resume, do nothing\n", g_state[state]); BTIF_STATE_RELEASE(p_btif); } else i_ret = -1; return i_ret; } static int mtk_btif_resume(struct platform_device *pdev) { int i_ret = 0; struct _mtk_btif_ *p_btif = NULL; /*Chaozhong: ToDo: to be implement*/ BTIF_DBG_FUNC("++\n"); p_btif = platform_get_drvdata(pdev); i_ret = _btif_resume(p_btif); BTIF_DBG_FUNC("--, i_ret:%d\n", i_ret); return 0; } /*-----------device node----------------*/ #if BTIF_CDEV_SUPPORT dev_t btif_dev; struct class *p_btif_class; struct device *p_btif_dev; const char *p_btif_dev_name = "btif"; static struct semaphore wr_mtx; static struct semaphore rd_mtx; unsigned char wr_buf[2048]; unsigned char rd_buf[2048]; static int rx_notify_flag; static DECLARE_WAIT_QUEUE_HEAD(btif_wq); static int btif_file_open(struct inode *pinode, struct file *pfile); static int btif_file_release(struct inode *pinode, struct file *pfile); static ssize_t btif_file_read(struct file *pfile, char __user *buf, size_t count, loff_t *f_ops); static ssize_t btif_file_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos); static long btif_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); #ifdef CONFIG_COMPAT static long btif_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); #endif static struct cdev btif_dev_c; static wait_queue_head_t btif_read_inq; /* read queues */ const struct file_operations mtk_btif_fops = { .owner = THIS_MODULE, .open = btif_file_open, .release = btif_file_release, .read = btif_file_read, .write = btif_file_write, .unlocked_ioctl = btif_unlocked_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = btif_compat_ioctl, #endif .poll = btif_poll, }; static int btif_chrdev_init(void) { int i_ret; int i_err; /* alloc device number dynamically */ i_ret = alloc_chrdev_region(&btif_dev, 0, 1, p_btif_dev_name); if (i_ret) { BTIF_ERR_FUNC("devuce number allocation failed, i_ret:%d\n", i_ret); } else { BTIF_INFO_FUNC("devuce number allocation succeed\n"); } cdev_init(&btif_dev_c, &mtk_btif_fops); btif_dev_c.owner = THIS_MODULE; i_err = cdev_add(&btif_dev_c, btif_dev, 1); if (i_err) { BTIF_ERR_FUNC("error add btif dev to kernel, error code:%d\n", i_err); unregister_chrdev_region(btif_dev, 1); btif_dev = 0; return -1; } BTIF_INFO_FUNC("add btif dev to kernel succeed\n"); p_btif_class = class_create(THIS_MODULE, p_btif_dev_name); if (IS_ERR(p_btif_class)) { BTIF_ERR_FUNC("error happened when doing class_create\n"); unregister_chrdev_region(btif_dev, 1); btif_dev = 0; return -2; } BTIF_INFO_FUNC("create class for btif succeed\n"); p_btif_dev = device_create(p_btif_class, NULL, btif_dev, 0, p_btif_dev_name); if (IS_ERR(p_btif_dev)) { BTIF_ERR_FUNC("error happened when doing device_create\n"); class_destroy(p_btif_class); p_btif_class = NULL; unregister_chrdev_region(btif_dev, 1); btif_dev = 0; return -3; } BTIF_INFO_FUNC("create device for btif succeed\n"); return 0; } void btif_rx_notify_cb(void) { BTIF_DBG_FUNC("++\n"); rx_notify_flag = 1; wake_up(&btif_wq); wake_up_interruptible(&btif_read_inq); BTIF_DBG_FUNC("--\n"); } unsigned int btif_poll(struct file *filp, poll_table *wait) { unsigned int mask = 0; unsigned int ava_len = 0; /* btif_bbs_read(&(g_btif[0].btif_buf), rd_buf, sizeof(rd_buf)); */ unsigned int wr_idx = g_btif[0].btif_buf.wr_idx; /* BTIF_Rx_IRQ_Disable(); */ ava_len = BBS_COUNT_CUR(&(g_btif[0].btif_buf), wr_idx); BTIF_INFO_FUNC("++\n"); if (ava_len == 0) { poll_wait(filp, &btif_read_inq, wait); wr_idx = g_btif[0].btif_buf.wr_idx; ava_len = BBS_COUNT_CUR(&(g_btif[0].btif_buf), wr_idx); /* btif_bbs_read(&(g_btif[0].btif_buf), rd_buf, sizeof(rd_buf)); */ if (ava_len) mask |= POLLIN | POLLRDNORM; /* readable */ } else { mask |= POLLIN | POLLRDNORM; /* readable */ } /*make for writable*/ mask |= POLLOUT | POLLWRNORM; /* writable */ BTIF_INFO_FUNC("--, mask:%d\n", mask); return mask; } static int _btif_file_open(void) { int i_ret = -1; struct _mtk_btif_ *p_btif = &g_btif[0]; BTIF_INFO_FUNC("++\n"); /*Chaozhong: ToDo: to be implement*/ i_ret = btif_open(p_btif); if ((i_ret != 0) && (i_ret != E_BTIF_ALREADY_OPEN)) { BTIF_ERR_FUNC("btif_open failed, error code:%d\n", i_ret); } else { BTIF_INFO_FUNC("btif_open succeed\n"); i_ret = 0; } /*semaphore for read and write operation init*/ sema_init(&wr_mtx, 1); sema_init(&rd_mtx, 1); /*buffer for read and write init*/ memset(wr_buf, 0, sizeof(wr_buf)); memset(rd_buf, 0, sizeof(rd_buf)); init_waitqueue_head(&(btif_read_inq)); btif_rx_notify_reg(p_btif, btif_rx_notify_cb); BTIF_INFO_FUNC("--\n"); return i_ret; } static int _btif_file_close(void) { int i_ret = -1; BTIF_INFO_FUNC("++\n"); /*Chaozhong: ToDo: to be implement*/ i_ret = btif_close(&g_btif[0]); if (i_ret != 0) BTIF_ERR_FUNC("btif_close failed, error code:%d\n", i_ret); else BTIF_INFO_FUNC("btif_close succeed\n"); BTIF_INFO_FUNC("--\n"); return i_ret; } static int btif_file_open(struct inode *pinode, struct file *pfile) { int i_ret = -1; BTIF_INFO_FUNC("pid:%d\n", current->pid); i_ret = 0; return i_ret; } static int btif_file_release(struct inode *pinode, struct file *pfile) { int i_ret = -1; BTIF_INFO_FUNC("pid:%d\n", current->pid); i_ret = 0; return i_ret; } static ssize_t btif_file_read(struct file *pfile, char __user *buf, size_t count, loff_t *f_ops) { return 0; } ssize_t btif_file_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { return 0; } #ifdef CONFIG_COMPAT long btif_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { long ret; BTIF_INFO_FUNC("cmd[0x%x]\n", cmd); ret = btif_unlocked_ioctl(filp, cmd, arg); return ret; } #endif long btif_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { #define BTIF_IOC_MAGIC 0xb0 #define BTIF_IOCTL_OPEN _IOW(BTIF_IOC_MAGIC, 1, int) #define BTIF_IOCTL_CLOSE _IOW(BTIF_IOC_MAGIC, 2, int) #define BTIF_IOCTL_LPBK_CTRL _IOWR(BTIF_IOC_MAGIC, 3, int) #define BTIF_IOCTL_LOG_FUNC_CTRL _IOWR(BTIF_IOC_MAGIC, 4, int) #define BTIF_IOCTL_RT_LOG_CTRL _IOWR(BTIF_IOC_MAGIC, 5, int) #define BTIF_IOCTL_LOG_DUMP _IOWR(BTIF_IOC_MAGIC, 6, int) #define BTIF_IOCTL_REG_DUMP _IOWR(BTIF_IOC_MAGIC, 7, int) #define BTIF_IOCTL_DMA_CTRL _IOWR(BTIF_IOC_MAGIC, 8, int) long ret = 0; /* unsigned char p_buf[NAME_MAX + 1]; */ struct _mtk_btif_ *p_btif = &g_btif[0]; BTIF_INFO_FUNC("++\n"); BTIF_DBG_FUNC("cmd (%u), arg (0x%lx)\n", cmd, arg); switch (cmd) { case BTIF_IOCTL_OPEN: ret = _btif_file_open(); break; case BTIF_IOCTL_CLOSE: ret = _btif_file_close(); break; case BTIF_IOCTL_LPBK_CTRL: ret = btif_lpbk_ctrl(p_btif, arg == 0 ? 0 : 1); break; case BTIF_IOCTL_LOG_FUNC_CTRL: if (arg == 0) { ret += btif_log_buf_disable(&p_btif->tx_log); ret += btif_log_buf_disable(&p_btif->rx_log); } else { ret += btif_log_buf_enable(&p_btif->tx_log); ret += btif_log_buf_enable(&p_btif->rx_log); } break; case BTIF_IOCTL_RT_LOG_CTRL: if (arg == 0) { ret += btif_log_output_disable(&p_btif->tx_log); ret += btif_log_output_disable(&p_btif->rx_log); } else { ret += btif_log_output_enable(&p_btif->tx_log); ret += btif_log_output_enable(&p_btif->rx_log); } break; case BTIF_IOCTL_LOG_DUMP: ret += btif_log_buf_dmp_out(&p_btif->tx_log); ret += btif_log_buf_dmp_out(&p_btif->rx_log); break; case BTIF_IOCTL_REG_DUMP: ret += btif_dump_reg(p_btif, REG_ALL); break; case BTIF_IOCTL_DMA_CTRL: if (arg == 0) { ret += btif_tx_dma_mode_set(0); ret += btif_rx_dma_mode_set(0); } else { ret += btif_tx_dma_mode_set(1); ret += btif_rx_dma_mode_set(1); } break; default: BTIF_INFO_FUNC("unknown cmd(%d)\n", cmd); ret = -2; break; } BTIF_INFO_FUNC("--\n"); return ret; } #endif /*-----------device property----------------*/ static ssize_t driver_flag_read(struct device_driver *drv, char *buf) { return snprintf(buf, PAGE_SIZE, "dbg level:%d\n", mtk_btif_dbg_lvl); } static ssize_t driver_flag_set(struct device_driver *drv, const char *buffer, size_t count) { char buf[256]; char *p_buf = NULL; unsigned long len = count; long x = 0; long y = 0; long z = 0; int result = 0; char *p_token = NULL; char *p_delimiter = " \t"; #ifdef BTIF_FLAG_SET_ENABLE_ALL_FUNC #if BTIF_DBG_SUPPORT struct _mtk_btif_ *p_btif = &g_btif[0]; #endif #endif BTIF_INFO_FUNC("buffer = %s, count = %zu\n", buffer, count); if (len >= sizeof(buf)) { BTIF_ERR_FUNC("input handling fail!\n"); len = sizeof(buf) - 1; return -1; } strncpy(buf, buffer, len); buf[len] = '\0'; p_buf = buf; p_token = strsep(&p_buf, p_delimiter); if (p_token != NULL) { result = kstrtol(p_token, 16, &x); BTIF_INFO_FUNC("x = 0x%08lx\n\r", x); } else x = 0; /* x = (NULL != p_token) ? kstrtol(p_token, 16, NULL) : 0;*/ p_token = strsep(&p_buf, "\t\n "); if (p_token != NULL) { result = kstrtol(p_token, 16, &y); BTIF_INFO_FUNC("y = 0x%08lx\n\r", y); } else y = 0; p_token = strsep(&p_buf, "\t\n "); if (p_token != NULL) result = kstrtol(p_token, 16, &z); else z = 0; BTIF_INFO_FUNC("x(0x%08lx), y(0x%08lx), z(0x%08lx)\n\r", x, y, z); switch (x) { #ifdef BTIF_FLAG_SET_ENABLE_ALL_FUNC case 1: mtk_btif_exp_open_test(); break; case 2: mtk_btif_exp_close_test(); break; case 3: mtk_btif_exp_write_test(); break; case 4: mtk_btif_exp_enter_dpidle_test(); break; case 5: mtk_btif_exp_exit_dpidle_test(); break; case 6: mtk_btif_exp_suspend_test(); break; case 7: mtk_btif_exp_resume_test(); break; #endif case 8: if (y > BTIF_LOG_LOUD) mtk_btif_dbg_lvl = BTIF_LOG_LOUD; else if (y < BTIF_LOG_ERR) mtk_btif_dbg_lvl = BTIF_LOG_WARN; else mtk_btif_dbg_lvl = y; BTIF_ERR_FUNC("mtk_btif_dbg_lvl set to %d\n", mtk_btif_dbg_lvl); break; #ifdef BTIF_FLAG_SET_ENABLE_ALL_FUNC case 9: mtk_btif_exp_open_test(); mtk_btif_exp_write_test(); mtk_btif_exp_close_test(); break; case 0xa: mtk_btif_exp_log_debug_test(y); break; case 0xb: btif_tx_dma_mode_set(1); btif_rx_dma_mode_set(1); break; case 0xc: btif_tx_dma_mode_set(0); btif_rx_dma_mode_set(0); break; case 0xd: mtk_btif_exp_restore_noirq_test(); break; case 0xe: btif_wakeup_consys_no_id(); break; case 0xf: mtk_btif_exp_clock_ctrl(y); break; case 0x10: y = y > G_MAX_PKG_LEN ? G_MAX_PKG_LEN : y; y = y < 1024 ? 1024 : y; BTIF_INFO_FUNC("g_max_pkg_len is set to %ld\n", y); g_max_pkg_len = y; break; case 0x11: y = y > BTIF_RX_BUFFER_SIZE ? BTIF_RX_BUFFER_SIZE : y; y = y < 1024 ? 1024 : y; BTIF_INFO_FUNC("g_max_pding_data_size is set to %ld\n", y); g_max_pding_data_size = y; break; #if BTIF_DBG_SUPPORT case 0x12: BTIF_INFO_FUNC("test btif_rxd thread block\n"); p_btif->test_case = BTIF_TEST_RX_THREAD_BLOCK; p_btif->delay_sched_time = y; g_enable_btif_rxd_test = 1; break; case 0x13: BTIF_INFO_FUNC("test rx dma irq block\n"); p_btif->test_case = BTIF_TEST_RX_IRQ_BLOCK; p_btif->delay_sched_time = y; btif_block_rx_dma_irq_test(); break; #endif #endif default: #ifdef BTIF_FLAG_SET_ENABLE_ALL_FUNC mtk_btif_exp_open_test(); mtk_btif_exp_write_stress_test(3030, 1); mtk_btif_exp_close_test(); #endif BTIF_WARN_FUNC("not supported.\n"); break; } return count; } static DRIVER_ATTR(flag, 0644, driver_flag_read, driver_flag_set); /*-----------End of platform bus related operation APIs------------*/ /*-----------------------platform driver ----------------*/ int _btif_irq_reg(struct _MTK_BTIF_IRQ_STR_ *p_irq, irq_handler_t irq_handler, void *data) { int i_ret = -1; unsigned int irq_id; unsigned int flag; if ((p_irq == NULL) || (irq_handler == NULL)) return E_BTIF_INVAL_PARAM; if (!(p_irq->is_irq_sup)) { BTIF_WARN_FUNC("%s is not supported\n", p_irq->name); return 0; } irq_id = p_irq->irq_id; #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 /*make this as default type */ 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 /*make this as default type */ flag = IRQF_TRIGGER_LOW; break; default: /*make this as default type */ flag = IRQF_TRIGGER_LOW; break; } #endif p_irq->p_irq_handler = irq_handler; i_ret = request_irq(irq_id, (irq_handler_t) irq_handler, flag, p_irq->name, data); if (i_ret) return i_ret; p_irq->reg_flag = true; return 0; } int _btif_irq_free(struct _MTK_BTIF_IRQ_STR_ *p_irq, void *data) { int i_ret = 0; unsigned int eint_num = p_irq->irq_id; if ((p_irq->is_irq_sup) && (p_irq->reg_flag)) { _btif_irq_ctrl(p_irq, false); free_irq(eint_num, data); p_irq->reg_flag = false; } /*do nothing for this operation*/ return i_ret; } int _btif_irq_ctrl(struct _MTK_BTIF_IRQ_STR_ *p_irq, bool en) { unsigned int eint_num = p_irq->irq_id; if (en) enable_irq(eint_num); else disable_irq_nosync(eint_num); return 0; } int _btif_irq_ctrl_sync(struct _MTK_BTIF_IRQ_STR_ *p_irq, bool en) { unsigned int eint_num = p_irq->irq_id; if (en) enable_irq(eint_num); else disable_irq(eint_num); return 0; } irqreturn_t btif_irq_handler(int irq, void *data) { /*search BTIF? just use index 0*/ /*Chaozhong: do we need lock here?*/ struct _mtk_btif_ *p_btif = (struct _mtk_btif_ *) data; BTIF_DBG_FUNC("++, p_btif(0x%p)\n", data); _btif_irq_ctrl(p_btif->p_btif_info->p_irq, false); #if MTK_BTIF_ENABLE_CLK_REF_COUNTER hal_btif_clk_ctrl(p_btif->p_btif_info, CLK_OUT_ENABLE); #endif hal_btif_irq_handler(p_btif->p_btif_info, NULL, 0); #if MTK_BTIF_ENABLE_CLK_REF_COUNTER hal_btif_clk_ctrl(p_btif->p_btif_info, CLK_OUT_DISABLE); #endif _btif_irq_ctrl(p_btif->p_btif_info->p_irq, true); _btif_rx_btm_sched(p_btif); BTIF_DBG_FUNC("--\n"); return IRQ_HANDLED; } irqreturn_t btif_tx_dma_irq_handler(int irq, void *data) { /*search BTIF? just use index 0*/ struct _mtk_btif_ *p_btif = (struct _mtk_btif_ *) data; struct _mtk_btif_dma_ *p_tx_dma = p_btif->p_tx_dma; struct _MTK_DMA_INFO_STR_ *p_dma_info = p_tx_dma->p_dma_info; BTIF_DBG_FUNC("++, p_btif(0x%p)\n", data); _btif_irq_ctrl(p_dma_info->p_irq, false); #if MTK_BTIF_ENABLE_CLK_REF_COUNTER hal_btif_dma_clk_ctrl(p_dma_info, CLK_OUT_ENABLE); #endif hal_tx_dma_irq_handler(p_dma_info); #if MTK_BTIF_ENABLE_CLK_REF_COUNTER hal_btif_dma_clk_ctrl(p_dma_info, CLK_OUT_DISABLE); #endif _btif_irq_ctrl(p_dma_info->p_irq, true); BTIF_DBG_FUNC("--\n"); return IRQ_HANDLED; } irqreturn_t btif_rx_dma_irq_handler(int irq, void *data) { /*search BTIF? just use index 0*/ struct _mtk_btif_ *p_btif = (struct _mtk_btif_ *) data; struct _mtk_btif_dma_ *p_rx_dma = p_btif->p_rx_dma; struct _MTK_DMA_INFO_STR_ *p_rx_dma_info = p_rx_dma->p_dma_info; BTIF_DBG_FUNC("++, p_btif(0x%p)\n", data); _btif_irq_ctrl(p_rx_dma_info->p_irq, false); #if MTK_BTIF_ENABLE_CLK_REF_COUNTER hal_btif_clk_ctrl(p_btif->p_btif_info, CLK_OUT_ENABLE); hal_btif_dma_clk_ctrl(p_rx_dma_info, CLK_OUT_ENABLE); #endif hal_rx_dma_irq_handler(p_rx_dma_info, NULL, 0); #if MTK_BTIF_ENABLE_CLK_REF_COUNTER hal_btif_dma_clk_ctrl(p_rx_dma_info, CLK_OUT_DISABLE); hal_btif_clk_ctrl(p_btif->p_btif_info, CLK_OUT_DISABLE); #endif _btif_irq_ctrl(p_rx_dma_info->p_irq, true); #ifdef BTIF_FLAG_SET_ENABLE_ALL_FUNC #if BTIF_DBG_SUPPORT if (g_enable_btif_rxd_test) schedule_delayed_work(&p_btif->btif_rx_test_work, msecs_to_jiffies(p_btif->delay_sched_time)); else #endif #endif _btif_rx_btm_sched(p_btif); BTIF_DBG_FUNC("--\n"); return IRQ_HANDLED; } unsigned int btif_dma_rx_data_receiver(struct _MTK_DMA_INFO_STR_ *p_dma_info, unsigned char *p_buf, unsigned int buf_len) { unsigned int index = 0; struct _mtk_btif_ *p_btif = &(g_btif[index]); btif_bbs_write(&(p_btif->btif_buf), p_buf, buf_len); /*save DMA Rx packet here*/ if (buf_len > 0) btif_log_buf_dmp_in(&p_btif->rx_log, p_buf, buf_len); return 0; } unsigned int btif_pio_rx_data_receiver(struct _MTK_BTIF_INFO_STR_ *p_btif_info, unsigned char *p_buf, unsigned int buf_len) { unsigned int index = 0; struct _mtk_btif_ *p_btif = &(g_btif[index]); btif_bbs_write(&(p_btif->btif_buf), p_buf, buf_len); /*save PIO Rx packet here*/ if (buf_len > 0) btif_log_buf_dmp_in(&p_btif->rx_log, p_buf, buf_len); return 0; } bool btif_parser_wmt_evt(struct _mtk_btif_ *p_btif, const char *sub_str, unsigned int str_len) { unsigned int data_cnt = 0; unsigned int copy_cnt = 0; char *local_buf = NULL; bool b_ret = false; struct _btif_buf_str_ *p_bbs = &(p_btif->btif_buf); unsigned int wr_idx = p_bbs->wr_idx; unsigned int rd_idx = p_bbs->rd_idx; data_cnt = copy_cnt = BBS_COUNT(p_bbs); if (data_cnt < str_len) { BTIF_WARN_FUNC("not enough data for parser,need(%d),have(%d)\n", str_len, data_cnt); return false; } BTIF_INFO_FUNC("data count in bbs buffer:%d,wr_idx(%d),rd_idx(%d)\n", data_cnt, wr_idx, rd_idx); local_buf = vmalloc((data_cnt + 3) & ~0x3UL); if (!local_buf) { BTIF_WARN_FUNC("vmalloc memory fail\n"); return false; } if (wr_idx >= rd_idx) { memcpy(local_buf, BBS_PTR(p_bbs, rd_idx), copy_cnt); } else { unsigned int tail_len = BBS_SIZE(p_bbs) - rd_idx; BTIF_INFO_FUNC("tail_Len(%d)\n", tail_len); memcpy(local_buf, BBS_PTR(p_bbs, rd_idx), tail_len); memcpy(local_buf + tail_len, BBS_PTR(p_bbs, 0), copy_cnt - tail_len); } do { int i = 0; int j = 0; int k = 0; int d = 0; BTIF_INFO_FUNC("sub_str_len:%d\n", str_len); for (i = 0; i < copy_cnt; i++) { BTIF_DBG_FUNC("i:%d\n", i); k = i; while (1) { if ((j >= str_len) || (k >= copy_cnt) || (sub_str[j++] != local_buf[k++])) break; } if (j == str_len) { for (d = i; d < (str_len + i); d++) BTIF_INFO_FUNC("0x%2x", local_buf[d]); BTIF_INFO_FUNC("find sub str index:%d\n", i); b_ret = true; break; } if (j < str_len) j = 0; } } while (0); vfree(local_buf); return b_ret; } int _btif_controller_tx_setup(struct _mtk_btif_ *p_btif) { int i_ret = -1; if (p_btif->tx_mode == BTIF_MODE_DMA) { i_ret = _btif_tx_dma_setup(p_btif); if (i_ret) { BTIF_ERR_FUNC( "_btif_tx_dma_setup failed,i_ret(%d), set tx to PIO mode\n", i_ret); i_ret = _btif_tx_pio_setup(p_btif); } } else /*enable Tx PIO mode*/ i_ret = _btif_tx_pio_setup(p_btif); return i_ret; } int _btif_controller_tx_free(struct _mtk_btif_ *p_btif) { int i_ret = -1; if (p_btif->tx_mode == BTIF_MODE_DMA) { i_ret = _btif_tx_dma_free(p_btif); if (i_ret) { BTIF_ERR_FUNC("_btif_tx_dma_free failed, i_ret(%d)\n", i_ret); } } else { /*do nothing for Tx PIO mode*/ } return i_ret; } int _btif_controller_rx_setup(struct _mtk_btif_ *p_btif) { int i_ret = -1; if (p_btif->rx_mode == BTIF_MODE_DMA) { i_ret = _btif_rx_dma_setup(p_btif); if (i_ret) { BTIF_ERR_FUNC( "_btif_tx_dma_setup failed, i_ret(%d), set tx to PIO mode\n", i_ret); i_ret = _btif_rx_pio_setup(p_btif); } } else { /*enable Tx PIO mode*/ i_ret = _btif_rx_pio_setup(p_btif); } return i_ret; } int _btif_controller_rx_free(struct _mtk_btif_ *p_btif) { int i_ret = -1; if (p_btif->rx_mode == BTIF_MODE_DMA) { i_ret = _btif_rx_dma_free(p_btif); if (i_ret) { BTIF_ERR_FUNC("_btif_rx_dma_free failed, i_ret(%d)\n", i_ret); } } else { /*do nothing for Rx PIO mode*/ } return i_ret; } int _btif_tx_pio_setup(struct _mtk_btif_ *p_btif) { int i_ret = -1; struct _MTK_BTIF_INFO_STR_ *p_btif_info = p_btif->p_btif_info; /*set Tx to PIO mode*/ p_btif->tx_mode = BTIF_MODE_PIO; /*enable Tx PIO mode*/ i_ret = hal_btif_tx_mode_ctrl(p_btif_info, BTIF_MODE_PIO); return i_ret; } int _btif_rx_pio_setup(struct _mtk_btif_ *p_btif) { int i_ret = -1; struct _MTK_BTIF_INFO_STR_ *p_btif_info = p_btif->p_btif_info; struct _MTK_BTIF_IRQ_STR_ *p_btif_irq = p_btif_info->p_irq; p_btif->rx_mode = BTIF_MODE_PIO; /*Enable Rx IRQ*/ _btif_irq_ctrl(p_btif_irq, true); /*enable Rx PIO mode*/ i_ret = hal_btif_rx_mode_ctrl(p_btif_info, BTIF_MODE_PIO); return i_ret; } int _btif_rx_dma_setup(struct _mtk_btif_ *p_btif) { int i_ret = -1; struct _MTK_BTIF_INFO_STR_ *p_btif_info = NULL; struct _MTK_BTIF_IRQ_STR_ *p_btif_irq = NULL; struct _MTK_DMA_INFO_STR_ *p_dma_info = p_btif->p_rx_dma->p_dma_info; p_btif_info = p_btif->p_btif_info; p_btif_irq = p_dma_info->p_irq; /*vFIFO reset*/ hal_btif_vfifo_reset(p_dma_info); i_ret = hal_btif_dma_clk_ctrl(p_dma_info, CLK_OUT_ENABLE); if (i_ret) { BTIF_ERR_FUNC( "hal_btif_dma_clk_ctrl failed, i_ret(%d), set rx to pio mode\n", i_ret); /*DMA control failed set Rx to PIO mode*/ return _btif_rx_pio_setup(p_btif); } /*hardware init*/ hal_btif_dma_hw_init(p_dma_info); hal_btif_dma_rx_cb_reg(p_dma_info, (dma_rx_buf_write) btif_dma_rx_data_receiver); /*DMA controller enable*/ i_ret = hal_btif_dma_ctrl(p_dma_info, DMA_CTRL_ENABLE); if (i_ret) { BTIF_ERR_FUNC( "hal_btif_dma_ctrl failed, i_ret(%d), set rx to pio mode\n", i_ret); hal_btif_dma_clk_ctrl(p_dma_info, CLK_OUT_DISABLE); /*DMA control failed set Rx to PIO mode*/ i_ret = _btif_rx_pio_setup(p_btif); } else { /*enable Rx DMA mode*/ hal_btif_rx_mode_ctrl(p_btif_info, BTIF_MODE_DMA); /*DMA Rx IRQ register*/ i_ret = _btif_irq_reg(p_btif_irq, btif_rx_dma_irq_handler, p_btif); if (i_ret) BTIF_ERR_FUNC("_btif_irq_reg failed, i_ret(%d)\n", i_ret); else BTIF_DBG_FUNC("succeed\n"); } return i_ret; } int _btif_rx_dma_free(struct _mtk_btif_ *p_btif) { struct _MTK_DMA_INFO_STR_ *p_dma_info = p_btif->p_rx_dma->p_dma_info; struct _MTK_BTIF_IRQ_STR_ *p_irq = p_btif->p_rx_dma->p_dma_info->p_irq; hal_btif_dma_rx_cb_reg(p_dma_info, (dma_rx_buf_write) NULL); _btif_irq_free(p_irq, p_btif); /*disable BTIF Rx DMA channel*/ hal_btif_dma_ctrl(p_dma_info, DMA_CTRL_DISABLE); /*disable clock output*/ return hal_btif_dma_clk_ctrl(p_dma_info, CLK_OUT_DISABLE); } int _btif_tx_dma_setup(struct _mtk_btif_ *p_btif) { int i_ret = -1; struct _MTK_BTIF_INFO_STR_ *p_btif_info = p_btif->p_btif_info; struct _MTK_DMA_INFO_STR_ *p_dma_info = p_btif->p_tx_dma->p_dma_info; struct _MTK_BTIF_IRQ_STR_ *p_btif_irq = p_dma_info->p_irq; /*vFIFO reset*/ hal_btif_vfifo_reset(p_dma_info); i_ret = hal_btif_dma_clk_ctrl(p_dma_info, CLK_OUT_ENABLE); if (i_ret) { BTIF_ERR_FUNC("hal_btif_dma_clk_ctrl failed, i_ret(%d)\n", i_ret); return i_ret; } /*DMA controller setup*/ hal_btif_dma_hw_init(p_dma_info); /*DMA HW Enable*/ i_ret = hal_btif_dma_ctrl(p_dma_info, DMA_CTRL_ENABLE); if (i_ret) { BTIF_ERR_FUNC( "hal_btif_dma_ctrl failed, i_ret(%d), set tx to pio mode\n", i_ret); #if !(MTK_BTIF_ENABLE_CLK_REF_COUNTER) hal_btif_dma_clk_ctrl(p_dma_info, CLK_OUT_DISABLE); #endif _btif_tx_pio_setup(p_btif); } else { hal_btif_tx_mode_ctrl(p_btif_info, BTIF_MODE_DMA); /*DMA Tx IRQ register*/ i_ret = _btif_irq_reg(p_btif_irq, btif_tx_dma_irq_handler, p_btif); if (i_ret) BTIF_ERR_FUNC("_btif_irq_reg failed, i_ret(%d)\n", i_ret); else BTIF_DBG_FUNC("succeed\n"); } return i_ret; } int _btif_tx_dma_free(struct _mtk_btif_ *p_btif) { struct _MTK_DMA_INFO_STR_ *p_dma_info = p_btif->p_tx_dma->p_dma_info; struct _MTK_BTIF_IRQ_STR_ *p_irq = p_btif->p_tx_dma->p_dma_info->p_irq; _btif_irq_free(p_irq, p_btif); /*disable BTIF Tx DMA channel*/ hal_btif_dma_ctrl(p_dma_info, DMA_CTRL_DISABLE); /*disable clock output*/ return hal_btif_dma_clk_ctrl(p_dma_info, CLK_OUT_DISABLE); } int btif_lpbk_ctrl(struct _mtk_btif_ *p_btif, bool flag) { int i_ret = -1; /*hold state mechine lock*/ if (_btif_state_hold(p_btif)) return E_BTIF_INTR; i_ret = _btif_exit_dpidle(p_btif); if (i_ret == 0) i_ret = _btif_lpbk_ctrl(p_btif, flag); else i_ret = E_BTIF_INVAL_STATE; BTIF_STATE_RELEASE(p_btif); return i_ret; } int _btif_lpbk_ctrl(struct _mtk_btif_ *p_btif, bool flag) { int i_ret = -1; if (flag) { i_ret = hal_btif_loopback_ctrl(p_btif->p_btif_info, true); BTIF_DBG_FUNC("loopback function enabled\n"); } else { i_ret = hal_btif_loopback_ctrl(p_btif->p_btif_info, false); BTIF_DBG_FUNC("loopback function disabled\n"); } if (i_ret == 0) p_btif->lpbk_flag = flag; return i_ret; } int btif_clock_ctrl(struct _mtk_btif_ *p_btif, int en) { int i_ret = 0; struct _MTK_BTIF_INFO_STR_ *p_btif_info = p_btif->p_btif_info; enum _ENUM_CLOCK_CTRL_ ctrl_flag = en == 0 ? CLK_OUT_DISABLE : CLK_OUT_ENABLE; i_ret = hal_btif_clk_ctrl(p_btif_info, ctrl_flag); if (p_btif->rx_mode == BTIF_MODE_DMA) i_ret += hal_btif_dma_clk_ctrl(p_btif->p_rx_dma->p_dma_info, ctrl_flag); if (p_btif->tx_mode == BTIF_MODE_DMA) i_ret += hal_btif_dma_clk_ctrl(p_btif->p_tx_dma->p_dma_info, ctrl_flag); return i_ret; } int _btif_controller_setup(struct _mtk_btif_ *p_btif) { int i_ret = -1; struct _MTK_BTIF_INFO_STR_ *p_btif_info = p_btif->p_btif_info; struct _MTK_BTIF_IRQ_STR_ *p_btif_irq = p_btif_info->p_irq; /*BTIF rx buffer init*/ /* memset(p_btif->rx_buf, 0, BTIF_RX_BUFFER_SIZE); */ BBS_INIT(&(p_btif->btif_buf)); /************************************************/ hal_btif_rx_cb_reg(p_btif_info, (btif_rx_buf_write) btif_pio_rx_data_receiver); i_ret = hal_btif_clk_ctrl(p_btif_info, CLK_OUT_ENABLE); if (i_ret) { BTIF_ERR_FUNC("hal_btif_clk_ctrl failed, i_ret(%d)\n", i_ret); return i_ret; } /*BTIF controller init*/ i_ret = hal_btif_hw_init(p_btif_info); if (i_ret) { hal_btif_clk_ctrl(p_btif_info, CLK_OUT_DISABLE); BTIF_ERR_FUNC("hal_btif_hw_init failed, i_ret(%d)\n", i_ret); return i_ret; } _btif_lpbk_ctrl(p_btif, p_btif->lpbk_flag); /*BTIF IRQ register*/ i_ret = _btif_irq_reg(p_btif_irq, btif_irq_handler, p_btif); if (i_ret) { hal_btif_clk_ctrl(p_btif_info, CLK_OUT_DISABLE); BTIF_ERR_FUNC("_btif_irq_reg failed, i_ret(%d)\n", i_ret); return i_ret; } /*disable IRQ*/ _btif_irq_ctrl(p_btif_irq, false); i_ret = 0; BTIF_DBG_FUNC("succeed\n"); return i_ret; } int _btif_controller_free(struct _mtk_btif_ *p_btif) { /*No need to set BTIF to PIO mode, only enable BTIF CG*/ hal_btif_rx_cb_reg(p_btif->p_btif_info, (btif_rx_buf_write) NULL); _btif_irq_free(p_btif->p_btif_info->p_irq, p_btif); return hal_btif_clk_ctrl(p_btif->p_btif_info, CLK_OUT_DISABLE); } int _btif_init(struct _mtk_btif_ *p_btif) { int i_ret = 0; i_ret = _btif_controller_setup(p_btif); if (i_ret) { BTIF_ERR_FUNC("_btif_controller_init failed, i_ret(%d)\n", i_ret); _btif_dpidle_notify_ctrl(p_btif, BTIF_DPIDLE_ENABLE); BTIF_STATE_RELEASE(p_btif); return i_ret; } i_ret = _btif_controller_tx_setup(p_btif); if (i_ret) { BTIF_ERR_FUNC("_btif_controller_tx_setup failed, i_ret(%d)\n", i_ret); _btif_controller_free(p_btif); _btif_dpidle_notify_ctrl(p_btif, BTIF_DPIDLE_ENABLE); BTIF_STATE_RELEASE(p_btif); return i_ret; } i_ret = _btif_controller_rx_setup(p_btif); if (i_ret) { BTIF_ERR_FUNC("_btif_controller_tx_setup failed, i_ret(%d)\n", i_ret); _btif_controller_tx_free(p_btif); _btif_controller_free(p_btif); _btif_dpidle_notify_ctrl(p_btif, BTIF_DPIDLE_ENABLE); BTIF_STATE_RELEASE(p_btif); return i_ret; } return i_ret; } int btif_open(struct _mtk_btif_ *p_btif) { int i_ret = -1; if (p_btif->enable) return E_BTIF_ALREADY_OPEN; /*hold state mechine lock*/ if (_btif_state_hold(p_btif)) return E_BTIF_INTR; /*disable deepidle*/ _btif_dpidle_notify_ctrl(p_btif, BTIF_DPIDLE_DISABLE); i_ret = _btif_init(p_btif); if (i_ret == 0) { /*set BTIF's enable flag*/ p_btif->enable = true; _btif_state_set(p_btif, B_S_ON); } else { _btif_dpidle_notify_ctrl(p_btif, BTIF_DPIDLE_ENABLE); } btif_log_buf_reset(&p_btif->tx_log); btif_log_buf_reset(&p_btif->rx_log); BTIF_STATE_RELEASE(p_btif); BTIF_DBG_FUNC("BTIF's Tx Mode:%d, Rx Mode(%d)\n", p_btif->tx_mode, p_btif->rx_mode); return i_ret; } int btif_close(struct _mtk_btif_ *p_btif) { int i_ret = 0; if (!(p_btif->enable)) return E_BTIF_NOT_OPEN; /*hold state mechine lock*/ if (_btif_state_hold(p_btif)) return E_BTIF_INTR; /*always set state back to B_S_ON before do close operation*/ _btif_exit_dpidle(p_btif); /*set BTIF's state to disable state*/ p_btif->enable = false; _btif_controller_free(p_btif); _btif_controller_tx_free(p_btif); _btif_controller_rx_free(p_btif); /*reset BTIF's rx_cb function*/ p_btif->rx_cb = NULL; p_btif->rx_notify = NULL; p_btif->lpbk_flag = false; /*set state mechine to B_S_OFF*/ _btif_state_set(p_btif, B_S_OFF); btif_log_buf_disable(&p_btif->tx_log); btif_log_buf_disable(&p_btif->rx_log); BTIF_STATE_RELEASE(p_btif); return i_ret; } int _btif_exit_dpidle(struct _mtk_btif_ *p_btif) { int i_ret = -1; enum _ENUM_BTIF_STATE_ state = B_S_MAX; state = _btif_state_get(p_btif); switch (state) { case B_S_DPIDLE: i_ret = _btif_exit_dpidle_from_dpidle(p_btif); break; case B_S_SUSPEND: /*in suspend state, need to do reinit of btif*/ i_ret = _btif_exit_dpidle_from_sus(p_btif); break; case B_S_OFF: i_ret = _btif_init(p_btif); break; case B_S_ON: i_ret = 0; /* for btif_close case */ break; default: i_ret = E_BTIF_INVAL_PARAM; BTIF_INFO_FUNC("invalid state change:%d->%d\n", state, B_S_ON); break; } if (i_ret == 0) i_ret = _btif_state_set(p_btif, B_S_ON); return i_ret; } int btif_exit_dpidle(struct _mtk_btif_ *p_btif) { int i_ret = 0; /*hold state mechine lock*/ if (_btif_state_hold(p_btif)) return E_BTIF_INTR; i_ret = _btif_exit_dpidle(p_btif); BTIF_STATE_RELEASE(p_btif); return i_ret; } int _btif_enter_dpidle(struct _mtk_btif_ *p_btif) { int i_ret = 0; enum _ENUM_BTIF_STATE_ state = B_S_MAX; state = _btif_state_get(p_btif); if (state == B_S_ON) { i_ret = _btif_enter_dpidle_from_on(p_btif); } else if (state == B_S_SUSPEND) { /*do reinit and enter deepidle*/ i_ret = _btif_enter_dpidle_from_sus(p_btif); } else if (state == B_S_DPIDLE) { /*do nothing*/ i_ret = 0; } else { BTIF_WARN_FUNC("operation is not allowed, current state:%d\n", state); i_ret = E_BTIF_INVAL_STATE; } /*anyway, set to B_S_DPIDLE state*/ if (i_ret == 0) i_ret = _btif_state_set(p_btif, B_S_DPIDLE); return i_ret; } int btif_enter_dpidle(struct _mtk_btif_ *p_btif) { int i_ret = 0; /*hold state mechine lock*/ if (_btif_state_hold(p_btif)) return E_BTIF_INTR; i_ret = _btif_enter_dpidle(p_btif); BTIF_STATE_RELEASE(p_btif); return i_ret; } int _btif_exit_dpidle_from_dpidle(struct _mtk_btif_ *p_btif) { int i_ret = 0; /*in dpidle state, only need to open related clock*/ if (p_btif->tx_mode == BTIF_MODE_DMA) { /*enable BTIF Tx DMA's clock*/ i_ret += hal_btif_dma_clk_ctrl(p_btif->p_tx_dma->p_dma_info, CLK_OUT_ENABLE); } if (p_btif->rx_mode == BTIF_MODE_DMA) { /*enable BTIF Rx DMA's clock*/ i_ret += hal_btif_dma_clk_ctrl(p_btif->p_rx_dma->p_dma_info, CLK_OUT_ENABLE); } /*enable BTIF's clock*/ i_ret += hal_btif_clk_ctrl(p_btif->p_btif_info, CLK_OUT_ENABLE); if (i_ret != 0) BTIF_WARN_FUNC("failed, i_ret:%d\n", i_ret); return i_ret; } int _btif_exit_dpidle_from_sus(struct _mtk_btif_ *p_btif) { /*in suspend state, need to do driver re-init*/ int i_ret = _btif_init(p_btif); return i_ret; } int _btif_enter_dpidle_from_sus(struct _mtk_btif_ *p_btif) { /*do driiver reinit*/ int i_ret = _btif_init(p_btif); if (i_ret == 0) i_ret = _btif_enter_dpidle_from_on(p_btif); return i_ret; } int _btif_enter_dpidle_from_on(struct _mtk_btif_ *p_btif) { #define MAX_WAIT_TIME_MS 5000 /* * this max wait time cannot exceed 12s, * because dpm will monitor each device's * resume/suspend process by start up a watch dog timer of 12s * incase of one driver's suspend/resume process block other device's * suspend/resume */ int i_ret = 0; unsigned int retry = 0; unsigned int wait_period = 1; unsigned int max_retry = MAX_WAIT_TIME_MS / wait_period; struct timespec64 timer_start; struct timespec64 timer_now; btif_do_gettimeofday(&timer_start); while ((!btif_is_tx_complete(p_btif)) && (retry < max_retry)) { btif_do_gettimeofday(&timer_now); if ((MAX_WAIT_TIME_MS/1000) <= (timer_now.tv_sec - timer_start.tv_sec)) { BTIF_WARN_FUNC("expired start:%ld,now:%ld,retry:%d\n", timer_start.tv_sec, timer_now.tv_sec, retry); break; } msleep(wait_period); retry++; } if (retry < max_retry) { if (p_btif->tx_mode == BTIF_MODE_DMA) { /*disable BTIF Tx DMA's clock*/ i_ret += hal_btif_dma_clk_ctrl(p_btif->p_tx_dma->p_dma_info, CLK_OUT_DISABLE); } if (p_btif->rx_mode == BTIF_MODE_DMA) { /*disable BTIF Rx DMA's clock*/ i_ret += hal_btif_dma_clk_ctrl(p_btif->p_rx_dma->p_dma_info, CLK_OUT_DISABLE); } /*disable BTIF's clock*/ i_ret += hal_btif_clk_ctrl(p_btif->p_btif_info, CLK_OUT_DISABLE); if (i_ret) BTIF_WARN_FUNC("failed, i_ret:%d\n", i_ret); } else i_ret = -1; return i_ret; } int _btif_dpidle_notify_ctrl(struct _mtk_btif_ *p_btif, enum _ENUM_BTIF_DPIDLE_ en_flag) { /*call WCP's API to control deepidle's enable/disable*/ if (en_flag == BTIF_DPIDLE_DISABLE) hal_btif_pm_ops(p_btif->p_btif_info, BTIF_PM_DPIDLE_DIS); else hal_btif_pm_ops(p_btif->p_btif_info, BTIF_PM_DPIDLE_EN); return 0; } int btif_rx_cb_reg(struct _mtk_btif_ *p_btif, MTK_WCN_BTIF_RX_CB rx_cb) { if (p_btif->rx_cb) { BTIF_WARN_FUNC ("rx cb already exist, rewrite from (0x%p) to (0x%p)\n", p_btif->rx_cb, rx_cb); } p_btif->rx_cb = rx_cb; return 0; } int btif_raise_wak_signal(struct _mtk_btif_ *p_btif) { int i_ret = 0; struct _MTK_BTIF_INFO_STR_ *p_btif_info = p_btif->p_btif_info; #if MTK_BTIF_ENABLE_CLK_REF_COUNTER hal_btif_clk_ctrl(p_btif->p_btif_info, CLK_OUT_ENABLE); #endif i_ret = hal_btif_raise_wak_sig(p_btif_info); #if MTK_BTIF_ENABLE_CLK_REF_COUNTER hal_btif_clk_ctrl(p_btif_info, CLK_OUT_DISABLE); #endif return i_ret; } bool btif_is_tx_complete(struct _mtk_btif_ *p_btif) { bool b_ret = false; enum _ENUM_BTIF_MODE_ tx_mode = p_btif->tx_mode; /* * make sure BTIF tx finished in PIO mode * make sure BTIF tx finished and DMA tx finished in DMA mode */ if (tx_mode == BTIF_MODE_DMA) { b_ret = hal_dma_is_tx_complete(p_btif->p_tx_dma->p_dma_info); if (b_ret == false) { BTIF_DBG_FUNC("Tx DMA is not finished\n"); return b_ret; } } b_ret = hal_btif_is_tx_complete(p_btif->p_btif_info); if (b_ret == false) { BTIF_DBG_FUNC("BTIF Tx is not finished\n"); return b_ret; } b_ret = true; return b_ret; } /*--------------------------------Functions-----------------------------------*/ #if ENABLE_BTIF_TX_DMA static int _btif_vfifo_init(struct _mtk_btif_dma_ *p_dma) { struct _DMA_VFIFO_ *p_vfifo = NULL; struct device *dev = NULL; struct _mtk_btif_ *p_btif = NULL; if (p_dma == NULL) { BTIF_ERR_FUNC("p_dma is NULL\n"); return E_BTIF_INVAL_PARAM; } p_btif = (struct _mtk_btif_ *)p_dma->p_btif; if (p_btif == NULL) { BTIF_ERR_FUNC("invalid parameter: p_btif(0x%p)\n", p_btif); return E_BTIF_INVAL_PARAM; } dev = (struct device *)p_btif->private_data; if (dev == NULL) BTIF_WARN_FUNC("Null dev pointer!!!!\n"); p_vfifo = p_dma->p_dma_info->p_vfifo; if (p_vfifo->p_vir_addr != NULL) { BTIF_ERR_FUNC ("BTIF vFIFO memory already allocated, do nothing\n"); return E_BTIF_BAD_POINTER; } /*vFIFO memory allocation*/ p_vfifo->p_vir_addr = dma_zalloc_coherent(dev, p_vfifo->vfifo_size, &p_vfifo->phy_addr, GFP_DMA | GFP_DMA32); if (p_vfifo->p_vir_addr == NULL) { BTIF_ERR_FUNC("alloc vFIFO memory for BTIF failed\n"); return E_BTIF_FAIL; } if (sizeof(dma_addr_t) == sizeof(unsigned long long)) BTIF_INFO_FUNC("alloc vFIFO succeed in arch64,vir addr:0x%p," "phy addr:0x%llx\n", p_vfifo->p_vir_addr, p_vfifo->phy_addr); else BTIF_INFO_FUNC("alloc vFIFO succeed in arch32,vir addr:0x%p," "phy addr:0x%08x\n", p_vfifo->p_vir_addr, p_vfifo->phy_addr); return 0; } static int _btif_vfifo_deinit(struct _mtk_btif_dma_ *p_dma) { struct _DMA_VFIFO_ *p_vfifo = NULL; struct device *dev = NULL; struct _mtk_btif_ *p_btif = NULL; if (p_dma == NULL) { BTIF_ERR_FUNC("p_dma is NULL\n"); return E_BTIF_INVAL_PARAM; } p_btif = (struct _mtk_btif_ *)p_dma->p_btif; if (p_btif == NULL) { BTIF_ERR_FUNC("invalid parameter: p_btif(0x%p)\n", p_btif); return E_BTIF_INVAL_PARAM; } dev = (struct device *)p_btif->private_data; if (dev == NULL) BTIF_WARN_FUNC("Null dev pointer!!!!\n"); p_vfifo = p_dma->p_dma_info->p_vfifo; /*free DMA memory if allocated successfully before*/ if (p_vfifo->p_vir_addr != NULL) { dma_free_coherent(dev, p_vfifo->vfifo_size, p_vfifo->p_vir_addr, p_vfifo->phy_addr); p_vfifo->p_vir_addr = NULL; } return 0; } #endif static int _btif_state_init(struct _mtk_btif_ *p_btif) { if (p_btif == NULL) { BTIF_ERR_FUNC("p_btif is NULL\n"); return E_BTIF_INVAL_PARAM; } p_btif->state = B_S_OFF; mutex_init(&(p_btif->state_mtx)); return 0; } static int _btif_state_hold(struct _mtk_btif_ *p_btif) { return mutex_lock_killable(&(p_btif->state_mtx)); } static int _btif_state_set(struct _mtk_btif_ *p_btif, enum _ENUM_BTIF_STATE_ state) { /*chaozhong: To do: need to finished state mechine here*/ int i_ret = 0; int ori_state = p_btif->state; if (ori_state < 0 || ori_state >= B_S_MAX) { BTIF_INFO_FUNC("ori_state is unexpected: %d\n", ori_state); return E_BTIF_INVAL_STATE; } if ((state >= B_S_OFF) && (state < B_S_MAX)) { if (ori_state == state) { BTIF_DBG_FUNC("already in %s state\n", g_state[state]); return i_ret; } BTIF_DBG_FUNC("%s->%s request\n", g_state[ori_state], g_state[state]); if (state == B_S_ON) _btif_dpidle_notify_ctrl(p_btif, BTIF_DPIDLE_DISABLE); switch (ori_state) { case B_S_ON: /*B_S_ON can only be switched to B_S_OFF, B_S_SUSPEND and B_S_DPIDLE*/ /*B_S_ON->B_S_OFF : do nothing here*/ /* * B_S_ON->B_S_DPLE : disable clock backup * BTIF and DMA controller's register if necessary */ if (state == B_S_DPIDLE) { /*clock control is done in _btif_enter_dpidle*/ p_btif->state = state; i_ret = 0; } else if (state == B_S_OFF) { /*clock controlled is done in btif_close*/ p_btif->state = state; i_ret = 0; } else if (state == B_S_SUSPEND) { /*clock controlled is done in btif_close*/ p_btif->state = state; i_ret = 0; } else { BTIF_ERR_FUNC("%s->%s is not allowed\n", g_state[ori_state], g_state[state]); i_ret = E_BTIF_INVAL_STATE; } break; case B_S_DPIDLE: /*B_S_DPIDLE can only be switched to B_S_ON and B_S_SUSPEND*/ /*B_S_DPIDLE-> B_S_ON: do nothing for this moment*/ /* * B_S_DPIDLE-> B_S_SUSPEND: * disable clock backup BTIF and DMA controller's register if necessary */ if (state == B_S_ON) { /*clock control is done in _btif_exit_dpidle*/ p_btif->state = state; i_ret = 0; } else if (state == B_S_SUSPEND) { /*clock control is done in _btif_exit_dpidle*/ p_btif->state = state; i_ret = 0; } else { BTIF_ERR_FUNC("%s->%s is not allowed\n", g_state[ori_state], g_state[state]); i_ret = E_BTIF_INVAL_STATE; } break; case B_S_SUSPEND: /*B_S_SUSPEND can be switched to B_S_IDLE and B_S_ON*/ /*reinit BTIF controller and DMA controller*/ if (state == B_S_DPIDLE) { /* * system call resume API, do resume operation, * change to deepidle state */ p_btif->state = state; i_ret = 0; } else if (state == B_S_ON) { /* * when stp want to send data before * system do resume operation */ p_btif->state = state; i_ret = 0; } else { BTIF_ERR_FUNC("%s->%s is not allowed\n", g_state[ori_state], g_state[state]); i_ret = E_BTIF_INVAL_STATE; } break; case B_S_OFF:{ /*B_S_OFF can only be switched to B_S_ON*/ if (state == B_S_ON) { /*clock control is done in btif_open*/ p_btif->state = state; i_ret = 0; } else { BTIF_ERR_FUNC("%s->%s is not allowed\n", g_state[ori_state], g_state[state]); i_ret = E_BTIF_INVAL_STATE; } } break; default: /*no this possibility*/ BTIF_ERR_FUNC ("request not allowed, should be never happen\n"); break; } if (state != B_S_ON) _btif_dpidle_notify_ctrl(p_btif, BTIF_DPIDLE_ENABLE); } else { i_ret = E_BTIF_INVAL_PARAM; BTIF_ERR_FUNC("invalid state:%d, do nothing\n", state); } return i_ret; } static enum _ENUM_BTIF_STATE_ _btif_state_get(struct _mtk_btif_ *p_btif) { return p_btif->state; } static int _btif_state_release(struct _mtk_btif_ *p_btif) { int i_ret = 0; BTIF_MUTEX_UNLOCK(&(p_btif->state_mtx)); return i_ret; } static int _btif_state_deinit(struct _mtk_btif_ *p_btif) { if (p_btif == NULL) { BTIF_ERR_FUNC("p_btif is NULL\n"); return E_BTIF_INVAL_PARAM; } p_btif->state = B_S_OFF; mutex_destroy(&(p_btif->state_mtx)); return 0; } static int btif_rx_data_consummer(struct _mtk_btif_ *p_btif) { unsigned int length = 0; unsigned char *p_buf = NULL; /*get BTIF rx buffer's information*/ struct _btif_buf_str_ *p_bbs = &(p_btif->btif_buf); /* * wr_idx of btif_buf may be modified in IRQ handler, * in order not to be effected by case in which irq interrupt this operation, * we record wr_idx here */ unsigned int wr_idx = p_bbs->wr_idx; length = BBS_COUNT_CUR(p_bbs, wr_idx); /*make sure length of rx buffer data > 0*/ do { if (length > 0) { /* * check if rx_cb empty or not, if registered , * call user's rx callback to handle these data */ if (p_btif->rx_cb) { if (p_bbs->rd_idx <= wr_idx) { p_buf = BBS_PTR(p_bbs, p_bbs->rd_idx); length = (wr_idx >= (p_bbs)->rd_idx) ? (wr_idx - (p_bbs)->rd_idx) : BBS_SIZE(p_bbs) - ((p_bbs)->rd_idx - wr_idx); if (p_btif->rx_cb) (*(p_btif->rx_cb)) (p_buf, length); else BTIF_ERR_FUNC("rx_cb = NULL\n"); /*update rx data read index*/ p_bbs->rd_idx = wr_idx; } else { unsigned int len_tail = BBS_SIZE(p_bbs) - (p_bbs)->rd_idx; p_buf = BBS_PTR(p_bbs, p_bbs->rd_idx); if (p_btif->rx_cb) (*(p_btif->rx_cb))(p_buf, len_tail); else BTIF_ERR_FUNC("rx_cb = NULL\n"); length = BBS_COUNT_CUR(p_bbs, wr_idx); length -= len_tail; /*p_buf = &(p_bbs->buf[0]);*/ p_buf = BBS_PTR(p_bbs, 0); if (p_btif->rx_cb) (*(p_btif->rx_cb)) (p_buf, length); else BTIF_ERR_FUNC("rx_cb = NULL\n"); /*update rx data read index*/ p_bbs->rd_idx = wr_idx; } } else if (p_btif->rx_notify != NULL) { (*p_btif->rx_notify) (); } else { BTIF_WARN_FUNC ("p_btif:0x%p, rx_notify, rx_cb = NULL\n", p_btif); break; } } else { BTIF_DBG_FUNC("length:%d\n", length); break; } wr_idx = p_bbs->wr_idx; length = BBS_COUNT_CUR(p_bbs, wr_idx); } while (1); return length; } #if BTIF_RXD_BE_BLOCKED_DETECT static int mtk_btif_rxd_be_blocked_by_timer(void) { int ret = 0; int counter = 0; unsigned int i; struct timespec64 now; int time_gap[MAX_BTIF_RXD_TIME_REC]; btif_do_gettimeofday(&now); for (i = 0; i < MAX_BTIF_RXD_TIME_REC; i++) { BTIF_INFO_FUNC("btif_rxd_time_stamp[%d]=%ld.%ld\n", i, btif_rxd_time_stamp[i].tv_sec, btif_rxd_time_stamp[i].tv_nsec); if (now.tv_sec >= btif_rxd_time_stamp[i].tv_sec) { time_gap[i] = now.tv_sec - btif_rxd_time_stamp[i].tv_sec; time_gap[i] *= 1000000; /*second*/ if (now.tv_nsec >= btif_rxd_time_stamp[i].tv_nsec) time_gap[i] += now.tv_nsec - btif_rxd_time_stamp[i].tv_nsec; else time_gap[i] += 1000000 - now.tv_nsec + btif_rxd_time_stamp[i].tv_nsec; if (time_gap[i] > 1000000) counter++; BTIF_INFO_FUNC("time_gap[%d]=%d,counter:%d\n", i, time_gap[i], counter); } else { time_gap[i] = 0; BTIF_ERR_FUNC("!!!now[%ld] (MAX_BTIF_RXD_TIME_REC - 2)) ret = 1; return ret; } static int mtk_btif_rxd_be_blocked_by_data(void) { unsigned int out_index = 0; unsigned int in_index = 0; unsigned int dump_size = 0; unsigned long flags; unsigned int sync_pkt_n = 0; struct _btif_log_buf_t_ *p_log_buf = NULL; struct _btif_log_queue_t_ *p_log_que = NULL; struct _mtk_btif_ *p_btif = &(g_btif[0]); p_log_que = &p_btif->rx_log; spin_lock_irqsave(&p_log_que->lock, flags); in_index = p_log_que->in; dump_size = p_log_que->size; out_index = p_log_que->size >= BTIF_LOG_ENTRY_NUM ? in_index : (BTIF_LOG_ENTRY_NUM - p_log_que->size + in_index) % BTIF_LOG_ENTRY_NUM; if (dump_size != 0) { while (dump_size--) { p_log_buf = p_log_que->p_queue + out_index; if ((0x7f == *(p_log_buf->buffer)) && (0x7f == *(p_log_buf->buffer + 1))) { sync_pkt_n++; BTIF_INFO_FUNC("tx pkt_count:%d is sync pkt\n", out_index); } out_index++; out_index %= BTIF_LOG_ENTRY_NUM; } } if (sync_pkt_n == 0) BTIF_ERR_FUNC("there is no sync pkt in BTIF buffer\n"); else BTIF_ERR_FUNC("there are %d sync pkt in BTIF buffer\n", sync_pkt_n); spin_unlock_irqrestore(&p_log_que->lock, flags); return sync_pkt_n; } int mtk_btif_rxd_be_blocked_flag_get(void) { int ret = 0; int condition1 = 0, condition2 = 0; condition1 = mtk_btif_rxd_be_blocked_by_timer(); condition2 = mtk_btif_rxd_be_blocked_by_data(); if (condition1 && condition2) { BTIF_ERR_FUNC("btif_rxd thread be blocked too long!\n"); ret = 1; } return ret; } EXPORT_SYMBOL(mtk_btif_rxd_be_blocked_flag_get); #endif static int btif_rx_thread(void *p_data) { #if BTIF_RXD_BE_BLOCKED_DETECT unsigned int i = 0; #endif struct _mtk_btif_ *p_btif = (struct _mtk_btif_ *)p_data; while (1) { if (wait_for_completion_interruptible(&p_btif->rx_comp)) BTIF_WARN_FUNC("wait_for_completion is interrupted"); if (mutex_lock_killable(&(p_btif->rx_thread_mtx))) { BTIF_ERR_FUNC( "mutex lock(rx_thread_mtx) return failed\n"); break; } if (kthread_should_stop()) { BTIF_WARN_FUNC("btif rx thread stopping ...\n"); mutex_unlock(&(p_btif->rx_thread_mtx)); break; } #if BTIF_RXD_BE_BLOCKED_DETECT btif_do_gettimeofday(&btif_rxd_time_stamp[i]); i++; if (i >= MAX_BTIF_RXD_TIME_REC) i = 0; #endif btif_rx_data_consummer(p_btif); mutex_unlock(&(p_btif->rx_thread_mtx)); } return 0; } static void btif_rx_worker(struct work_struct *p_work) { /*get mtk_btif's pointer*/ struct _mtk_btif_ *p_btif = container_of(p_work, struct _mtk_btif_, rx_work); BTIF_DBG_FUNC("p_btif:0x%p\n", p_btif); /*lock rx_mutex*/ if (mutex_lock_killable(&(p_btif->rx_mtx))) { BTIF_ERR_FUNC("mutex_lock_killable return failed\n"); return; } btif_rx_data_consummer(p_btif); BTIF_MUTEX_UNLOCK(&(p_btif->rx_mtx)); } static void btif_tx_worker(struct work_struct *p_work) { int i_ret = 0; int leng_sent = 0; /*tx fifo out*/ int how_much_get = 0; unsigned char local_buf[384]; /*get mtk_btif's pointer*/ struct _mtk_btif_ *p_btif = container_of(p_work, struct _mtk_btif_, tx_work); BTIF_DBG_FUNC("p_btif:0x%p\n", p_btif); if (mutex_lock_killable(&(p_btif->tx_mtx))) { BTIF_ERR_FUNC("mutex_lock_killable return failed\n"); return; } how_much_get = kfifo_out(p_btif->p_tx_fifo, local_buf, sizeof(local_buf)); do { while (leng_sent < how_much_get) { i_ret = _btif_send_data(p_btif, local_buf + leng_sent, how_much_get - leng_sent); if (i_ret > 0) { leng_sent += i_ret; } else if (i_ret == 0) { BTIF_WARN_FUNC ("_btif_send_data return 0, retry\n"); } else { BTIF_WARN_FUNC ("btif send data fail, reset, i_ret(%d)\n", i_ret); kfifo_reset(p_btif->p_tx_fifo); break; } } how_much_get = kfifo_out(p_btif->p_tx_fifo, local_buf, sizeof(local_buf)); leng_sent = 0; } while (how_much_get > 0); BTIF_MUTEX_UNLOCK(&(p_btif->tx_mtx)); } static void btif_rx_tasklet(unsigned long func_data) { unsigned long flags; /*get mtk_btif's pointer*/ struct _mtk_btif_ *p_btif = (struct _mtk_btif_ *) func_data; BTIF_DBG_FUNC("p_btif:0x%p\n", p_btif); /*lock rx_spinlock*/ spin_lock_irqsave(&p_btif->rx_tasklet_spinlock, flags); btif_rx_data_consummer(p_btif); spin_unlock_irqrestore(&p_btif->rx_tasklet_spinlock, flags); } static int _btif_tx_ctx_init(struct _mtk_btif_ *p_btif) { int i_ret = -1; if (p_btif == NULL) { BTIF_ERR_FUNC("p_btif is NULL\n"); return E_BTIF_INVAL_PARAM; } if (p_btif->tx_ctx == BTIF_TX_SINGLE_CTX) { p_btif->p_tx_wq = create_singlethread_workqueue("btif_txd"); if (!(p_btif->p_tx_wq)) { BTIF_ERR_FUNC("create workqueue for tx thread fail\n"); i_ret = -ENOMEM; goto btm_init_err; } mutex_init(&(p_btif->tx_mtx)); /* init btif tx work */ INIT_WORK(&(p_btif->tx_work), btif_tx_worker); BTIF_INFO_FUNC("btif_tx_worker init succeed\n"); p_btif->p_tx_fifo = kzalloc(sizeof(struct kfifo), GFP_ATOMIC); if (p_btif->p_tx_fifo == NULL) { i_ret = -ENOMEM; BTIF_ERR_FUNC("kzalloc for p_btif->p_tx_fifo failed\n"); goto btm_init_err; } i_ret = kfifo_alloc(p_btif->p_tx_fifo, BTIF_TX_BUFFER_FIFO_SIZE, GFP_ATOMIC); if (i_ret != 0) { BTIF_ERR_FUNC("kfifo_alloc failed, errno(%d)\n", i_ret); i_ret = -ENOMEM; goto btm_init_err; } } else if (p_btif->tx_ctx == BTIF_TX_USER_CTX) { BTIF_INFO_FUNC ("nothing is done when btif tx in user's thread\n"); } else { BTIF_ERR_FUNC("unsupported tx context type:%d\n", p_btif->tx_ctx); goto btm_init_err; } BTIF_INFO_FUNC("succeed\n"); i_ret = 0; return i_ret; btm_init_err: if (p_btif->tx_ctx == BTIF_TX_SINGLE_CTX) { if (p_btif->p_tx_wq) { destroy_workqueue(p_btif->p_tx_wq); p_btif->p_tx_wq = NULL; BTIF_INFO_FUNC("btif_tx_workqueue destroyed\n"); } kfree(p_btif->p_tx_fifo); } return i_ret; } static int _btif_tx_ctx_deinit(struct _mtk_btif_ *p_btif) { int i_ret = 0; if (p_btif->tx_ctx == BTIF_TX_SINGLE_CTX) { if (p_btif->p_tx_wq) { destroy_workqueue(p_btif->p_tx_wq); p_btif->p_tx_wq = NULL; BTIF_INFO_FUNC("btif_tx_workqueue destroyed\n"); } if (p_btif->p_tx_fifo) { kfifo_free(p_btif->p_tx_fifo); kfree(p_btif->p_tx_fifo); p_btif->p_tx_fifo = NULL; } } return i_ret; } static int _btif_rx_btm_init(struct _mtk_btif_ *p_btif) { int i_ret = -1; if (p_btif == NULL) { BTIF_ERR_FUNC("p_btif is NULL\n"); return E_BTIF_INVAL_PARAM; } if (p_btif->btm_type == BTIF_THREAD_CTX) { init_completion(&p_btif->rx_comp); mutex_init(&p_btif->rx_thread_mtx); #ifdef BTIF_FLAG_SET_ENABLE_ALL_FUNC #if BTIF_DBG_SUPPORT INIT_DELAYED_WORK(&p_btif->btif_rx_test_work, btif_rx_test_handler); #endif #endif /*create kernel thread for later rx data handle*/ p_btif->p_task = kthread_create(btif_rx_thread, p_btif, "btif_rxd"); if (p_btif->p_task == NULL) { BTIF_ERR_FUNC("kthread_create fail\n"); i_ret = -ENOMEM; goto btm_init_err; } #if ENABLE_BTIF_RX_THREAD_RT_SCHED { int i_ret = -1; int policy = SCHED_FIFO; struct sched_param param; param.sched_priority = MAX_RT_PRIO - 20; i_ret = sched_setscheduler(p_btif->p_task, policy, ¶m); if (i_ret != 0) BTIF_WARN_FUNC("set RT to workqueue failed\n"); else BTIF_INFO_FUNC("set RT to workqueue succeed\n"); } #endif wake_up_process(p_btif->p_task); BTIF_INFO_FUNC("btif_rxd start to work!\n"); } else if (p_btif->btm_type == BTIF_WQ_CTX) { p_btif->p_rx_wq = create_singlethread_workqueue("btif_rxwq"); if (!(p_btif->p_rx_wq)) { BTIF_ERR_FUNC("create_singlethread_workqueue fail\n"); i_ret = -ENOMEM; goto btm_init_err; } mutex_init(&(p_btif->rx_mtx)); /* init btif rx work */ INIT_WORK(&(p_btif->rx_work), btif_rx_worker); BTIF_INFO_FUNC("btif_rx_worker init succeed\n"); } else if (p_btif->btm_type == BTIF_TASKLET_CTX) { /*init rx tasklet*/ tasklet_init(&(p_btif->rx_tasklet), btif_rx_tasklet, (unsigned long)p_btif); spin_lock_init(&(p_btif->rx_tasklet_spinlock)); BTIF_INFO_FUNC("btif_rx_tasklet init succeed\n"); } else { BTIF_ERR_FUNC("unsupported rx context type:%d\n", p_btif->btm_type); } /*spinlock init*/ spin_lock_init(&(p_btif->rx_irq_spinlock)); BTIF_INFO_FUNC("rx_spin_lock init succeed\n"); i_ret = 0; return i_ret; btm_init_err: if (p_btif->btm_type == BTIF_THREAD_CTX) { /*do nothing*/ BTIF_INFO_FUNC("failed\n"); } else if (p_btif->btm_type == BTIF_WQ_CTX) { if (p_btif->p_rx_wq) { destroy_workqueue(p_btif->p_rx_wq); p_btif->p_rx_wq = NULL; BTIF_INFO_FUNC("btif_rx_workqueue destroyed\n"); } } return i_ret; } static int _btif_rx_btm_sched(struct _mtk_btif_ *p_btif) { if (p_btif == NULL) { BTIF_ERR_FUNC("p_btif is NULL\n"); return E_BTIF_INVAL_PARAM; } if (p_btif->btm_type == BTIF_THREAD_CTX) { complete(&p_btif->rx_comp); BTIF_DBG_FUNC("schedule btif_rx_thread\n"); } else if (p_btif->btm_type == BTIF_WQ_CTX) { queue_work(p_btif->p_rx_wq, &(p_btif->rx_work)); BTIF_DBG_FUNC("schedule btif_rx_worker\n"); } else if (p_btif->btm_type == BTIF_TASKLET_CTX) { /*schedule it!*/ tasklet_schedule(&(p_btif->rx_tasklet)); BTIF_DBG_FUNC("schedule btif_rx_tasklet\n"); } else { BTIF_ERR_FUNC("unsupported rx context type:%d\n", p_btif->btm_type); } return 0; } static int _btif_rx_btm_deinit(struct _mtk_btif_ *p_btif) { if (p_btif == NULL) { BTIF_ERR_FUNC("p_btif is NULL\n"); return E_BTIF_INVAL_PARAM; } if (p_btif->btm_type == BTIF_THREAD_CTX) { if (p_btif->p_task != NULL) { BTIF_INFO_FUNC("signaling btif rx thread to stop...\n"); kthread_stop(p_btif->p_task); } mutex_destroy(&p_btif->rx_thread_mtx); } else if (p_btif->btm_type == BTIF_WQ_CTX) { if (p_btif->p_rx_wq) { cancel_work_sync(&(p_btif->rx_work)); BTIF_INFO_FUNC("btif_rx_worker cancelled\n"); destroy_workqueue(p_btif->p_rx_wq); p_btif->p_rx_wq = NULL; BTIF_INFO_FUNC("btif_rx_workqueue destroyed\n"); } mutex_destroy(&(p_btif->rx_mtx)); } else if (p_btif->btm_type == BTIF_TASKLET_CTX) { tasklet_kill(&(p_btif->rx_tasklet)); BTIF_INFO_FUNC("rx_tasklet killed\n"); } else { BTIF_ERR_FUNC("unsupported rx context type:%d\n", p_btif->btm_type); } spin_lock_init(&(p_btif->rx_irq_spinlock)); return 0; } void btif_dump_bbs_str(unsigned char *p_str, struct _btif_buf_str_ *p_bbs) { BTIF_INFO_FUNC ("%s UBS:%p\n Size:%d\n read:0x%08x\n write:0x%08x\n", p_str, p_bbs, p_bbs->size, p_bbs->rd_idx, p_bbs->wr_idx); } unsigned int btif_bbs_write(struct _btif_buf_str_ *p_bbs, unsigned char *p_buf, unsigned int buf_len) { /*in IRQ context, so read operation won't interrupt this operation*/ unsigned int wr_len = 0; unsigned int emp_len = BBS_LEFT(p_bbs); unsigned int ava_len = emp_len - 1; struct _mtk_btif_ *p_btif = container_of(p_bbs, struct _mtk_btif_, btif_buf); if (ava_len <= 0) { BTIF_ERR_FUNC ("no empty space for write, (%d)ava_len, (%d)to write\n", ava_len, buf_len); hal_btif_dump_reg(p_btif->p_btif_info, REG_ALL); hal_dma_dump_reg(p_btif->p_rx_dma->p_dma_info, REG_ALL); return 0; } if (ava_len < buf_len) { BTIF_ERR_FUNC("BTIF overrun, (%d)empty, (%d)needed\n", emp_len, buf_len); hal_btif_dump_reg(p_btif->p_btif_info, REG_ALL); hal_dma_dump_reg(p_btif->p_rx_dma->p_dma_info, REG_ALL); _btif_dump_memory("", p_buf, buf_len); } if (buf_len >= g_max_pkg_len) { BTIF_WARN_FUNC("buf_len too long, (%d)ava_len, (%d)to write\n", ava_len, buf_len); hal_btif_dump_reg(p_btif->p_btif_info, REG_ALL); hal_dma_dump_reg(p_btif->p_rx_dma->p_dma_info, REG_ALL); _btif_dump_memory("", p_buf, buf_len); } wr_len = min(buf_len, ava_len); btif_bbs_wr_direct(p_bbs, p_buf, wr_len); if (BBS_COUNT(p_bbs) >= g_max_pding_data_size) { BTIF_WARN_FUNC("Rx buf_len too long, size(%d)\n", BBS_COUNT(p_bbs)); btif_dump_bbs_str("Rx buffer tooo long", p_bbs); hal_btif_dump_reg(p_btif->p_btif_info, REG_ALL); hal_dma_dump_reg(p_btif->p_rx_dma->p_dma_info, REG_ALL); _btif_dump_memory("", p_buf, buf_len); BBS_INIT(p_bbs); } return wr_len; } unsigned int btif_bbs_wr_direct(struct _btif_buf_str_ *p_bbs, unsigned char *p_buf, unsigned int buf_len) { unsigned int tail_len = 0; unsigned int l = 0; unsigned int tmp_wr_idx = p_bbs->wr_idx; tail_len = BBS_SIZE(p_bbs) - (tmp_wr_idx & BBS_MASK(p_bbs)); l = min(tail_len, buf_len); memcpy((p_bbs->p_buf) + (tmp_wr_idx & BBS_MASK(p_bbs)), p_buf, l); memcpy(p_bbs->p_buf, p_buf + l, buf_len - l); mb(); /* for p_bbs write */ tmp_wr_idx += buf_len; tmp_wr_idx &= BBS_MASK(p_bbs); p_bbs->wr_idx = tmp_wr_idx; mb(); /* for p_bbs write */ return buf_len; } int _btif_dma_write(struct _mtk_btif_ *p_btif, const unsigned char *p_buf, unsigned int buf_len) { unsigned int i_ret = 0; unsigned int retry = 0; unsigned int max_tx_retry = 10; struct _MTK_DMA_INFO_STR_ *p_dma_info = p_btif->p_tx_dma->p_dma_info; _btif_irq_ctrl_sync(p_dma_info->p_irq, false); do { /*wait until tx is allowed*/ while (!hal_dma_is_tx_allow(p_dma_info) && (retry < max_tx_retry)) { retry++; if (retry >= max_tx_retry) { BTIF_ERR_FUNC("wait for tx allowed timeout\n"); break; } } if (retry >= max_tx_retry) break; if (buf_len <= hal_dma_get_ava_room(p_dma_info)) i_ret = hal_dma_send_data(p_dma_info, p_buf, buf_len); else i_ret = 0; } while (0); _btif_irq_ctrl_sync(p_dma_info->p_irq, true); return i_ret; } int _btif_pio_write(struct _mtk_btif_ *p_btif, const unsigned char *p_buf, unsigned int buf_len) { unsigned int i_ret = 0; unsigned int sent_len = 0; unsigned int retry = 0; unsigned int max_tx_retry = 10; struct _MTK_BTIF_INFO_STR_ *p_btif_info = p_btif->p_btif_info; while ((sent_len < buf_len)) { if (hal_btif_is_tx_allow(p_btif_info)) { i_ret = hal_btif_send_data(p_btif_info, p_buf + sent_len, buf_len - sent_len); if (i_ret > 0) { sent_len += i_ret; BTIF_DBG_FUNC("lent sent:%d, total sent:%d\n", i_ret, sent_len); retry = 0; } } if ((++retry > max_tx_retry) || (i_ret < 0)) { BTIF_INFO_FUNC("exceed retry times limit :%d\n", retry); break; } } i_ret = sent_len; return i_ret; } int _btif_dump_memory(char *str, unsigned char *p_buf, unsigned int buf_len) { unsigned int idx = 0; pr_debug("%s:, length:%d\n", str, buf_len); for (idx = 0; idx < buf_len;) { pr_debug("%02x ", p_buf[idx]); idx++; if (idx % 8 == 0) pr_debug("\n"); } return 0; } int btif_send_data(struct _mtk_btif_ *p_btif, const unsigned char *p_buf, unsigned int buf_len) { int i_ret = 0; if (p_btif->tx_ctx == BTIF_TX_USER_CTX) { i_ret = _btif_send_data(p_btif, p_buf, buf_len); } else if (p_btif->tx_ctx == BTIF_TX_SINGLE_CTX) { int length = 0; /*tx fifo in*/ length = kfifo_in(p_btif->p_tx_fifo, (unsigned char *)p_buf, buf_len); if (length == buf_len) { queue_work(p_btif->p_tx_wq, &(p_btif->tx_work)); BTIF_DBG_FUNC("schedule btif_tx_worker\n"); i_ret = length; } else { i_ret = 0; BTIF_ERR_FUNC("fifo in failed, buf_len(%d),len(%d),%s", buf_len, length, "don't schedule btif_tx_worker\n"); } } else { BTIF_ERR_FUNC("invalid btif tx context:%d\n", p_btif->tx_ctx); i_ret = 0; } return i_ret; } int _btif_send_data(struct _mtk_btif_ *p_btif, const unsigned char *p_buf, unsigned int buf_len) { int i_ret = 0; unsigned int state = 0; /*make sure BTIF in ON state before doing tx operation*/ if (_btif_state_hold(p_btif)) return E_BTIF_INTR; state = _btif_state_get(p_btif); if (state != B_S_ON) i_ret = _btif_exit_dpidle(p_btif); if (i_ret != 0) { i_ret = E_BTIF_INVAL_STATE; } else if (p_btif->tx_mode == BTIF_MODE_DMA) { /*_btif_dump_memory("tx data:", p_buf, buf_len);*/ i_ret = _btif_dma_write(p_btif, p_buf, buf_len); } else if (p_btif->tx_mode == BTIF_MODE_PIO) { /*_btif_dump_memory("tx data:", p_buf, buf_len);*/ i_ret = _btif_pio_write(p_btif, p_buf, buf_len); } else { BTIF_ERR_FUNC("invalid tx mode:%d\n", p_btif->tx_mode); i_ret = 0; } /*save Tx packet here*/ if (i_ret > 0) btif_log_buf_dmp_in(&p_btif->tx_log, p_buf, i_ret); BTIF_STATE_RELEASE(p_btif); return i_ret; } int btif_dump_reg(struct _mtk_btif_ *p_btif, enum _ENUM_BTIF_REG_ID_ flag) { int i_ret = 0; unsigned int ori_state = 0; /*make sure BTIF in ON state before doing tx operation*/ if (_btif_state_hold(p_btif)) return E_BTIF_INTR; ori_state = _btif_state_get(p_btif); if (ori_state == B_S_OFF) { i_ret = E_BTIF_INVAL_STATE; BTIF_ERR_FUNC ("BTIF in OFF state, shouldn't dump register\n"); goto dmp_reg_err; } if ((ori_state != B_S_ON) && (ori_state < B_S_MAX)) { BTIF_ERR_FUNC("BTIF's original state is %s, not B_S_ON\n", g_state[ori_state]); BTIF_ERR_FUNC("This should never happen in normal mode!"); i_ret = _btif_exit_dpidle(p_btif); } if (i_ret != 0) { i_ret = E_BTIF_INVAL_STATE; BTIF_ERR_FUNC("switch to B_S_ON failed\n"); goto dmp_reg_err; } /*dump BTIF register*/ hal_btif_dump_reg(p_btif->p_btif_info, flag); /*dump BTIF Tx DMA channel register if in DMA mode*/ if (p_btif->tx_mode == BTIF_MODE_DMA) hal_dma_dump_reg(p_btif->p_tx_dma->p_dma_info, flag); else BTIF_INFO_FUNC("BTIF Tx in PIO mode,no need to dump reg\n"); /*dump BTIF Rx DMA channel register if in DMA mode*/ if (p_btif->rx_mode == BTIF_MODE_DMA) hal_dma_dump_reg(p_btif->p_rx_dma->p_dma_info, flag); else BTIF_INFO_FUNC("BTIF Rx in PIO mode,no need to dump reg\n"); switch (ori_state) { case B_S_SUSPEND: /*return to dpidle state*/ /* break; */ case B_S_DPIDLE: /*return to dpidle state*/ _btif_enter_dpidle(p_btif); break; case B_S_ON: /*nothing needs to be done*/ break; default: break; } dmp_reg_err: BTIF_STATE_RELEASE(p_btif); return i_ret; } void btif_dump_dma_vfifo(struct _mtk_btif_ *p_btif) { if (p_btif->tx_mode == BTIF_MODE_DMA) hal_dma_dump_vfifo(p_btif->p_tx_dma->p_dma_info); if (p_btif->rx_mode == BTIF_MODE_DMA) hal_dma_dump_vfifo(p_btif->p_rx_dma->p_dma_info); } int btif_rx_notify_reg(struct _mtk_btif_ *p_btif, MTK_BTIF_RX_NOTIFY rx_notify) { if (p_btif->rx_notify) { BTIF_WARN_FUNC ("rx cb already exist, rewrite from (0x%p) to (0x%p)\n", p_btif->rx_notify, rx_notify); } p_btif->rx_notify = rx_notify; return 0; } int btif_dump_data(const char *p_buf, int len) { unsigned int idx = 0; unsigned char str[30]; unsigned char *p_str; p_str = &str[0]; for (idx = 0; idx < len; idx++, p_buf++) { if (sprintf(p_str, "%02x ", *p_buf) < 0) { BTIF_INFO_FUNC("sprintf error"); return -1; } p_str += 3; if (7 == (idx % 8)) { *p_str++ = '\n'; *p_str = '\0'; pr_info("%s", str); p_str = &str[0]; } } if (len % 8) { *p_str++ = '\n'; *p_str = '\0'; pr_info("%s", str); } return 0; } int btif_log_buf_dmp_in(struct _btif_log_queue_t_ *p_log_que, const char *p_buf, int len) { struct _btif_log_buf_t_ *p_log_buf = NULL; char *dir = NULL; struct timespec64 *p_timer = NULL; unsigned long flags; bool output_flag = false; struct timespec64 *p_ts = NULL; BTIF_DBG_FUNC("++\n"); if ((p_log_que == NULL) || (p_buf == NULL) || (len == 0)) { BTIF_ERR_FUNC("invalid para, p_log_que(%p), buf(%p), len(%d)\n", p_log_que, p_buf, len); return 0; } if (!(p_log_que->enable)) return 0; dir = p_log_que->dir == BTIF_TX ? "Tx" : "Rx"; output_flag = p_log_que->output_flag; spin_lock_irqsave(&(p_log_que->lock), flags); /*get next log buffer for record usage*/ p_log_buf = p_log_que->p_queue + p_log_que->in; p_timer = &p_log_buf->timer; p_ts = &p_log_buf->ts; /*log time stamp*/ btif_do_gettimeofday(p_timer); *p_ts = ktime_to_timespec64(ktime_get()); /*record data information including length and content*/ p_log_buf->len = len; memcpy(p_log_buf->buffer, p_buf, len > BTIF_LOG_SZ ? BTIF_LOG_SZ : len); /*update log queue size information*/ p_log_que->size++; p_log_que->size = p_log_que->size > BTIF_LOG_ENTRY_NUM ? BTIF_LOG_ENTRY_NUM : p_log_que->size; /*update log queue index information*/ p_log_que->in++; p_log_que->in %= BTIF_LOG_ENTRY_NUM; spin_unlock_irqrestore(&p_log_que->lock, flags); /*check if log dynamic output function is enabled or not*/ if (output_flag) { pr_info("BTIF-DBG, dir:%s, %d.%ds(%lld.%.9ld) len:%d\n", dir, (int)p_timer->tv_sec, (int)p_timer->tv_nsec, (long long)p_ts->tv_sec, p_ts->tv_nsec, len); /*output buffer content*/ btif_dump_data((char *)p_buf, len); } BTIF_DBG_FUNC("--\n"); return 0; } static void btif_log_buf_dmp_out_work(struct work_struct *work) { struct _btif_log_queue_t_ *p_log_que = container_of(work, struct _btif_log_queue_t_, dump_work); struct _btif_log_buf_t_ *p_log_buf = NULL; unsigned char *p_buf = NULL; unsigned int len = 0; unsigned int pkt_count = 0; unsigned char *p_dir = NULL; struct timespec64 *p_timer = NULL; struct timespec64 *p_ts = NULL; int i; if (p_log_que == NULL || p_log_que->p_dump_queue == NULL) return; BTIF_DBG_FUNC("++\n"); p_dir = p_log_que->dir == BTIF_TX ? "Tx" : "Rx"; BTIF_INFO_FUNC("btif %s log buffer size:%d\n", p_dir, p_log_que->dump_size); for (i = 0; i < p_log_que->dump_size; i++) { p_log_buf = p_log_que->p_dump_queue + i; len = p_log_buf->len; p_buf = p_log_buf->buffer; p_timer = &p_log_buf->timer; p_ts = &p_log_buf->ts; len = len > BTIF_LOG_SZ ? BTIF_LOG_SZ : len; BTIF_INFO_FUNC( "dir:%s,pkt_count:%d,%d.%d(%lld.%.9ld)len:%d\n", p_dir, pkt_count++, (int)p_timer->tv_sec, (int)p_timer->tv_nsec, (long long)p_ts->tv_sec, p_ts->tv_nsec, len); /*output buffer content*/ btif_dump_data(p_log_buf->buffer, len); } p_log_que->dump_size = 0; kfree(p_log_que->p_dump_queue); p_log_que->p_dump_queue = NULL; BTIF_DBG_FUNC("--\n"); } int btif_log_buf_dmp_out(struct _btif_log_queue_t_ *p_log_que) { unsigned int out_index = 0; unsigned int in_index = 0; unsigned int dump_size = 0; unsigned long flags; spin_lock_irqsave(&p_log_que->lock, flags); if (p_log_que->p_dump_queue != NULL) { spin_unlock_irqrestore(&p_log_que->lock, flags); BTIF_DBG_FUNC("log dump is ongoing\n"); return -1; } dump_size = p_log_que->size; p_log_que->p_dump_queue = kmalloc_array( dump_size, sizeof(struct _btif_log_buf_t_), GFP_ATOMIC); if (p_log_que->p_dump_queue == NULL) { spin_unlock_irqrestore(&p_log_que->lock, flags); BTIF_DBG_FUNC("no memory for log dump %d\n", dump_size); return -1; } in_index = p_log_que->in; p_log_que->dump_size = dump_size; out_index = dump_size >= BTIF_LOG_ENTRY_NUM ? in_index : (BTIF_LOG_ENTRY_NUM - dump_size + in_index) % BTIF_LOG_ENTRY_NUM; if (out_index + dump_size > BTIF_LOG_ENTRY_NUM) { int tail_num = BTIF_LOG_ENTRY_NUM - out_index; memcpy(p_log_que->p_dump_queue, p_log_que->p_queue + out_index, sizeof(struct _btif_log_buf_t_) * tail_num); memcpy(p_log_que->p_dump_queue + tail_num, p_log_que->p_queue, sizeof(struct _btif_log_buf_t_) * (dump_size - tail_num)); } else { memcpy(p_log_que->p_dump_queue, p_log_que->p_queue + out_index, sizeof(struct _btif_log_buf_t_) * dump_size); } spin_unlock_irqrestore(&p_log_que->lock, flags); schedule_work(&p_log_que->dump_work); BTIF_DBG_FUNC("log dump is scheduled %d\n", dump_size); return 0; } int btif_log_buf_enable(struct _btif_log_queue_t_ *p_log_que) { unsigned long flags; spin_lock_irqsave(&p_log_que->lock, flags); p_log_que->enable = true; spin_unlock_irqrestore(&p_log_que->lock, flags); BTIF_INFO_FUNC("enable %s log function\n", p_log_que->dir == BTIF_TX ? "Tx" : "Rx"); return 0; } int btif_log_buf_disable(struct _btif_log_queue_t_ *p_log_que) { unsigned long flags; spin_lock_irqsave(&p_log_que->lock, flags); p_log_que->enable = false; spin_unlock_irqrestore(&p_log_que->lock, flags); BTIF_INFO_FUNC("disable %s log function\n", p_log_que->dir == BTIF_TX ? "Tx" : "Rx"); return 0; } int btif_log_output_enable(struct _btif_log_queue_t_ *p_log_que) { unsigned long flags; spin_lock_irqsave(&p_log_que->lock, flags); p_log_que->output_flag = true; spin_unlock_irqrestore(&p_log_que->lock, flags); BTIF_INFO_FUNC("%s log rt output enabled\n", p_log_que->dir == BTIF_TX ? "Tx" : "Rx"); return 0; } int btif_log_output_disable(struct _btif_log_queue_t_ *p_log_que) { unsigned long flags; spin_lock_irqsave(&p_log_que->lock, flags); p_log_que->output_flag = false; spin_unlock_irqrestore(&p_log_que->lock, flags); BTIF_INFO_FUNC("%s log rt output disabled\n", p_log_que->dir == BTIF_TX ? "Tx" : "Rx"); return 0; } int btif_log_buf_reset(struct _btif_log_queue_t_ *p_log_que) { unsigned long flags; spin_lock_irqsave(&p_log_que->lock, flags); /*tx log buffer init*/ p_log_que->in = 0; p_log_que->out = 0; p_log_que->size = 0; p_log_que->enable = true; spin_unlock_irqrestore(&p_log_que->lock, flags); BTIF_DBG_FUNC("reset %s log buffer\n", p_log_que->dir == BTIF_TX ? "Tx" : "Rx"); return 0; } int btif_log_buf_init(struct _mtk_btif_ *p_btif) { /*tx log buffer init*/ p_btif->tx_log.dir = BTIF_TX; p_btif->tx_log.in = 0; p_btif->tx_log.out = 0; p_btif->tx_log.size = 0; p_btif->tx_log.output_flag = false; p_btif->tx_log.enable = true; spin_lock_init(&(p_btif->tx_log.lock)); INIT_WORK(&(p_btif->tx_log.dump_work), btif_log_buf_dmp_out_work); BTIF_DBG_FUNC("tx_log.p_queue:0x%p\n", p_btif->tx_log.p_queue); /*rx log buffer init*/ p_btif->rx_log.dir = BTIF_RX; p_btif->rx_log.in = 0; p_btif->rx_log.out = 0; p_btif->rx_log.size = 0; p_btif->rx_log.output_flag = false; p_btif->rx_log.enable = true; spin_lock_init(&(p_btif->rx_log.lock)); INIT_WORK(&(p_btif->rx_log.dump_work), btif_log_buf_dmp_out_work); BTIF_DBG_FUNC("rx_log.p_queue:0x%p\n", p_btif->rx_log.p_queue); return 0; } #if BTIF_CDEV_SUPPORT int btif_tx_dma_mode_set(int en) { int index = 0; enum _ENUM_BTIF_MODE_ mode = (en == 1) ? BTIF_MODE_DMA : BTIF_MODE_PIO; for (index = 0; index < BTIF_PORT_NR; index++) g_btif[index].tx_mode = mode; return 0; } int btif_rx_dma_mode_set(int en) { int index = 0; enum _ENUM_BTIF_MODE_ mode = (en == 1) ? BTIF_MODE_DMA : BTIF_MODE_PIO; for (index = 0; index < BTIF_PORT_NR; index++) g_btif[index].rx_mode = mode; return 0; } #endif static int BTIF_init(void) { int i_ret = -1; int index = 0; struct _mtk_btif_dma_ *p_tx_dma = NULL; struct _mtk_btif_dma_ *p_rx_dma = NULL; unsigned char *p_btif_buffer = NULL; unsigned char *p_tx_queue = NULL; unsigned char *p_rx_queue = NULL; BTIF_DBG_FUNC("++\n"); /*Platform Driver initialization*/ i_ret = platform_driver_register(&mtk_btif_dev_drv); if (i_ret) { BTIF_ERR_FUNC("registered failed, ret(%d)\n", i_ret); goto err_exit1; } i_ret = driver_create_file(&mtk_btif_dev_drv.driver, &driver_attr_flag); if (i_ret) BTIF_ERR_FUNC("BTIF pdriver_create_file failed, ret(%d)\n", i_ret); /* we keep waiting because KE happens if probe function is not called. */ while (btif_probed == 0) msleep(500); /*SW init*/ for (index = 0; index < BTIF_PORT_NR; index++) { p_btif_buffer = kmalloc(BTIF_RX_BUFFER_SIZE, GFP_ATOMIC); if (!p_btif_buffer) { BTIF_ERR_FUNC("p_btif_buffer kmalloc memory fail\n"); return -1; } BTIF_INFO_FUNC("p_btif_buffer get memory 0x%p\n", p_btif_buffer); p_tx_queue = kmalloc_array(BTIF_LOG_ENTRY_NUM, sizeof(struct _btif_log_buf_t_), GFP_ATOMIC); if (!p_tx_queue) { BTIF_ERR_FUNC("p_tx_queue kmalloc memory fail\n"); kfree(p_btif_buffer); return -1; } BTIF_INFO_FUNC("p_tx_queue get memory 0x%p\n", p_tx_queue); p_rx_queue = kmalloc_array(BTIF_LOG_ENTRY_NUM, sizeof(struct _btif_log_buf_t_), GFP_ATOMIC); if (!p_rx_queue) { BTIF_ERR_FUNC("p_rx_queue kmalloc memory fail\n"); kfree(p_btif_buffer); kfree(p_tx_queue); return -1; } BTIF_INFO_FUNC("p_rx_queue get memory 0x%p\n", p_rx_queue); INIT_LIST_HEAD(&(g_btif[index].user_list)); BBS_INIT(&(g_btif[index].btif_buf)); g_btif[index].enable = false; g_btif[index].open_counter = 0; g_btif[index].setting = &g_btif_setting[index]; g_btif[index].p_btif_info = hal_btif_info_get(); g_btif[index].tx_mode = g_btif_setting[index].tx_mode; g_btif[index].rx_mode = g_btif_setting[index].rx_mode; g_btif[index].btm_type = g_btif_setting[index].rx_type; g_btif[index].tx_ctx = g_btif_setting[index].tx_type; g_btif[index].lpbk_flag = false; g_btif[index].rx_cb = NULL; g_btif[index].rx_notify = NULL; g_btif[index].btif_buf.p_buf = p_btif_buffer; g_btif[index].tx_log.p_queue = (struct _btif_log_buf_t_ *) p_tx_queue; g_btif[index].rx_log.p_queue = (struct _btif_log_buf_t_ *) p_rx_queue; btif_log_buf_init(&g_btif[index]); #if !(MTK_BTIF_ENABLE_CLK_REF_COUNTER) /*enable BTIF clock gating by default*/ i_ret = hal_btif_clk_ctrl(g_btif[index].p_btif_info, CLK_OUT_DISABLE); if (i_ret != 0) { BTIF_ERR_FUNC("BTIF controller CG failed\n"); goto err_exit2; } #endif /* * viftual FIFO memory must be physical continuous, * because DMA will access it directly without MMU */ #if ENABLE_BTIF_TX_DMA p_tx_dma = &g_dma[index][BTIF_TX]; g_btif[index].p_tx_dma = p_tx_dma; p_tx_dma->dir = BTIF_TX; p_tx_dma->p_btif = &(g_btif[index]); /*DMA Tx vFIFO initialization*/ p_tx_dma->p_dma_info = hal_btif_dma_info_get(DMA_DIR_TX); /*spinlock init*/ spin_lock_init(&(p_tx_dma->iolock)); /*entry setup*/ atomic_set(&(p_tx_dma->entry), 0); /*vFIFO initialization*/ i_ret = _btif_vfifo_init(p_tx_dma); if (i_ret != 0) { BTIF_ERR_FUNC("BTIF Tx vFIFO allocation failed\n"); goto err_exit2; } #if !(MTK_BTIF_ENABLE_CLK_REF_COUNTER) /*enable BTIF Tx DMA channel's clock gating by default*/ i_ret = hal_btif_dma_clk_ctrl(p_tx_dma->p_dma_info, CLK_OUT_DISABLE); if (i_ret != 0) { BTIF_ERR_FUNC("BTIF Tx DMA's CG failed\n"); goto err_exit2; } #endif #else g_btif[index].p_tx_dma = NULL; /*force tx mode to DMA no matter what it is in default setting*/ g_btif[index].tx_mode = BTIF_MODE_PIO; #endif #if ENABLE_BTIF_RX_DMA p_rx_dma = &g_dma[index][BTIF_RX]; g_btif[index].p_rx_dma = p_rx_dma; p_rx_dma->p_btif = &(g_btif[index]); p_rx_dma->dir = BTIF_RX; /*DMA Tx vFIFO initialization*/ p_rx_dma->p_dma_info = hal_btif_dma_info_get(DMA_DIR_RX); /*spinlock init*/ spin_lock_init(&(p_rx_dma->iolock)); /*entry setup*/ atomic_set(&(p_rx_dma->entry), 0); /*vFIFO initialization*/ i_ret = _btif_vfifo_init(p_rx_dma); if (i_ret != 0) { BTIF_ERR_FUNC("BTIF Rx vFIFO allocation failed\n"); goto err_exit2; } #if !(MTK_BTIF_ENABLE_CLK_REF_COUNTER) /*enable BTIF Tx DMA channel's clock gating by default*/ i_ret = hal_btif_dma_clk_ctrl(p_rx_dma->p_dma_info, CLK_OUT_DISABLE); if (i_ret != 0) { BTIF_ERR_FUNC("BTIF Rx DMA's CG failed\n"); goto err_exit2; } #endif #else g_btif[index].p_rx_dma = NULL; /*force rx mode to DMA no matter what it is in default setting*/ g_btif[index].rx_mode = BTIF_MODE_PIO; #endif /*PM state mechine initialization*/ i_ret = _btif_state_init(&(g_btif[index])); if (i_ret != 0) { BTIF_ERR_FUNC("BTIF state mechanism init failed\n"); goto err_exit2; } /*Rx bottom half initialization*/ i_ret = _btif_rx_btm_init(&(g_btif[index])); if (i_ret != 0) { BTIF_ERR_FUNC("BTIF Rx btm init failed\n"); goto err_exit3; } i_ret = _btif_tx_ctx_init(&(g_btif[index])); if (i_ret != 0) { BTIF_ERR_FUNC("BTIF Tx context init failed\n"); goto err_exit4; } /*Character Device initialization*/ /*Chaozhong: ToDo: to be initialized*/ mutex_init(&g_btif[index].ops_mtx); } /*Debug purpose initialization*/ #if BTIF_CDEV_SUPPORT btif_chrdev_init(); #endif return 0; err_exit4: for (index = 0; index < BTIF_PORT_NR; index++) _btif_tx_ctx_deinit(&(g_btif[index])); err_exit3: for (index = 0; index < BTIF_PORT_NR; index++) { _btif_rx_btm_deinit(&(g_btif[index])); _btif_state_deinit(&(g_btif[index])); } err_exit2: for (index = 0; index < BTIF_PORT_NR; index++) { p_tx_dma = &g_dma[index][BTIF_TX]; p_rx_dma = &g_dma[index][BTIF_RX]; #if ENABLE_BTIF_TX_DMA _btif_vfifo_deinit(p_tx_dma); #endif #if ENABLE_BTIF_RX_DMA _btif_vfifo_deinit(p_rx_dma); #endif g_btif[index].open_counter = 0; g_btif[index].enable = false; } driver_remove_file(&mtk_btif_dev_drv.driver, &driver_attr_flag); platform_driver_unregister(&mtk_btif_dev_drv); err_exit1: i_ret = -1; BTIF_DBG_FUNC("--\n"); return i_ret; } static void BTIF_exit(void) { unsigned int index = 0; struct _mtk_btif_dma_ *p_tx_dma = NULL; struct _mtk_btif_dma_ *p_rx_dma = NULL; BTIF_DBG_FUNC("++\n"); for (index = 0; index < BTIF_PORT_NR; index++) { g_btif[index].open_counter = 0; g_btif[index].enable = false; p_tx_dma = &g_dma[index][BTIF_TX]; p_rx_dma = &g_dma[index][BTIF_RX]; #if ENABLE_BTIF_TX_DMA _btif_vfifo_deinit(p_tx_dma); #endif #if ENABLE_BTIF_RX_DMA _btif_vfifo_deinit(p_rx_dma); #endif _btif_state_deinit(&(g_btif[index])); _btif_rx_btm_deinit(&(g_btif[index])); mutex_destroy(&g_btif[index].ops_mtx); } #if !defined(CONFIG_MTK_CLKMGR) hal_btif_clk_unprepare(); #endif driver_remove_file(&mtk_btif_dev_drv.driver, &driver_attr_flag); platform_driver_unregister(&mtk_btif_dev_drv); BTIF_DBG_FUNC("--\n"); } int mtk_btif_hal_get_log_lvl(void) { return mtk_btif_dbg_lvl; } void mtk_btif_read_cpu_sw_rst_debug(void) { mtk_btif_read_cpu_sw_rst_debug_plat(); } static int _btif_rx_thread_lock(struct _mtk_btif_ *p_btif, bool enable) { if (enable) { if (!mutex_trylock(&(p_btif->rx_thread_mtx))) return -1; } else mutex_unlock(&(p_btif->rx_thread_mtx)); return 0; } int btif_rx_data_path_lock(struct _mtk_btif_ *p_btif) { /* rx_thread_lock takes the mutex, and dma_lock takes the spinlock, * and we must take the thread_lock before spinlock */ if (_btif_rx_thread_lock(p_btif, true)) return E_BTIF_FAIL; if (hal_rx_dma_lock(true)) { _btif_rx_thread_lock(p_btif, false); return E_BTIF_FAIL; } return 0; } int btif_rx_data_path_unlock(struct _mtk_btif_ *p_btif) { hal_rx_dma_lock(false); _btif_rx_thread_lock(p_btif, false); return 0; } int btif_rx_dma_has_pending_data(struct _mtk_btif_ *p_btif) { return hal_dma_rx_has_pending(p_btif->p_rx_dma->p_dma_info); } int btif_tx_dma_has_pending_data(struct _mtk_btif_ *p_btif) { return hal_dma_tx_has_pending(p_btif->p_tx_dma->p_dma_info); } int btif_rx_buf_has_pending_data(struct _mtk_btif_ *p_btif) { struct _btif_buf_str_ *p_bbs; p_bbs = &(p_btif->btif_buf); return !BBS_EMPTY(p_bbs); } struct task_struct *btif_rx_thread_get(struct _mtk_btif_ *p_btif) { return p_btif->p_task; } #ifdef BTIF_FLAG_SET_ENABLE_ALL_FUNC #if BTIF_DBG_SUPPORT static void btif_rx_test_handler(struct work_struct *work) { struct _mtk_btif_ *p_btif = container_of(to_delayed_work(work), struct _mtk_btif_, btif_rx_test_work); struct _mtk_btif_dma_ *p_rx_dma = p_btif->p_rx_dma; struct _MTK_DMA_INFO_STR_ *p_rx_dma_info = p_rx_dma->p_dma_info; switch (p_btif->test_case) { case BTIF_TEST_RX_THREAD_BLOCK: BTIF_INFO_FUNC("rx thread block test: release event\n"); _btif_rx_btm_sched(p_btif); g_enable_btif_rxd_test = 0; break; case BTIF_TEST_RX_IRQ_BLOCK: BTIF_INFO_FUNC("rx dma irq block test: enable IRQ\n"); _btif_irq_ctrl(p_rx_dma_info->p_irq, true); break; }; } static int btif_block_rx_dma_irq_test(void) { struct _mtk_btif_ *p_btif = &g_btif[0]; struct _mtk_btif_dma_ *p_rx_dma = p_btif->p_rx_dma; struct _MTK_DMA_INFO_STR_ *p_rx_dma_info = p_rx_dma->p_dma_info; BTIF_INFO_FUNC("rx dma irq block test: disable IRQ\n"); _btif_irq_ctrl(p_rx_dma_info->p_irq, false); return schedule_delayed_work(&p_btif->btif_rx_test_work, msecs_to_jiffies(p_btif->delay_sched_time)); } #endif #endif void btif_do_gettimeofday(struct timespec64 *tv) { struct timespec64 now; ktime_get_real_ts64(&now); tv->tv_sec = now.tv_sec; tv->tv_nsec = now.tv_nsec; } /*---------------------------------------------------------------------------*/ module_init(BTIF_init); module_exit(BTIF_exit); /*---------------------------------------------------------------------------*/ MODULE_AUTHOR("MBJ/WCN/SE/SS1/Chaozhong.Liang"); MODULE_DESCRIPTION("MTK BTIF Driver$1.0$"); MODULE_LICENSE("GPL"); /*---------------------------------------------------------------------------*/