• 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/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