Merge remote-tracking branch 'aosp/android-3.0' into develop-3.0
[firefly-linux-kernel-4.4.55.git] / drivers / mtk_wcn_bt / bt_hwctl_dev.c
1 /* Copyright Statement:
2  *
3  * This software/firmware and related documentation ("MediaTek Software") are
4  * protected under relevant copyright laws. The information contained herein is
5  * confidential and proprietary to MediaTek Inc. and/or its licensors. Without
6  * the prior written permission of MediaTek inc. and/or its licensors, any
7  * reproduction, modification, use or disclosure of MediaTek Software, and
8  * information contained herein, in whole or in part, shall be strictly
9  * prohibited.
10  * 
11  * MediaTek Inc. (C) 2010. All rights reserved.
12  * 
13  * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
14  * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
15  * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER
16  * ON AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL
17  * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
19  * NONINFRINGEMENT. NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH
20  * RESPECT TO THE SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY,
21  * INCORPORATED IN, OR SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES
22  * TO LOOK ONLY TO SUCH THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO.
23  * RECEIVER EXPRESSLY ACKNOWLEDGES THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO
24  * OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES CONTAINED IN MEDIATEK
25  * SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK SOFTWARE
26  * RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
27  * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S
28  * ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE
29  * RELEASED HEREUNDER WILL BE, AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE
30  * MEDIATEK SOFTWARE AT ISSUE, OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE
31  * CHARGE PAID BY RECEIVER TO MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
32  *
33  * The following software/firmware and/or related documentation ("MediaTek
34  * Software") have been modified by MediaTek Inc. All revisions are subject to
35  * any receiver's applicable license agreements with MediaTek Inc.
36  */
37
38 #include <linux/kernel.h>
39 #include <linux/module.h>
40 #include <linux/init.h>
41 #include <linux/types.h>
42 #include <linux/wait.h>
43 #include <linux/slab.h>
44 #include <linux/fs.h>
45 #include <linux/sched.h>
46 #include <linux/poll.h>
47 #include <linux/device.h>
48 #include <linux/platform_device.h>
49 #include <linux/cdev.h>
50 #include <linux/errno.h>
51 #include <asm/io.h>
52 #include <asm/uaccess.h>
53 #include <net/bluetooth/bluetooth.h>
54 #include <net/bluetooth/hci_core.h>
55 #include <linux/gpio.h>
56 #include <mach/gpio.h>
57
58 #include "bt_hwctl.h"
59
60
61 #define BT_HWCTL_DEBUG_EN     0
62
63 #define BT_HWCTL_ALERT(f, s...) \
64     printk(KERN_ALERT "BTHWCTL " f, ## s)
65
66 #if BT_HWCTL_DEBUG_EN
67 #define BT_HWCTL_DEBUG(f, s...) \
68     printk(KERN_INFO "BTHWCTL " f, ## s)
69 #else
70 #define BT_HWCTL_DEBUG(f, s...) \
71     ((void)0)
72 #endif
73
74 /**************************************************************************
75  *                        D E F I N I T I O N S                           *
76 ***************************************************************************/
77
78 #define BTHWCTL_NAME                 "bthwctl"
79 #define BTHWCTL_DEV_NAME             "/dev/bthwctl"
80 #define BTHWCTL_IOC_MAGIC            0xf6
81 #define BTHWCTL_IOCTL_SET_POWER      _IOWR(BTHWCTL_IOC_MAGIC, 0, uint32_t)
82 #define BTHWCTL_IOCTL_SET_EINT       _IOWR(BTHWCTL_IOC_MAGIC, 1, uint32_t)
83
84 wait_queue_head_t eint_wait;
85 int eint_gen;
86 int eint_mask;
87 struct work_struct mtk_wcn_bt_event_work;
88 struct workqueue_struct *mtk_wcn_bt_workqueue;
89
90 struct bt_hwctl {
91     bool powerup;
92     dev_t dev_t;
93     struct class *cls;
94     struct device *dev;
95     struct cdev cdev;
96     struct mutex sem;
97 };
98 static struct bt_hwctl *bh = NULL;
99
100 static struct mt6622_platform_data *mt6622_pdata;
101
102 /*****************************************************************************
103  *  bt_hwctl_open
104 *****************************************************************************/
105 static int bt_hwctl_open(struct inode *inode, struct file *file)
106 {
107     BT_HWCTL_DEBUG("bt_hwctl_open\n");
108     eint_gen = 0;
109     eint_mask = 0;
110     return 0;
111 }
112
113 /*****************************************************************************
114  *  bt_hwctl_ioctl
115 *****************************************************************************/
116 static long bt_hwctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
117 {
118     int ret = 0;
119      
120     BT_HWCTL_DEBUG("bt_hwctl_ioctl\n");
121     
122     if(!bh) {
123         BT_HWCTL_ALERT("bt_hwctl struct not initialized\n");
124         return -EFAULT;
125     }
126     
127     switch(cmd)
128     {
129         case BTHWCTL_IOCTL_SET_POWER:
130         {
131             unsigned long pwr = 0;
132             if (copy_from_user(&pwr, (void*)arg, sizeof(unsigned long)))
133                 return -EFAULT;
134                 
135             BT_HWCTL_DEBUG("BTHWCTL_IOCTL_SET_POWER: %d\n", (int)pwr);
136             
137             mutex_lock(&bh->sem);
138             if (pwr){
139                 ret = mt_bt_power_on();
140             }
141             else{
142                 mt_bt_power_off();
143             }
144             mutex_unlock(&bh->sem);
145             
146             break;
147         }
148         case BTHWCTL_IOCTL_SET_EINT:
149         {
150             unsigned long eint = 0;
151             if (copy_from_user(&eint, (void*)arg, sizeof(unsigned long)))
152                 return -EFAULT;
153                 
154             BT_HWCTL_DEBUG("BTHWCTL_IOCTL_SET_EINT: %d\n", (int)eint);
155             
156             mutex_lock(&bh->sem);
157             if (eint){
158                 /* Enable irq from user space */ 
159                 BT_HWCTL_DEBUG("Set BT EINT enable\n");
160                 mt_bt_enable_irq();
161             }
162             else{
163                 /* Disable irq from user space, maybe time to close driver */
164                 BT_HWCTL_DEBUG("Set BT EINT disable\n");
165                 mt_bt_disable_irq();
166                 eint_mask = 1;
167                 wake_up_interruptible(&eint_wait);
168             }
169             mutex_unlock(&bh->sem);
170             
171             break;
172         }    
173         default:
174             BT_HWCTL_ALERT("BTHWCTL_IOCTL not support\n");
175             return -EPERM;
176     }
177     
178     return ret;
179 }
180
181 /*****************************************************************************
182  *  bt_hwctl_release
183 *****************************************************************************/
184 static int bt_hwctl_release(struct inode *inode, struct file *file)
185 {
186     BT_HWCTL_DEBUG("bt_hwctl_release\n");
187     eint_gen = 0;
188     eint_mask = 0;
189     return 0;
190 }
191
192 /*****************************************************************************
193  *  bt_hwctl_poll
194 *****************************************************************************/
195 static unsigned int bt_hwctl_poll(struct file *file, poll_table *wait)
196 {
197     uint32_t mask = 0;
198     
199     BT_HWCTL_DEBUG("bt_hwctl_poll eint_gen %d, eint_mask %d ++\n", eint_gen, eint_mask);
200     //poll_wait(file, &eint_wait, wait);
201     wait_event_interruptible(eint_wait, (eint_gen == 1 || eint_mask == 1));
202     BT_HWCTL_DEBUG("bt_hwctl_poll eint_gen %d, eint_mask %d --\n", eint_gen, eint_mask);
203     
204     if(eint_gen == 1){
205         mask = POLLIN|POLLRDNORM;
206         eint_gen = 0;
207     }
208     else if (eint_mask == 1){
209         mask = POLLERR;
210         eint_mask = 0;
211     }
212     
213     return mask;
214 }
215
216 static void mtk_wcn_bt_work_fun(struct work_struct *work)
217 {
218     struct hci_dev *hdev = NULL;
219
220     /* BlueZ stack, hci_uart driver */
221     hdev = hci_dev_get(0);
222     if(hdev == NULL){
223         /* Avoid the early interrupt before hci0 registered */
224         //printk(KERN_ALERT "hdev is NULL\n ");
225     }else{
226         //printk(KERN_ALERT "Send host wakeup command\n");
227         hci_send_cmd(hdev, 0xFCC1, 0, NULL);
228     }
229     
230     mt_bt_enable_irq();
231 }
232
233 static int mt6622_probe(struct platform_device *pdev)
234 {
235     struct mt6622_platform_data *pdata = pdev->dev.platform_data;
236     
237     printk("mt6622_probe.\n");
238     
239     mt6622_pdata = pdata;
240     if(pdata == NULL) {
241         printk("mt6622_probe failed.\n");
242         return -1;
243     }
244     
245                 if(pdata->power_gpio.io != INVALID_GPIO) {
246                         if (gpio_request(pdata->power_gpio.io, "BT_PWR_EN")){
247                                 printk("mt6622 power_gpio is busy!\n");
248                                 return -1;
249                         }
250                 }
251                 
252                 if(pdata->reset_gpio.io != INVALID_GPIO) {
253                         if (gpio_request(pdata->reset_gpio.io, "BT_RESET")){
254                                 printk("mt6622 reset_gpio is busy!\n");
255                                 gpio_free(pdata->power_gpio.io);
256                                 return -1;
257                         }
258                 }
259                 
260                 if(pdata->irq_gpio.io != INVALID_GPIO) {
261                         if (gpio_request(pdata->irq_gpio.io, "BT_EINT")){
262                                 printk("mt6622 irq_gpio is busy!\n");
263                                 gpio_free(pdata->power_gpio.io);
264                                 gpio_free(pdata->reset_gpio.io);
265                                 return -1;
266                         }
267                 }
268                 
269                 mt_bt_power_init();
270                 
271                 return 0;
272 }
273
274 static int mt6622_remove(struct platform_device *pdev)
275 {
276         struct mt6622_platform_data *pdata = pdev->dev.platform_data;
277         
278         printk("mt6622_remove.\n");
279         
280         if(pdata) {
281           if(pdata->power_gpio.io != INVALID_GPIO)
282                 gpio_free(pdata->power_gpio.io);
283           if(pdata->reset_gpio.io != INVALID_GPIO)
284                 gpio_free(pdata->reset_gpio.io);
285           if(pdata->irq_gpio.io != INVALID_GPIO)
286                 gpio_free(pdata->irq_gpio.io);
287   }
288         
289         return 0;
290 }
291
292 void *mt_bt_get_platform_data(void)
293 {
294         return (void *)mt6622_pdata;
295 }
296
297 /**************************************************************************
298  *                K E R N E L   I N T E R F A C E S                       *
299 ***************************************************************************/
300 static struct file_operations bt_hwctl_fops = {
301     .owner      = THIS_MODULE,
302 //    .ioctl      = bt_hwctl_ioctl,
303     .unlocked_ioctl = bt_hwctl_ioctl,
304     .open       = bt_hwctl_open,
305     .release    = bt_hwctl_release,
306     .poll       = bt_hwctl_poll,
307 };
308
309 static struct platform_driver mt6622_driver = {
310     .probe = mt6622_probe,
311     .remove = mt6622_remove,
312     .driver = {
313         .name = "mt6622",
314         .owner = THIS_MODULE,
315     },
316 };
317
318 /*****************************************************************************/
319 static int __init bt_hwctl_init(void)
320 {
321     int ret = -1, err = -1;
322     
323     BT_HWCTL_DEBUG("bt_hwctl_init\n");
324     
325     platform_driver_register(&mt6622_driver);
326     
327     if (!(bh = kzalloc(sizeof(struct bt_hwctl), GFP_KERNEL)))
328     {
329         BT_HWCTL_ALERT("bt_hwctl_init allocate dev struct failed\n");
330         err = -ENOMEM;
331         goto ERR_EXIT;
332     }
333     
334     ret = alloc_chrdev_region(&bh->dev_t, 0, 1, BTHWCTL_NAME);
335     if (ret) {
336         BT_HWCTL_ALERT("alloc chrdev region failed\n");
337         goto ERR_EXIT;
338     }
339     
340     BT_HWCTL_DEBUG("alloc %s:%d:%d\n", BTHWCTL_NAME, MAJOR(bh->dev_t), MINOR(bh->dev_t));
341     
342     cdev_init(&bh->cdev, &bt_hwctl_fops);
343     
344     bh->cdev.owner = THIS_MODULE;
345     bh->cdev.ops = &bt_hwctl_fops;
346     
347     err = cdev_add(&bh->cdev, bh->dev_t, 1);
348     if (err) {
349         BT_HWCTL_ALERT("add chrdev failed\n");
350         goto ERR_EXIT;
351     }
352     
353     bh->cls = class_create(THIS_MODULE, BTHWCTL_NAME);
354     if (IS_ERR(bh->cls)) {
355         err = PTR_ERR(bh->cls);
356         BT_HWCTL_ALERT("class_create failed, errno:%d\n", err);
357         goto ERR_EXIT;
358     }
359     
360     bh->dev = device_create(bh->cls, NULL, bh->dev_t, NULL, BTHWCTL_NAME);
361     mutex_init(&bh->sem);
362     
363     init_waitqueue_head(&eint_wait);
364     
365     INIT_WORK(&mtk_wcn_bt_event_work, mtk_wcn_bt_work_fun);
366     mtk_wcn_bt_workqueue = create_singlethread_workqueue("mtk_wcn_bt");
367     if (!mtk_wcn_bt_workqueue) {
368         printk("create_singlethread_workqueue failed.\n");
369         err = -ESRCH;
370         goto ERR_EXIT;
371     }    
372     
373     /* request gpio used by BT */
374     //mt_bt_gpio_init();
375     
376     BT_HWCTL_DEBUG("bt_hwctl_init ok\n");
377     
378     return 0;
379     
380 ERR_EXIT:
381     if (err == 0)
382         cdev_del(&bh->cdev);
383     if (ret == 0)
384         unregister_chrdev_region(bh->dev_t, 1);
385         
386     if (bh){
387         kfree(bh);
388         bh = NULL;
389     }     
390     return -1;
391 }
392
393 /*****************************************************************************/
394 static void __exit bt_hwctl_exit(void)
395 {
396     BT_HWCTL_DEBUG("bt_hwctl_exit\n");
397     
398     platform_driver_unregister(&mt6622_driver);
399     
400     if (bh){
401         cdev_del(&bh->cdev);
402         
403         unregister_chrdev_region(bh->dev_t, 1);
404         device_destroy(bh->cls, bh->dev_t);
405         
406         class_destroy(bh->cls);
407         mutex_destroy(&bh->sem);
408         
409         kfree(bh);
410         bh = NULL;
411     }
412     
413     cancel_work_sync(&mtk_wcn_bt_event_work);
414     destroy_workqueue(mtk_wcn_bt_workqueue);    
415     
416     /* release gpio used by BT */
417     //mt_bt_gpio_release();
418 }
419
420 EXPORT_SYMBOL(mt_bt_get_platform_data);
421 EXPORT_SYMBOL(eint_wait);
422 EXPORT_SYMBOL(eint_gen);
423 EXPORT_SYMBOL(mtk_wcn_bt_event_work);
424 EXPORT_SYMBOL(mtk_wcn_bt_workqueue);
425
426 module_init(bt_hwctl_init);
427 module_exit(bt_hwctl_exit);
428 MODULE_AUTHOR("Tingting Lei <tingting.lei@mediatek.com>");
429 MODULE_DESCRIPTION("Bluetooth hardware control driver");
430 MODULE_LICENSE("GPL");