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