1 #include <linux/kernel.h>
2 #include <linux/types.h>
3 #include <linux/module.h>
4 #include <linux/init.h>
5 #include <linux/gpio.h>
6 #include <linux/err.h>
7 #include <linux/device.h>
8 #include <linux/delay.h>
9 #include <linux/of_gpio.h>
10 #include <linux/clk.h>
11 #include <linux/regulator/consumer.h>
12 #include <linux/platform_device.h>
13 #include <linux/rfkill.h>
14
15 #include "sunxi-rfkill.h"
16
17 static struct sunxi_modem_platdata *modem_data;
18 static const struct of_device_id sunxi_modem_ids[];
19
20 static int sunxi_modem_on(struct sunxi_modem_platdata *data, bool on_off);
21 static DEFINE_MUTEX(sunxi_modem_mutex);
22
sunxi_modem_set_power(bool on_off)23 void sunxi_modem_set_power(bool on_off)
24 {
25 struct platform_device *pdev;
26 int ret = 0;
27
28 if (!modem_data)
29 return;
30
31 pdev = modem_data->pdev;
32 mutex_lock(&sunxi_modem_mutex);
33 if (on_off != modem_data->power_state) {
34 ret = sunxi_modem_on(modem_data, on_off);
35 if (ret)
36 dev_err(&pdev->dev, "set power failed\n");
37 }
38 mutex_unlock(&sunxi_modem_mutex);
39 }
40 EXPORT_SYMBOL_GPL(sunxi_modem_set_power);
41
sunxi_modem_on(struct sunxi_modem_platdata * data,bool on_off)42 static int sunxi_modem_on(struct sunxi_modem_platdata *data, bool on_off)
43 {
44 struct platform_device *pdev = data->pdev;
45 struct device *dev = &pdev->dev;
46 int ret = 0, i;
47
48 if (on_off) {
49 for (i = 0; i < PWR_MAX; i++) {
50 if (!IS_ERR_OR_NULL(data->power[i])) {
51 if (data->power_vol[i]) {
52 ret = regulator_set_voltage(data->power[i],
53 data->power_vol[i], data->power_vol[i]);
54 if (ret < 0) {
55 dev_err(dev, "modem power[%d] (%s) set voltage failed\n",
56 i, data->power_name[i]);
57 return ret;
58 }
59
60 ret = regulator_get_voltage(data->power[i]);
61 if (ret != data->power_vol[i]) {
62 dev_err(dev, "modem power[%d] (%s) get voltage failed\n",
63 i, data->power_name[i]);
64 return ret;
65 }
66 }
67
68 ret = regulator_enable(data->power[i]);
69 if (ret < 0) {
70 dev_err(dev, "modem power[%d] (%s) enable failed\n",
71 i, data->power_name[i]);
72 return ret;
73 }
74 }
75 }
76
77 if (gpio_is_valid(data->gpio_modem_rst)) {
78 mdelay(10);
79 gpio_set_value(data->gpio_modem_rst, !data->gpio_modem_rst_assert);
80 }
81 } else {
82 if (gpio_is_valid(data->gpio_modem_rst))
83 gpio_set_value(data->gpio_modem_rst, data->gpio_modem_rst_assert);
84
85 for (i = 0; i < PWR_MAX; i++) {
86 if (!IS_ERR_OR_NULL(data->power[i])) {
87 ret = regulator_disable(data->power[i]);
88 if (ret < 0) {
89 dev_err(dev, "modem power[%d] (%s) disable failed\n",
90 i, data->power_name[i]);
91 return ret;
92 }
93 }
94 }
95 }
96
97 data->power_state = on_off;
98 dev_info(dev, "modem power %s success\n", on_off ? "on" : "off");
99
100 return 0;
101 }
102
sunxi_modem_set_block(void * data,bool blocked)103 static int sunxi_modem_set_block(void *data, bool blocked)
104 {
105 struct sunxi_modem_platdata *platdata = data;
106 struct platform_device *pdev = platdata->pdev;
107 int ret;
108
109 if (!modem_data)
110 return 0;
111
112 if (blocked != platdata->power_state) {
113 dev_warn(&pdev->dev, "block state already is %d\n", blocked);
114 return 0;
115 }
116
117 dev_info(&pdev->dev, "set block: %d\n", blocked);
118 ret = sunxi_modem_on(platdata, !blocked);
119 if (ret) {
120 dev_err(&pdev->dev, "set block failed\n");
121 return ret;
122 }
123
124 return 0;
125 }
126
127 static const struct rfkill_ops sunxi_modem_rfkill_ops = {
128 .set_block = sunxi_modem_set_block,
129 };
130
sunxi_modem_init(struct platform_device * pdev)131 int sunxi_modem_init(struct platform_device *pdev)
132 {
133 struct device_node *np = of_find_matching_node(pdev->dev.of_node, sunxi_modem_ids);
134 struct device *dev = &pdev->dev;
135 struct sunxi_modem_platdata *data;
136 enum of_gpio_flags config;
137 int ret = 0;
138 int count, i;
139
140 if (!dev)
141 return -ENOMEM;
142
143 if (!np)
144 return 0;
145
146 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
147 data->pdev = pdev;
148
149 count = of_property_count_strings(np, "modem_power");
150 if (count <= 0) {
151 dev_warn(dev, "Missing modem_power.\n");
152 } else {
153 if (count > PWR_MAX) {
154 dev_warn(dev, "modem power count large than max(%d > %d).\n",
155 count, PWR_MAX);
156 count = PWR_MAX;
157 }
158 ret = of_property_read_string_array(np, "modem_power",
159 (const char **)data->power_name, count);
160 if (ret < 0)
161 return -ENOMEM;
162
163 ret = of_property_read_u32_array(np, "modem_power_vol",
164 (u32 *)data->power_vol, count);
165 if (ret < 0)
166 dev_warn(dev, "Missing modem_power_vol config.\n");
167
168 for (i = 0; i < count; i++) {
169 data->power[i] = regulator_get(dev, data->power_name[i]);
170 if (IS_ERR_OR_NULL(data->power[i]))
171 return -ENOMEM;
172
173 dev_info(dev, "modem power[%d] (%s) voltage: %dmV\n",
174 i, data->power_name[i], data->power_vol[i] / 1000);
175 }
176 }
177
178 for (i = 0; i < count; i++) {
179 data->power[i] = regulator_get(dev, data->power_name[i]);
180 if (IS_ERR_OR_NULL(data->power[i]))
181 return -ENOMEM;
182 dev_info(dev, "modem power[%d] (%s)\n", i, data->power_name[i]);
183 }
184
185 data->gpio_modem_rst = of_get_named_gpio_flags(np, "modem_rst", 0, &config);
186 if (!gpio_is_valid(data->gpio_modem_rst)) {
187 dev_err(dev, "get gpio modem_rst failed\n");
188 } else {
189 data->gpio_modem_rst_assert = (config == OF_GPIO_ACTIVE_LOW) ? 0 : 1;
190 dev_info(dev, "modem_rst gpio=%d assert=%d\n", data->gpio_modem_rst, data->gpio_modem_rst_assert);
191
192 ret = devm_gpio_request(dev, data->gpio_modem_rst, "modem_rst");
193 if (ret < 0) {
194 dev_err(dev, "can't request modem_rst gpio %d\n",
195 data->gpio_modem_rst);
196 return ret;
197 }
198
199 ret = gpio_direction_output(data->gpio_modem_rst, data->gpio_modem_rst_assert);
200 if (ret < 0) {
201 dev_err(dev, "can't request output direction modem_rst gpio %d\n",
202 data->gpio_modem_rst);
203 return ret;
204 }
205 gpio_set_value(data->gpio_modem_rst, data->gpio_modem_rst_assert);
206 }
207
208 data->rfkill = rfkill_alloc("sunxi-modem", dev, RFKILL_TYPE_WWAN,
209 &sunxi_modem_rfkill_ops, data);
210 if (!data->rfkill)
211 ret = -ENOMEM;
212
213 rfkill_set_states(data->rfkill, true, false);
214
215 ret = rfkill_register(data->rfkill);
216 if (ret)
217 goto fail_rfkill;
218
219 data->power_state = 0;
220 modem_data = data;
221 return 0;
222
223 fail_rfkill:
224 if (data->rfkill)
225 rfkill_destroy(data->rfkill);
226
227 return ret;
228 }
229
sunxi_modem_deinit(struct platform_device * pdev)230 int sunxi_modem_deinit(struct platform_device *pdev)
231 {
232 struct sunxi_modem_platdata *data = platform_get_drvdata(pdev);
233 struct rfkill *rfk;
234 int i;
235
236 if (!data)
237 return 0;
238
239 rfk = data->rfkill;
240 if (rfk) {
241 rfkill_unregister(rfk);
242 rfkill_destroy(rfk);
243 }
244
245 if (data->power_state)
246 sunxi_modem_set_power(0);
247
248 for (i = 0; i < PWR_MAX; i++) {
249 if (!IS_ERR_OR_NULL(data->power[i]))
250 regulator_put(data->power[i]);
251 }
252
253 modem_data = NULL;
254
255 return 0;
256 }
257
258 static const struct of_device_id sunxi_modem_ids[] = {
259 { .compatible = "allwinner,sunxi-modem" },
260 { /* Sentinel */ }
261 };
262
263 MODULE_DESCRIPTION("sunxi modem rfkill driver");
264 MODULE_LICENSE("GPL");
265