From: wdc Date: Sat, 4 May 2013 02:12:08 +0000 (+0800) Subject: rda5876 bt: update driver for android4.2 X-Git-Tag: firefly_0821_release~7174 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=f92746e099d766b2cb8390aa9665cdd2dbee301b;p=firefly-linux-kernel-4.4.55.git rda5876 bt: update driver for android4.2 --- diff --git a/arch/arm/mach-rk2928/board-rk2928-phonepad-760.c b/arch/arm/mach-rk2928/board-rk2928-phonepad-760.c index 8a5a5dd1a27f..b4bbc895789b 100755 --- a/arch/arm/mach-rk2928/board-rk2928-phonepad-760.c +++ b/arch/arm/mach-rk2928/board-rk2928-phonepad-760.c @@ -847,7 +847,7 @@ int rk2928_sd_vcc_reset(){ * SDMMC devices, include the module of SD,MMC,and SDIO.noted by xbw at 2012-03-05 **************************************************************************************************/ -#ifdef CONFIG_RFKILL_RK +#if defined CONFIG_RFKILL_RK // bluetooth rfkill device, its driver in net/rfkill/rfkill-rk.c static struct rfkill_rk_platform_data rfkill_rk_platdata = { .type = RFKILL_TYPE_BLUETOOTH, @@ -904,6 +904,35 @@ static struct platform_device device_rfkill_rk = { }, }; #endif + +#if defined CONFIG_TCC_BT_DEV +static struct tcc_bt_platform_data tcc_bt_platdata = { + + .power_gpio = { // ldoon + .io = RK2928_PIN3_PC0, + .enable = GPIO_HIGH, + .iomux = { + .name = NULL, + }, + }, + + .wake_host_gpio = { // BT_HOST_WAKE, for bt wakeup host when it is in deep sleep + .io = RK2928_PIN0_PC5, // set io to INVALID_GPIO for disable it + .enable = IRQF_TRIGGER_RISING,// set IRQF_TRIGGER_FALLING for falling, set IRQF_TRIGGER_RISING for rising + .iomux = { + .name = NULL, + }, + }, +}; + +static struct platform_device device_tcc_bt = { + .name = "tcc_bt_dev", + .id = -1, + .dev = { + .platform_data = &tcc_bt_platdata, + }, +}; +#endif #ifdef CONFIG_SDMMC_RK29 #include "board-rk2928-phonepad-sdmmc.c" #endif @@ -1364,6 +1393,9 @@ static struct platform_device *devices[] __initdata = { &device_rfkill_rk, #endif +#ifdef CONFIG_TCC_BT_DEV + &device_tcc_bt, +#endif }; //i2c #ifdef CONFIG_I2C0_RK30 diff --git a/drivers/misc/tcc_bt_dev.c b/drivers/misc/tcc_bt_dev.c index 8e160a0e2bd9..840b17d0158c 100755 --- a/drivers/misc/tcc_bt_dev.c +++ b/drivers/misc/tcc_bt_dev.c @@ -1,3 +1,4 @@ + /* * Copyright (C) 2010-2011 RDA Micro * This file belong to RDA micro @@ -10,261 +11,364 @@ #include #include #include - -//#include #include #include - -//#include -//#include - #include #include #include - #include #include #include #include +#include +#include -#ifndef ON -#define ON 1 -#endif +#include +#include -#ifndef OFF -#define OFF 0 + +#ifndef HOST_WAKE_DELAY +#define HOST_WAKE_DELAY #endif -#define LDO_ON_PIN RK2928_PIN3_PC0 -#define RDA_BT_HOST_WAKE_PIN RK2928_PIN0_PC5 +#define DEV_NAME "tcc_bt_dev" -#define BT_DEV_ON 1 -#define BT_DEV_OFF 0 +#define BT_DEV_MAJOR_NUM 234 +#define BT_DEV_MINOR_NUM 0 -#define BT_DEV_MAJOR_NUM 234 -#define BT_DEV_MINOR_NUM 0 +#define IOCTL_BT_DEV_POWER _IO(BT_DEV_MAJOR_NUM, 100) +#define IOCTL_BT_SET_EINT _IO(BT_DEV_MAJOR_NUM, 101) -//#define IOCTL_BT_DEV_POWER _IO(BT_DEV_MAJOR_NUM, 100) -//#define IOCTL_BT_DEV_SPECIFIC _IO(BT_DEV_MAJOR_NUM, 101) -//#define IOCTL_BT_DEV_IS_POWER _IO(BT_DEV_MAJOR_NUM, 102) +static wait_queue_head_t eint_wait; +static struct work_struct rda_bt_event_work; +static struct workqueue_struct *rda_bt_workqueue; +static int irq_num = -1; +static int eint_gen; +static int eint_mask; +static struct class *bt_dev_class; +static struct mutex sem; +static struct tcc_bt_platform_data *tcc_bt_pdata = NULL; -#define IOCTL_BT_DEV_POWER _IO(BT_DEV_MAJOR_NUM, 100) -#define IOCTL_BT_DEV_CTRL _IO(BT_DEV_MAJOR_NUM, 101) -#define IOCTL_BT_SET_EINT _IO(BT_DEV_MAJOR_NUM, 102) -#define IOCTL_BT_DEV_SPECIFIC _IO(BT_DEV_MAJOR_NUM, 103) +static void rda_bt_enable_irq(void) +{ + if(irq_num != -1) + { + enable_irq(irq_num); + } +} -static int bt_is_power = 0; -static int rda_bt_irq = 0; -static int irq_mask = 0; -extern void export_bt_hci_wakeup_chip(void); +static void rda_bt_disable_irq(void) +{ + if(irq_num != -1) + { + disable_irq_nosync(irq_num); + } +} -#define DEV_NAME "tcc_bt_dev" -static struct class *bt_dev_class; +static void rda_bt_work_fun(struct work_struct *work) +{ + struct hci_dev *hdev = NULL; + + /* BlueZ stack, hci_uart driver */ + hdev = hci_dev_get(0); + + if(hdev == NULL) + { + /* Avoid the early interrupt before hci0 registered */ + //printk(KERN_ALERT "hdev is NULL\n "); + } + else + { + printk(KERN_ALERT "Send host wakeup command.\n"); + hci_send_cmd(hdev, 0xC0FC, 0, NULL); + } + + rda_bt_enable_irq(); +} -typedef struct { - int module; // 0x12:CSR, 0x34:Broadcom - int TMP1; - int TMP2; -} tcc_bt_info_t; static int tcc_bt_dev_open(struct inode *inode, struct file *file) { - printk("[## BT ##] tcc_bt_dev_open\n"); - return 0; + printk("[## BT ##] tcc_bt_dev_open.\n"); + eint_gen = 0; + eint_mask = 0; + return 0; } static int tcc_bt_dev_release(struct inode *inode, struct file *file) { - printk("[## BT ##] tcc_bt_dev_release\n"); - return 0; + printk("[## BT ##] tcc_bt_dev_release.\n"); + eint_gen = 0; + eint_mask = 0; + return 0; } -static int tcc_bt_power_control(int on_off) +/***************************************************************************** + * tcc_bt_dev_poll +*****************************************************************************/ + +static unsigned int tcc_bt_dev_poll(struct file *file, poll_table *wait) { -// volatile PGPIO pGPIO = (volatile PGPIO)tcc_p2v(HwGPIO_BASE); + uint32_t mask = 0; - printk("[## BT ##] tcc_bt_power_control input[%d]\n", on_off); - - if(on_off == BT_DEV_ON) - { - gpio_direction_output(LDO_ON_PIN, GPIO_HIGH); - bt_is_power = on_off; - msleep(500); - } - else if(on_off == BT_DEV_OFF) - { - gpio_direction_output(LDO_ON_PIN, GPIO_LOW); - bt_is_power = BT_DEV_OFF; - msleep(500); - } - else - { - printk("[## BT_ERR ##] input_error On[%d] Off[%d]\n", BT_DEV_ON, BT_DEV_OFF); - } + printk("[## BT ##] tcc_bt_poll eint_gen %d, eint_mask %d ++\n", eint_gen, eint_mask); - return 0; + wait_event_interruptible(eint_wait, (eint_gen == 1 || eint_mask == 1)); + + printk("[## BT ##] tcc_bt_poll eint_gen %d, eint_mask %d --\n", eint_gen, eint_mask); + + if(eint_gen == 1) + { + mask = POLLIN|POLLRDNORM; + eint_gen = 0; + } + else if (eint_mask == 1) + { + mask = POLLERR; + eint_mask = 0; + } + + return mask; } - -static int tcc_bt_get_info(tcc_bt_info_t* arg) -{ - tcc_bt_info_t *info_t; - int module_t; +static int tcc_bt_power_control(int on_off) +{ + printk("[## BT ##] tcc_bt_power_control input[%d].\n", on_off); - info_t = (tcc_bt_info_t *)arg; - copy_from_user(info_t, (tcc_bt_info_t *)arg, sizeof(tcc_bt_info_t)); - - module_t = 0x56; - - printk("[## BT ##] module[0x%x]\n", module_t); - - info_t->module = module_t; - - copy_to_user((tcc_bt_info_t *)arg, info_t, sizeof(tcc_bt_info_t)); + if(on_off) + { + gpio_direction_output(tcc_bt_pdata->power_gpio.io, tcc_bt_pdata->power_gpio.enable); + msleep(500); + enable_irq(irq_num); + } + else + { + gpio_direction_output(tcc_bt_pdata->power_gpio.io, !tcc_bt_pdata->power_gpio.enable); + disable_irq(irq_num); + msleep(500); + } - return 0; + return 0; } + static long tcc_bt_dev_ioctl(struct file *file, unsigned int cmd,unsigned long arg) { - void __user *argp = (void __user *)arg; - char msg[14]; - int ret = -1; - char rate; - //printk("[## BT ##] tcc_bt_dev_ioctl cmd[%d] arg[%d]\n", cmd, arg); - switch(cmd) - { - case IOCTL_BT_DEV_POWER: - if (copy_from_user(&rate, argp, sizeof(rate))) - return -EFAULT; - printk("[## BT ##] IOCTL_BT_DEV_POWER cmd[%d] parm1[%d]\n", cmd, rate); - tcc_bt_power_control(rate); - // GPIO Control - break; - - case IOCTL_BT_DEV_SPECIFIC: - printk("[## BT ##] IOCTL_BT_DEV_SPECIFIC cmd[%d]\n", cmd); - tcc_bt_get_info((tcc_bt_info_t*)arg); - break; - - //case IOCTL_BT_DEV_IS_POWER: - //if (copy_to_user(argp, &bt_is_power, sizeof(bt_is_power))) - //return -EFAULT; - case IOCTL_BT_SET_EINT: - printk("[## BT ##] IOCTL_BT_SET_EINT cmd[%d]\n", cmd); - if (irq_mask) - { - irq_mask = 0; - enable_irq(rda_bt_irq); - } - break; - default : - printk("[## BT ##] tcc_bt_dev_ioctl cmd[%d]\n", cmd); - break; - } + void __user *argp = (void __user *)arg; + int ret = -1; + int rate = 0; + + switch(cmd) + { + case IOCTL_BT_DEV_POWER: + { + printk("[## BT ##] IOCTL_BT_DEV_POWER cmd[%d] parm1[%d].\n", cmd, rate); + if (copy_from_user(&rate, argp, sizeof(rate))) + return -EFAULT; + + mutex_lock(&sem); + ret = tcc_bt_power_control(rate); + mutex_unlock(&sem); + break; + } + + case IOCTL_BT_SET_EINT: + { + if (copy_from_user(&rate, argp, sizeof(rate))) + return -EFAULT; + printk("[## BT ##] IOCTL_BT_SET_EINT cmd[%d].\n", cmd); + mutex_lock(&sem); + + if(rate) + { + rda_bt_enable_irq(); + } + else + { + rda_bt_disable_irq(); + eint_mask = 1; + wake_up_interruptible(&eint_wait); + } + mutex_unlock(&sem); + ret = 0; + break; + } + + default : + { + printk("[## BT ##] tcc_bt_dev_ioctl cmd[%d].\n", cmd); + break; + } + } - return 0; + return ret; } -struct file_operations tcc_bt_dev_ops = { - .owner = THIS_MODULE, - .unlocked_ioctl = tcc_bt_dev_ioctl, - .open = tcc_bt_dev_open, - .release = tcc_bt_dev_release, +struct file_operations tcc_bt_dev_ops = +{ + .owner = THIS_MODULE, + .unlocked_ioctl = tcc_bt_dev_ioctl, + .open = tcc_bt_dev_open, + .release = tcc_bt_dev_release, + .poll = tcc_bt_dev_poll, }; +#ifdef HOST_WAKE_DELAY struct wake_lock rda_bt_wakelock; -static irqreturn_t rda_5876_host_wake_irq(int irq, void *dev) +#endif + +static irqreturn_t rda_bt_host_wake_irq(int irq, void *dev) { - printk("rda_5876_host_wake_irq\n"); - //export_bt_hci_wakeup_chip(); - wake_lock_timeout(&rda_bt_wakelock, 3 * HZ); - disable_irq(rda_bt_irq); - irq_mask = 1; - return IRQ_HANDLED; + printk("rda_bt_host_wake_irq.\n"); + +#ifdef HOST_WAKE_DELAY + wake_lock_timeout(&rda_bt_wakelock, 3 * HZ); +#endif + + if(irq_num != -1) + { + disable_irq_nosync(irq_num); + } + +#ifdef CONFIG_BT_HCIUART + if(rda_bt_workqueue) + queue_work(rda_bt_workqueue, &rda_bt_event_work); +#else + /* Maybe handle the interrupt in user space? */ + eint_gen = 1; + wake_up_interruptible(&eint_wait); + /* Send host wakeup command in user space, enable irq then */ +#endif + + return IRQ_HANDLED; } -static int tcc_bt_init_module(void) +static int tcc_bt_probe(struct platform_device *pdev) { int ret; - wake_lock_init(&rda_bt_wakelock, WAKE_LOCK_SUSPEND, "rda_bt_wake"); - ret=gpio_request(LDO_ON_PIN, "ldoonpin"); - if(ret < 0) { - printk("%s:fail to request gpio %d\n",__func__,LDO_ON_PIN); - return ret; + struct tcc_bt_platform_data *pdata = pdev->dev.platform_data; + + printk("tcc_bt_probe.\n"); + if(pdata == NULL) { + printk("mt6622_probe failed.\n"); + return -1; + } + tcc_bt_pdata = pdata; + + if(pdata->power_gpio.io != INVALID_GPIO) { + if (gpio_request(pdata->power_gpio.io, "ldoonpin")){ + printk("tcc bt ldoonpin is busy!\n"); + return -1; } - gpio_set_value(LDO_ON_PIN, GPIO_LOW);//GPIO_LOW - gpio_direction_output(LDO_ON_PIN, GPIO_LOW);//GPIO_LOW - - if(rda_bt_irq == 0){ - if(gpio_request(RDA_BT_HOST_WAKE_PIN, "bt_wake") != 0){ - printk("RDA_BT_HOST_WAKE_PIN request fail!\n"); - return -EIO; - } - gpio_direction_input(RDA_BT_HOST_WAKE_PIN); - - rda_bt_irq = gpio_to_irq(RDA_BT_HOST_WAKE_PIN); - ret = request_irq(rda_bt_irq, rda_5876_host_wake_irq, IRQF_TRIGGER_RISING, "bt_host_wake",NULL); - if(ret){ - printk("bt_host_wake irq request fail\n"); - rda_bt_irq = 0; - gpio_free(RDA_BT_HOST_WAKE_PIN); - return -EIO; - } - enable_irq_wake(rda_bt_irq); //bt irq can wakeup host when host sleep - disable_irq(rda_bt_irq); - irq_mask = 1; - printk("request_irq bt_host_wake\n"); + } + gpio_direction_output(pdata->power_gpio.io, !pdata->power_gpio.enable);//GPIO_LOW + +#ifdef HOST_WAKE_DELAY + wake_lock_init(&rda_bt_wakelock, WAKE_LOCK_SUSPEND, "rda_bt_wake"); +#endif + + if(pdata->wake_host_gpio.io != INVALID_GPIO) { + if (gpio_request(pdata->wake_host_gpio.io, "tcc_bt_wake")){ + printk("tcc bt wakeis busy!\n"); + gpio_free(pdata->wake_host_gpio.io); + return -1; } + } + + gpio_direction_input(pdata->wake_host_gpio.io); + irq_num = gpio_to_irq(pdata->wake_host_gpio.io); + ret = request_irq(irq_num, rda_bt_host_wake_irq, pdata->wake_host_gpio.enable, "tcc_bt_host_wake",NULL); + if(ret < 0) + { + printk("bt_host_wake irq request fail.\n"); + irq_num = -1; + goto error; + } + + mutex_init(&sem); + printk("[## BT ##] init_module\n"); - printk("[## BT ##] init_module\n"); - ret = register_chrdev(BT_DEV_MAJOR_NUM, DEV_NAME, &tcc_bt_dev_ops); - - bt_dev_class = class_create(THIS_MODULE, DEV_NAME); - device_create(bt_dev_class, NULL, MKDEV(BT_DEV_MAJOR_NUM, BT_DEV_MINOR_NUM), NULL, DEV_NAME); -#if 0 - if(machine_is_tcc8900()){ - gpio_request(TCC_GPB(25), "bt_power"); - gpio_request(TCC_GPEXT2(9), "bt_reset"); - gpio_direction_output(TCC_GPB(25), 0); // output - gpio_direction_output(TCC_GPEXT2(9), 0); - }else if(machine_is_tcc9300() || machine_is_tcc8800()) { // #elif defined (CONFIG_MACH_TCC9300) - //gpio_set_value(TCC_GPEXT1(7), 0); /* BT-ON Disable */ - gpio_request(TCC_GPEXT3(2), "bt_wake"); - gpio_request(TCC_GPEXT2(4), "bt_reset"); - gpio_direction_output(TCC_GPEXT3(2), 0); // output - gpio_direction_output(TCC_GPEXT2(4), 0); - } - else if(machine_is_m801_88()) - { - gpio_request(TCC_GPA(13), "bt_reset"); - gpio_request(TCC_GPB(22), "BT WAKE"); - gpio_direction_output(TCC_GPA(13), 0); // output - gpio_direction_output(TCC_GPB(22), 0); // output - } -#endif - if(ret < 0){ - printk("[## BT ##] [%d]fail to register the character device\n", ret); - return ret; + ret = register_chrdev(BT_DEV_MAJOR_NUM, DEV_NAME, &tcc_bt_dev_ops); + if(ret < 0) + { + printk("[## BT ##] [%d]fail to register the character device.\n", ret); + goto error; + } + + bt_dev_class = class_create(THIS_MODULE, DEV_NAME); + if (IS_ERR(bt_dev_class)) + { + printk("BT RDA class_create failed\n"); + goto error; } + + device_create(bt_dev_class, NULL, MKDEV(BT_DEV_MAJOR_NUM, BT_DEV_MINOR_NUM), NULL, DEV_NAME); + + init_waitqueue_head(&eint_wait); + + INIT_WORK(&rda_bt_event_work, rda_bt_work_fun); + + rda_bt_workqueue = create_singlethread_workqueue("rda_bt"); + + if (!rda_bt_workqueue) + { + printk("create_singlethread_workqueue failed.\n"); + ret = -ESRCH; + goto error; + } return 0; + +error: + gpio_free(pdata->power_gpio.io); + gpio_free(pdata->wake_host_gpio.io); + return ret; } -static void tcc_bt_cleanup_module(void) +static int tcc_bt_remove(struct platform_device *pdev) { - printk("[## BT ##] cleanup_module\n"); + printk("[## BT ##] cleanup_module.\n"); unregister_chrdev(BT_DEV_MAJOR_NUM, DEV_NAME); + if(tcc_bt_pdata) + tcc_bt_pdata = NULL; + + cancel_work_sync(&rda_bt_event_work); + destroy_workqueue(rda_bt_workqueue); + +#ifdef HOST_WAKE_DELAY wake_lock_destroy(&rda_bt_wakelock); +#endif + + return 0; +} + +static struct platform_driver tcc_bt_driver = { + .probe = tcc_bt_probe, + .remove = tcc_bt_remove, + .driver = { + .name = "tcc_bt_dev", + .owner = THIS_MODULE, + }, +}; + +static int __init tcc_bt_init_module(void) +{ + printk("Enter %s\n", __func__); + return platform_driver_register(&tcc_bt_driver); } +static void __exit tcc_bt_cleanup_module(void) +{ + printk("Enter %s\n", __func__); + platform_driver_unregister(&tcc_bt_driver); +} -late_initcall(tcc_bt_init_module); +module_init(tcc_bt_init_module); module_exit(tcc_bt_cleanup_module); diff --git a/include/linux/rfkill-rk.h b/include/linux/rfkill-rk.h index 8c892b1ad201..6ad5c884c09a 100755 --- a/include/linux/rfkill-rk.h +++ b/include/linux/rfkill-rk.h @@ -69,5 +69,10 @@ struct mt6622_platform_data { struct rfkill_rk_gpio rts_gpio; }; +struct tcc_bt_platform_data { + struct rfkill_rk_gpio power_gpio; + struct rfkill_rk_gpio wake_host_gpio; +}; + #endif /* __RFKILL_GPIO_H */