• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 Unionman Technology Co., Ltd.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  */
18 
19 #include <linux/module.h>
20 #include <linux/interrupt.h>
21 #include <linux/irq.h>
22 #include <linux/of_irq.h>
23 #include <linux/delay.h>
24 #include <linux/cdev.h>
25 #include <linux/proc_fs.h>
26 #include <linux/device.h>
27 #include <linux/platform_device.h>
28 #include <linux/rfkill.h>
29 #include <linux/of.h>
30 #include <linux/pinctrl/consumer.h>
31 #include <linux/amlogic/aml_gpio_consumer.h>
32 #include <linux/of_gpio.h>
33 #include <linux/gpio.h>
34 #include <linux/amlogic/cpu_version.h>
35 #include <linux/amlogic/iomap.h>
36 #include <linux/io.h>
37 #include <linux/uaccess.h>
38 #include <linux/pwm.h>
39 #include <linux/pci.h>
40 #include <linux/gpio/consumer.h>
41 #include "dhd_static_buf.h"
42 #include "wifi_dt.h"
43 
44 #define OWNER_NAME "sdio_wifi"
45 
46 struct wifi_plat_info {
47     int interrupt_pin;
48     int irq_num;
49     int irq_trigger_type;
50 
51     int power_on_pin;
52     int power_on_pin_level;
53     int power_on_pin_OD;
54     int power_on_pin2;
55 
56     struct gpio_desc *interrupt_desc;
57     struct gpio_desc *power_desc;
58 
59     int plat_info_valid;
60     struct pinctrl *p;
61     struct device *dev;
62 };
63 
64 #define WIFI_POWER_DRIVER_NAME "wifi_power"
65 #define WIFI_POWER_DEVICE_NAME "wifi_power"
66 #define WIFI_POWER_CLASS_NAME "wifi_power"
67 
68 #define WIFI_POWER_UP _IO('m', 1)
69 #define WIFI_POWER_DOWN _IO('m', 2)
70 #define USB_POWER_UP _IO('m', 3)
71 #define USB_POWER_DOWN _IO('m', 4)
72 #define SDIO_GET_DEV_TYPE _IO('m', 5)
73 
74 #define BT_BIT 0
75 #define WIFI_BIT 1
76 
77 static struct wifi_plat_info wifi_info;
78 static dev_t wifi_power_devno;
79 static struct cdev *wifi_power_cdev;
80 static struct device *devp;
81 struct wifi_power_platform_data *pdata;
82 static int power_flag;
83 
84 static DEFINE_MUTEX(wifi_bt_mutex);
85 
86 #define WIFI_INFO(fmt, args...) dev_info(wifi_info.dev, "[%s] " fmt, __func__, ##args)
87 
88 #ifdef CONFIG_OF
89 static const struct of_device_id wifi_match[] = {
90     {.compatible = "amlogic, wifi-dev", .data = (void *)&wifi_info},
91     {},
92 };
93 
wifi_get_driver_data(struct platform_device * pdev)94 static struct wifi_plat_info *wifi_get_driver_data(struct platform_device *pdev)
95 {
96     const struct of_device_id *match;
97 
98     match = of_match_node(wifi_match, pdev->dev.of_node);
99     if (!match) {
100         return NULL;
101     }
102     return (struct wifi_plat_info *)match->data;
103 }
104 #else
105 #define wifi_match NULL
106 #endif
107 
108 #define SHOW_PIN_OWN(pin_str, pin_num) WIFI_INFO("%s(%d)\n", pin_str, pin_num)
109 
set_power(int value)110 static int set_power(int value)
111 {
112     if (!wifi_info.power_on_pin_OD) {
113         if (wifi_info.power_on_pin_level) {
114             return gpio_direction_output(wifi_info.power_on_pin, !value);
115         } else {
116             return gpio_direction_output(wifi_info.power_on_pin, value);
117         }
118     } else {
119         if (wifi_info.power_on_pin_level) {
120             if (value) {
121                 gpio_direction_input(wifi_info.power_on_pin);
122             } else {
123                 gpio_direction_output(wifi_info.power_on_pin, 0);
124             }
125         } else {
126             if (value) {
127                 gpio_direction_output(wifi_info.power_on_pin, 0);
128             } else {
129                 gpio_direction_input(wifi_info.power_on_pin);
130             }
131         }
132     }
133     return 0;
134 }
135 
set_power2(int value)136 static int set_power2(int value)
137 {
138     if (wifi_info.power_on_pin_level) {
139         return gpio_direction_output(wifi_info.power_on_pin2, !value);
140     } else {
141         return gpio_direction_output(wifi_info.power_on_pin2, value);
142     }
143 }
144 
set_wifi_power(int is_power)145 static int set_wifi_power(int is_power)
146 {
147     int ret = 0;
148 
149     if (is_power) {
150         if (wifi_info.power_on_pin) {
151             ret = set_power(1);
152             if (ret) {
153                 WIFI_INFO("power up failed(%d)\n", ret);
154             }
155         }
156         if (wifi_info.power_on_pin2) {
157             ret = set_power2(1);
158             if (ret) {
159                 WIFI_INFO("power2 up failed(%d)\n", ret);
160             }
161         }
162     } else {
163         if (wifi_info.power_on_pin) {
164             ret = set_power(0);
165             if (ret) {
166                 WIFI_INFO("power down failed(%d)\n", ret);
167             }
168         }
169         if (wifi_info.power_on_pin2) {
170             ret = set_power2(0);
171             if (ret) {
172                 WIFI_INFO("power2 down failed(%d)\n", ret);
173             }
174         }
175     }
176     return ret;
177 }
178 
wifi_power_control(int is_power,int shift)179 static void wifi_power_control(int is_power, int shift)
180 {
181     mutex_lock(&wifi_bt_mutex);
182     if (is_power) {
183         if (!power_flag) {
184             set_wifi_power(is_power);
185             WIFI_INFO("Set %s power on !\n", (shift ? "WiFi" : "BT"));
186             msleep(200L);
187             sdio_reinit();
188         }
189         power_flag |= (1 << shift);
190         WIFI_INFO("Set %s power on !\n", (shift ? "WiFi" : "BT"));
191     } else {
192         power_flag &= ~(1 << shift);
193         if (!power_flag) {
194             set_wifi_power(is_power);
195             WIFI_INFO("Set %s power down\n", (shift ? "WiFi" : "BT"));
196         }
197     }
198     mutex_unlock(&wifi_bt_mutex);
199 }
200 
aml_set_bt_power(int is_power)201 void aml_set_bt_power(int is_power)
202 {
203     wifi_power_control(is_power, BT_BIT);
204 }
205 EXPORT_SYMBOL(aml_set_bt_power);
206 
aml_set_wifi_power(int is_power)207 void aml_set_wifi_power(int is_power)
208 {
209     wifi_power_control(is_power, WIFI_BIT);
210 }
211 EXPORT_SYMBOL(aml_set_wifi_power);
212 
wifi_power_open(struct inode * inode,struct file * file)213 static int wifi_power_open(struct inode *inode, struct file *file)
214 {
215     struct cdev *cdevp = inode->i_cdev;
216     file->private_data = cdevp;
217     return 0;
218 }
219 
wifi_power_release(struct inode * inode,struct file * file)220 static int wifi_power_release(struct inode *inode, struct file *file)
221 {
222     return 0;
223 }
224 
wifi_power_ioctl(struct file * filp,unsigned int cmd,unsigned long arg)225 static long wifi_power_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
226 {
227     char dev_type[10] = {'\0'};
228 
229     switch (cmd) {
230         case WIFI_POWER_UP:
231             aml_set_wifi_power(0);
232             mdelay(200L);
233             aml_set_wifi_power(1);
234             mdelay(200L);
235             WIFI_INFO("ioctl wifi power up!\n");
236             break;
237         case WIFI_POWER_DOWN:
238             aml_set_wifi_power(0);
239             WIFI_INFO("ioctl wifi power down!\n");
240             break;
241         case USB_POWER_UP:
242             WIFI_INFO(KERN_INFO "ioctl usb wifi power up!\n");
243             break;
244         case USB_POWER_DOWN:
245             WIFI_INFO(KERN_INFO "ioctl usb wifi power down!\n");
246             break;
247         case SDIO_GET_DEV_TYPE: /* Now only support sdio */
248             if (strlen("sdio") >= sizeof(dev_type)) {
249                 memcpy(dev_type, "sdio", (sizeof(dev_type) - 1));
250             } else {
251                 memcpy(dev_type, "sdio", strlen("sdio"));
252             }
253             WIFI_INFO("wifi interface dev type: %s, length = %d\n", dev_type, (int)strlen(dev_type));
254             if (copy_to_user((char __user *)arg, dev_type, strlen(dev_type))) {
255                 return -ENOTTY;
256             }
257             break;
258         default:
259             WIFI_INFO("usb wifi_power_ioctl: default !!!\n");
260             return -EINVAL;
261     }
262 
263     return 0;
264 }
265 
266 static const struct file_operations wifi_power_fops = {
267     .unlocked_ioctl = wifi_power_ioctl,
268     .compat_ioctl = wifi_power_ioctl,
269     .open = wifi_power_open,
270     .release = wifi_power_release,
271 };
272 
273 static struct class wifi_power_class = {
274     .name = WIFI_POWER_CLASS_NAME,
275     .owner = THIS_MODULE,
276 };
277 
wifi_set_up_power(void)278 int wifi_set_up_power(void)
279 {
280     int ret;
281     /* setup power */
282     if (wifi_info.power_on_pin) {
283         ret = gpio_request(wifi_info.power_on_pin, OWNER_NAME);
284         if (ret) {
285             WIFI_INFO("power_on_pin request failed(%d)\n", ret);
286         }
287         if (wifi_info.power_on_pin_level) {
288             ret = set_power(1);
289         } else {
290             ret = set_power(0);
291         }
292         if (ret) {
293             WIFI_INFO("power_on_pin output failed(%d)\n", ret);
294         }
295         SHOW_PIN_OWN("WIFI: power_on_pin ", wifi_info.power_on_pin);
296     }
297 
298     if (wifi_info.power_on_pin2) {
299         ret = gpio_request(wifi_info.power_on_pin2, OWNER_NAME);
300         if (ret) {
301             WIFI_INFO("power_on_pin2 request failed(%d)\n", ret);
302         }
303         if (wifi_info.power_on_pin_level) {
304             ret = set_power2(1);
305         } else {
306             ret = set_power2(0);
307         }
308         if (ret) {
309             WIFI_INFO("power_on_pin2 output failed(%d)\n", ret);
310         }
311         SHOW_PIN_OWN("WIFI: power_on_pin2 ", wifi_info.power_on_pin2);
312     }
313     return ret;
314 }
315 
wifi_setup_dt(void)316 int wifi_setup_dt(void)
317 {
318     int ret;
319 
320     WIFI_INFO("wifi_setup_dt\n");
321     if (!wifi_info.plat_info_valid) {
322         WIFI_INFO("wifi_setup_dt : invalid device tree setting\n");
323         return -1;
324     }
325 
326     /* setup irq */
327     if (wifi_info.interrupt_pin) {
328         ret = gpio_request(wifi_info.interrupt_pin, OWNER_NAME);
329         if (ret) {
330             WIFI_INFO("interrupt_pin request failed(%d)\n", ret);
331         }
332 
333         ret = gpio_direction_input(wifi_info.interrupt_pin);
334         if (ret) {
335             WIFI_INFO("set interrupt_pin input failed(%d)\n", ret);
336         }
337 
338         wifi_info.irq_num = gpio_to_irq(wifi_info.interrupt_pin);
339         if (wifi_info.irq_num) {
340             WIFI_INFO("irq num is:(%d)\n", wifi_info.irq_num);
341         }
342 
343         SHOW_PIN_OWN("interrupt_pin", wifi_info.interrupt_pin);
344     }
345 
346     /* setup power */
347     wifi_set_up_power();
348 
349     return 0;
350 }
351 
wifi_teardown_dt(void)352 void wifi_teardown_dt(void)
353 {
354     WIFI_INFO("wifi_teardown_dt\n");
355 
356     if (!wifi_info.plat_info_valid) {
357         WIFI_INFO("wifi_teardown_dt : invalid device tree setting\n");
358         return;
359     }
360 
361     if (wifi_info.power_on_pin) {
362         gpio_free(wifi_info.power_on_pin);
363     }
364 
365     if (wifi_info.power_on_pin2) {
366         gpio_free(wifi_info.power_on_pin2);
367     }
368 
369     if (wifi_info.interrupt_pin) {
370         gpio_free(wifi_info.interrupt_pin);
371     }
372 }
373 
wifi_dev_probe(struct platform_device * pdev)374 static int wifi_dev_probe(struct platform_device *pdev)
375 {
376     int ret;
377 #ifdef CONFIG_OF
378     struct wifi_plat_info *plat;
379     const char *value;
380     int desc;
381 #else
382     struct wifi_plat_info *plat = (struct wifi_plat_info *)(pdev->dev.platform_data);
383 #endif
384 
385 #ifdef CONFIG_OF
386     if (pdev->dev.of_node) {
387         plat = wifi_get_driver_data(pdev);
388         plat->plat_info_valid = 0;
389         plat->dev = &pdev->dev;
390 
391         ret = of_property_read_string(pdev->dev.of_node, "interrupt_pin", &value);
392         if (ret) {
393             plat->interrupt_pin = 0;
394         } else {
395             desc = of_get_named_gpio_flags(pdev->dev.of_node, "interrupt_pin", 0, NULL);
396             plat->interrupt_desc = gpio_to_desc(desc);
397             plat->interrupt_pin = desc;
398 
399             ret = of_property_read_string(pdev->dev.of_node, "irq_trigger_type", &value);
400             if (ret) {
401                 WIFI_INFO("no irq_trigger_type");
402                 plat->irq_trigger_type = 0;
403                 return -1;
404             }
405 
406             if (strcmp(value, "GPIO_IRQ_HIGH") == 0) {
407                 plat->irq_trigger_type = GPIO_IRQ_HIGH;
408             } else if (strcmp(value, "GPIO_IRQ_LOW") == 0) {
409                 plat->irq_trigger_type = GPIO_IRQ_LOW;
410             } else if (strcmp(value, "GPIO_IRQ_RISING") == 0) {
411                 plat->irq_trigger_type = GPIO_IRQ_RISING;
412             } else if (strcmp(value, "GPIO_IRQ_FALLING") == 0) {
413                 plat->irq_trigger_type = GPIO_IRQ_FALLING;
414             } else {
415                 WIFI_INFO("unknown irq trigger type-%s\n", value);
416                 return -1;
417             }
418 
419             WIFI_INFO("interrupt_pin=%d\n", plat->interrupt_pin);
420             WIFI_INFO("irq_num=%d, irq_trigger_type=%d\n", plat->irq_num, plat->irq_trigger_type);
421         }
422 
423         ret = of_property_read_string(pdev->dev.of_node, "power_on_pin", &value);
424         if (ret) {
425             WIFI_INFO("no power_on_pin");
426             plat->power_on_pin = 0;
427             plat->power_on_pin_OD = 0;
428         } else {
429             desc = of_get_named_gpio_flags(pdev->dev.of_node, "power_on_pin", 0, NULL);
430             plat->power_desc = gpio_to_desc(desc);
431             plat->power_on_pin = desc;
432         }
433 
434         ret = of_property_read_u32(pdev->dev.of_node, "power_on_pin_level", &plat->power_on_pin_level);
435         if (ret) {
436             plat->power_on_pin_level = 0;
437         }
438 
439         ret = of_property_read_u32(pdev->dev.of_node, "power_on_pin_OD", &plat->power_on_pin_OD);
440         if (ret) {
441             plat->power_on_pin_OD = 0;
442         }
443 
444         ret = of_property_read_string(pdev->dev.of_node, "power_on_pin2", &value);
445         if (ret) {
446             plat->power_on_pin2 = 0;
447         } else {
448             desc = of_get_named_gpio_flags(pdev->dev.of_node, "power_on_pin2", 0, NULL);
449             plat->power_on_pin2 = desc;
450         }
451 
452         if (of_get_property(pdev->dev.of_node, "dhd_static_buf", NULL)) {
453             WIFI_INFO("dhd_static_buf setup\n");
454         }
455 
456         plat->plat_info_valid = 1;
457     }
458 #endif
459 
460     ret = alloc_chrdev_region(&wifi_power_devno, 0, 1, WIFI_POWER_DRIVER_NAME);
461     if (ret < 0) {
462         ret = -ENODEV;
463         goto out;
464     }
465 
466     ret = class_register(&wifi_power_class);
467     if (ret < 0) {
468         goto error1;
469     }
470 
471     wifi_power_cdev = cdev_alloc();
472     if (!wifi_power_cdev) {
473         goto error2;
474     }
475     cdev_init(wifi_power_cdev, &wifi_power_fops);
476     wifi_power_cdev->owner = THIS_MODULE;
477     ret = cdev_add(wifi_power_cdev, wifi_power_devno, 1);
478     if (ret) {
479         goto error3;
480     }
481 
482     devp = device_create(&wifi_power_class, NULL, wifi_power_devno, NULL, WIFI_POWER_DEVICE_NAME);
483     if (IS_ERR(devp)) {
484         ret = PTR_ERR(devp);
485         goto error3;
486     }
487     devp->platform_data = pdata;
488 
489     wifi_setup_dt();
490 
491     return 0;
492 error3:
493     cdev_del(wifi_power_cdev);
494 error2:
495     class_unregister(&wifi_power_class);
496 error1:
497     unregister_chrdev_region(wifi_power_devno, 1);
498 out:
499     return ret;
500 }
501 
wifi_dev_remove(struct platform_device * pdev)502 static int wifi_dev_remove(struct platform_device *pdev)
503 {
504     WIFI_INFO("wifi_dev_remove\n");
505     wifi_teardown_dt();
506     return 0;
507 }
508 
509 static struct platform_driver wifi_plat_driver = {
510     .probe = wifi_dev_probe,
511     .remove = wifi_dev_remove,
512     .driver = {.name = "wifi", .owner = THIS_MODULE, .of_match_table = wifi_match},
513 };
514 
wifi_dt_init(void)515 static int __init wifi_dt_init(void)
516 {
517     int ret;
518 
519     ret = platform_driver_register(&wifi_plat_driver);
520     return ret;
521 }
522 
523 device_initcall_sync(wifi_dt_init);
524 
wifi_dt_exit(void)525 static void __exit wifi_dt_exit(void)
526 {
527     platform_driver_unregister(&wifi_plat_driver);
528 }
529 module_exit(wifi_dt_exit);
530 
531 MODULE_LICENSE("GPL v2");
532 MODULE_AUTHOR("AlgoIdeas <yu19881234@163.com>");
533 MODULE_DESCRIPTION("Wifi device tree driver for Amlogic");
534 
535 /**************** wifi mac *****************/
536 u8 WIFI_MAC[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
chartonum(char c)537 static unsigned char chartonum(char c)
538 {
539     if (c >= '0' && c <= '9') {
540         return c - '0';
541     }
542     if (c >= 'A' && c <= 'F') {
543         return (c - 'A') + 10L;
544     }
545     if (c >= 'a' && c <= 'f') {
546         return (c - 'a') + 10L;
547     }
548     return 0;
549 }
550 
mac_addr_set(char * line)551 static int __init mac_addr_set(char *line)
552 {
553     unsigned char mac[6];
554     int i = 0;
555 
556     WIFI_INFO("try to wifi mac from emmc key!\n");
557     for (i = 0; i < 6L && line[0] != '\0' && line[1] != '\0'; i++) {
558         mac[i] = chartonum(line[0]) << 4L | chartonum(line[1]);
559         line += 3L;
560     }
561     memcpy(WIFI_MAC, mac, 6L);
562     WIFI_INFO("uboot setup mac-addr: %x:%x:%x:%x:%x:%x\n", WIFI_MAC[0], WIFI_MAC[1], WIFI_MAC[2L], WIFI_MAC[3L],
563               WIFI_MAC[4L], WIFI_MAC[5L]);
564 
565     return 1;
566 }
567 
568 __setup("mac_wifi=", mac_addr_set);
569 
wifi_get_mac(void)570 u8 *wifi_get_mac(void)
571 {
572     return WIFI_MAC;
573 }
574 EXPORT_SYMBOL(wifi_get_mac);
575 
extern_wifi_set_enable(int is_on)576 void extern_wifi_set_enable(int is_on)
577 {
578     if (is_on) {
579         set_wifi_power(1);
580         WIFI_INFO("WIFI  Enable! %d\n", wifi_info.power_on_pin);
581     } else {
582         set_wifi_power(0);
583         WIFI_INFO("WIFI  Disable! %d\n", wifi_info.power_on_pin);
584     }
585 }
586 EXPORT_SYMBOL(extern_wifi_set_enable);
587 
wifi_irq_num(void)588 int wifi_irq_num(void)
589 {
590     return wifi_info.irq_num;
591 }
592 EXPORT_SYMBOL(wifi_irq_num);
593 
wifi_irq_trigger_level(void)594 int wifi_irq_trigger_level(void)
595 {
596     return wifi_info.irq_trigger_type;
597 }
598 EXPORT_SYMBOL(wifi_irq_trigger_level);
599 MODULE_DESCRIPTION("Amlogic WIFI device tree driver");
600 MODULE_AUTHOR("AlgoIdeas <yu19881234@163.com>");
601 MODULE_LICENSE("GPL");
602