+\r
/*\r
* Copyright (C) 2010-2011 RDA Micro <anli@rdamicro.com>\r
* This file belong to RDA micro\r
#include <asm/uaccess.h>\r
#include <linux/ioctl.h>\r
#include <linux/device.h>\r
-\r
-//#include <mach/bsp.h>\r
#include <asm/io.h>\r
#include <linux/delay.h>\r
-\r
-//#include <linux/tcc_bt_dev.h>\r
-//#include <mach/tcc_pca953x.h>\r
-\r
#include <linux/gpio.h>\r
#include <mach/gpio.h>\r
#include <asm/mach-types.h>\r
-\r
#include <mach/iomux.h>\r
#include <linux/interrupt.h>\r
#include <asm/irq.h>\r
#include <linux/wakelock.h>\r
+#include <linux/rfkill-rk.h>\r
+#include <linux/platform_device.h>\r
\r
-#ifndef ON\r
-#define ON 1\r
-#endif\r
+#include <net/bluetooth/bluetooth.h>\r
+#include <net/bluetooth/hci_core.h>\r
\r
-#ifndef OFF\r
-#define OFF 0\r
+\r
+#ifndef HOST_WAKE_DELAY\r
+#define HOST_WAKE_DELAY\r
#endif\r
\r
-#define LDO_ON_PIN RK2928_PIN3_PC0\r
-#define RDA_BT_HOST_WAKE_PIN RK2928_PIN0_PC5\r
+#define DEV_NAME "tcc_bt_dev"\r
\r
-#define BT_DEV_ON 1\r
-#define BT_DEV_OFF 0\r
+#define BT_DEV_MAJOR_NUM 234\r
+#define BT_DEV_MINOR_NUM 0\r
\r
-#define BT_DEV_MAJOR_NUM 234\r
-#define BT_DEV_MINOR_NUM 0\r
+#define IOCTL_BT_DEV_POWER _IO(BT_DEV_MAJOR_NUM, 100)\r
+#define IOCTL_BT_SET_EINT _IO(BT_DEV_MAJOR_NUM, 101)\r
\r
-//#define IOCTL_BT_DEV_POWER _IO(BT_DEV_MAJOR_NUM, 100)\r
-//#define IOCTL_BT_DEV_SPECIFIC _IO(BT_DEV_MAJOR_NUM, 101)\r
-//#define IOCTL_BT_DEV_IS_POWER _IO(BT_DEV_MAJOR_NUM, 102)\r
+static wait_queue_head_t eint_wait;\r
+static struct work_struct rda_bt_event_work;\r
+static struct workqueue_struct *rda_bt_workqueue;
\r
+static int irq_num = -1;
+static int eint_gen;\r
+static int eint_mask;\r
+static struct class *bt_dev_class;\r
+static struct mutex sem;\r
+static struct tcc_bt_platform_data *tcc_bt_pdata = NULL;\r
\r
-#define IOCTL_BT_DEV_POWER _IO(BT_DEV_MAJOR_NUM, 100)\r
-#define IOCTL_BT_DEV_CTRL _IO(BT_DEV_MAJOR_NUM, 101)\r
-#define IOCTL_BT_SET_EINT _IO(BT_DEV_MAJOR_NUM, 102)\r
-#define IOCTL_BT_DEV_SPECIFIC _IO(BT_DEV_MAJOR_NUM, 103)\r
\r
+static void rda_bt_enable_irq(void)\r
+{\r
+ if(irq_num != -1) \r
+ {\r
+ enable_irq(irq_num);\r
+ }\r
+}\r
\r
-static int bt_is_power = 0;\r
-static int rda_bt_irq = 0;\r
-static int irq_mask = 0;\r
-extern void export_bt_hci_wakeup_chip(void);\r
+static void rda_bt_disable_irq(void)\r
+{\r
+ if(irq_num != -1)\r
+ { \r
+ disable_irq_nosync(irq_num);\r
+ } \r
+}\r
\r
-#define DEV_NAME "tcc_bt_dev"\r
-static struct class *bt_dev_class;\r
+static void rda_bt_work_fun(struct work_struct *work)\r
+{\r
+ struct hci_dev *hdev = NULL;\r
+\r
+ /* BlueZ stack, hci_uart driver */\r
+ hdev = hci_dev_get(0);\r
+ \r
+ if(hdev == NULL)\r
+ {\r
+ /* Avoid the early interrupt before hci0 registered */\r
+ //printk(KERN_ALERT "hdev is NULL\n ");\r
+ }\r
+ else\r
+ {\r
+ printk(KERN_ALERT "Send host wakeup command.\n");\r
+ hci_send_cmd(hdev, 0xC0FC, 0, NULL);\r
+ }\r
+ \r
+ rda_bt_enable_irq();\r
+}\r
\r
-typedef struct {\r
- int module; // 0x12:CSR, 0x34:Broadcom\r
- int TMP1;\r
- int TMP2;\r
-} tcc_bt_info_t;\r
\r
static int tcc_bt_dev_open(struct inode *inode, struct file *file)\r
{\r
- printk("[## BT ##] tcc_bt_dev_open\n");\r
- return 0;\r
+ printk("[## BT ##] tcc_bt_dev_open.\n");\r
+ eint_gen = 0;\r
+ eint_mask = 0;\r
+ return 0;\r
}\r
\r
static int tcc_bt_dev_release(struct inode *inode, struct file *file)\r
{\r
- printk("[## BT ##] tcc_bt_dev_release\n");\r
- return 0;\r
+ printk("[## BT ##] tcc_bt_dev_release.\n");\r
+ eint_gen = 0;\r
+ eint_mask = 0;\r
+ return 0;\r
}\r
\r
-static int tcc_bt_power_control(int on_off)\r
+/*****************************************************************************\r
+ * tcc_bt_dev_poll\r
+*****************************************************************************/\r
+\r
+static unsigned int tcc_bt_dev_poll(struct file *file, poll_table *wait)\r
{\r
-// volatile PGPIO pGPIO = (volatile PGPIO)tcc_p2v(HwGPIO_BASE);\r
+ uint32_t mask = 0;\r
\r
- printk("[## BT ##] tcc_bt_power_control input[%d]\n", on_off);\r
- \r
- if(on_off == BT_DEV_ON)\r
- { \r
- gpio_direction_output(LDO_ON_PIN, GPIO_HIGH);\r
- bt_is_power = on_off;\r
- msleep(500);\r
- }\r
- else if(on_off == BT_DEV_OFF)\r
- {\r
- gpio_direction_output(LDO_ON_PIN, GPIO_LOW);\r
- bt_is_power = BT_DEV_OFF;\r
- msleep(500);\r
- }\r
- else\r
- {\r
- printk("[## BT_ERR ##] input_error On[%d] Off[%d]\n", BT_DEV_ON, BT_DEV_OFF);\r
- }\r
+ printk("[## BT ##] tcc_bt_poll eint_gen %d, eint_mask %d ++\n", eint_gen, eint_mask);\r
\r
- return 0;\r
+ wait_event_interruptible(eint_wait, (eint_gen == 1 || eint_mask == 1));\r
+ \r
+ printk("[## BT ##] tcc_bt_poll eint_gen %d, eint_mask %d --\n", eint_gen, eint_mask);\r
+ \r
+ if(eint_gen == 1)\r
+ {\r
+ mask = POLLIN|POLLRDNORM;\r
+ eint_gen = 0;\r
+ }\r
+ else if (eint_mask == 1)\r
+ {\r
+ mask = POLLERR;\r
+ eint_mask = 0;\r
+ }\r
+ \r
+ return mask;\r
}\r
\r
-\r
-static int tcc_bt_get_info(tcc_bt_info_t* arg)\r
-{\r
- tcc_bt_info_t *info_t;\r
- int module_t;\r
+static int tcc_bt_power_control(int on_off)\r
+{ \r
+ printk("[## BT ##] tcc_bt_power_control input[%d].\n", on_off);\r
\r
- info_t = (tcc_bt_info_t *)arg;\r
- copy_from_user(info_t, (tcc_bt_info_t *)arg, sizeof(tcc_bt_info_t));\r
-\r
- module_t = 0x56; \r
-\r
- printk("[## BT ##] module[0x%x]\n", module_t);\r
-\r
- info_t->module = module_t;\r
-\r
- copy_to_user((tcc_bt_info_t *)arg, info_t, sizeof(tcc_bt_info_t));\r
+ if(on_off)\r
+ { \r
+ gpio_direction_output(tcc_bt_pdata->power_gpio.io, tcc_bt_pdata->power_gpio.enable);\r
+ msleep(500);
+ enable_irq(irq_num); \r
+ }\r
+ else\r
+ {\r
+ gpio_direction_output(tcc_bt_pdata->power_gpio.io, !tcc_bt_pdata->power_gpio.enable);\r
+ disable_irq(irq_num);
+ msleep(500);\r
+ }\r
\r
- return 0;\r
+ return 0;\r
}\r
\r
+\r
static long tcc_bt_dev_ioctl(struct file *file, unsigned int cmd,unsigned long arg)\r
{\r
- void __user *argp = (void __user *)arg;\r
- char msg[14];\r
- int ret = -1;\r
- char rate;\r
- //printk("[## BT ##] tcc_bt_dev_ioctl cmd[%d] arg[%d]\n", cmd, arg);\r
- switch(cmd)\r
- {\r
- case IOCTL_BT_DEV_POWER: \r
- if (copy_from_user(&rate, argp, sizeof(rate)))\r
- return -EFAULT;\r
- printk("[## BT ##] IOCTL_BT_DEV_POWER cmd[%d] parm1[%d]\n", cmd, rate);\r
- tcc_bt_power_control(rate);\r
- // GPIO Control\r
- break;\r
- \r
- case IOCTL_BT_DEV_SPECIFIC:\r
- printk("[## BT ##] IOCTL_BT_DEV_SPECIFIC cmd[%d]\n", cmd);\r
- tcc_bt_get_info((tcc_bt_info_t*)arg);\r
- break;\r
-\r
- //case IOCTL_BT_DEV_IS_POWER:\r
- //if (copy_to_user(argp, &bt_is_power, sizeof(bt_is_power)))\r
- //return -EFAULT;\r
- case IOCTL_BT_SET_EINT:\r
- printk("[## BT ##] IOCTL_BT_SET_EINT cmd[%d]\n", cmd);\r
- if (irq_mask)\r
- {\r
- irq_mask = 0;\r
- enable_irq(rda_bt_irq);\r
- }\r
- break;\r
- default :\r
- printk("[## BT ##] tcc_bt_dev_ioctl cmd[%d]\n", cmd);\r
- break;\r
- }\r
+ void __user *argp = (void __user *)arg;\r
+ int ret = -1;\r
+ int rate = 0;\r
+\r
+ switch(cmd)\r
+ {\r
+ case IOCTL_BT_DEV_POWER:\r
+ {\r
+ printk("[## BT ##] IOCTL_BT_DEV_POWER cmd[%d] parm1[%d].\n", cmd, rate);\r
+ if (copy_from_user(&rate, argp, sizeof(rate)))\r
+ return -EFAULT;\r
+\r
+ mutex_lock(&sem); \r
+ ret = tcc_bt_power_control(rate);\r
+ mutex_unlock(&sem); \r
+ break;\r
+ } \r
+ \r
+ case IOCTL_BT_SET_EINT:\r
+ {\r
+ if (copy_from_user(&rate, argp, sizeof(rate)))\r
+ return -EFAULT;\r
+ printk("[## BT ##] IOCTL_BT_SET_EINT cmd[%d].\n", cmd);\r
+ mutex_lock(&sem); \r
+\r
+ if(rate)\r
+ {\r
+ rda_bt_enable_irq();\r
+ }\r
+ else\r
+ {\r
+ rda_bt_disable_irq();\r
+ eint_mask = 1;\r
+ wake_up_interruptible(&eint_wait);\r
+ } \r
+ mutex_unlock(&sem); \r
+ ret = 0;\r
+ break;\r
+ }\r
+ \r
+ default :\r
+ {\r
+ printk("[## BT ##] tcc_bt_dev_ioctl cmd[%d].\n", cmd);\r
+ break;\r
+ }\r
+ }\r
\r
- return 0;\r
+ return ret;\r
}\r
\r
\r
-struct file_operations tcc_bt_dev_ops = {\r
- .owner = THIS_MODULE,\r
- .unlocked_ioctl = tcc_bt_dev_ioctl,\r
- .open = tcc_bt_dev_open,\r
- .release = tcc_bt_dev_release,\r
+struct file_operations tcc_bt_dev_ops = \r
+{\r
+ .owner = THIS_MODULE,\r
+ .unlocked_ioctl = tcc_bt_dev_ioctl,\r
+ .open = tcc_bt_dev_open,\r
+ .release = tcc_bt_dev_release,\r
+ .poll = tcc_bt_dev_poll,\r
};\r
\r
+#ifdef HOST_WAKE_DELAY\r
struct wake_lock rda_bt_wakelock;\r
-static irqreturn_t rda_5876_host_wake_irq(int irq, void *dev)\r
+#endif\r
+\r
+static irqreturn_t rda_bt_host_wake_irq(int irq, void *dev)\r
{\r
- printk("rda_5876_host_wake_irq\n");\r
- //export_bt_hci_wakeup_chip();\r
- wake_lock_timeout(&rda_bt_wakelock, 3 * HZ); \r
- disable_irq(rda_bt_irq);\r
- irq_mask = 1; \r
- return IRQ_HANDLED;\r
+ printk("rda_bt_host_wake_irq.\n");\r
+ \r
+#ifdef HOST_WAKE_DELAY\r
+ wake_lock_timeout(&rda_bt_wakelock, 3 * HZ); \r
+#endif
+
+ if(irq_num != -1) \r
+ { \r
+ disable_irq_nosync(irq_num);\r
+ } \r
+\r
+#ifdef CONFIG_BT_HCIUART\r
+ if(rda_bt_workqueue)\r
+ queue_work(rda_bt_workqueue, &rda_bt_event_work);\r
+#else\r
+ /* Maybe handle the interrupt in user space? */\r
+ eint_gen = 1;\r
+ wake_up_interruptible(&eint_wait);\r
+ /* Send host wakeup command in user space, enable irq then */\r
+#endif\r
+ \r
+ return IRQ_HANDLED;\r
}\r
\r
-static int tcc_bt_init_module(void)\r
+static int tcc_bt_probe(struct platform_device *pdev)\r
{\r
int ret;\r
\r
- wake_lock_init(&rda_bt_wakelock, WAKE_LOCK_SUSPEND, "rda_bt_wake");\r
- ret=gpio_request(LDO_ON_PIN, "ldoonpin");\r
- if(ret < 0) {\r
- printk("%s:fail to request gpio %d\n",__func__,LDO_ON_PIN);\r
- return ret;\r
+ struct tcc_bt_platform_data *pdata = pdev->dev.platform_data;\r
+ \r
+ printk("tcc_bt_probe.\n");\r
+ if(pdata == NULL) {\r
+ printk("mt6622_probe failed.\n");\r
+ return -1;\r
+ }\r
+ tcc_bt_pdata = pdata;\r
+ \r
+ if(pdata->power_gpio.io != INVALID_GPIO) {\r
+ if (gpio_request(pdata->power_gpio.io, "ldoonpin")){\r
+ printk("tcc bt ldoonpin is busy!\n");\r
+ return -1;\r
}\r
- gpio_set_value(LDO_ON_PIN, GPIO_LOW);//GPIO_LOW\r
- gpio_direction_output(LDO_ON_PIN, GPIO_LOW);//GPIO_LOW\r
-\r
- if(rda_bt_irq == 0){\r
- if(gpio_request(RDA_BT_HOST_WAKE_PIN, "bt_wake") != 0){\r
- printk("RDA_BT_HOST_WAKE_PIN request fail!\n");\r
- return -EIO;\r
- }\r
- gpio_direction_input(RDA_BT_HOST_WAKE_PIN);\r
-\r
- rda_bt_irq = gpio_to_irq(RDA_BT_HOST_WAKE_PIN);\r
- ret = request_irq(rda_bt_irq, rda_5876_host_wake_irq, IRQF_TRIGGER_RISING, "bt_host_wake",NULL);\r
- if(ret){\r
- printk("bt_host_wake irq request fail\n");\r
- rda_bt_irq = 0;\r
- gpio_free(RDA_BT_HOST_WAKE_PIN);\r
- return -EIO;\r
- }\r
- enable_irq_wake(rda_bt_irq); //bt irq can wakeup host when host sleep\r
- disable_irq(rda_bt_irq);\r
- irq_mask = 1;\r
- printk("request_irq bt_host_wake\n");\r
+ }\r
+ gpio_direction_output(pdata->power_gpio.io, !pdata->power_gpio.enable);//GPIO_LOW\r
+ \r
+#ifdef HOST_WAKE_DELAY\r
+ wake_lock_init(&rda_bt_wakelock, WAKE_LOCK_SUSPEND, "rda_bt_wake");\r
+#endif\r
+\r
+ if(pdata->wake_host_gpio.io != INVALID_GPIO) {\r
+ if (gpio_request(pdata->wake_host_gpio.io, "tcc_bt_wake")){\r
+ printk("tcc bt wakeis busy!\n");\r
+ gpio_free(pdata->wake_host_gpio.io);\r
+ return -1;\r
}\r
+ } \r
+\r
+ gpio_direction_input(pdata->wake_host_gpio.io);\r
+ irq_num = gpio_to_irq(pdata->wake_host_gpio.io);\r
+ ret = request_irq(irq_num, rda_bt_host_wake_irq, pdata->wake_host_gpio.enable, "tcc_bt_host_wake",NULL); \r
+ if(ret < 0)\r
+ {\r
+ printk("bt_host_wake irq request fail.\n");\r
+ irq_num = -1;\r
+ goto error;\r
+ }\r
\r
+\r
+ mutex_init(&sem); \r
+ printk("[## BT ##] init_module\n");\r
\r
- printk("[## BT ##] init_module\n");\r
- ret = register_chrdev(BT_DEV_MAJOR_NUM, DEV_NAME, &tcc_bt_dev_ops);\r
-\r
- bt_dev_class = class_create(THIS_MODULE, DEV_NAME);\r
- device_create(bt_dev_class, NULL, MKDEV(BT_DEV_MAJOR_NUM, BT_DEV_MINOR_NUM), NULL, DEV_NAME);\r
-#if 0\r
- if(machine_is_tcc8900()){\r
- gpio_request(TCC_GPB(25), "bt_power");\r
- gpio_request(TCC_GPEXT2(9), "bt_reset");\r
- gpio_direction_output(TCC_GPB(25), 0); // output\r
- gpio_direction_output(TCC_GPEXT2(9), 0);\r
- }else if(machine_is_tcc9300() || machine_is_tcc8800()) { // #elif defined (CONFIG_MACH_TCC9300)\r
- //gpio_set_value(TCC_GPEXT1(7), 0); /* BT-ON Disable */\r
- gpio_request(TCC_GPEXT3(2), "bt_wake");\r
- gpio_request(TCC_GPEXT2(4), "bt_reset");\r
- gpio_direction_output(TCC_GPEXT3(2), 0); // output\r
- gpio_direction_output(TCC_GPEXT2(4), 0);\r
- }\r
- else if(machine_is_m801_88())\r
- {\r
- gpio_request(TCC_GPA(13), "bt_reset");\r
- gpio_request(TCC_GPB(22), "BT WAKE");\r
- gpio_direction_output(TCC_GPA(13), 0); // output\r
- gpio_direction_output(TCC_GPB(22), 0); // output\r
- }\r
-#endif \r
- if(ret < 0){\r
- printk("[## BT ##] [%d]fail to register the character device\n", ret);\r
- return ret;\r
+ ret = register_chrdev(BT_DEV_MAJOR_NUM, DEV_NAME, &tcc_bt_dev_ops);\r
+ if(ret < 0)\r
+ {\r
+ printk("[## BT ##] [%d]fail to register the character device.\n", ret);\r
+ goto error;\r
+ }\r
+ \r
+ bt_dev_class = class_create(THIS_MODULE, DEV_NAME);\r
+ if (IS_ERR(bt_dev_class)) \r
+ {\r
+ printk("BT RDA class_create failed\n");\r
+ goto error;\r
}\r
+ \r
+ device_create(bt_dev_class, NULL, MKDEV(BT_DEV_MAJOR_NUM, BT_DEV_MINOR_NUM), NULL, DEV_NAME);\r
+
+ init_waitqueue_head(&eint_wait);\r
+ \r
+ INIT_WORK(&rda_bt_event_work, rda_bt_work_fun);\r
+ \r
+ rda_bt_workqueue = create_singlethread_workqueue("rda_bt");\r
+ \r
+ if (!rda_bt_workqueue)\r
+ {\r
+ printk("create_singlethread_workqueue failed.\n");\r
+ ret = -ESRCH;\r
+ goto error;\r
+ }
\r
return 0;\r
+ \r
+error:\r
+ gpio_free(pdata->power_gpio.io); \r
+ gpio_free(pdata->wake_host_gpio.io);\r
+ return ret; \r
}\r
\r
-static void tcc_bt_cleanup_module(void)\r
+static int tcc_bt_remove(struct platform_device *pdev)\r
{\r
- printk("[## BT ##] cleanup_module\n");\r
+ printk("[## BT ##] cleanup_module.\n");\r
unregister_chrdev(BT_DEV_MAJOR_NUM, DEV_NAME);\r
+ if(tcc_bt_pdata)\r
+ tcc_bt_pdata = NULL;
+
+ cancel_work_sync(&rda_bt_event_work);\r
+ destroy_workqueue(rda_bt_workqueue); \r
+ \r
+#ifdef HOST_WAKE_DELAY \r
wake_lock_destroy(&rda_bt_wakelock);\r
+#endif
+\r
+ return 0;\r
+}\r
+\r
+static struct platform_driver tcc_bt_driver = {\r
+ .probe = tcc_bt_probe,\r
+ .remove = tcc_bt_remove,\r
+ .driver = {\r
+ .name = "tcc_bt_dev",\r
+ .owner = THIS_MODULE,\r
+ },\r
+};\r
+\r
+static int __init tcc_bt_init_module(void)\r
+{\r
+ printk("Enter %s\n", __func__);\r
+ return platform_driver_register(&tcc_bt_driver);\r
}\r
\r
+static void __exit tcc_bt_cleanup_module(void)\r
+{\r
+ printk("Enter %s\n", __func__);\r
+ platform_driver_unregister(&tcc_bt_driver);\r
+}\r
\r
-late_initcall(tcc_bt_init_module);\r
+module_init(tcc_bt_init_module);\r
module_exit(tcc_bt_cleanup_module);\r
\r
\r