Merge branch 'linux-5.3' of git://github.com/skeggsb/linux into drm-fixes
[linux-2.6-microblaze.git] / arch / arm / mach-pxa / tosa-bt.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Bluetooth built-in chip control
4  *
5  * Copyright (c) 2008 Dmitry Baryshkov
6  */
7
8 #include <linux/kernel.h>
9 #include <linux/module.h>
10 #include <linux/platform_device.h>
11 #include <linux/gpio.h>
12 #include <linux/delay.h>
13 #include <linux/rfkill.h>
14
15 #include "tosa_bt.h"
16
17 static void tosa_bt_on(struct tosa_bt_data *data)
18 {
19         gpio_set_value(data->gpio_reset, 0);
20         gpio_set_value(data->gpio_pwr, 1);
21         gpio_set_value(data->gpio_reset, 1);
22         mdelay(20);
23         gpio_set_value(data->gpio_reset, 0);
24 }
25
26 static void tosa_bt_off(struct tosa_bt_data *data)
27 {
28         gpio_set_value(data->gpio_reset, 1);
29         mdelay(10);
30         gpio_set_value(data->gpio_pwr, 0);
31         gpio_set_value(data->gpio_reset, 0);
32 }
33
34 static int tosa_bt_set_block(void *data, bool blocked)
35 {
36         pr_info("BT_RADIO going: %s\n", blocked ? "off" : "on");
37
38         if (!blocked) {
39                 pr_info("TOSA_BT: going ON\n");
40                 tosa_bt_on(data);
41         } else {
42                 pr_info("TOSA_BT: going OFF\n");
43                 tosa_bt_off(data);
44         }
45
46         return 0;
47 }
48
49 static const struct rfkill_ops tosa_bt_rfkill_ops = {
50         .set_block = tosa_bt_set_block,
51 };
52
53 static int tosa_bt_probe(struct platform_device *dev)
54 {
55         int rc;
56         struct rfkill *rfk;
57
58         struct tosa_bt_data *data = dev->dev.platform_data;
59
60         rc = gpio_request(data->gpio_reset, "Bluetooth reset");
61         if (rc)
62                 goto err_reset;
63         rc = gpio_direction_output(data->gpio_reset, 0);
64         if (rc)
65                 goto err_reset_dir;
66         rc = gpio_request(data->gpio_pwr, "Bluetooth power");
67         if (rc)
68                 goto err_pwr;
69         rc = gpio_direction_output(data->gpio_pwr, 0);
70         if (rc)
71                 goto err_pwr_dir;
72
73         rfk = rfkill_alloc("tosa-bt", &dev->dev, RFKILL_TYPE_BLUETOOTH,
74                            &tosa_bt_rfkill_ops, data);
75         if (!rfk) {
76                 rc = -ENOMEM;
77                 goto err_rfk_alloc;
78         }
79
80         rc = rfkill_register(rfk);
81         if (rc)
82                 goto err_rfkill;
83
84         platform_set_drvdata(dev, rfk);
85
86         return 0;
87
88 err_rfkill:
89         rfkill_destroy(rfk);
90 err_rfk_alloc:
91         tosa_bt_off(data);
92 err_pwr_dir:
93         gpio_free(data->gpio_pwr);
94 err_pwr:
95 err_reset_dir:
96         gpio_free(data->gpio_reset);
97 err_reset:
98         return rc;
99 }
100
101 static int tosa_bt_remove(struct platform_device *dev)
102 {
103         struct tosa_bt_data *data = dev->dev.platform_data;
104         struct rfkill *rfk = platform_get_drvdata(dev);
105
106         platform_set_drvdata(dev, NULL);
107
108         if (rfk) {
109                 rfkill_unregister(rfk);
110                 rfkill_destroy(rfk);
111         }
112         rfk = NULL;
113
114         tosa_bt_off(data);
115
116         gpio_free(data->gpio_pwr);
117         gpio_free(data->gpio_reset);
118
119         return 0;
120 }
121
122 static struct platform_driver tosa_bt_driver = {
123         .probe = tosa_bt_probe,
124         .remove = tosa_bt_remove,
125
126         .driver = {
127                 .name = "tosa-bt",
128         },
129 };
130 module_platform_driver(tosa_bt_driver);
131
132 MODULE_LICENSE("GPL");
133 MODULE_AUTHOR("Dmitry Baryshkov");
134 MODULE_DESCRIPTION("Bluetooth built-in chip control");