| /* |
| * Copyright (c) 2016,2017 MediaTek Inc. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| * See http://www.gnu.org/licenses/gpl-2.0.html for more details. |
| */ |
| #include <linux/version.h> |
| #if (KERNEL_VERSION(4, 11, 0) > LINUX_VERSION_CODE) |
| #include <linux/sched.h> |
| #else |
| #include <uapi/linux/sched/types.h> |
| #endif |
| |
| #include "btmtk_define.h" |
| #include "btmtk_uart_tty.h" |
| #include "btmtk_main.h" |
| //#include "connv3.h" |
| #if (USE_DEVICE_NODE == 1) |
| #include "btmtk_proj_sp.h" |
| #include <linux/platform_device.h> |
| #include <linux/of_device.h> |
| #include "connv3_debug_utility.h" |
| #include "connv3_mcu_log.h" |
| #include "btmtk_fw_log.h" |
| #include "connv3.h" |
| |
| #if IS_ENABLED(CONFIG_MTK_UARTHUB) |
| /* uarthub API */ |
| extern int mtk8250_uart_hub_enable_bypass_mode(int bypass); |
| extern int mtk8250_uart_hub_is_ready(void); |
| extern int mtk8250_uart_hub_set_request(void); |
| extern int mtk8250_uart_hub_clear_request(void); |
| extern int mtk8250_uart_hub_fifo_ctrl(int ctrl); |
| extern int mtk8250_uart_hub_dump_with_tag(const char *tag); |
| #endif |
| |
| #endif |
| |
| #define LOG TRUE |
| |
| /*============================================================================*/ |
| /* Function Prototype */ |
| /*============================================================================*/ |
| int btmtk_cif_send_cmd(struct btmtk_dev *bdev, const uint8_t *cmd, |
| const int cmd_len, int retry, int delay); |
| static int btmtk_tx_thread_exit(struct btmtk_uart_dev *cif_dev); |
| static int btmtk_tx_thread_start(struct btmtk_dev *bdev); |
| static int btmtk_uart_tx_thread(void *data); |
| static int btmtk_uart_fw_own(struct btmtk_dev *bdev); |
| static int btmtk_uart_driver_own(struct btmtk_dev *bdev); |
| |
| |
| /*============================================================================*/ |
| /* Global variable */ |
| /*============================================================================*/ |
| static DECLARE_WAIT_QUEUE_HEAD(tx_wait_q); |
| static DECLARE_WAIT_QUEUE_HEAD(drv_own_wait_q); |
| static DECLARE_WAIT_QUEUE_HEAD(fw_to_md_wait_q); |
| static DEFINE_MUTEX(btmtk_uart_ops_mutex); |
| #define UART_OPS_MUTEX_LOCK() mutex_lock(&btmtk_uart_ops_mutex) |
| #define UART_OPS_MUTEX_UNLOCK() mutex_unlock(&btmtk_uart_ops_mutex) |
| static DEFINE_MUTEX(btmtk_uart_own_mutex); |
| #define UART_OWN_MUTEX_LOCK() mutex_lock(&btmtk_uart_own_mutex) |
| #define UART_OWN_MUTEX_UNLOCK() mutex_unlock(&btmtk_uart_own_mutex) |
| |
| static char event_need_compare[EVENT_COMPARE_SIZE] = {0}; |
| static char event_need_compare_len; |
| static char event_compare_status; |
| static struct tty_struct *g_tty; |
| static struct tty_ldisc_ops btmtk_uart_ldisc; |
| |
| |
| #if (USE_DEVICE_NODE == 1) |
| |
| #if IS_ENABLED(CONFIG_MTK_UARTHUB) |
| static int btmtk_wakeup_uarthub(void) { |
| int ready_retry = 50, ret = 0; |
| |
| /* Set TX,RX request */ |
| ret = mtk8250_uart_hub_set_request(); |
| if (ret) { |
| BTMTK_ERR("%s mtk8250_uart_hub_set_request fail ret[%d]", __func__, ret); |
| return -1; |
| } |
| |
| /* Polling UARTHUB is ready state */ |
| while (mtk8250_uart_hub_is_ready() <= 0 && --ready_retry) { |
| BTMTK_WARN("%s ready_retry[%d]", __func__, ready_retry); |
| usleep_range(1000, 1100); |
| } |
| |
| if (ready_retry <= 0) { |
| ret = mtk8250_uart_hub_dump_with_tag("BT driver own"); |
| BTMTK_ERR("%s mtk8250_uart_hub_dump_with_tag ready_retry[%d] ret[%d]", __func__, ready_retry, ret); |
| return -1; |
| } |
| return 0; |
| } |
| #endif // (CONFIG_MTK_UARTHUB) |
| #endif //(USE_DEVICE_NODE == 1) |
| |
| #if (SLEEP_ENABLE == 1) |
| |
| #if (KERNEL_VERSION(4, 15, 0) > LINUX_VERSION_CODE) |
| static void btmtk_fw_own_timer(unsigned long arg) |
| { |
| struct btmtk_uart_dev *cif_dev = (struct btmtk_uart_dev *)arg; |
| |
| if (atomic_read(&cif_dev->fw_own_timer_flag)) { |
| atomic_set(&cif_dev->fw_own_timer_flag, FW_OWN_TIMER_RUNNING); |
| wake_up_interruptible(&tx_wait_q); |
| } else |
| BTMTK_WARN("%s: not create yet", __func__); |
| } |
| #else |
| static void btmtk_fw_own_timer(struct timer_list *timer) |
| { |
| struct btmtk_uart_dev *cif_dev = from_timer(cif_dev, timer, fw_own_timer); |
| |
| if (atomic_read(&cif_dev->fw_own_timer_flag)) { |
| atomic_set(&cif_dev->fw_own_timer_flag, FW_OWN_TIMER_RUNNING); |
| wake_up_interruptible(&tx_wait_q); |
| } else |
| BTMTK_WARN("%s: not create yet", __func__); |
| |
| } |
| #endif |
| |
| static void btmtk_uart_update_fw_own_timer(struct btmtk_uart_dev *cif_dev) |
| { |
| |
| if (atomic_read(&cif_dev->fw_own_timer_flag)) { |
| BTMTK_DBG_LIMITTED("update fw own timer"); |
| atomic_set(&cif_dev->fw_own_timer_flag, FW_OWN_TIMER_INIT); |
| mod_timer(&cif_dev->fw_own_timer, jiffies + msecs_to_jiffies(FW_OWN_TIMEOUT)); |
| } else |
| BTMTK_WARN_LIMITTED("%s: not create yet", __func__); |
| } |
| |
| static void btmtk_uart_create_fw_own_timer(struct btmtk_uart_dev *cif_dev) |
| { |
| BTMTK_DBG("%s: create fw own timer", __func__); |
| #if (KERNEL_VERSION(4, 15, 0) > LINUX_VERSION_CODE) |
| init_timer(&cif_dev->fw_own_timer); |
| cif_dev->fw_own_timer.function = btmtk_fw_own_timer; |
| cif_dev->fw_own_timer.data = (unsigned long)cif_dev; |
| #else |
| timer_setup(&cif_dev->fw_own_timer, btmtk_fw_own_timer, 0); |
| #endif |
| atomic_set(&cif_dev->fw_own_timer_flag, FW_OWN_TIMER_INIT); |
| } |
| |
| static void btmtk_uart_delete_fw_own_timer(struct btmtk_uart_dev *cif_dev) |
| { |
| if (atomic_read(&cif_dev->fw_own_timer_flag)) { |
| del_timer_sync(&cif_dev->fw_own_timer); |
| atomic_set(&cif_dev->fw_own_timer_flag, FW_OWN_TIMER_UKNOWN); |
| BTMTK_WARN("%s timer deleted", __func__); |
| } else |
| BTMTK_WARN_LIMITTED("%s: not create yet", __func__); |
| } |
| #endif //(SLEEP_ENABLE == 1) |
| |
| static int btmtk_uart_open(struct hci_dev *hdev) |
| { |
| BTMTK_INFO("%s enter!", __func__); |
| return 0; |
| } |
| |
| static int btmtk_uart_close(struct hci_dev *hdev) |
| { |
| |
| |
| struct btmtk_uart_dev *cif_dev = NULL; |
| struct btmtk_dev *bdev = hci_get_drvdata(hdev); |
| int ret; |
| |
| BTMTK_INFO("%s enter!", __func__); |
| if (bdev == NULL) { |
| BTMTK_ERR("%s, bdev is NULL", __func__); |
| return -EINVAL; |
| } |
| |
| cif_dev = (struct btmtk_uart_dev *)bdev->cif_dev; |
| ret = 0; |
| |
| #if (SLEEP_ENABLE == 1) |
| btmtk_uart_delete_fw_own_timer(cif_dev); |
| #endif |
| |
| #if (USE_DEVICE_NODE == 1) |
| cancel_work_sync(&bdev->reset_waker); |
| |
| #if IS_ENABLED(CONFIG_MTK_UARTHUB) |
| /* Clr TX,RX request, let uarthub can sleep */ |
| ret = mtk8250_uart_hub_clear_request(); |
| if (ret) { |
| BTMTK_ERR("%s mtk8250_uart_hub_clear_request fail ret[%d]", __func__, ret); |
| } |
| #endif |
| |
| btmtk_tx_thread_exit(bdev->cif_dev); |
| |
| btmtk_set_gpio_default(); |
| if (connv3_pwr_off(CONNV3_DRV_TYPE_BT)) |
| BTMTK_ERR("%s: ConnInfra power off failed!", __func__); |
| btmtk_pwrctrl_post_off(); |
| #endif |
| BTMTK_INFO("%s end!", __func__); |
| |
| return 0; |
| } |
| |
| static int btmtk_send_to_tx_queue(struct btmtk_uart_dev *cif_dev, struct sk_buff *skb) |
| { |
| ulong flags = 0; |
| |
| /* error handle */ |
| if (!atomic_read(&cif_dev->thread_status)) { |
| BTMTK_WARN("%s tx thread already stopped, don't send cmd anymore!!", __func__); |
| /* Removed kfree_skb: leave free to btmtk_main_send_cmd */ |
| return -1; |
| } |
| |
| spin_lock_irqsave(&cif_dev->tx_lock, flags); |
| skb_queue_tail(&cif_dev->tx_queue, skb); |
| spin_unlock_irqrestore(&cif_dev->tx_lock, flags); |
| wake_up_interruptible(&tx_wait_q); |
| |
| return 0; |
| } |
| |
| int btmtk_uart_send_cmd(struct btmtk_dev *bdev, struct sk_buff *skb, |
| int delay, int retry, int pkt_type) |
| { |
| struct btmtk_uart_dev *cif_dev = NULL; |
| int ret = -1; |
| |
| if (bdev == NULL) { |
| BTMTK_ERR("bdev is NULL"); |
| ret = -1; |
| /* Removed: leave free to btmtk_main_send_cmd */ |
| #if 0 |
| kfree_skb(skb); |
| skb = NULL; |
| #endif |
| goto exit; |
| } |
| |
| cif_dev = (struct btmtk_uart_dev *)bdev->cif_dev; |
| if (cif_dev == NULL) { |
| BTMTK_ERR("cif_dev is NULL, bdev=%p", bdev); |
| ret = -1; |
| /* Removed: leave free to btmtk_main_send_cmd */ |
| #if 0 |
| kfree_skb(skb); |
| skb = NULL; |
| #endif |
| goto exit; |
| } |
| |
| /* send pkt through tx_thread or not */ |
| /* if want to send_and_recv cmd in tx_thread would not be able to send the pkt */ |
| if (pkt_type == BTMTK_TX_PKT_SEND_DIRECT) { |
| BTMTK_WARN("%s send pkt not through tx_thread", __func__); |
| ret = btmtk_cif_send_cmd(bdev, skb->data, skb->len, delay, retry); |
| |
| /* in normal case, cif_send success would kfree_skb in tx_thread */ |
| /* but in this case, would not pass by tx_thread, so need not kfree_skb */ |
| if (ret >= 0) { |
| kfree_skb(skb); |
| skb = NULL; |
| } |
| } else |
| ret = btmtk_send_to_tx_queue(cif_dev, skb); |
| |
| exit: |
| return ret; |
| |
| } |
| |
| static int btmtk_uart_read_register(struct btmtk_dev *bdev, u32 reg, u32 *val) |
| { |
| int ret = 0; |
| #if (USE_DEVICE_NODE == 0) |
| u8 cmd[READ_REGISTER_CMD_LEN] = {0x01, 0x6F, 0xFC, 0x0C, |
| 0x01, 0x08, 0x08, 0x00, |
| 0x02, 0x01, 0x00, 0x01, |
| 0x00, 0x00, 0x00, 0x00}; |
| |
| u8 event[READ_REGISTER_EVT_HDR_LEN] = {0x04, 0xE4, 0x10, 0x02, |
| 0x08, 0x0C, 0x00, 0x00, |
| 0x00, 0x00, 0x01}; |
| |
| BTMTK_INFO("%s: read cr %x", __func__, reg); |
| |
| memcpy(&cmd[MCU_ADDRESS_OFFSET_CMD], ®, sizeof(reg)); |
| |
| ret = btmtk_main_send_cmd(bdev, cmd, READ_REGISTER_CMD_LEN, event, READ_REGISTER_EVT_HDR_LEN, DELAY_TIMES, |
| RETRY_TIMES, BTMTK_TX_CMD_FROM_DRV); |
| |
| memcpy(val, bdev->io_buf + MCU_ADDRESS_OFFSET_EVT - HCI_TYPE_SIZE, sizeof(u32)); |
| *val = le32_to_cpu(*val); |
| |
| BTMTK_INFO("%s: reg=%x, value=0x%08x", __func__, reg, *val); |
| #endif |
| return ret; |
| } |
| |
| #if (USE_DEVICE_NODE == 0) |
| static int btmtk_uart_write_register(struct btmtk_dev *bdev, u32 reg, u32 *val) |
| { |
| int ret = 0; |
| |
| u8 cmd[WRITE_REGISTER_CMD_LEN] = { 0x01, 0x6F, 0xFC, 0x14, |
| 0x01, 0x08, 0x10, 0x00, |
| 0x01, 0x01, 0x00, 0x01, |
| 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, |
| 0xFF, 0xFF, 0xFF, 0xFF }; |
| u8 event[WRITE_REGISTER_EVT_HDR_LEN] = { 0x04, 0xE4, 0x08, |
| 0x02, 0x08, 0x04, 0x00, |
| 0x00, 0x00, 0x00, 0x01 }; |
| |
| BTMTK_INFO("%s: write reg=%x, value=0x%08x", __func__, reg, *val); |
| memcpy(&cmd[MCU_ADDRESS_OFFSET_CMD], ®, BT_REG_LEN); |
| memcpy(&cmd[MCU_VALUE_OFFSET_CMD], val, BT_REG_VALUE_LEN); |
| |
| ret = btmtk_main_send_cmd(bdev, cmd, WRITE_REGISTER_CMD_LEN, event, WRITE_REGISTER_EVT_HDR_LEN, DELAY_TIMES, |
| RETRY_TIMES, BTMTK_TX_CMD_FROM_DRV); |
| |
| return ret; |
| } |
| #endif |
| |
| int btmtk_uart_event_filter(struct btmtk_dev *bdev, struct sk_buff *skb) |
| { |
| const u8 read_address_event[READ_ADDRESS_EVT_HDR_LEN] = { 0x4, 0x0E, 0x0A, 0x01, 0x09, 0x10, 0x00 }; |
| const u8 get_baudrate_event[GETBAUD_EVT_LEN] = { 0x04, 0xE4, 0x0A, 0x02, 0x04, 0x06, 0x00, 0x00, 0x02 }; |
| |
| if (bdev == NULL) { |
| BTMTK_ERR("%s: bdev is NULL", __func__); |
| return -1; |
| } |
| |
| if (event_compare_status == BTMTK_EVENT_COMPARE_STATE_NEED_COMPARE && |
| skb->len >= event_need_compare_len) { |
| memset(bdev->io_buf, 0, IO_BUF_SIZE); |
| if ((skb->len == (GETBAUD_EVT_LEN - HCI_TYPE_SIZE + BAUD_SIZE)) && |
| memcmp(skb->data, &get_baudrate_event[1], GETBAUD_EVT_LEN - 1) == 0) { |
| BTMTK_INFO("%s: GET BAUD = %02X %02X %02X, FC = %02X", __func__, |
| skb->data[10], skb->data[9], skb->data[8], skb->data[11]); |
| event_compare_status = BTMTK_EVENT_COMPARE_STATE_COMPARE_SUCCESS; |
| } else if ((skb->len == (READ_ADDRESS_EVT_HDR_LEN - HCI_TYPE_SIZE + BD_ADDRESS_SIZE)) && |
| memcmp(skb->data, &read_address_event[1], READ_ADDRESS_EVT_HDR_LEN - 1) == 0) { |
| memcpy(bdev->bdaddr, &skb->data[READ_ADDRESS_EVT_PAYLOAD_OFFSET - 1], BD_ADDRESS_SIZE); |
| BTMTK_INFO("%s: GET BDADDR = "MACSTR, __func__, MAC2STR(bdev->bdaddr)); |
| event_compare_status = BTMTK_EVENT_COMPARE_STATE_COMPARE_SUCCESS; |
| } else if (memcmp(skb->data, event_need_compare, |
| event_need_compare_len) == 0) { |
| /* if it is wobx debug event, just print in kernel log, drop it |
| * by driver, don't send to stack |
| */ |
| if (skb->data[0] == WOBLE_DEBUG_EVT_TYPE) |
| BTMTK_INFO_RAW(skb->data, skb->len, "%s: wobx debug log:", __func__); |
| |
| /* If driver need to check result from skb, it can get from io_buf */ |
| /* Such as chip_id, fw_version, etc. */ |
| bdev->io_buf[0] = bt_cb(skb)->pkt_type; |
| memmove(&bdev->io_buf[1], skb->data, skb->len); |
| /* if io_buf is not update timely, it will write wrong number to register |
| * it might make uart pinmux been changed, add delay or print log can avoid this |
| * or mstar member said we can also use dsb(ISHST); |
| */ |
| #if (USE_DEVICE_NODE == 0) |
| msleep(IO_BUF_DELAY_TIME); |
| #endif |
| bdev->recv_evt_len = skb->len; |
| event_compare_status = BTMTK_EVENT_COMPARE_STATE_COMPARE_SUCCESS; |
| BTMTK_DBG("%s, compare success", __func__); |
| } else { |
| BTMTK_INFO("%s compare fail", __func__); |
| BTMTK_INFO_RAW(event_need_compare, event_need_compare_len, |
| "%s: event_need_compare_len[%d]", __func__, event_need_compare_len); |
| BTMTK_INFO_RAW(skb->data, skb->len, "%s: skb->data:", __func__); |
| return 0; |
| } |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| int btmtk_uart_send_and_recv(struct btmtk_dev *bdev, |
| struct sk_buff *skb, |
| const uint8_t *event, const int event_len, |
| int delay, int retry, int pkt_type) |
| { |
| unsigned long comp_event_timo = 0, start_time = 0; |
| int ret = 0; |
| struct btmtk_uart_dev *cif_dev = NULL; |
| |
| if (bdev == NULL) { |
| BTMTK_ERR("%s: bdev is NULL", __func__); |
| return -1; |
| } |
| |
| cif_dev = (struct btmtk_uart_dev *)bdev->cif_dev; |
| |
| #if (SLEEP_ENABLE == 1) |
| /* error handle of deadlock */ |
| /* if in fw_own, want to send_and_rev cmd */ |
| /* cmd would get evt_comp_sem and then wait event */ |
| /* however btmtk_uart_driver_own would be triggered before wmt cmd sended */ |
| /* then driver_own cmd would not get evt_comp_sem */ |
| |
| /* can not wait BTMTK_FW_OWNING because would wait for itself */ |
| if (cif_dev->own_state == BTMTK_FW_OWN) { |
| BTMTK_INFO("%s: wait driver own", __func__); |
| reinit_completion(&bdev->drv_own_comp); |
| atomic_set(&cif_dev->need_drv_own, 1); |
| wake_up_interruptible(&tx_wait_q); |
| if (!wait_for_completion_timeout(&bdev->drv_own_comp, msecs_to_jiffies(WAIT_DRV_OWN_TIMEOUT))) |
| BTMTK_WARN("%s: wait driver own timeout", __func__); |
| } |
| |
| if (cif_dev->own_state == BTMTK_FW_OWN || cif_dev->own_state == BTMTK_OWN_FAIL) { |
| BTMTK_ERR("%s: wait driver own fail", __func__); |
| return -1; |
| } |
| |
| #endif |
| |
| BTMTK_DBG_RAW(skb->data, skb->len, "%s: len[%d] ", __func__, skb->len); |
| |
| if (event) { |
| if (event_len > EVENT_COMPARE_SIZE) { |
| BTMTK_ERR("%s, event_len (%d) > EVENT_COMPARE_SIZE(%d), error", |
| __func__, event_len, EVENT_COMPARE_SIZE); |
| return -1; |
| } |
| |
| down(&cif_dev->evt_comp_sem); |
| event_compare_status = BTMTK_EVENT_COMPARE_STATE_NEED_COMPARE; |
| memcpy(event_need_compare, event + 1, event_len - 1); |
| event_need_compare_len = event_len - 1; |
| |
| start_time = jiffies; |
| /* check hci event /wmt event for uart/UART interface, check hci |
| * event for USB interface |
| */ |
| #if (USE_DEVICE_NODE == 0) |
| comp_event_timo = jiffies + msecs_to_jiffies(WOBLE_COMP_EVENT_TIMO); |
| #else |
| comp_event_timo = jiffies + msecs_to_jiffies(2000); |
| #endif |
| BTMTK_DBG("event_need_compare_len %d, event_compare_status %d", |
| event_need_compare_len, event_compare_status); |
| } else { |
| event_compare_status = BTMTK_EVENT_COMPARE_STATE_COMPARE_SUCCESS; |
| } |
| |
| ret = btmtk_uart_send_cmd(bdev, skb, delay, retry, pkt_type); |
| |
| if (ret < 0) { |
| BTMTK_ERR("%s btmtk_uart_send_cmd failed!!", __func__); |
| goto fw_assert; |
| } |
| |
| do { |
| ret = -1; |
| |
| /* check if event_compare_success */ |
| if (event_compare_status == BTMTK_EVENT_COMPARE_STATE_COMPARE_SUCCESS) { |
| ret = 0; |
| break; |
| } |
| |
| /* error handle*/ |
| if (btmtk_get_chip_state(bdev) == BTMTK_STATE_FW_DUMP || !atomic_read(&cif_dev->thread_status)) { |
| BTMTK_WARN("%s thread stopped or fw dumping, don't wait evt anymore!!", __func__); |
| ret = 0; |
| break; |
| } |
| |
| usleep_range(10, 100); |
| } while (time_before(jiffies, comp_event_timo)); |
| |
| |
| event_compare_status = BTMTK_EVENT_COMPARE_STATE_NOTHING_NEED_COMPARE; |
| |
| if (ret == -1) { |
| BTMTK_ERR("%s wait event timeout!!", __func__); |
| bdev->recv_evt_len = 0; |
| ret = -ERRNUM; |
| goto fw_assert; |
| } |
| |
| if (event) |
| up(&cif_dev->evt_comp_sem); |
| |
| return ret; |
| |
| |
| fw_assert: |
| if (event) |
| up(&cif_dev->evt_comp_sem); |
| //btmtk_send_assert_cmd(bdev); |
| return ret; |
| } |
| |
| int btmtk_uart_send_set_uart_cmd(struct hci_dev *hdev, struct UART_CONFIG *uart_cfg) |
| { |
| u8 baud_115200[] = { 0x01, 0x6F, 0xFC, 0x0A, 0x01, 0x04, |
| 0x06, 0x00, 0x01, 0x01, 0xC2, 0x01, 0x00, 0x03 }; |
| u8 baud_921600[] = { 0x01, 0x6F, 0xFC, 0x0A, 0x01, 0x04, |
| 0x06, 0x00, 0x01, 0x00, 0x10, 0x0E, 0x00, 0x03 }; |
| u8 baud_3M[] = { 0x01, 0x6F, 0xFC, 0x0A, 0x01, 0x04, |
| 0x06, 0x00, 0x01, 0xC0, 0xC6, 0x2D, 0x00, 0x03 }; |
| u8 baud_4M[] = { 0x01, 0x6F, 0xFC, 0x0A, 0x01, 0x04, |
| 0x06, 0x00, 0x01, 0x00, 0x09, 0x3D, 0x00, 0x03 }; |
| u8 baud_8M[] = { 0x01, 0x6F, 0xFC, 0x0A, 0x01, 0x04, |
| 0x06, 0x00, 0x01, 0x00, 0x12, 0x7A, 0x00, 0x03 }; |
| u8 baud_10M[] = { 0x01, 0x6F, 0xFC, 0x0A, 0x01, 0x04, |
| 0x06, 0x00, 0x01, 0x80, 0x96, 0x98, 0x00, 0x03 }; |
| u8 baud_12M[] = { 0x01, 0x6F, 0xFC, 0x0A, 0x01, 0x04, |
| 0x06, 0x00, 0x01, 0x00, 0x1B, 0xB7, 0x00, 0x03 }; |
| u8 event[] = {0x04, 0xE4, 0x06, 0x02, 0x04, 0x02, 0x00, 0x00, 0x01}; |
| u8 *cmd = NULL; |
| struct btmtk_uart_dev *cif_dev = NULL; |
| struct btmtk_dev *bdev = hci_get_drvdata(hdev); |
| int ret = -1; |
| |
| if (bdev == NULL) { |
| BTMTK_ERR("%s, bdev is NULL", __func__); |
| return -EINVAL; |
| } |
| |
| cif_dev = (struct btmtk_uart_dev *)bdev->cif_dev; |
| |
| switch (uart_cfg->iBaudrate) { |
| case 921600: |
| cmd = baud_921600; |
| break; |
| case 3000000: |
| cmd = baud_3M; |
| break; |
| case 4000000: |
| cmd = baud_4M; |
| break; |
| case 8000000: |
| cmd = baud_8M; |
| break; |
| case 10000000: |
| cmd = baud_10M; |
| break; |
| case 12000000: |
| cmd = baud_12M; |
| break; |
| default: |
| /* default chip baud is 115200 */ |
| cmd = baud_115200; |
| return 0; |
| //break; |
| } |
| |
| switch (uart_cfg->fc) { |
| case UART_HW_FC: |
| cmd[BT_FLOWCTRL_OFFSET] = BT_HW_FC; |
| break; |
| case UART_MTK_SW_FC: |
| case UART_LINUX_FC: |
| cmd[BT_FLOWCTRL_OFFSET] = BT_SW_FC; |
| break; |
| default: |
| /* default disable flow control */ |
| cmd[BT_FLOWCTRL_OFFSET] = BT_NONE_FC; |
| } |
| |
| /* uarthub setting |
| * ex: 0x13 means hub enable, rhw disable, crc disable |
| */ |
| cmd[13] = (cif_dev->fw_hub_en << 4 | !cif_dev->rhw_en << 1 | !cif_dev->crc_en << 0); |
| |
| ret = btmtk_main_send_cmd(bdev, |
| cmd, SETBAUD_CMD_LEN, event, SETBAUD_EVT_LEN, |
| 0, 0, BTMTK_TX_CMD_FROM_DRV); |
| if (ret < 0) { |
| BTMTK_ERR("%s failed!!", __func__); |
| return ret; |
| } |
| |
| cif_dev->uart_baudrate_set = 1; |
| BTMTK_INFO("%s done", __func__); |
| |
| return 0; |
| } |
| |
| static int btmtk_uart_send_query_uart_cmd(struct hci_dev *hdev) |
| { |
| u8 cmd[] = { 0x01, 0x6F, 0xFC, 0x05, 0x01, 0x04, 0x01, 0x00, 0x02}; |
| u8 event[] = { 0x04, 0xE4, 0x0a, 0x02, 0x04, 0x06, 0x00, 0x00, 0x02}; |
| struct btmtk_dev *bdev = hci_get_drvdata(hdev); |
| int ret = -1; |
| |
| ret = btmtk_main_send_cmd(bdev, cmd, GETBAUD_CMD_LEN, event, GETBAUD_EVT_LEN, DELAY_TIMES, |
| RETRY_TIMES, BTMTK_TX_CMD_FROM_DRV); |
| if (ret < 0) { |
| BTMTK_ERR("%s btmtk_uart_send_query_uart_cmd failed!!", __func__); |
| return ret; |
| } |
| |
| BTMTK_INFO("%s done", __func__); |
| return ret; |
| } |
| |
| int btmtk_uart_send_wakeup_cmd(struct hci_dev *hdev) |
| { |
| u8 cmd[] = { 0x01, 0x6f, 0xfc, 0x01, 0xFF }; |
| /* event before fw dl */ |
| u8 event[] = { 0x04, 0xE4, 0x06, 0x02, 0x03, 0x02, 0x00, 0x00, 0x03}; |
| /* event after fw dl */ |
| u8 event2[] = { 0x04, 0xE4, 0x07, 0x02, 0x03, 0x03, 0x00, 0x00, 0x03, 0x01 }; |
| struct btmtk_dev *bdev = hci_get_drvdata(hdev); |
| struct btmtk_uart_dev *cif_dev = NULL; |
| int ret = -1; |
| |
| if (bdev == NULL) { |
| BTMTK_ERR("%s: bdev is NULL", __func__); |
| return -1; |
| } |
| |
| cif_dev = (struct btmtk_uart_dev *)bdev->cif_dev; |
| |
| if (cif_dev->uart_baudrate_set == 0) { |
| BTMTK_INFO("%s uart baudrate is 115200, no need", __func__); |
| return 0; |
| } |
| if (is_mt6639(bdev->chip_id) || is_mt66xx(bdev->chip_id)) { |
| if (cif_dev->fw_dl_ready) |
| ret = btmtk_main_send_cmd(bdev, cmd+4, 1, event2, WAKEUP_EVT_LEN + 1, |
| 0, 0, BTMTK_TX_CMD_FROM_DRV); |
| else |
| ret = btmtk_main_send_cmd(bdev, cmd+4, 1, event, WAKEUP_EVT_LEN, |
| 0, 0, BTMTK_TX_CMD_FROM_DRV); |
| } else |
| ret = btmtk_main_send_cmd(bdev, cmd, WAKEUP_CMD_LEN, event, WAKEUP_EVT_LEN, |
| 0, 0, BTMTK_TX_CMD_FROM_DRV); |
| |
| if (ret < 0) { |
| BTMTK_ERR("%s failed!!", __func__); |
| return ret; |
| } |
| |
| BTMTK_INFO("%s done", __func__); |
| return ret; |
| } |
| |
| #if (USE_DEVICE_NODE == 0) |
| static int btmtk_uart_subsys_reset(struct btmtk_dev *bdev) |
| { |
| struct ktermios new_termios; |
| struct tty_struct *tty; |
| struct UART_CONFIG uart_cfg; |
| struct btmtk_uart_dev *cif_dev = NULL; |
| int ret = -1; |
| |
| if (bdev == NULL) { |
| BTMTK_ERR("%s: bdev is NULL", __func__); |
| return -1; |
| } |
| |
| cif_dev = (struct btmtk_uart_dev *)bdev->cif_dev; |
| uart_cfg = cif_dev->uart_cfg; |
| tty = cif_dev->tty; |
| new_termios = tty->termios; |
| |
| BTMTK_INFO("%s tigger reset pin: %d", __func__, bdev->bt_cfg.dongle_reset_gpio_pin); |
| gpio_set_value(bdev->bt_cfg.dongle_reset_gpio_pin, 0); |
| msleep(SUBSYS_RESET_GPIO_DELAY_TIME); |
| gpio_set_value(bdev->bt_cfg.dongle_reset_gpio_pin, 1); |
| /* Basically, we need to polling the cr (BT_MISC) untill the subsys reset is completed |
| * However, there is no uart_hw mechnism in buzzard, we can't read the info from controller now |
| * use msleep instead currently |
| */ |
| msleep(SUBSYS_RESET_GPIO_DELAY_TIME); |
| |
| /* Flush any pending characters in the driver and discipline. */ |
| tty_ldisc_flush(tty); |
| tty_driver_flush_buffer(tty); |
| |
| /* set tty host baud and flowcontrol to default value */ |
| BTMTK_INFO("Set default baud: %d, disable flowcontrol", BT_UART_DEFAULT_BAUD); |
| tty_termios_encode_baud_rate(&new_termios, BT_UART_DEFAULT_BAUD, BT_UART_DEFAULT_BAUD); |
| new_termios.c_cflag &= ~(CRTSCTS); |
| new_termios.c_iflag &= ~(NOFLSH|CRTSCTS); |
| tty_set_termios(tty, &new_termios); |
| |
| /* set chip baud and flowcontrol to config setting */ |
| ret = btmtk_uart_send_set_uart_cmd(bdev->hdev, &uart_cfg); |
| if (ret < 0) { |
| BTMTK_ERR("%s btmtk_uart_send_set_uart_cmd failed!!", __func__); |
| goto exit; |
| } |
| |
| /* set tty host baud and flowcontrol to config setting */ |
| BTMTK_INFO("Set config baud: %d, flowcontrol: %d", uart_cfg.iBaudrate, uart_cfg.fc); |
| tty_termios_encode_baud_rate(&new_termios, uart_cfg.iBaudrate, uart_cfg.iBaudrate); |
| |
| switch (uart_cfg.fc) { |
| /* HW FC Enable */ |
| case UART_HW_FC: |
| new_termios.c_cflag |= CRTSCTS; |
| new_termios.c_iflag &= ~(NOFLSH); |
| break; |
| /* Linux Software FC */ |
| case UART_LINUX_FC: |
| new_termios.c_iflag |= (IXON | IXOFF | IXANY); |
| new_termios.c_cflag &= ~(CRTSCTS); |
| new_termios.c_iflag &= ~(NOFLSH); |
| break; |
| /* MTK Software FC */ |
| case UART_MTK_SW_FC: |
| new_termios.c_iflag |= CRTSCTS; |
| new_termios.c_cflag &= ~(NOFLSH); |
| break; |
| /* default disable flow control */ |
| default: |
| new_termios.c_cflag &= ~(CRTSCTS); |
| new_termios.c_iflag &= ~(NOFLSH|CRTSCTS); |
| } |
| |
| tty_set_termios(tty, &new_termios); |
| ret = btmtk_uart_send_wakeup_cmd(bdev->hdev); |
| if (ret < 0) { |
| BTMTK_ERR("%s btmtk_uart_send_set_uart_cmd failed!!", __func__); |
| goto exit; |
| } |
| |
| BTMTK_INFO("%s done", __func__); |
| |
| exit: |
| return ret; |
| } |
| |
| |
| #else // (USE_DEVICE_NODE == 1) |
| static int btmtk_uart_subsys_reset(struct btmtk_dev *bdev) |
| { |
| BTMTK_DBG("%s sp no need to reset flow, bt on/off directly", __func__); |
| return 0; |
| } |
| |
| static int btmtk_sp_pre_open(struct btmtk_dev *bdev) |
| { |
| struct ktermios new_termios; |
| struct tty_struct *tty; |
| struct UART_CONFIG uart_cfg; |
| struct btmtk_uart_dev *cif_dev = NULL; |
| int ret = -1; |
| int cif_event = 0; |
| struct btmtk_cif_state *cif_state = NULL; |
| |
| if (bdev == NULL) { |
| BTMTK_ERR("%s: bdev is NULL", __func__); |
| return -1; |
| } |
| |
| cif_dev = (struct btmtk_uart_dev *)bdev->cif_dev; |
| uart_cfg = cif_dev->uart_cfg; |
| tty = cif_dev->tty; |
| new_termios = tty->termios; |
| |
| btmtk_pwrctrl_pre_on(bdev); |
| if (connv3_pwr_on(CONNV3_DRV_TYPE_BT)) { |
| BTMTK_ERR("ConnInfra power on failed!"); |
| return -EFAULT; |
| } |
| |
| /* start tx_thread */ |
| if (btmtk_tx_thread_start(bdev)) |
| return -EFAULT; |
| |
| btmtk_set_uart_auxFunc(); |
| |
| /* temp solution wait pmic enable */ |
| msleep(100); |
| |
| if (connv3_ext_32k_on()) { |
| BTMTK_ERR("connv3_ext_32k_on failed!"); |
| return -EFAULT; |
| } |
| |
| #if IS_ENABLED(CONFIG_MTK_UARTHUB) |
| if (cif_dev->hub_en) { |
| /* use uarthub multi-host mode (default) */ |
| ret = mtk8250_uart_hub_enable_bypass_mode(0); |
| BTMTK_INFO("%s mtk8250_uart_hub_enable_bypass_mode(0) ret[%d]", __func__, ret); |
| |
| ret = btmtk_wakeup_uarthub(); |
| |
| /* use uarthub bypass mode */ |
| ret = mtk8250_uart_hub_enable_bypass_mode(1); |
| BTMTK_INFO("%s mtk8250_uart_hub_enable_bypass_mode(1) ret[%d]", __func__, ret); |
| } |
| #endif |
| /* set tty host baud and flowcontrol to default value */ |
| BTMTK_INFO("Set default baud: %d, disable flowcontrol", BT_UART_DEFAULT_BAUD); |
| tty_termios_encode_baud_rate(&new_termios, BT_UART_DEFAULT_BAUD, BT_UART_DEFAULT_BAUD); |
| new_termios.c_cflag &= ~(CRTSCTS); |
| new_termios.c_iflag &= ~(NOFLSH|CRTSCTS); |
| tty_set_termios(tty, &new_termios); |
| |
| /* update baurdrate from dts */ |
| if (cif_dev->baudrate) |
| uart_cfg.iBaudrate = cif_dev->baudrate; |
| |
| /* uarhub setting */ |
| cif_dev->fw_hub_en = 0; |
| cif_dev->rhw_en = 0; |
| cif_dev->crc_en = 0; |
| cif_dev->fw_dl_ready = 0; |
| cif_dev->flush_en = 1; |
| |
| /* set chip baud and flowcontrol to config setting */ |
| ret = btmtk_uart_send_set_uart_cmd(bdev->hdev, &uart_cfg); |
| if (ret < 0) { |
| BTMTK_WARN("%s retry send cmd", __func__); |
| ret = btmtk_uart_send_set_uart_cmd(bdev->hdev, &uart_cfg); |
| if (ret < 0) |
| goto exit; |
| } |
| |
| /* set tty host baud and flowcontrol to config setting */ |
| BTMTK_INFO("Set config baud: %d, flowcontrol: %d", uart_cfg.iBaudrate, uart_cfg.fc); |
| |
| tty_termios_encode_baud_rate(&new_termios, uart_cfg.iBaudrate, uart_cfg.iBaudrate); |
| |
| switch (uart_cfg.fc) { |
| /* HW FC Enable */ |
| case UART_HW_FC: |
| new_termios.c_cflag |= CRTSCTS; |
| new_termios.c_iflag &= ~(NOFLSH); |
| break; |
| /* Linux Software FC */ |
| case UART_LINUX_FC: |
| new_termios.c_iflag |= (IXON | IXOFF | IXANY); |
| new_termios.c_cflag &= ~(CRTSCTS); |
| new_termios.c_iflag &= ~(NOFLSH); |
| break; |
| /* MTK Software FC */ |
| case UART_MTK_SW_FC: |
| new_termios.c_iflag |= CRTSCTS; |
| new_termios.c_cflag &= ~(NOFLSH); |
| break; |
| /* default disable flow control */ |
| default: |
| new_termios.c_cflag &= ~(CRTSCTS); |
| new_termios.c_iflag &= ~(NOFLSH|CRTSCTS); |
| } |
| |
| tty_set_termios(tty, &new_termios); |
| |
| ret = btmtk_uart_send_wakeup_cmd(bdev->hdev); |
| if (ret < 0) |
| goto exit; |
| |
| #if IS_ENABLED(CONFIG_MTK_UARTHUB) |
| if (cif_dev->hub_en) { |
| /* disable ADSP,MD when fw dl */ |
| ret = mtk8250_uart_hub_fifo_ctrl(1); |
| BTMTK_INFO("%s: Set mtk8250_uart_hub_fifo_ctrl(1) ret[%d]", __func__, ret); |
| } |
| #endif |
| |
| |
| ret = btmtk_load_rom_patch(bdev); |
| cif_event = HIF_EVENT_PROBE; |
| cif_state = &bdev->cif_state[cif_event]; |
| /* Set End/Error state */ |
| if (ret == 0) { |
| btmtk_set_chip_state((void *)bdev, cif_state->ops_end); |
| } else { |
| BTMTK_ERR("%s: btmtk_load_rom_patch failed (%d)", __func__, ret); |
| btmtk_set_chip_state((void *)bdev, cif_state->ops_error); |
| return ret; |
| } |
| |
| #if IS_ENABLED(CONFIG_MTK_UARTHUB) |
| if (cif_dev->hub_en) { |
| /* enable ADSP,MD when fw dl done*/ |
| ret = mtk8250_uart_hub_fifo_ctrl(0); |
| BTMTK_INFO("%s: Set mtk8250_uart_hub_fifo_ctrl(0) ret[%d]", __func__, ret); |
| |
| /* uarhub setting */ |
| cif_dev->fw_hub_en = 1; |
| cif_dev->rhw_en = 1; |
| cif_dev->crc_en = 1; |
| cif_dev->fw_dl_ready = 1; |
| |
| /* set chip baud and flowcontrol to config setting */ |
| ret = btmtk_uart_send_set_uart_cmd(bdev->hdev, &uart_cfg); |
| if (ret < 0) { |
| BTMTK_WARN("%s after fwdl, send uarhub setting cmd", __func__); |
| goto exit; |
| } |
| |
| /* after fw dl, use uarthub multi-host mode */ |
| ret = mtk8250_uart_hub_enable_bypass_mode(0); |
| BTMTK_INFO("%s after fw dl, mtk8250_uart_hub_enable_bypass_mode(0) ret[%d]", __func__, ret); |
| |
| ret = btmtk_uart_send_wakeup_cmd(bdev->hdev); |
| if (ret < 0) |
| goto exit; |
| } |
| #endif |
| |
| BTMTK_INFO("%s done", __func__); |
| |
| exit: |
| return ret; |
| } |
| |
| #endif //(USE_DEVICE_NODE) |
| |
| static int btmtk_uart_pre_open(struct btmtk_dev *bdev) |
| { |
| int ret = 0; |
| #if (SLEEP_ENABLE == 1) |
| struct btmtk_uart_dev *cif_dev = NULL; |
| if (bdev == NULL) { |
| BTMTK_ERR("%s: bdev is NULL", __func__); |
| return -1; |
| } |
| cif_dev = (struct btmtk_uart_dev *)bdev->cif_dev; |
| BTMTK_INFO("%s init to driver own state", __func__); |
| /* not start fw_own_timer until bt open done */ |
| atomic_set(&cif_dev->fw_own_timer_flag, FW_OWN_TIMER_UKNOWN); |
| cif_dev->own_state = BTMTK_DRV_OWN; |
| #endif |
| |
| #if (USE_DEVICE_NODE == 1) |
| ret = btmtk_sp_pre_open(bdev); |
| #endif |
| |
| return ret; |
| } |
| |
| static void btmtk_uart_open_done(struct btmtk_dev *bdev) |
| { |
| #if (SLEEP_ENABLE == 1) |
| struct btmtk_uart_dev *cif_dev = NULL; |
| |
| BTMTK_INFO("%s start", __func__); |
| |
| if (bdev == NULL) { |
| BTMTK_ERR("%s: bdev is NULL", __func__); |
| } |
| |
| cif_dev = (struct btmtk_uart_dev *)bdev->cif_dev; |
| |
| /* start fw own timer */ |
| btmtk_uart_create_fw_own_timer(cif_dev); |
| #endif |
| |
| #if (USE_DEVICE_NODE == 1) |
| if (connv3_pwr_on_done(CONNV3_DRV_TYPE_BT)) |
| BTMTK_ERR("%s: ConnInfra power done failed!", __func__); |
| #endif |
| |
| } |
| |
| |
| static void btmtk_uart_waker_notify(struct btmtk_dev *bdev) |
| { |
| BTMTK_INFO("%s enter!", __func__); |
| if (bdev == NULL) { |
| BTMTK_ERR("%s: bdev is NULL", __func__); |
| return; |
| } |
| schedule_work(&bdev->reset_waker); |
| } |
| |
| static int btmtk_uart_set_para(struct btmtk_dev *bdev, int val) |
| { |
| struct btmtk_uart_dev *cif_dev = NULL; |
| |
| BTMTK_INFO("%s start val[%d]", __func__, val); |
| |
| if (bdev == NULL) { |
| BTMTK_ERR("%s: bdev is NULL", __func__); |
| return -1; |
| } |
| |
| cif_dev = (struct btmtk_uart_dev *)bdev->cif_dev; |
| cif_dev->hub_en = !!(val & BTMTK_HUB_EN); |
| cif_dev->sleep_en = !!(val & BTMTK_SLEEP_EN); |
| |
| BTMTK_INFO("%s hub_en[%d] sleep_en[%d]", __func__, cif_dev->hub_en, cif_dev->sleep_en); |
| return 0; |
| } |
| |
| |
| static void btmtk_uart_cif_mutex_lock(struct btmtk_dev *bdev) |
| { |
| UART_OPS_MUTEX_LOCK(); |
| } |
| |
| static void btmtk_uart_cif_mutex_unlock(struct btmtk_dev *bdev) |
| { |
| UART_OPS_MUTEX_UNLOCK(); |
| } |
| |
| static void btmtk_uart_chip_reset_notify(struct btmtk_dev *bdev) |
| { |
| //struct btmtk_uart_dev *cif_dev = (struct btmtk_uart_dev *)bdev->cif_dev; |
| } |
| |
| static int btmtk_uart_load_fw_patch_using_dma(struct btmtk_dev *bdev, u8 *image, |
| u8 *fwbuf, int section_dl_size, int section_offset) |
| { |
| int cur_len = 0; |
| int count = 0; |
| int ret = -1; |
| struct btmtk_uart_dev *cif_dev = NULL; |
| s32 sent_len; |
| u8 cmd[LD_PATCH_CMD_LEN] = {0x02, 0x6F, 0xFC, 0x05, 0x00, 0x01, 0x01, 0x01, 0x00, PATCH_PHASE3}; |
| u8 event[LD_PATCH_EVT_LEN] = {0x04, 0xE4, 0x05, 0x02, 0x01, 0x01, 0x00, 0x00}; /* event[7] is status*/ |
| |
| if (bdev == NULL || image == NULL || fwbuf == NULL) { |
| BTMTK_ERR("%s: invalid parameters!", __func__); |
| ret = -1; |
| goto exit; |
| } |
| cif_dev = (struct btmtk_uart_dev *)bdev->cif_dev; |
| BTMTK_INFO("%s: loading rom patch... start", __func__); |
| while (1) { |
| sent_len = (section_dl_size - cur_len) >= (UPLOAD_PATCH_UNIT) ? |
| (UPLOAD_PATCH_UNIT) : (section_dl_size - cur_len); |
| /* wait tty buffer clean */ |
| if (cif_dev->flush_en) { |
| do { |
| count = tty_chars_in_buffer(cif_dev->tty); |
| //BTMTK_DBG("%s: char in buffer before flush count[%d]", __func__, count); |
| } while (count != 0); |
| tty_driver_flush_buffer(cif_dev->tty); |
| } |
| |
| if (sent_len > 0) { |
| memcpy(image, fwbuf + section_offset + cur_len, sent_len); |
| |
| ret = cif_dev->tty->ops->write(cif_dev->tty, image, sent_len); |
| |
| // can use next cur_len - current cur_len = ret |
| //BTMTK_DBG("%s, send length: ret= %d", __func__, ret); |
| |
| if (ret < 0) { |
| BTMTK_ERR("%s: send patch failed, terminate", __func__); |
| goto exit; |
| } |
| cur_len += ret; |
| } else |
| break; |
| } |
| |
| BTMTK_INFO("%s: send dl cmd", __func__); |
| ret = btmtk_main_send_cmd(bdev, |
| cmd, LD_PATCH_CMD_LEN, |
| event, LD_PATCH_EVT_LEN, |
| PATCH_DOWNLOAD_PHASE3_DELAY_TIME, |
| PATCH_DOWNLOAD_PHASE3_RETRY, |
| BTMTK_TX_ACL_FROM_DRV); |
| if (ret < 0) { |
| BTMTK_ERR("%s: send wmd dl cmd failed, terminate!", __func__); |
| return ret; |
| } |
| BTMTK_INFO("%s: loading rom patch... Done", __func__); |
| |
| exit: |
| return ret; |
| } |
| |
| int btmtk_cif_send_cmd(struct btmtk_dev *bdev, const uint8_t *cmd, |
| const int cmd_len, int retry, int delay) |
| { |
| int ret = -1, len = 0, count = 0; |
| struct btmtk_uart_dev *cif_dev = NULL; |
| u8 assert_cmd[ASSERT_CMD_LEN] = { 0x01, 0x5B, 0xFD, 0x00 }; |
| |
| if (bdev == NULL) { |
| BTMTK_ERR("%s: bdev is NULL", __func__); |
| return -1; |
| } |
| |
| cif_dev = (struct btmtk_uart_dev *)bdev->cif_dev; |
| |
| /* BTMTK_INFO("%s: tty %p\n", __func__, bdev->tty); */ |
| |
| /* wait tty buffer clean */ |
| if (cif_dev->flush_en) { |
| do { |
| count = tty_chars_in_buffer(cif_dev->tty); |
| //BTMTK_DBG("%s: char in buffer before flush count[%d]", __func__, count); |
| } while (count != 0); |
| |
| tty_driver_flush_buffer(cif_dev->tty); |
| } |
| |
| count = 0; |
| |
| if(cmd_len == ASSERT_CMD_LEN && memcmp(assert_cmd, cmd, ASSERT_CMD_LEN) == 0) { |
| BTMTK_INFO("%s: trigger assert", __func__); |
| btmtk_set_chip_state(bdev, BTMTK_STATE_SEND_ASSERT); |
| } |
| |
| while (len != cmd_len && count < BTMTK_MAX_SEND_RETRY) { |
| ret = cif_dev->tty->ops->write(cif_dev->tty, cmd + len, cmd_len - len); |
| len += ret; |
| count++; |
| } |
| |
| if (count == BTMTK_MAX_SEND_RETRY) { |
| BTMTK_ERR("%s: retry[%d] fail", __func__, count); |
| ret = -1; |
| } |
| |
| BTMTK_INFO_RAW(cmd, cmd_len, "%s, len[%d] retry[%d] room[%d] CMD : ", __func__, cmd_len, |
| count, tty_write_room(cif_dev->tty)); |
| |
| return ret; |
| } |
| |
| /* bt_tx_wait_for_msg |
| * |
| * Check needing action of current bt status to wake up bt thread |
| * |
| * Arguments: |
| * [IN] bdev - bt driver control strcuture |
| * |
| * Return Value: |
| * return check - 1 for waking up bt thread, 0 otherwise |
| * |
| */ |
| static u32 btmtk_thread_wait_for_msg(struct btmtk_dev *bdev) |
| { |
| u32 ret = 0; |
| struct btmtk_uart_dev *cif_dev = NULL; |
| cif_dev = (struct btmtk_uart_dev *)bdev->cif_dev; |
| |
| if (!skb_queue_empty(&cif_dev->tx_queue)) { |
| ret |= BTMTK_THREAD_TX; |
| } |
| |
| #if (SLEEP_ENABLE == 1) |
| if (atomic_read(&cif_dev->need_drv_own)) { |
| //BTMTK_DBG("%s: set drv own", __func__); |
| atomic_set(&cif_dev->need_drv_own, 0); |
| ret |= BTMTK_THREAD_RX; |
| } |
| |
| if (atomic_read(&cif_dev->fw_own_timer_flag) == FW_OWN_TIMER_RUNNING) { |
| //BTMTK_DBG("%s: set fw own", __func__); |
| /* incase of tx_thread keep running for FW_OWN_TIMER_RUNNING */ |
| atomic_set(&cif_dev->fw_own_timer_flag, FW_OWN_TIMER_DONE); |
| ret |= BTMTK_THREAD_FW_OWN; |
| } |
| #endif |
| |
| if (kthread_should_stop()) { |
| BTMTK_DBG("%s: kthread_should_stop", __func__); |
| ret |= BTMTK_THREAD_STOP; |
| } |
| |
| return ret; |
| } |
| |
| static int btmtk_uart_tx_thread(void *data) |
| { |
| struct btmtk_dev *bdev = data; |
| struct btmtk_uart_dev *cif_dev = NULL; |
| int state = BTMTK_STATE_INIT; |
| unsigned char fstate = BTMTK_FOPS_STATE_INIT; |
| struct sk_buff *skb; |
| u32 thread_flag = 0; |
| int ret = 0; |
| ulong flags = 0; |
| |
| BTMTK_INFO("%s start", __func__); |
| |
| if (bdev == NULL) { |
| BTMTK_ERR("%s: bdev is NULL", __func__); |
| return -1; |
| } |
| /* avoid unused var for USE_DEVICE_NODE == 0*/ |
| ret = 0; |
| |
| cif_dev = (struct btmtk_uart_dev *)bdev->cif_dev; |
| |
| atomic_set(&cif_dev->thread_status, 1); |
| |
| while (1) { |
| wait_event_interruptible(tx_wait_q, |
| (thread_flag = btmtk_thread_wait_for_msg(bdev))); |
| |
| if (thread_flag & BTMTK_THREAD_STOP) { |
| BTMTK_WARN("%s: thread is stopped, break", __func__); |
| break; |
| } |
| |
| state = btmtk_get_chip_state(bdev); |
| fstate = btmtk_fops_get_state(bdev); |
| |
| #if (SLEEP_ENABLE == 1) |
| if (fstate == BTMTK_FOPS_STATE_CLOSING) { |
| //BTMTK_DBG("%s: no fw own when closing", __func__); |
| thread_flag &= ~BTMTK_THREAD_FW_OWN; |
| } |
| |
| if (state == BTMTK_STATE_FW_DUMP || state == BTMTK_STATE_SEND_ASSERT |
| || state == BTMTK_STATE_SUBSYS_RESET) { |
| //BTMTK_DBG("%s: no fw/driver own, no tx when dumping", __func__); |
| /* if disable tx would not send rhw debug sop */ |
| thread_flag &= ~(BTMTK_THREAD_FW_OWN); |
| /* incase of aftrer fw coredump, send fw own fail and trigger assert again */ |
| btmtk_uart_delete_fw_own_timer(cif_dev); |
| } |
| |
| if (thread_flag & (BTMTK_THREAD_TX | BTMTK_THREAD_RX)) { |
| ret = btmtk_uart_driver_own(bdev); |
| if (ret) { |
| BTMTK_ERR("%s: set driver own return fail", __func__); |
| //btmtk_reset_trigger(bdev); |
| btmtk_send_assert_cmd(bdev); |
| break; |
| } |
| |
| /* if bt fw closed, no need to send fw own */ |
| } else if (thread_flag & BTMTK_THREAD_FW_OWN) { |
| ret = btmtk_uart_fw_own(bdev); |
| if (ret) { |
| BTMTK_ERR("%s: set fw own return fail", __func__); |
| //btmtk_reset_trigger(bdev); |
| btmtk_send_assert_cmd(bdev); |
| break; |
| } |
| } |
| #endif |
| |
| if (thread_flag & BTMTK_THREAD_TX) { |
| if (cif_dev->own_state != BTMTK_DRV_OWN) { |
| BTMTK_WARN("%s not in dirver_own state[%d] can not send cmd", __func__, cif_dev->own_state); |
| continue; |
| } |
| while (skb_queue_len(&cif_dev->tx_queue)) { |
| spin_lock_irqsave(&cif_dev->tx_lock, flags); |
| skb = skb_dequeue(&cif_dev->tx_queue); |
| spin_unlock_irqrestore(&cif_dev->tx_lock, flags); |
| if (skb) { |
| btmtk_cif_send_cmd(bdev, |
| skb->data, skb->len, |
| 5, 0); |
| kfree_skb(skb); |
| skb = NULL; |
| } |
| } |
| } |
| } |
| atomic_set(&cif_dev->thread_status, 0); |
| BTMTK_INFO("%s end", __func__); |
| return 0; |
| } |
| |
| static int btmtk_tx_thread_start(struct btmtk_dev *bdev) |
| { |
| int i = 0; |
| struct btmtk_uart_dev *cif_dev = NULL; |
| |
| if (bdev == NULL) { |
| BTMTK_ERR("%s: bdev is NULL", __func__); |
| return -1; |
| } |
| |
| cif_dev = (struct btmtk_uart_dev *)bdev->cif_dev; |
| |
| BTMTK_INFO("%s start", __func__); |
| |
| if (!atomic_read(&cif_dev->thread_status)) { |
| cif_dev->tx_task = kthread_run(btmtk_uart_tx_thread, |
| bdev, "btmtk_uart_tx_thread"); |
| if (IS_ERR(cif_dev->tx_task)) { |
| BTMTK_ERR("%s create tx thread FAILED", __func__); |
| return -1; |
| } |
| |
| while (!atomic_read(&cif_dev->thread_status) && i < RETRY_TIMES) { |
| BTMTK_INFO("%s: wait btmtk_uart_tx_thread start ...", __func__); |
| msleep(100); |
| i++; |
| if (i == RETRY_TIMES - 1) { |
| BTMTK_INFO("%s: wait btmtk_uart_tx_thread start failed", __func__); |
| return -1; |
| } |
| } |
| |
| BTMTK_INFO("%s started", __func__); |
| } else { |
| BTMTK_INFO("%s already running", __func__); |
| } |
| skb_queue_purge(&cif_dev->tx_queue); |
| |
| |
| return 0; |
| } |
| |
| |
| static int btmtk_tx_thread_exit(struct btmtk_uart_dev *cif_dev) |
| { |
| int i = 0; |
| BTMTK_INFO("%s start", __func__); |
| |
| if (cif_dev == NULL) { |
| BTMTK_ERR("%s: cif_dev is NULL", __func__); |
| return -1; |
| } |
| |
| if (!IS_ERR(cif_dev->tx_task) && atomic_read(&cif_dev->thread_status)) { |
| kthread_stop(cif_dev->tx_task); |
| |
| while (atomic_read(&cif_dev->thread_status) && i < RETRY_TIMES) { |
| BTMTK_INFO("%s: wait btmtk_uart_tx_thread stop ...", __func__); |
| msleep(100); |
| i++; |
| if (i == RETRY_TIMES - 1) { |
| BTMTK_INFO("%s: wait btmtk_uart_tx_thread stop failed", __func__); |
| break; |
| } |
| } |
| } |
| skb_queue_purge(&cif_dev->tx_queue); |
| |
| BTMTK_INFO("%s done", __func__); |
| return 0; |
| } |
| |
| /* Allocate Uart-Related memory */ |
| static int btmtk_uart_allocate_memory(void) |
| { |
| return 0; |
| } |
| |
| int btmtk_cif_send_calibration(struct btmtk_dev *bdev) |
| { |
| return 0; |
| } |
| |
| #if (USE_DEVICE_NODE == 0) |
| static int btmtk_uart_set_pinmux(struct btmtk_dev *bdev) |
| { |
| int err = 0; |
| u32 val; |
| |
| /* BT_PINMUX_CTRL_REG setup */ |
| btmtk_uart_read_register(bdev, BT_PINMUX_CTRL_REG, &val); |
| val |= BT_PINMUX_CTRL_ENABLE; |
| err = btmtk_uart_write_register(bdev, BT_PINMUX_CTRL_REG, &val); |
| if (err < 0) { |
| BTMTK_ERR("btmtk_uart_write_register failed!"); |
| return -1; |
| } |
| btmtk_uart_read_register(bdev, BT_PINMUX_CTRL_REG, &val); |
| |
| /* BT_PINMUX_CTRL_REG setup */ |
| btmtk_uart_read_register(bdev, BT_SUBSYS_RST_REG, &val); |
| val |= BT_SUBSYS_RST_ENABLE; |
| err = btmtk_uart_write_register(bdev, BT_SUBSYS_RST_REG, &val); |
| if (err < 0) { |
| BTMTK_ERR("btmtk_uart_write_register failed!"); |
| return -1; |
| } |
| btmtk_uart_read_register(bdev, BT_SUBSYS_RST_REG, &val); |
| |
| BTMTK_INFO("%s done", __func__); |
| return 0; |
| } |
| #endif |
| |
| static int btmtk_uart_init(struct btmtk_dev *bdev) |
| { |
| int err = 0; |
| |
| err = btmtk_main_cif_initialize(bdev, HCI_UART); |
| if (err < 0) { |
| BTMTK_ERR("[ERR] btmtk_main_cif_initialize failed!"); |
| goto end; |
| } |
| |
| #if (USE_DEVICE_NODE == 0) |
| err = btmtk_register_hci_device(bdev); |
| if (err < 0) { |
| BTMTK_ERR("btmtk_register_hci_device failed!"); |
| goto deinit; |
| } |
| |
| err = btmtk_uart_set_pinmux(bdev); |
| if (err < 0) { |
| BTMTK_ERR("btmtk_uart_set_pinmux failed!"); |
| goto free_hci; |
| } |
| #endif |
| |
| INIT_WORK(&bdev->reset_waker, btmtk_reset_waker); |
| goto end; |
| |
| #if (USE_DEVICE_NODE == 0) |
| free_hci: |
| btmtk_deregister_hci_device(bdev); |
| deinit: |
| btmtk_main_cif_uninitialize(bdev, HCI_UART); |
| #endif |
| end: |
| BTMTK_INFO("%s done", __func__); |
| return err; |
| } |
| |
| /* ------ LDISC part ------ */ |
| /* btmtk_uart_tty_probe |
| * |
| * Called when line discipline changed to HCI_UART. |
| * |
| * Arguments: |
| * tty pointer to tty info structure |
| * Return Value: |
| * 0 if success, otherwise error code |
| */ |
| static int btmtk_uart_tty_probe(struct tty_struct *tty) |
| { |
| struct btmtk_dev *bdev = NULL; |
| struct btmtk_uart_dev *cif_dev = NULL; |
| |
| BTMTK_INFO("%s: tty %p", __func__, tty); |
| |
| bdev = dev_get_drvdata(tty->dev); |
| if (!bdev) { |
| BTMTK_ERR("[ERR] bdev is NULL"); |
| return -ENOMEM; |
| } |
| |
| /* Init tty-related operation */ |
| tty->receive_room = 65536; |
| #if (defined(ANDROID_OS) && (KERNEL_VERSION(5, 15, 0) > LINUX_VERSION_CODE)) || defined(LINUX_OS) |
| tty->port->low_latency = 1; |
| #endif |
| |
| btmtk_uart_allocate_memory(); |
| |
| cif_dev = (struct btmtk_uart_dev *)bdev->cif_dev; |
| tty->disc_data = bdev; |
| cif_dev->tty = tty; |
| dev_set_drvdata(tty->dev, bdev); |
| |
| spin_lock_init(&cif_dev->tx_lock); |
| skb_queue_head_init(&cif_dev->tx_queue); |
| |
| /* start tx_thread */ |
| if (btmtk_tx_thread_start(bdev)) |
| return -EFAULT; |
| |
| cif_dev->stp_cursor = 2; |
| cif_dev->stp_dlen = 0; |
| |
| /* definition changed!! */ |
| if (tty->ldisc->ops->flush_buffer) |
| tty->ldisc->ops->flush_buffer(tty); |
| |
| tty_driver_flush_buffer(tty); |
| |
| BTMTK_INFO("%s: tty done %p", __func__, tty); |
| |
| return 0; |
| } |
| |
| /* btmtk_uart_tty_disconnect |
| * |
| * Called when the line discipline is changed to something |
| * else, the tty is closed, or the tty detects a hangup. |
| */ |
| static void btmtk_uart_tty_disconnect(struct tty_struct *tty) |
| { |
| struct btmtk_dev *bdev = tty->disc_data; |
| #if (USE_DEVICE_NODE == 0) |
| struct btmtk_uart_dev *cif_dev = (struct btmtk_uart_dev *)bdev->cif_dev; |
| btmtk_woble_uninitialize(&cif_dev->bt_woble); |
| #endif |
| BTMTK_INFO("%s: tty %p", __func__, tty); |
| cancel_work_sync(&bdev->reset_waker); |
| btmtk_tx_thread_exit(bdev->cif_dev); |
| btmtk_main_cif_disconnect_notify(bdev, HCI_UART); |
| } |
| |
| /* |
| * We don't provide read/write/poll interface for user space. |
| */ |
| #if (defined(ANDROID_OS) && (KERNEL_VERSION(5, 10, 0) > LINUX_VERSION_CODE)) || defined(LINUX_OS) |
| static ssize_t btmtk_uart_tty_read(struct tty_struct *tty, struct file *file, |
| unsigned char *buf, size_t count) |
| #else |
| static ssize_t btmtk_uart_tty_read(struct tty_struct *tty, struct file *file, |
| unsigned char *buf, size_t nr, |
| void **cookie, unsigned long offset) |
| #endif |
| { |
| BTMTK_INFO("%s: tty %p", __func__, tty); |
| return 0; |
| } |
| |
| static ssize_t btmtk_uart_tty_write(struct tty_struct *tty, struct file *file, |
| const unsigned char *data, size_t count) |
| { |
| BTMTK_INFO("%s: tty %p", __func__, tty); |
| return 0; |
| } |
| |
| static unsigned int btmtk_uart_tty_poll(struct tty_struct *tty, struct file *filp, poll_table *wait) |
| { |
| unsigned int mask = 0; |
| struct btmtk_dev *bdev = tty->disc_data; |
| struct btmtk_uart_dev *cif_dev = bdev->cif_dev; |
| |
| if (cif_dev->subsys_reset == 1) { |
| mask |= POLLIN | POLLRDNORM; /* readable */ |
| BTMTK_INFO("%s: tty %p", __func__, tty); |
| } |
| return mask; |
| } |
| |
| /* btmtk_uart_tty_ioctl() |
| * |
| * Process IOCTL system call for the tty device. |
| * |
| * Arguments: |
| * |
| * tty pointer to tty instance data |
| * file pointer to open file object for device |
| * cmd IOCTL command code |
| * arg argument for IOCTL call (cmd dependent) |
| * |
| * Return Value: Command dependent |
| */ |
| static int btmtk_uart_tty_ioctl(struct tty_struct *tty, struct file *file, |
| unsigned int cmd, unsigned long arg) |
| { |
| int err = 0; |
| #if (USE_DEVICE_NODE == 0) |
| int cif_event = 0; |
| struct btmtk_cif_state *cif_state = NULL; |
| #endif |
| struct UART_CONFIG uart_cfg; |
| struct btmtk_dev *bdev = tty->disc_data; |
| struct btmtk_uart_dev *cif_dev = NULL; |
| |
| if (bdev == NULL) { |
| BTMTK_ERR("%s: bdev is NULL", __func__); |
| return -1; |
| } |
| |
| cif_dev = (struct btmtk_uart_dev *)bdev->cif_dev; |
| |
| BTMTK_INFO("%s: tty %p cmd = %u", __func__, tty, cmd); |
| |
| switch (cmd) { |
| case HCIUARTSETPROTO: |
| BTMTK_INFO("%s: <!!> Set low_latency to TRUE <!!>", __func__); |
| #if (defined(ANDROID_OS) && (KERNEL_VERSION(5, 15, 0) > LINUX_VERSION_CODE)) || defined(LINUX_OS) |
| tty->port->low_latency = 1; |
| #endif |
| break; |
| case HCIUARTSETBAUD: |
| if (copy_from_user(&uart_cfg, (struct UART_CONFIG __user *)arg, |
| sizeof(struct UART_CONFIG))) |
| return -ENOMEM; |
| cif_dev->uart_cfg = uart_cfg; |
| BTMTK_INFO("%s: <!!> Set BAUDRATE, fc = %d iBaudrate = %d <!!>", |
| __func__, (int)uart_cfg.fc, uart_cfg.iBaudrate); |
| #if (USE_DEVICE_NODE == 0) |
| err = btmtk_uart_send_set_uart_cmd(bdev->hdev, &uart_cfg); |
| #endif |
| break; |
| case HCIUARTSETWAKEUP: |
| BTMTK_INFO("%s: <!!> Send Wakeup <!!>", __func__); |
| #if (USE_DEVICE_NODE == 0) |
| err = btmtk_uart_send_wakeup_cmd(bdev->hdev); |
| #endif |
| break; |
| case HCIUARTGETBAUD: |
| BTMTK_INFO("%s: <!!> Get BAUDRATE <!!>", __func__); |
| #if (USE_DEVICE_NODE == 0) |
| err = btmtk_uart_send_query_uart_cmd(bdev->hdev); |
| #endif |
| break; |
| case HCIUARTSETSTP: |
| BTMTK_INFO("%s: <!!> Set STP mandatory command <!!>", __func__); |
| break; |
| case HCIUARTLOADPATCH: |
| |
| #if (USE_DEVICE_NODE == 0) |
| BTMTK_INFO("%s: <!!> Set HCIUARTLOADPATCH command <!!>", __func__); |
| |
| err = btmtk_load_rom_patch(bdev); |
| cif_event = HIF_EVENT_PROBE; |
| cif_state = &bdev->cif_state[cif_event]; |
| /* Set End/Error state */ |
| if (err == 0) |
| btmtk_set_chip_state((void *)bdev, cif_state->ops_end); |
| else { |
| BTMTK_ERR("%s: Set HCIUARTLOADPATCH command failed (%d)", __func__, err); |
| btmtk_set_chip_state((void *)bdev, cif_state->ops_error); |
| } |
| |
| err = btmtk_woble_initialize(bdev, &cif_dev->bt_woble); |
| if (err < 0) |
| BTMTK_ERR("btmtk_woble_initialize failed!"); |
| else |
| BTMTK_ERR("btmtk_woble_initialize"); |
| #endif |
| break; |
| case HCIUARTINIT: |
| BTMTK_INFO("%s: <!!> Set HCIUARTINIT <!!>", __func__); |
| err = btmtk_uart_init(bdev); |
| break; |
| default: |
| /* pr_info("<!!> n_tty_ioctl_helper <!!>\n"); */ |
| err = n_tty_ioctl_helper(tty, file, cmd, arg); |
| break; |
| }; |
| |
| return err; |
| } |
| |
| #if (defined(ANDROID_OS) && (KERNEL_VERSION(5, 4, 0) > LINUX_VERSION_CODE)) || defined(LINUX_OS) |
| static long btmtk_uart_tty_compat_ioctl(struct tty_struct *tty, struct file *file, |
| unsigned int cmd, unsigned long arg) |
| #else |
| static int btmtk_uart_tty_compat_ioctl(struct tty_struct *tty, struct file *file, |
| unsigned int cmd, unsigned long arg) |
| #endif |
| { |
| int err = 0; |
| int cif_event = 0; |
| struct btmtk_cif_state *cif_state = NULL; |
| struct UART_CONFIG uart_cfg; |
| struct btmtk_dev *bdev = tty->disc_data; |
| struct btmtk_uart_dev *cif_dev = NULL; |
| |
| if (bdev == NULL) { |
| BTMTK_ERR("%s: bdev is NULL", __func__); |
| return -1; |
| } |
| |
| cif_dev = (struct btmtk_uart_dev *)bdev->cif_dev; |
| |
| BTMTK_INFO("%s: tty %p cmd = %u", __func__, tty, cmd); |
| |
| switch (cmd) { |
| case HCIUARTSETPROTO: |
| BTMTK_INFO("%s: <!!> Set low_latency to TRUE <!!>", __func__); |
| #if (defined(ANDROID_OS) && (KERNEL_VERSION(5, 15, 0) > LINUX_VERSION_CODE)) || defined(LINUX_OS) |
| tty->port->low_latency = 1; |
| #endif |
| break; |
| case HCIUARTSETBAUD: |
| if (copy_from_user(&uart_cfg, (struct UART_CONFIG __user *)arg, |
| sizeof(struct UART_CONFIG))) |
| return -ENOMEM; |
| cif_dev->uart_cfg = uart_cfg; |
| BTMTK_INFO("%s: <!!> Set BAUDRATE, fc = %d iBaudrate = %d <!!>", |
| __func__, (int)uart_cfg.fc, uart_cfg.iBaudrate); |
| err = btmtk_uart_send_set_uart_cmd(bdev->hdev, &uart_cfg); |
| break; |
| case HCIUARTSETWAKEUP: |
| BTMTK_INFO("%s: <!!> Send Wakeup <!!>", __func__); |
| err = btmtk_uart_send_wakeup_cmd(bdev->hdev); |
| break; |
| case HCIUARTGETBAUD: |
| BTMTK_INFO("%s: <!!> Get BAUDRATE <!!>", __func__); |
| err = btmtk_uart_send_query_uart_cmd(bdev->hdev); |
| break; |
| case HCIUARTSETSTP: |
| BTMTK_INFO("%s: <!!> Set STP mandatory command <!!>", __func__); |
| break; |
| case HCIUARTLOADPATCH: |
| BTMTK_INFO("%s: <!!> Set HCIUARTLOADPATCH command <!!>", __func__); |
| err = btmtk_load_rom_patch(bdev); |
| cif_event = HIF_EVENT_PROBE; |
| cif_state = &bdev->cif_state[cif_event]; |
| /* Set End/Error state */ |
| if (err == 0) |
| btmtk_set_chip_state((void *)bdev, cif_state->ops_end); |
| else { |
| BTMTK_ERR("%s: Set HCIUARTLOADPATCH command failed (%d)", __func__, err); |
| btmtk_set_chip_state((void *)bdev, cif_state->ops_error); |
| } |
| |
| err = btmtk_woble_initialize(bdev, &cif_dev->bt_woble); |
| if (err < 0) |
| BTMTK_ERR("btmtk_woble_initialize failed!"); |
| else |
| BTMTK_ERR("btmtk_woble_initialize"); |
| |
| break; |
| case HCIUARTINIT: |
| BTMTK_INFO("%s: <!!> Set HCIUARTINIT <!!>", __func__); |
| err = btmtk_uart_init(bdev); |
| break; |
| default: |
| /* pr_info("<!!> n_tty_ioctl_helper <!!>\n"); */ |
| err = n_tty_ioctl_helper(tty, file, cmd, arg); |
| break; |
| }; |
| |
| return err; |
| } |
| |
| #if (defined(ANDROID_OS) && (KERNEL_VERSION(5, 15, 0) > LINUX_VERSION_CODE)) || defined(LINUX_OS) |
| static void btmtk_uart_tty_receive(struct tty_struct *tty, const u8 *data, char *flags, int count) |
| #else |
| static void btmtk_uart_tty_receive(struct tty_struct *tty, const u8 *data, const char *flags, int count) |
| #endif |
| { |
| int ret = -1; |
| struct btmtk_uart_dev *cif_dev = NULL; |
| unsigned char fstate = BTMTK_FOPS_STATE_INIT; |
| struct btmtk_dev *bdev = tty->disc_data; |
| |
| if (bdev == NULL) { |
| BTMTK_ERR("%s: bdev is NULL", __func__); |
| return; |
| } |
| |
| fstate = btmtk_fops_get_state(bdev); |
| if (fstate == BTMTK_FOPS_STATE_CLOSED) { |
| BTMTK_DBG_RAW(data, count, "[SKIP] %s: count[%d]", __func__, count); |
| return; |
| } |
| |
| cif_dev = (struct btmtk_uart_dev *)bdev->cif_dev; |
| #if (SLEEP_ENABLE == 1) |
| //BTMTK_INFO_RAW(data, count, "%s: count[%d]", __func__, count); |
| |
| /* if flag is BTMTK_FW_OWNING not set driver own , because data is fw own event */ |
| if (data != NULL && (count > 1 || data[0] != 0x00) && cif_dev->own_state != BTMTK_FW_OWNING) { |
| atomic_set(&cif_dev->need_drv_own, 1); |
| atomic_set(&cif_dev->fw_wake, 1); |
| wake_up_interruptible(&tx_wait_q); |
| } |
| #endif |
| |
| /* add hci device part */ |
| ret = btmtk_recv(bdev->hdev, data, count); |
| if (ret < 0) |
| BTMTK_ERR("%s, ret = %d", __func__, ret); |
| } |
| |
| /* btmtk_uart_tty_wakeup() |
| * |
| * Callback for transmit wakeup. Called when low level |
| * device driver can accept more send data. |
| * |
| * Arguments: tty pointer to associated tty instance data |
| * Return Value: None |
| */ |
| static void btmtk_uart_tty_wakeup(struct tty_struct *tty) |
| { |
| BTMTK_INFO("%s: tty %p", __func__, tty); |
| } |
| |
| #if (SLEEP_ENABLE == 0) |
| static int btmtk_uart_fw_own(struct btmtk_dev *bdev) |
| { |
| int ret; |
| u8 cmd[] = { 0x01, 0x6F, 0xFC, 0x05, 0x01, 0x03, 0x01, 0x00, 0x01 }; |
| u8 evt[] = { 0x04, 0xE4, 0x06, 0x02, 0x03, 0x02, 0x00, 0x00, 0x01 }; |
| |
| |
| BTMTK_INFO("%s", __func__); |
| ret = btmtk_main_send_cmd(bdev, cmd, FWOWN_CMD_LEN, evt, OWNTYPE_EVT_LEN, |
| DELAY_TIMES, RETRY_TIMES, BTMTK_TX_CMD_FROM_DRV); |
| |
| return ret; |
| } |
| |
| static int btmtk_uart_driver_own(struct btmtk_dev *bdev) |
| { |
| int ret; |
| u8 cmd[] = { 0xFF }; |
| u8 evt[] = { 0x04, 0xE4, 0x06, 0x02, 0x03, 0x02, 0x00, 0x00, 0x03 }; |
| |
| BTMTK_INFO("%s", __func__); |
| ret = btmtk_main_send_cmd(bdev, cmd, DRVOWN_CMD_LEN, evt, OWNTYPE_EVT_LEN, |
| DELAY_TIMES, RETRY_TIMES, BTMTK_TX_CMD_FROM_DRV); |
| |
| return ret; |
| } |
| |
| #else //(SLEEP_ENABLE == 1) |
| static int btmtk_uart_fw_own(struct btmtk_dev *bdev) |
| { |
| int ret = 0; |
| struct btmtk_uart_dev *cif_dev = NULL; |
| u8 cmd[] = { 0x01, 0x6F, 0xFC, 0x06, 0x01, 0x03, 0x02, 0x00, 0x01, 0x01 }; |
| u8 evt[] = { 0x04, 0xE4, 0x07, 0x02, 0x03, 0x03, 0x00, 0x00, 0x01, 0x01 }; |
| |
| if (bdev == NULL) { |
| BTMTK_ERR("%s: bdev is NULL", __func__); |
| return -1; |
| } |
| |
| UART_OWN_MUTEX_LOCK(); |
| cif_dev = (struct btmtk_uart_dev *)bdev->cif_dev; |
| /* no need to compare BTMTK_FW_OWNING because the state must be fw_own/fail before leaving mutex */ |
| if (cif_dev->own_state == BTMTK_FW_OWN || cif_dev->own_state == BTMTK_OWN_FAIL) { |
| BTMTK_WARN("Already at fw own state or error state[%d], skip", cif_dev->own_state); |
| goto unlock; |
| } |
| |
| cif_dev->own_state = BTMTK_FW_OWNING; |
| |
| if (cif_dev->sleep_en) { |
| ret = btmtk_main_send_cmd(bdev, cmd, FWOWN_CMD_LEN, evt, OWNTYPE_EVT_LEN, |
| DELAY_TIMES, RETRY_TIMES, BTMTK_TX_PKT_SEND_DIRECT); |
| } else |
| ret = 0; |
| |
| if (ret < 0) { |
| BTMTK_ERR("%s failed!!", __func__); |
| cif_dev->own_state = BTMTK_OWN_FAIL; |
| goto unlock; |
| } else { |
| cif_dev->own_state = BTMTK_FW_OWN; |
| atomic_set(&cif_dev->fw_wake, 0); |
| |
| #if IS_ENABLED(CONFIG_MTK_UARTHUB) |
| /* Clr TX,RX request, let uarthub can sleep */ |
| if (cif_dev->hub_en && cif_dev->sleep_en) { |
| ret = mtk8250_uart_hub_clear_request(); |
| if (ret) |
| BTMTK_ERR("%s mtk8250_uart_hub_clear_request fail ret[%d]", __func__, ret); |
| } |
| #endif |
| BTMTK_INFO("%s success", __func__); |
| } |
| unlock: |
| UART_OWN_MUTEX_UNLOCK(); |
| return ret; |
| } |
| |
| static int btmtk_uart_driver_own(struct btmtk_dev *bdev) |
| { |
| int ret = 0, retry = 5; |
| struct btmtk_uart_dev *cif_dev = NULL; |
| u8 wakeup_cmd[] = { 0xFF }; |
| u8 fw_own_clr_cmd[] = { 0x01, 0x6F, 0xFC, 0x06, 0x01, 0x03, 0x02, 0x00, 0x03, 0x01 }; |
| u8 evt[] = { 0x04, 0xE4, 0x07, 0x02, 0x03, 0x03, 0x00, 0x00, 0x03, 0x01 }; |
| |
| if (bdev == NULL) { |
| BTMTK_ERR("%s: bdev is NULL", __func__); |
| return -1; |
| } |
| |
| UART_OWN_MUTEX_LOCK(); |
| cif_dev = (struct btmtk_uart_dev *)bdev->cif_dev; |
| if (cif_dev->own_state == BTMTK_DRV_OWN || cif_dev->own_state == BTMTK_OWN_FAIL) { |
| //BTMTK_WARN("Already at driver own state or error state[%d], skip", cif_dev->own_state); |
| btmtk_uart_update_fw_own_timer(cif_dev); |
| goto unlock; |
| } |
| |
| cif_dev->own_state = BTMTK_DRV_OWNING; |
| |
| #if IS_ENABLED(CONFIG_MTK_UARTHUB) |
| if (cif_dev->hub_en && cif_dev->sleep_en) { |
| ret = btmtk_wakeup_uarthub(); |
| if (ret < 0) { |
| BTMTK_ERR("%s wakeup uart_hub fail", __func__); |
| cif_dev->own_state = BTMTK_OWN_FAIL; |
| goto unlock; |
| } |
| } |
| #endif |
| |
| if (cif_dev->sleep_en) { |
| do { |
| /* if fw already wake, no need to send 0xFF and wait 5ms before clr fw own */ |
| if (!atomic_read(&cif_dev->fw_wake)){ |
| /* no need to wait event */ |
| ret = btmtk_main_send_cmd(bdev, wakeup_cmd, DRVOWN_CMD_LEN, NULL, 0, |
| DELAY_TIMES, RETRY_TIMES, BTMTK_TX_PKT_SEND_DIRECT); |
| if (ret < 0) |
| BTMTK_ERR("%s wakeup_cmd fail retry[%d]", __func__, retry); |
| /* wait a while for fw wakeup */ |
| usleep_range(5000, 5100); |
| } |
| /* fw own clr cmd for notice is wakeup by bt driver */ |
| ret = btmtk_main_send_cmd(bdev, fw_own_clr_cmd, 10, evt, OWNTYPE_EVT_LEN, |
| DELAY_TIMES, RETRY_TIMES, BTMTK_TX_PKT_SEND_DIRECT); |
| if (ret < 0) |
| BTMTK_ERR("%s fw_own_clr_cmd fail retry[%d]", __func__, retry); |
| } while (ret < 0 && --retry); |
| } else |
| ret = 0; |
| |
| if (ret < 0) { |
| BTMTK_ERR("%s fail", __func__); |
| cif_dev->own_state = BTMTK_OWN_FAIL; |
| goto unlock; |
| } else if (cif_dev->no_fw_own == 0) { |
| btmtk_uart_update_fw_own_timer(cif_dev); |
| cif_dev->own_state = BTMTK_DRV_OWN; |
| complete(&bdev->drv_own_comp); |
| BTMTK_INFO("%s success", __func__); |
| } |
| |
| unlock: |
| UART_OWN_MUTEX_UNLOCK(); |
| return ret; |
| } |
| #endif |
| |
| static int btmtk_cif_probe(struct tty_struct *tty) |
| { |
| int ret = -1; |
| int cif_event = 0; |
| struct btmtk_cif_state *cif_state = NULL; |
| struct btmtk_dev *bdev = NULL; |
| struct btmtk_uart_dev *cif_dev; |
| #if (USE_DEVICE_NODE == 1) |
| struct btmtk_main_info *bmain_info = btmtk_get_main_info(); |
| #endif |
| |
| /* Mediatek Driver Version */ |
| BTMTK_INFO("%s: MTK BT Driver Version: %s", __func__, VERSION); |
| |
| /* Retrieve priv data and set to interface structure */ |
| bdev = btmtk_get_dev(); |
| if (!bdev) { |
| BTMTK_INFO("%s: bdev is NULL", __func__); |
| return -ENODEV; |
| } |
| |
| cif_dev = devm_kzalloc(tty->dev, sizeof(*cif_dev), GFP_KERNEL); |
| if (!cif_dev) |
| return -ENOMEM; |
| |
| bdev->intf_dev = tty->dev; |
| bdev->cif_dev = cif_dev; |
| dev_set_drvdata(tty->dev, bdev); |
| g_tty = tty; |
| |
| /* Retrieve current HIF event state */ |
| cif_event = HIF_EVENT_PROBE; |
| if (BTMTK_CIF_IS_NULL(bdev, cif_event)) { |
| /* Error */ |
| BTMTK_WARN("%s priv setting is NULL", __func__); |
| return -ENODEV; |
| } |
| |
| cif_state = &bdev->cif_state[cif_event]; |
| |
| /* Set Entering state */ |
| btmtk_set_chip_state((void *)bdev, cif_state->ops_enter); |
| |
| /* Init completion */ |
| init_completion(&bdev->dump_comp); |
| init_completion(&bdev->drv_own_comp); |
| |
| /* Init semaphore */ |
| sema_init(&cif_dev->evt_comp_sem, 1); |
| |
| /* Do HIF events */ |
| ret = btmtk_uart_tty_probe(tty); |
| #if (USE_DEVICE_NODE == 1) |
| btmtk_connv3_sub_drv_init(bdev); |
| btmtk_pwrctrl_register_evt(); |
| |
| /* Init coredump */ |
| bmain_info->hif_hook.coredump_handler = connv3_coredump_init(CONNV3_DEBUG_TYPE_BT, NULL); |
| #endif |
| return ret; |
| } |
| |
| static void btmtk_cif_disconnect(struct tty_struct *tty) |
| { |
| int cif_event = 0; |
| struct btmtk_cif_state *cif_state = NULL; |
| struct btmtk_dev *bdev = NULL; |
| struct btmtk_uart_dev *cif_dev; |
| |
| bdev = dev_get_drvdata(tty->dev); |
| if (bdev == NULL) { |
| BTMTK_ERR("%s: bdev is NULL", __func__); |
| return; |
| } |
| |
| cif_dev = bdev->cif_dev; |
| |
| /* Retrieve current HIF event state */ |
| cif_event = HIF_EVENT_DISCONNECT; |
| if (BTMTK_CIF_IS_NULL(bdev, cif_event)) { |
| /* Error */ |
| BTMTK_WARN("%s priv setting is NULL", __func__); |
| return; |
| } |
| |
| cif_state = &bdev->cif_state[cif_event]; |
| |
| btmtk_uart_cif_mutex_lock(bdev); |
| /* Set Entering state */ |
| btmtk_set_chip_state((void *)bdev, cif_state->ops_enter); |
| |
| /* temp solution for disconnect at random time would KE */ |
| BTMTK_INFO("%s wait", __func__); |
| msleep(3000); |
| |
| /* Do HIF events */ |
| btmtk_uart_tty_disconnect(tty); |
| |
| /* Set End/Error state */ |
| btmtk_set_chip_state((void *)bdev, cif_state->ops_end); |
| btmtk_uart_cif_mutex_unlock(bdev); |
| devm_kfree(tty->dev, cif_dev); |
| |
| BTMTK_INFO("%s end", __func__); |
| } |
| |
| static int btmtk_cif_suspend(void) |
| { |
| int cif_event = 0, state, ret = 0; |
| struct btmtk_cif_state *cif_state = NULL; |
| struct tty_struct *tty = g_tty; |
| struct btmtk_dev *bdev = NULL; |
| struct btmtk_main_info *bmain_info = btmtk_get_main_info(); |
| unsigned char fstate = BTMTK_FOPS_STATE_INIT; |
| #if (USE_DEVICE_NODE == 0) |
| struct btmtk_uart_dev *cif_dev = (struct btmtk_uart_dev *)bdev->cif_dev; |
| struct btmtk_woble *bt_woble = &cif_dev->bt_woble; |
| #endif |
| BTMTK_INFO("%s", __func__); |
| |
| if (tty == NULL) { |
| BTMTK_ERR("%s: tty is NULL, maybe not run btmtk_cif_probe yet", __func__); |
| return -EAGAIN; |
| } |
| |
| bdev = dev_get_drvdata(tty->dev); |
| |
| if (bdev == NULL) { |
| BTMTK_ERR("%s: bdev is NULL", __func__); |
| return -EAGAIN; |
| } |
| |
| if (bdev->get_hci_reset) { |
| BTMTK_WARN("open flow not ready(%d), retry", bdev->get_hci_reset); |
| return -EAGAIN; |
| } |
| |
| if (bmain_info->reset_stack_flag) { |
| BTMTK_WARN("reset stack flag(%d), retry", bmain_info->reset_stack_flag); |
| return -EAGAIN; |
| } |
| |
| fstate = btmtk_fops_get_state(bdev); |
| if ((fstate == BTMTK_FOPS_STATE_CLOSING) || |
| (fstate == BTMTK_FOPS_STATE_OPENING)) { |
| BTMTK_WARN("%s: fops open/close is on-going, retry", __func__); |
| return -EAGAIN; |
| } |
| |
| if (bdev->suspend_count++) { |
| BTMTK_WARN("Has suspended. suspend_count: %d, end", bdev->suspend_count); |
| return 0; |
| } |
| |
| state = btmtk_get_chip_state(bdev); |
| /* Retrieve current HIF event state */ |
| if (state == BTMTK_STATE_FW_DUMP) { |
| BTMTK_WARN("%s: FW dumping ongoing, don't dos suspend flow!!!", __func__); |
| cif_event = HIF_EVENT_FW_DUMP; |
| } else |
| cif_event = HIF_EVENT_SUSPEND; |
| |
| cif_state = &bdev->cif_state[cif_event]; |
| |
| /* Set Entering state */ |
| btmtk_set_chip_state((void *)bdev, cif_state->ops_enter); |
| |
| #if (USE_DEVICE_NODE == 0) //temp for resolve fw/driver own fail in sp |
| ret = btmtk_woble_suspend(bt_woble); |
| if (ret < 0) |
| BTMTK_ERR("%s: btmtk_woble_suspend return fail %d", __func__, ret); |
| |
| |
| ret = btmtk_uart_fw_own(bdev); |
| if (ret < 0) |
| BTMTK_ERR("%s: set fw own return fail %d", __func__, ret); |
| #endif |
| |
| /* Set End/Error state */ |
| if (ret == 0) |
| btmtk_set_chip_state((void *)bdev, cif_state->ops_end); |
| else |
| btmtk_set_chip_state((void *)bdev, cif_state->ops_error); |
| |
| return 0; |
| } |
| |
| static int btmtk_cif_resume(void) |
| { |
| struct tty_struct *tty = g_tty; |
| struct btmtk_dev *bdev = NULL; |
| struct btmtk_cif_state *cif_state = NULL; |
| int ret = 0; |
| |
| #if (USE_DEVICE_NODE == 0) |
| struct btmtk_uart_dev *cif_dev = (struct btmtk_uart_dev *)bdev->cif_dev; |
| struct btmtk_woble *bt_woble = &cif_dev->bt_woble; |
| #endif |
| |
| BTMTK_INFO("%s", __func__); |
| |
| if (tty == NULL) { |
| BTMTK_ERR("%s: tty is NULL, maybe not run btmtk_cif_probe yet", __func__); |
| return -EAGAIN; |
| } |
| |
| bdev = dev_get_drvdata(tty->dev); |
| |
| if (bdev == NULL) { |
| BTMTK_ERR("%s: bdev is NULL", __func__); |
| return -1; |
| } |
| bdev->suspend_count--; |
| |
| if (bdev->suspend_count) { |
| BTMTK_INFO("data->suspend_count %d, return 0", bdev->suspend_count); |
| return 0; |
| } |
| |
| cif_state = &bdev->cif_state[HIF_EVENT_RESUME]; |
| |
| /* Set Entering state */ |
| btmtk_set_chip_state((void *)bdev, cif_state->ops_enter); |
| |
| #if (USE_DEVICE_NODE == 0) //temp for resolve fw/driver own fail in sp |
| ret = btmtk_uart_driver_own(bdev); |
| if (ret < 0) |
| BTMTK_ERR("%s: set driver own return fail %d", __func__, ret); |
| |
| ret = btmtk_woble_resume(bt_woble); |
| if (ret < 0) |
| BTMTK_ERR("%s: btmtk_woble_resume return fail %d", __func__, ret); |
| #endif |
| |
| /* Set End/Error state */ |
| if (ret == 0) |
| btmtk_set_chip_state((void *)bdev, cif_state->ops_end); |
| else |
| btmtk_set_chip_state((void *)bdev, cif_state->ops_error); |
| return 0; |
| } |
| |
| static int btmtk_pm_notification(struct notifier_block *this, unsigned long event, void *ptr) |
| { |
| int retry = 40; |
| int ret = NOTIFY_DONE; |
| |
| BTMTK_DBG("event = %ld", event); |
| |
| /* if get into suspend flow while doing audio pinmux setting |
| * it may have chance mischange uart pinmux we want to write |
| * retry and wait audio setting done then do suspend flow |
| */ |
| switch (event) { |
| case PM_SUSPEND_PREPARE: |
| do { |
| ret = btmtk_cif_suspend(); |
| if (ret == 0) { |
| break; |
| } else if (retry <= 0) { |
| BTMTK_ERR("not ready to suspend"); |
| #if (USE_DEVICE_NODE == 0) |
| return NOTIFY_STOP; |
| #else |
| return NOTIFY_BAD; |
| #endif |
| } |
| msleep(50); |
| } while (retry-- > 0); |
| break; |
| case PM_POST_SUSPEND: |
| btmtk_cif_resume(); |
| break; |
| default: |
| break; |
| } |
| |
| return ret; |
| } |
| |
| static struct notifier_block btmtk_pm_notifier = { |
| .notifier_call = btmtk_pm_notification, |
| }; |
| |
| static int uart_register(void) |
| { |
| u32 err = 0; |
| |
| BTMTK_INFO("%s", __func__); |
| |
| /* Register the tty discipline */ |
| memset(&btmtk_uart_ldisc, 0, sizeof(btmtk_uart_ldisc)); |
| #if (defined(ANDROID_OS) && (KERNEL_VERSION(5, 15, 0) > LINUX_VERSION_CODE)) || defined(LINUX_OS) |
| btmtk_uart_ldisc.magic = TTY_LDISC_MAGIC; |
| #else |
| btmtk_uart_ldisc.num = N_MTK; |
| #endif |
| btmtk_uart_ldisc.name = "n_mtk"; |
| btmtk_uart_ldisc.open = btmtk_cif_probe; |
| btmtk_uart_ldisc.close = btmtk_cif_disconnect; |
| btmtk_uart_ldisc.read = btmtk_uart_tty_read; |
| btmtk_uart_ldisc.write = btmtk_uart_tty_write; |
| btmtk_uart_ldisc.ioctl = btmtk_uart_tty_ioctl; |
| btmtk_uart_ldisc.compat_ioctl = btmtk_uart_tty_compat_ioctl; |
| btmtk_uart_ldisc.poll = btmtk_uart_tty_poll; |
| btmtk_uart_ldisc.receive_buf = btmtk_uart_tty_receive; |
| btmtk_uart_ldisc.write_wakeup = btmtk_uart_tty_wakeup; |
| btmtk_uart_ldisc.owner = THIS_MODULE; |
| |
| #if (defined(ANDROID_OS) && (KERNEL_VERSION(5, 15, 0) > LINUX_VERSION_CODE)) || defined(LINUX_OS) |
| err = tty_register_ldisc(N_MTK, &btmtk_uart_ldisc); |
| #else |
| err = tty_register_ldisc(&btmtk_uart_ldisc); |
| #endif |
| if (err) { |
| BTMTK_ERR("MTK line discipline registration failed. (%d)", err); |
| return err; |
| } |
| err = register_pm_notifier(&btmtk_pm_notifier); |
| if (err) { |
| BTMTK_ERR("Register pm notifier failed. (%d)", err); |
| return err; |
| } |
| |
| BTMTK_INFO("%s done", __func__); |
| return err; |
| } |
| static int uart_deregister(void) |
| { |
| u32 err = 0; |
| |
| err = unregister_pm_notifier(&btmtk_pm_notifier); |
| if (err) { |
| BTMTK_ERR("Unregister pm notifier failed. (%d)", err); |
| return err; |
| } |
| |
| #if (defined(ANDROID_OS) && (KERNEL_VERSION(5, 15, 0) > LINUX_VERSION_CODE)) || defined(LINUX_OS) |
| err = tty_unregister_ldisc(N_MTK); |
| if (err) { |
| BTMTK_ERR("line discipline registration failed. (%d)", err); |
| return err; |
| } |
| #else |
| tty_unregister_ldisc(&btmtk_uart_ldisc); |
| #endif |
| |
| return 0; |
| } |
| |
| int btmtk_cif_register(void) |
| { |
| int ret = -1; |
| struct hif_hook_ptr hook; |
| |
| BTMTK_INFO("%s", __func__); |
| |
| memset(&hook, 0, sizeof(hook)); |
| #if (USE_DEVICE_NODE == 1) |
| hook.fw_log_state = fw_log_bt_state_cb; |
| hook.log_init = btmtk_connsys_log_init; |
| hook.log_read_to_user = btmtk_connsys_log_read_to_user; |
| hook.log_get_buf_size = btmtk_connsys_log_get_buf_size; |
| hook.log_deinit = btmtk_connsys_log_deinit; |
| hook.log_handler = btmtk_connsys_log_handler; |
| hook.init = btmtk_chardev_init; |
| hook.dump_debug_sop = btmtk_uart_sp_dump_debug_sop; |
| #endif |
| hook.open = btmtk_uart_open; |
| hook.close = btmtk_uart_close; |
| hook.pre_open = btmtk_uart_pre_open; |
| hook.open_done = btmtk_uart_open_done; |
| hook.reg_read = btmtk_uart_read_register; |
| hook.send_cmd = btmtk_uart_send_cmd; |
| hook.send_and_recv = btmtk_uart_send_and_recv; |
| hook.event_filter = btmtk_uart_event_filter; |
| hook.subsys_reset = btmtk_uart_subsys_reset; |
| hook.chip_reset_notify = btmtk_uart_chip_reset_notify; |
| hook.cif_mutex_lock = btmtk_uart_cif_mutex_lock; |
| hook.cif_mutex_unlock = btmtk_uart_cif_mutex_unlock; |
| hook.dl_dma = btmtk_uart_load_fw_patch_using_dma; |
| hook.waker_notify = btmtk_uart_waker_notify; |
| hook.set_para= btmtk_uart_set_para; |
| btmtk_reg_hif_hook(&hook); |
| |
| ret = uart_register(); |
| if (ret < 0) { |
| BTMTK_ERR("*** UART registration fail(%d)! ***", ret); |
| return ret; |
| } |
| |
| BTMTK_INFO("%s: Done", __func__); |
| return 0; |
| } |
| |
| int btmtk_cif_deregister(void) |
| { |
| int ret = -1; |
| |
| BTMTK_INFO("%s", __func__); |
| ret = uart_deregister(); |
| if (ret < 0) { |
| BTMTK_ERR("*** UART deregistration fail(%d)! ***", ret); |
| return ret; |
| } |
| BTMTK_INFO("%s: Done", __func__); |
| return 0; |
| } |
| |
| #if (USE_DEVICE_NODE == 1) |
| void btmtk_connsys_log_init(void (*log_event_cb)(void)) |
| { |
| if (connv3_log_init(CONNV3_DEBUG_TYPE_BT, 2048, 2048, log_event_cb)) |
| BTMTK_ERR("*** %s fail! ***", __func__); |
| } |
| |
| void btmtk_connsys_log_deinit(void) |
| { |
| connv3_log_deinit(CONNV3_DEBUG_TYPE_BT); |
| } |
| |
| int btmtk_connsys_log_handler(u8 *buf, u32 size) |
| { |
| return connv3_log_handler(CONNV3_DEBUG_TYPE_BT, CONNV3_LOG_TYPE_PRIMARY, buf, size); |
| } |
| |
| ssize_t btmtk_connsys_log_read_to_user(char __user *buf, size_t count) |
| { |
| return connv3_log_read_to_user(CONNV3_DEBUG_TYPE_BT, buf, count); |
| } |
| |
| unsigned int btmtk_connsys_log_get_buf_size(void) |
| { |
| return connv3_log_get_buf_size(CONNV3_DEBUG_TYPE_BT); |
| } |
| #endif |