• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/interrupt.h>
12 #include <linux/rfkill.h>
13 #include <linux/regulator/consumer.h>
14 #include <linux/platform_device.h>
15 #include <linux/miscdevice.h>
16 #include <linux/capability.h>
17 #include <linux/pm_wakeirq.h>
18 #include "internal.h"
19 #include "sunxi-rfkill.h"
20 
21 static struct sunxi_wlan_platdata *wlan_data;
22 static const struct of_device_id sunxi_wlan_ids[];
23 
24 static int sunxi_wlan_on(struct sunxi_wlan_platdata *data, bool on_off);
25 static DEFINE_MUTEX(sunxi_wlan_mutex);
26 
27 #if IS_ENABLED(CONFIG_MMC_SUNXI) || IS_ENABLED(CONFIG_AW_MMC)
28 extern void sunxi_mmc_rescan_card(unsigned ids);
29 #else
sunxi_mmc_rescan_card(unsigned ids)30 static void sunxi_mmc_rescan_card(unsigned ids)
31 {
32 	(void)ids;
33 	pr_err("sunxi-wlan: %s not support", __func__);
34 }
35 #endif
36 
sunxi_wlan_set_power(bool on_off)37 void sunxi_wlan_set_power(bool on_off)
38 {
39 	struct platform_device *pdev;
40 	int ret = 0;
41 
42 	if (!wlan_data)
43 		return;
44 
45 	pdev = wlan_data->pdev;
46 	mutex_lock(&sunxi_wlan_mutex);
47 	rfkill_poweren_set(WL_DEV_WIFI, on_off);
48 	if (on_off != wlan_data->power_state) {
49 		ret = sunxi_wlan_on(wlan_data, on_off);
50 		if (ret)
51 			dev_err(&pdev->dev, "set power failed\n");
52 	}
53 
54 	rfkill_chipen_set(WL_DEV_WIFI, on_off);
55 
56 	mutex_unlock(&sunxi_wlan_mutex);
57 }
58 EXPORT_SYMBOL_GPL(sunxi_wlan_set_power);
59 
sunxi_wlan_get_bus_index(void)60 int sunxi_wlan_get_bus_index(void)
61 {
62 	struct platform_device *pdev;
63 
64 	if (!wlan_data)
65 		return -EINVAL;
66 
67 	pdev = wlan_data->pdev;
68 	dev_info(&pdev->dev, "bus_index: %d\n", wlan_data->bus_index);
69 	return wlan_data->bus_index;
70 }
71 EXPORT_SYMBOL_GPL(sunxi_wlan_get_bus_index);
72 
sunxi_wlan_get_oob_irq(int * irq_flags,int * wakup_enable)73 int sunxi_wlan_get_oob_irq(int *irq_flags, int *wakup_enable)
74 {
75 	struct platform_device *pdev;
76 	int host_oob_irq = 0;
77 	int oob_irq_flags = 0;
78 
79 	if (!wlan_data || !gpio_is_valid(wlan_data->gpio_wlan_hostwake))
80 		return 0;
81 
82 	pdev = wlan_data->pdev;
83 
84 	host_oob_irq = gpio_to_irq(wlan_data->gpio_wlan_hostwake);
85 	if (host_oob_irq < 0)
86 		dev_err(&pdev->dev, "map gpio [%d] to virq failed, errno = %d\n",
87 			wlan_data->gpio_wlan_hostwake, host_oob_irq);
88 
89 	oob_irq_flags = IRQF_SHARED;
90 	if (wlan_data->gpio_wlan_hostwake_assert)
91 		oob_irq_flags |= IRQF_TRIGGER_HIGH;
92 	else
93 		oob_irq_flags |= IRQF_TRIGGER_LOW;
94 
95 	*irq_flags = oob_irq_flags;
96 	*wakup_enable = wlan_data->wakeup_enable;
97 
98 	return host_oob_irq;
99 }
100 EXPORT_SYMBOL_GPL(sunxi_wlan_get_oob_irq);
101 
sunxi_wlan_on(struct sunxi_wlan_platdata * data,bool on_off)102 static int sunxi_wlan_on(struct sunxi_wlan_platdata *data, bool on_off)
103 {
104 	struct platform_device *pdev = data->pdev;
105 	struct device *dev = &pdev->dev;
106 	int ret = 0, i;
107 
108 	if (on_off) {
109 		for (i = 0; i < CLK_MAX; i++) {
110 			if (!IS_ERR_OR_NULL(data->clk[i]))
111 				clk_prepare_enable(data->clk[i]);
112 		}
113 
114 		for (i = 0; i < PWR_MAX; i++) {
115 			if (!IS_ERR_OR_NULL(data->power[i])) {
116 				if (data->power_vol[i]) {
117 					ret = regulator_set_voltage(data->power[i],
118 							data->power_vol[i], data->power_vol[i]);
119 					if (ret < 0) {
120 						dev_err(dev, "wlan power[%d] (%s) set voltage failed\n",
121 									i, data->power_name[i]);
122 						return ret;
123 					}
124 
125 					ret = regulator_get_voltage(data->power[i]);
126 					if (ret != data->power_vol[i]) {
127 						dev_err(dev, "wlan power[%d] (%s) get voltage failed\n",
128 								i, data->power_name[i]);
129 						return ret;
130 					}
131 				}
132 
133 				ret = regulator_enable(data->power[i]);
134 				if (ret < 0) {
135 					dev_err(dev, "wlan power[%d] (%s) enable failed\n",
136 								i, data->power_name[i]);
137 					return ret;
138 				}
139 			}
140 		}
141 
142 		if (gpio_is_valid(data->gpio_wlan_regon)) {
143 			mdelay(10);
144 			gpio_set_value(data->gpio_wlan_regon, data->gpio_wlan_regon_assert);
145 		}
146 	} else {
147 		if (gpio_is_valid(data->gpio_wlan_regon))
148 			gpio_set_value(data->gpio_wlan_regon, !data->gpio_wlan_regon_assert);
149 
150 		for (i = 0; i < PWR_MAX; i++) {
151 			if (!IS_ERR_OR_NULL(data->power[i])) {
152 				ret = regulator_disable(data->power[i]);
153 				if (ret < 0) {
154 					dev_err(dev, "wlan power[%d] (%s) disable failed\n",
155 								i, data->power_name[i]);
156 					return ret;
157 				}
158 			}
159 		}
160 
161 		for (i = 0; i < CLK_MAX; i++) {
162 			if (!IS_ERR_OR_NULL(data->clk[i]))
163 				clk_disable_unprepare(data->clk[i]);
164 		}
165 	}
166 
167 	wlan_data->power_state = on_off;
168 	dev_info(dev, "wlan power %s success\n", on_off ? "on" : "off");
169 
170 	return 0;
171 }
172 
power_state_show(struct device * dev,struct device_attribute * attr,char * buf)173 static ssize_t power_state_show(struct device *dev,
174 	struct device_attribute *attr, char *buf)
175 {
176 	if (!wlan_data)
177 		return 0;
178 	return sprintf(buf, "%d\n", wlan_data->power_state);
179 }
180 
power_state_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)181 static ssize_t power_state_store(struct device *dev,
182 	struct device_attribute *attr, const char *buf, size_t count)
183 {
184 	unsigned long state;
185 	int err;
186 
187 	if (!wlan_data)
188 		return 0;
189 
190 	if (!capable(CAP_NET_ADMIN))
191 		return -EPERM;
192 
193 	err = kstrtoul(buf, 0, &state);
194 	if (err)
195 		return err;
196 
197 	if (state > 1)
198 		return -EINVAL;
199 
200 	if (state != wlan_data->power_state) {
201 		sunxi_wlan_set_power(state);
202 	}
203 
204 	return count;
205 }
206 
207 static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR,
208 		power_state_show, power_state_store);
209 
scan_device_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)210 static ssize_t scan_device_store(struct device *dev,
211 	struct device_attribute *attr, const char *buf, size_t count)
212 {
213 	unsigned long state;
214 	int err;
215 	int bus = wlan_data->bus_index;
216 
217 	if (!wlan_data)
218 		return 0;
219 
220 	err = kstrtoul(buf, 0, &state);
221 	if (err)
222 		return err;
223 
224 	dev_info(dev, "start scan device on bus_index: %d\n",
225 			wlan_data->bus_index);
226 	if (bus < 0) {
227 		dev_err(dev, "scan device fail!\n");
228 		return -1;
229 	}
230 	sunxi_mmc_rescan_card(bus);
231 
232 	return count;
233 }
234 
235 static DEVICE_ATTR(scan_device, S_IRUGO | S_IWUSR,
236 		NULL, scan_device_store);
237 
238 static struct attribute *misc_attributes[] = {
239 	&dev_attr_power_state.attr,
240 	&dev_attr_scan_device.attr,
241 	NULL,
242 };
243 
244 static struct attribute_group misc_attribute_group = {
245 	.name  = "rf-ctrl",
246 	.attrs = misc_attributes,
247 };
248 
249 static struct miscdevice sunxi_wlan_dev = {
250 	.minor = MISC_DYNAMIC_MINOR,
251 	.name  = "sunxi-wlan",
252 };
253 
sunxi_wlan_init(struct platform_device * pdev)254 int sunxi_wlan_init(struct platform_device *pdev)
255 {
256 	struct device_node *np = of_find_matching_node(pdev->dev.of_node, sunxi_wlan_ids);
257 	struct device *dev = &pdev->dev;
258 	struct sunxi_wlan_platdata *data;
259 	enum of_gpio_flags config;
260 	u32 val;
261 	int ret = 0;
262 	int count, i;
263 
264 	if (!dev)
265 		return -ENOMEM;
266 
267 	if (!np)
268 		return 0;
269 
270 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
271 
272 	data->pdev = pdev;
273 
274 	data->bus_index = -1;
275 	if (!of_property_read_u32(np, "wlan_busnum", &val)) {
276 		switch (val) {
277 		case 0:
278 		case 1:
279 		case 2:
280 		case 3:
281 			data->bus_index = val;
282 			break;
283 		default:
284 			dev_err(dev, "unsupported wlan_busnum (%u)\n", val);
285 			return -EINVAL;
286 		}
287 	}
288 	dev_info(dev, "wlan_busnum (%u)\n", val);
289 
290 	count = of_property_count_strings(np, "wlan_power");
291 	if (count <= 0) {
292 		dev_warn(dev, "Missing wlan_power.\n");
293 	} else {
294 		if (count > PWR_MAX) {
295 			dev_warn(dev, "wlan power count large than max(%d > %d).\n",
296 						count, PWR_MAX);
297 			count = PWR_MAX;
298 		}
299 		ret = of_property_read_string_array(np, "wlan_power",
300 					(const char **)data->power_name, count);
301 		if (ret < 0)
302 			return -ENOMEM;
303 
304 		ret = of_property_read_u32_array(np, "wlan_power_vol",
305 					(u32 *)data->power_vol, count);
306 		if (ret < 0)
307 			dev_warn(dev, "Missing wlan_power_vol config.\n");
308 
309 		for (i = 0; i < count; i++) {
310 			data->power[i] = regulator_get(dev, data->power_name[i]);
311 
312 			if (IS_ERR_OR_NULL(data->power[i]))
313 				return -ENOMEM;
314 
315 			dev_info(dev, "wlan power[%d] (%s) voltage: %dmV\n",
316 					i, data->power_name[i], data->power_vol[i] / 1000);
317 		}
318 	}
319 
320 	count = of_property_count_strings(np, "clock-names");
321 	if (count <= 0) {
322 		count = CLK_MAX;
323 		for (i = 0; i < count; i++) {
324 			data->clk[i] = of_clk_get(np, i);
325 			if (IS_ERR_OR_NULL(data->clk[i]))
326 				break;
327 			data->clk_name[i] = devm_kzalloc(dev, 16, GFP_KERNEL);
328 			sprintf(data->clk_name[i], "clk%d", i);
329 			dev_info(dev, "wlan clock[%d] (%s)\n", i, data->clk_name[i]);
330 		}
331 	} else {
332 		if (count > CLK_MAX) {
333 			dev_warn(dev, "wlan clocks count large than max(%d).\n", CLK_MAX);
334 			count = CLK_MAX;
335 		}
336 		ret = of_property_read_string_array(np, "clock-names",
337 					(const char **)data->clk_name, count);
338 		if (ret < 0)
339 			return -ENOMEM;
340 
341 		for (i = 0; i < count; i++) {
342 			data->clk[i] = of_clk_get(np, i);
343 			if (IS_ERR_OR_NULL(data->clk[i]))
344 				return -ENOMEM;
345 			dev_info(dev, "wlan clock[%d] (%s)\n", i, data->clk_name[i]);
346 		}
347 	}
348 
349 	data->gpio_wlan_regon = of_get_named_gpio_flags(np, "wlan_regon", 0, &config);
350 	if (!gpio_is_valid(data->gpio_wlan_regon)) {
351 		dev_err(dev, "get gpio wlan_regon failed\n");
352 	} else {
353 		data->gpio_wlan_regon_assert = (config == OF_GPIO_ACTIVE_LOW) ? 0 : 1;
354 		dev_info(dev, "wlan_regon gpio=%d assert=%d\n", data->gpio_wlan_regon, data->gpio_wlan_regon_assert);
355 
356 		ret = devm_gpio_request(dev, data->gpio_wlan_regon,
357 				"wlan_regon");
358 		if (ret < 0) {
359 			dev_err(dev, "can't request wlan_regon gpio %d\n",
360 				data->gpio_wlan_regon);
361 			return ret;
362 		}
363 
364 		ret = gpio_direction_output(data->gpio_wlan_regon, !data->gpio_wlan_regon_assert);
365 		if (ret < 0) {
366 			dev_err(dev, "can't request output direction wlan_regon gpio %d\n",
367 				data->gpio_wlan_regon);
368 			return ret;
369 		}
370 	}
371 
372 	data->gpio_wlan_hostwake = of_get_named_gpio_flags(np, "wlan_hostwake", 0, &config);
373 	if (!gpio_is_valid(data->gpio_wlan_hostwake)) {
374 		dev_err(dev, "get gpio wlan_hostwake failed\n");
375 	} else {
376 		data->gpio_wlan_hostwake_assert = (config == OF_GPIO_ACTIVE_LOW) ? 0 : 1;
377 		dev_info(dev, "wlan_hostwake gpio=%d assert=%d\n", data->gpio_wlan_hostwake, data->gpio_wlan_hostwake_assert);
378 
379 		ret = devm_gpio_request(dev, data->gpio_wlan_hostwake,
380 				"wlan_hostwake");
381 		if (ret < 0) {
382 			dev_err(dev, "can't request wlan_hostwake gpio %d\n",
383 				data->gpio_wlan_hostwake);
384 			return ret;
385 		}
386 
387 		ret = gpio_direction_input(data->gpio_wlan_hostwake);
388 		if (ret < 0) {
389 			dev_err(dev,
390 				"can't request input direction wlan_hostwake gpio %d\n",
391 				data->gpio_wlan_hostwake);
392 			return ret;
393 		}
394 
395 		/*
396 		 * wakeup_source relys on wlan_hostwake, if wlan_hostwake gpio
397 		 * isn't configured, then whether wakeup_source is configured
398 		 * or not is unmeaningful.
399 		 */
400 		if (!of_property_read_bool(np, "wakeup-source")) {
401 			dev_info(dev, "wakeup source is disbled!\n");
402 		} else {
403 			dev_info(dev, "wakeup source is enabled\n");
404 			data->wakeup_enable = 1;
405 		}
406 	}
407 
408 	ret = misc_register(&sunxi_wlan_dev);
409 	if (ret) {
410 		dev_err(dev, "sunxi-wlan register driver as misc device error!\n");
411 		return ret;
412 	}
413 	ret = sysfs_create_group(&sunxi_wlan_dev.this_device->kobj,
414 			&misc_attribute_group);
415 	if (ret) {
416 		dev_err(dev, "sunxi-wlan register sysfs create group failed!\n");
417 		return ret;
418 	}
419 
420 	data->power_state = 0;
421 	wlan_data = data;
422 	return 0;
423 }
424 
sunxi_wlan_deinit(struct platform_device * pdev)425 int sunxi_wlan_deinit(struct platform_device *pdev)
426 {
427 	struct sunxi_wlan_platdata *data = wlan_data;
428 	int i;
429 
430 	if (!data)
431 		return 0;
432 
433 	sysfs_remove_group(&(sunxi_wlan_dev.this_device->kobj),
434 			&misc_attribute_group);
435 	misc_deregister(&sunxi_wlan_dev);
436 
437 	if (data->power_state)
438 		sunxi_wlan_set_power(0);
439 
440 	for (i = 0; i < PWR_MAX; i++) {
441 		if (!IS_ERR_OR_NULL(data->power[i]))
442 			regulator_put(data->power[i]);
443 	}
444 
445 	wlan_data = NULL;
446 
447 	return 0;
448 }
449 
450 static const struct of_device_id sunxi_wlan_ids[] = {
451 	{ .compatible = "allwinner,sunxi-wlan" },
452 	{ /* Sentinel */ }
453 };
454 
455 MODULE_DESCRIPTION("sunxi wlan driver");
456 MODULE_LICENSE("GPL");
457