Merge remote-tracking branch 'stable/linux-3.0.y' into develop-3.0
[firefly-linux-kernel-4.4.55.git] / drivers / misc / bp / bp-auto.c
1 #include <linux/module.h>\r
2 #include <linux/kernel.h>\r
3 #include <linux/i2c.h>\r
4 #include <linux/irq.h>\r
5 #include <linux/gpio.h>\r
6 #include <linux/input.h>\r
7 #include <linux/platform_device.h>\r
8 #include <linux/fs.h>\r
9 #include <linux/uaccess.h>\r
10 #include <linux/miscdevice.h>\r
11 #include <linux/circ_buf.h>\r
12 #include <linux/interrupt.h>\r
13 #include <linux/miscdevice.h>\r
14 #include <mach/iomux.h>\r
15 #include <mach/gpio.h>\r
16 #include <asm/gpio.h>\r
17 #include <linux/delay.h>\r
18 #include <linux/poll.h>\r
19 #include <linux/wait.h>\r
20 #include <linux/wakelock.h>\r
21 #include <linux/workqueue.h>\r
22 #include <linux/slab.h>\r
23 #include <linux/earlysuspend.h>\r
24 \r
25 #include <linux/bp-auto.h>\r
26 \r
27 #if 0\r
28 #define DBG(x...)  printk(x)\r
29 #else\r
30 #define DBG(x...)\r
31 #endif\r
32 \r
33 struct bp_private_data *g_bp;\r
34 static struct class *g_bp_class;\r
35 static struct bp_operate *g_bp_ops[BP_ID_NUM]; \r
36 struct class *bp_class = NULL; \r
37 \r
38 static void ap_wakeup_bp(struct bp_private_data *bp, int wake)\r
39 {\r
40         if(bp->ops->ap_wake_bp)\r
41                 bp->ops->ap_wake_bp(bp, wake);  \r
42         \r
43 }\r
44 \r
45 static int bp_request_gpio(struct bp_private_data *bp)\r
46 {\r
47         int result = 0;\r
48         \r
49         if(bp->pdata->gpio_valid)\r
50         {\r
51                 if(bp->pdata->bp_power > 0)\r
52                 {\r
53                         bp->ops->bp_power = bp->pdata->bp_power;\r
54                 }\r
55 \r
56                 if(bp->pdata->bp_en > 0)\r
57                 {\r
58                         bp->ops->bp_en = bp->pdata->bp_en;\r
59                 }\r
60 \r
61                 if(bp->pdata->bp_reset > 0)\r
62                 {\r
63                         bp->ops->bp_reset = bp->pdata->bp_reset;\r
64                 }\r
65 \r
66                 if(bp->pdata->ap_ready > 0)\r
67                 {\r
68                         bp->ops->ap_ready = bp->pdata->ap_ready;\r
69                 }\r
70 \r
71                 if(bp->pdata->bp_ready > 0)\r
72                 {\r
73                         bp->ops->bp_ready = bp->pdata->bp_ready;\r
74                 }\r
75 \r
76                 if(bp->pdata->ap_wakeup_bp > 0)\r
77                 {\r
78                         bp->ops->ap_wakeup_bp = bp->pdata->ap_wakeup_bp;\r
79                 }\r
80 \r
81                 if(bp->pdata->bp_wakeup_ap > 0)\r
82                 {\r
83                         bp->ops->bp_wakeup_ap = bp->pdata->bp_wakeup_ap;\r
84                 }\r
85 \r
86                 if(bp->pdata->bp_usb_en > 0)\r
87                 {\r
88                         bp->ops->bp_usb_en = bp->pdata->bp_usb_en;\r
89                 }\r
90                 \r
91                 if(bp->pdata->bp_uart_en > 0)\r
92                 {\r
93                         bp->ops->bp_uart_en = bp->pdata->bp_uart_en;\r
94                 }\r
95 \r
96         }\r
97         \r
98         if(bp->ops->bp_power != BP_UNKNOW_DATA)\r
99         {\r
100                 result = gpio_request(bp->ops->bp_power, "bp_power");\r
101                 if(result)\r
102                 {\r
103                         printk("%s:fail to request gpio %d\n",__func__, bp->ops->bp_power);\r
104                         //return -1;\r
105                 }\r
106         }\r
107         \r
108         if(bp->ops->bp_en != BP_UNKNOW_DATA)\r
109         {\r
110                 result = gpio_request(bp->ops->bp_en, "bp_en");\r
111                 if(result)\r
112                 {\r
113                         printk("%s:fail to request gpio %d\n",__func__, bp->ops->bp_en);\r
114                         //return -1;\r
115                 }\r
116         }\r
117 \r
118 \r
119         if(bp->ops->bp_reset != BP_UNKNOW_DATA)\r
120         {\r
121                 result = gpio_request(bp->ops->bp_reset, "bp_reset");\r
122                 if(result)\r
123                 {\r
124                         printk("%s:fail to request gpio %d\n",__func__, bp->ops->bp_reset);\r
125                         //return -1;\r
126                 }\r
127         }\r
128 \r
129 \r
130         if(bp->ops->ap_ready != BP_UNKNOW_DATA)\r
131         {\r
132                 result = gpio_request(bp->ops->ap_ready, "ap_ready");\r
133                 if(result)\r
134                 {\r
135                         printk("%s:fail to request gpio %d\n",__func__, bp->ops->ap_ready);\r
136                         //return -1;\r
137                 }\r
138         }\r
139 \r
140 \r
141         if(bp->ops->bp_ready != BP_UNKNOW_DATA)\r
142         {\r
143                 result = gpio_request(bp->ops->bp_ready, "bp_ready");\r
144                 if(result)\r
145                 {\r
146                         printk("%s:fail to request gpio %d\n",__func__, bp->ops->bp_ready);\r
147                         //return -1;\r
148                 }\r
149         }\r
150 \r
151 \r
152         if(bp->ops->ap_wakeup_bp != BP_UNKNOW_DATA)\r
153         {\r
154                 result = gpio_request(bp->ops->ap_wakeup_bp, "ap_wakeup_bp");\r
155                 if(result)\r
156                 {\r
157                         printk("%s:fail to request gpio %d\n",__func__, bp->ops->ap_wakeup_bp);\r
158                         //return -1;\r
159                 }\r
160         }\r
161 \r
162         if(bp->ops->bp_wakeup_ap != BP_UNKNOW_DATA)\r
163         {\r
164                 result = gpio_request(bp->ops->bp_wakeup_ap, "bp_wakeup_ap");\r
165                 if(result)\r
166                 {\r
167                         printk("%s:fail to request gpio %d\n",__func__, bp->ops->bp_wakeup_ap);\r
168                         //return -1;\r
169                 }\r
170         }\r
171 \r
172         if(bp->ops->bp_usb_en != BP_UNKNOW_DATA)\r
173         {\r
174                 result = gpio_request(bp->ops->bp_usb_en, "bp_usb_en");\r
175                 if(result)\r
176                 {\r
177                         printk("%s:fail to request gpio %d\n",__func__, bp->ops->bp_usb_en);\r
178                         //return -1;\r
179                 }\r
180         }\r
181 \r
182         if(bp->ops->bp_uart_en != BP_UNKNOW_DATA)\r
183         {\r
184                 result = gpio_request(bp->ops->bp_uart_en, "bp_uart_en");\r
185                 if(result)\r
186                 {\r
187                         printk("%s:fail to request gpio %d\n",__func__, bp->ops->bp_uart_en);\r
188                         //return -1;\r
189                 }\r
190         }\r
191         \r
192         return result;\r
193 }\r
194 \r
195 \r
196 static irqreturn_t bp_wake_up_irq(int irq, void *dev_id)\r
197 {\r
198         \r
199         struct bp_private_data *bp = dev_id;\r
200         if(bp->ops->bp_wake_ap)\r
201                 bp->ops->bp_wake_ap(bp);\r
202         \r
203         return IRQ_HANDLED;\r
204 }\r
205 \r
206 static int bp_id_open(struct inode *inode, struct file *file)\r
207 {\r
208         struct bp_private_data *bp = g_bp;\r
209         \r
210         return 0;\r
211 }\r
212 \r
213 static int bp_id_release(struct inode *inode, struct file *file)\r
214 {\r
215 \r
216         return 0;\r
217 }\r
218 \r
219 static long bp_id_ioctl(struct file *file, unsigned int cmd, unsigned long arg)\r
220 {\r
221         struct bp_private_data *bp = g_bp;\r
222         void __user *argp = (void __user *)arg;\r
223         int result = 0;\r
224 \r
225         switch(cmd)\r
226         {       \r
227                 case BP_IOCTL_SET_PVID:\r
228                         \r
229                         break;\r
230         \r
231                 case BP_IOCTL_GET_BPID:\r
232                         if (copy_to_user(argp, &bp->ops->bp_id, sizeof(bp->ops->bp_id)))\r
233                         {\r
234                                 printk("%s:failed to copy status to user space.\n",__FUNCTION__);\r
235                                 return -EFAULT;\r
236                         }\r
237                         \r
238                         break;\r
239                         \r
240                 default:\r
241                         break;\r
242         }\r
243         \r
244         return 0;\r
245 }\r
246 \r
247 \r
248 static int bp_dev_open(struct inode *inode, struct file *file)\r
249 {\r
250         struct bp_private_data *bp = g_bp;\r
251         device_init_wakeup(bp->dev, 1);\r
252         return 0;\r
253 }\r
254 static ssize_t bp_dev_write(struct file *file, const char __user *buf,size_t len, loff_t *off)\r
255 {       \r
256         static char cmd[2];\r
257         struct bp_private_data *bp = g_bp;\r
258         \r
259         int ret = 0;
260         if (len > 2) 
261         {
262                 return -EINVAL;
263         }
264         ret = copy_from_user(&cmd, buf, len);
265         if (ret != 0) {
266                 return -EFAULT;
267         }
268         printk(" received cmd = %c\n",cmd[0]);\r
269         switch(bp->ops->bp_id)\r
270         {\r
271                 case BP_ID_MT6229:\r
272                         if (cmd[0] == '0')\r
273                         {\r
274                                 gpio_direction_output(bp->ops->ap_ready, GPIO_LOW);\r
275                         }       \r
276                         if (cmd[0] == '1')\r
277                         {\r
278                                 gpio_direction_output(bp->ops->ap_ready, GPIO_HIGH);\r
279                         }\r
280                         if (cmd[0] == '2')\r
281                         {\r
282                                 gpio_direction_output(bp->ops->bp_uart_en, GPIO_LOW);\r
283                         }\r
284                         if (cmd[0] == '3')\r
285                         {\r
286                                 gpio_direction_output(bp->ops->bp_uart_en, GPIO_HIGH);\r
287                         }\r
288                         if (cmd[0] == '4')\r
289                         {\r
290                                 gpio_direction_output(bp->ops->bp_usb_en, GPIO_HIGH);\r
291                         }if (cmd[0] == '5')\r
292                         {\r
293                                 gpio_direction_output(bp->ops->bp_usb_en, GPIO_LOW);\r
294                         }\r
295                         break;\r
296                 \r
297                 case BP_ID_MU509:\r
298                         break;\r
299 \r
300                 default:\r
301                         break;\r
302 \r
303         }\r
304         return len;\r
305 }\r
306 static int bp_dev_release(struct inode *inode, struct file *file)\r
307 {\r
308         return 0;\r
309 }\r
310 \r
311 static long bp_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)\r
312 {\r
313         struct bp_private_data *bp = g_bp;\r
314         void __user *argp = (void __user *)arg;\r
315         int result = 0;\r
316 \r
317         switch(cmd)\r
318         {\r
319                 case BP_IOCTL_RESET:    \r
320                         if(bp->ops->reset)\r
321                         {\r
322                                 bp->ops->reset(bp);\r
323                         }\r
324                         else if(bp->ops->active)\r
325                         {\r
326                                 bp->ops->active(bp, 0);\r
327                                 msleep(100);\r
328                                 bp->ops->active(bp, 1);\r
329                         }\r
330                         break;\r
331                         \r
332                 case BP_IOCTL_POWON:\r
333                         if(bp->ops->active)\r
334                         bp->ops->active(bp, 1);\r
335                         break;\r
336                         \r
337                 case BP_IOCTL_POWOFF:\r
338                         if(bp->ops->active)\r
339                         bp->ops->active(bp, 0);\r
340                         break;\r
341         \r
342                 case BP_IOCTL_WRITE_STATUS:\r
343                         \r
344                         break;\r
345         \r
346                 case BP_IOCTL_GET_STATUS:\r
347                         \r
348                         break;\r
349 \r
350                 case BP_IOCTL_SET_PVID:\r
351                         \r
352                         break;\r
353         \r
354                 case BP_IOCTL_GET_BPID:\r
355                         if (copy_to_user(argp, &bp->ops->bp_id, sizeof(bp->ops->bp_id)))\r
356                         {\r
357                                 printk("%s:failed to copy status to user space.\n",__FUNCTION__);\r
358                                 return -EFAULT;\r
359                         }\r
360                         \r
361                         break;\r
362                         \r
363                 default:\r
364                         break;\r
365         }\r
366         \r
367         return 0;\r
368 }\r
369 \r
370 static ssize_t bp_status_read(struct class *cls, struct class_attribute *attr, char *_buf)\r
371 {\r
372         struct bp_private_data *bp = g_bp;\r
373         \r
374         return sprintf(_buf, "%d\n", bp->status);\r
375         \r
376 }\r
377 \r
378 static ssize_t bp_status_write(struct class *cls, struct class_attribute *attr, const char *_buf, size_t _count)\r
379 {       \r
380         struct bp_private_data *bp = g_bp;\r
381         int result = 0;\r
382         int status = 0;\r
383         \r
384         status = simple_strtoul(_buf, NULL, 16);\r
385         if(status == bp->status) \r
386                 return _count;\r
387         \r
388         bp->status = status;\r
389         \r
390         if(bp->ops->write_status)\r
391                 result = bp->ops->write_status(bp);     \r
392            \r
393         return result; \r
394 }\r
395 static CLASS_ATTR(bp_status, 0777, bp_status_read, bp_status_write);\r
396 static int bp_probe(struct platform_device *pdev)\r
397 {\r
398         struct bp_platform_data *pdata = pdev->dev.platform_data;\r
399         struct bp_private_data *bp = NULL;\r
400         int i = 0, result;      \r
401 \r
402         if(!pdata)\r
403                 return -1;\r
404         \r
405         DBG("%s:init start\n",__func__);\r
406         \r
407         if(pdata->init_platform_hw)\r
408                 pdata->init_platform_hw();\r
409         \r
410         bp = kzalloc(sizeof(struct bp_private_data), GFP_KERNEL);\r
411         if(bp == NULL)\r
412         {\r
413                 printk("%s:fail malloc bp data\n",__func__);\r
414                 return -1;\r
415         }\r
416 \r
417         bp->pdata = pdata;\r
418         bp->dev = &pdev->dev;\r
419         \r
420         //select modem acccording to pdata defaultly\r
421         if((pdata->bp_id > BP_ID_INVALID) && (pdata->bp_id < BP_ID_NUM))\r
422         {\r
423                 if(g_bp_ops[pdata->bp_id])\r
424                 {\r
425                         bp->ops = g_bp_ops[pdata->bp_id];\r
426                         printk("%s:bp_id=%d\n",__func__,bp->ops->bp_id);\r
427                 }\r
428                 else\r
429                 {\r
430                         printk("%s:error:g_bp_ops[%d] = 0x%p\n",__func__, pdata->bp_id, g_bp_ops[pdata->bp_id]);\r
431                 }\r
432                 \r
433         }\r
434         else\r
435         {\r
436                 printk("%s:bp_id=%d is out of range\n",__func__, pdata->bp_id);\r
437         }\r
438         \r
439         bp_request_gpio(bp);\r
440         \r
441         if((bp->ops->bp_wakeup_ap) && (bp->ops->trig != BP_UNKNOW_DATA))\r
442         {\r
443                 result = request_irq(bp->ops->bp_wakeup_ap, bp_wake_up_irq, bp->ops->trig, "bp_wakeup_ap", bp);\r
444                 if (result < 0) {\r
445                         printk("%s: request_irq(%d) failed\n", __func__, bp->ops->bp_wakeup_ap);\r
446                         gpio_free(pdata->bp_wakeup_ap);\r
447                         return result;\r
448                 }\r
449         }\r
450 \r
451         if(bp->ops->init)\r
452                 bp->ops->init(bp);\r
453         \r
454         enable_irq_wake(bp->ops->bp_wakeup_ap);\r
455         wake_lock_init(&bp->bp_wakelock, WAKE_LOCK_SUSPEND, "bp_wakelock");\r
456         \r
457         bp->status = BP_OFF;\r
458 \r
459         if(!bp->ops->private_miscdev)\r
460         {\r
461                 bp->fops.owner = THIS_MODULE;\r
462                 bp->fops.open = bp_dev_open;\r
463                 bp->fops.write = bp_dev_write;\r
464                 bp->fops.release = bp_dev_release;      \r
465                 bp->fops.unlocked_ioctl = bp_dev_ioctl;\r
466 \r
467                 bp->miscdev.minor = MISC_DYNAMIC_MINOR;\r
468                 if(bp->ops->misc_name)\r
469                 bp->miscdev.name = bp->ops->misc_name;\r
470                 else    \r
471                 bp->miscdev.name = "bp-auto";\r
472                 bp->miscdev.fops = &bp->fops;\r
473         }\r
474         else\r
475         {\r
476                 memcpy(&bp->miscdev, bp->ops->private_miscdev, sizeof(*bp->ops->private_miscdev));\r
477 \r
478         }\r
479         \r
480         result = misc_register(&bp->miscdev);\r
481         if (result < 0) {\r
482                 printk("misc_register err\n");\r
483                 return result;\r
484         }\r
485 \r
486         bp->id_fops.owner = THIS_MODULE;\r
487         bp->id_fops.open = bp_id_open;\r
488         bp->id_fops.release = bp_id_release;    \r
489         bp->id_fops.unlocked_ioctl = bp_id_ioctl;\r
490 \r
491         bp->id_miscdev.minor = MISC_DYNAMIC_MINOR;\r
492         bp->id_miscdev.name = "bp_id";\r
493         bp->id_miscdev.fops = &bp->id_fops;\r
494         result = misc_register(&bp->id_miscdev);\r
495         if (result < 0) {\r
496                 printk("misc_register err\n");\r
497                 return result;\r
498         }\r
499         \r
500         g_bp = bp;\r
501 \r
502         platform_set_drvdata(pdev, bp); \r
503         \r
504         printk("%s:init success\n",__func__);\r
505         return result;\r
506 \r
507 }\r
508 \r
509 int bp_suspend(struct platform_device *pdev, pm_message_t state)\r
510 {\r
511         struct bp_private_data *bp = platform_get_drvdata(pdev);\r
512         \r
513         if(bp->ops->suspend)\r
514                 bp->ops->suspend(bp);\r
515         \r
516         return 0;\r
517 }\r
518 \r
519 int bp_resume(struct platform_device *pdev)\r
520 {\r
521         struct bp_private_data *bp = platform_get_drvdata(pdev);\r
522         \r
523         if(bp->ops->resume)\r
524                 bp->ops->resume(bp);\r
525 \r
526         return 0;\r
527 }\r
528 \r
529 void bp_shutdown(struct platform_device *pdev)\r
530 {\r
531         struct bp_private_data *bp = platform_get_drvdata(pdev);\r
532 \r
533         if(bp->ops->shutdown)\r
534                 bp->ops->shutdown(bp);\r
535         \r
536 }\r
537 \r
538 \r
539 int bp_register_slave(struct bp_private_data *bp,\r
540                         struct bp_platform_data *slave_pdata,\r
541                         struct bp_operate *(*get_bp_ops)(void))\r
542 {\r
543         int result = 0;\r
544         struct bp_operate *ops = get_bp_ops();\r
545         if((ops->bp_id >= BP_ID_NUM) || (ops->bp_id <= BP_ID_INVALID))\r
546         {       \r
547                 printk("%s:%s id is error %d\n", __func__, ops->name, ops->bp_id);\r
548                 return -1;      \r
549         }\r
550         g_bp_ops[ops->bp_id] = ops;\r
551         printk("%s:%s,id=%d\n",__func__,g_bp_ops[ops->bp_id]->name, ops->bp_id);\r
552         return result;\r
553 }\r
554 \r
555 \r
556 int bp_unregister_slave(struct bp_private_data *bp,\r
557                         struct bp_platform_data *slave_pdata,\r
558                         struct bp_operate *(*get_bp_ops)(void))\r
559 {\r
560         int result = 0;\r
561         struct bp_operate *ops = get_bp_ops();\r
562         if((ops->bp_id >= BP_ID_NUM) || (ops->bp_id <= BP_ID_INVALID))\r
563         {       \r
564                 printk("%s:%s id is error %d\n", __func__, ops->name, ops->bp_id);\r
565                 return -1;      \r
566         }\r
567         printk("%s:%s,id=%d\n",__func__,g_bp_ops[ops->bp_id]->name, ops->bp_id);\r
568         g_bp_ops[ops->bp_id] = NULL;    \r
569         return result;\r
570 }\r
571 \r
572 \r
573 static struct platform_driver bp_driver = {\r
574         .probe          = bp_probe,\r
575         .shutdown       = bp_shutdown,\r
576         .suspend        = bp_suspend,\r
577         .resume         = bp_resume,\r
578         .driver = {\r
579                 .name   = "bp-auto",\r
580                 .owner  = THIS_MODULE,\r
581         },\r
582 };\r
583 \r
584 static int __init bp_init(void)\r
585 {\r
586         int ret ;\r
587         bp_class = class_create(THIS_MODULE, "bp-auto");\r
588         ret =  class_create_file(bp_class, &class_attr_bp_status);\r
589         if (ret)\r
590         {\r
591                 printk("Fail to create class bp-auto\n");\r
592         }\r
593         return platform_driver_register(&bp_driver);\r
594 }\r
595 \r
596 static void __exit bp_exit(void)\r
597 {\r
598         platform_driver_unregister(&bp_driver);\r
599         class_remove_file(bp_class, &class_attr_bp_status);\r
600 }\r
601 \r
602 module_init(bp_init);\r
603 module_exit(bp_exit);\r
604 \r
605 MODULE_AUTHOR("ROCKCHIP Corporation:lw@rock-chips.com");\r
606 MODULE_DESCRIPTION("device interface for auto modem driver");\r
607 MODULE_LICENSE("GPL");\r
608 \r