]>
Commit | Line | Data |
---|---|---|
5a781787 VH |
1 | /* |
2 | * Arche Platform driver to control APB. | |
3 | * | |
4 | * Copyright 2014-2015 Google Inc. | |
5 | * Copyright 2014-2015 Linaro Ltd. | |
6 | * | |
7 | * Released under the GPLv2 only. | |
8 | */ | |
9 | ||
3b858df0 | 10 | #include <linux/clk.h> |
5a781787 | 11 | #include <linux/delay.h> |
5a781787 | 12 | #include <linux/gpio.h> |
3b858df0 | 13 | #include <linux/interrupt.h> |
5a781787 VH |
14 | #include <linux/of_gpio.h> |
15 | #include <linux/of_irq.h> | |
3b858df0 | 16 | #include <linux/module.h> |
5a781787 | 17 | #include <linux/pinctrl/consumer.h> |
3b858df0 VK |
18 | #include <linux/platform_device.h> |
19 | #include <linux/pm.h> | |
20 | #include <linux/regulator/consumer.h> | |
21 | #include <linux/spinlock.h> | |
1e5dd1f8 | 22 | #include "arche_platform.h" |
5a781787 | 23 | |
5a781787 VH |
24 | |
25 | struct arche_apb_ctrl_drvdata { | |
26 | /* Control GPIO signals to and from AP <=> AP Bridges */ | |
5a781787 VH |
27 | int resetn_gpio; |
28 | int boot_ret_gpio; | |
29 | int pwroff_gpio; | |
30 | int wake_in_gpio; | |
31 | int wake_out_gpio; | |
32 | int pwrdn_gpio; | |
33 | ||
a821adb4 | 34 | enum arche_platform_state state; |
af3aae10 | 35 | bool init_disabled; |
5a781787 VH |
36 | |
37 | struct regulator *vcore; | |
38 | struct regulator *vio; | |
39 | ||
40 | unsigned int clk_en_gpio; | |
41 | struct clk *clk; | |
42 | ||
43 | struct pinctrl *pinctrl; | |
44 | struct pinctrl_state *pin_default; | |
5a781787 VH |
45 | }; |
46 | ||
47 | /* | |
48 | * Note that these low level api's are active high | |
49 | */ | |
f1e9cbd5 | 50 | static inline void deassert_reset(unsigned int gpio) |
5a781787 VH |
51 | { |
52 | gpio_set_value(gpio, 1); | |
5a781787 VH |
53 | } |
54 | ||
f1e9cbd5 | 55 | static inline void assert_reset(unsigned int gpio) |
5a781787 VH |
56 | { |
57 | gpio_set_value(gpio, 0); | |
58 | } | |
59 | ||
5a781787 VH |
60 | /* |
61 | * Note: Please do not modify the below sequence, as it is as per the spec | |
62 | */ | |
80a057aa | 63 | static int coldboot_seq(struct platform_device *pdev) |
5a781787 VH |
64 | { |
65 | struct device *dev = &pdev->dev; | |
6961e046 | 66 | struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); |
5a781787 VH |
67 | int ret; |
68 | ||
c5e7cbaf VH |
69 | if (apb->init_disabled || |
70 | apb->state == ARCHE_PLATFORM_STATE_ACTIVE) | |
af3aae10 VH |
71 | return 0; |
72 | ||
5a781787 | 73 | /* Hold APB in reset state */ |
6ceed512 | 74 | assert_reset(apb->resetn_gpio); |
5a781787 VH |
75 | |
76 | /* Enable power to APB */ | |
dcf77c39 | 77 | if (!IS_ERR(apb->vcore)) { |
5a781787 VH |
78 | ret = regulator_enable(apb->vcore); |
79 | if (ret) { | |
80 | dev_err(dev, "failed to enable core regulator\n"); | |
81 | return ret; | |
82 | } | |
83 | } | |
aa187d33 | 84 | |
dcf77c39 | 85 | if (!IS_ERR(apb->vio)) { |
5a781787 VH |
86 | ret = regulator_enable(apb->vio); |
87 | if (ret) { | |
88 | dev_err(dev, "failed to enable IO regulator\n"); | |
6ceed512 | 89 | return ret; |
5a781787 VH |
90 | } |
91 | } | |
92 | ||
5a781787 | 93 | gpio_set_value(apb->boot_ret_gpio, 0); |
aa187d33 VH |
94 | |
95 | /* On DB3 clock was not mandatory */ | |
6ceed512 VH |
96 | if (gpio_is_valid(apb->clk_en_gpio)) |
97 | gpio_set_value(apb->clk_en_gpio, 1); | |
aa187d33 VH |
98 | |
99 | usleep_range(100, 200); | |
5a781787 | 100 | |
6ceed512 VH |
101 | /* deassert reset to APB : Active-low signal */ |
102 | deassert_reset(apb->resetn_gpio); | |
d258432f | 103 | |
6ceed512 | 104 | apb->state = ARCHE_PLATFORM_STATE_ACTIVE; |
5a781787 | 105 | |
6ceed512 | 106 | return 0; |
5a781787 VH |
107 | } |
108 | ||
80a057aa | 109 | static int fw_flashing_seq(struct platform_device *pdev) |
33d76291 VH |
110 | { |
111 | struct device *dev = &pdev->dev; | |
112 | struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); | |
113 | int ret; | |
114 | ||
c5e7cbaf VH |
115 | if (apb->init_disabled || |
116 | apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING) | |
af3aae10 VH |
117 | return 0; |
118 | ||
33d76291 VH |
119 | ret = regulator_enable(apb->vcore); |
120 | if (ret) { | |
121 | dev_err(dev, "failed to enable core regulator\n"); | |
122 | return ret; | |
123 | } | |
124 | ||
125 | ret = regulator_enable(apb->vio); | |
126 | if (ret) { | |
127 | dev_err(dev, "failed to enable IO regulator\n"); | |
128 | return ret; | |
129 | } | |
130 | ||
131 | /* for flashing device should be in reset state */ | |
132 | assert_reset(apb->resetn_gpio); | |
133 | apb->state = ARCHE_PLATFORM_STATE_FW_FLASHING; | |
134 | ||
135 | return 0; | |
136 | } | |
137 | ||
80a057aa | 138 | static int standby_boot_seq(struct platform_device *pdev) |
33d76291 VH |
139 | { |
140 | struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); | |
141 | ||
af3aae10 VH |
142 | if (apb->init_disabled) |
143 | return 0; | |
144 | ||
c5e7cbaf VH |
145 | /* Even if it is in OFF state, then we do not want to change the state */ |
146 | if (apb->state == ARCHE_PLATFORM_STATE_STANDBY || | |
147 | apb->state == ARCHE_PLATFORM_STATE_OFF) | |
33d76291 VH |
148 | return 0; |
149 | ||
150 | /* | |
151 | * As per WDM spec, do nothing | |
152 | * | |
153 | * Pasted from WDM spec, | |
154 | * - A falling edge on POWEROFF_L is detected (a) | |
155 | * - WDM enters standby mode, but no output signals are changed | |
156 | * */ | |
157 | ||
158 | /* TODO: POWEROFF_L is input to WDM module */ | |
159 | apb->state = ARCHE_PLATFORM_STATE_STANDBY; | |
160 | return 0; | |
161 | } | |
162 | ||
80a057aa | 163 | static void poweroff_seq(struct platform_device *pdev) |
5667ab17 | 164 | { |
6961e046 VH |
165 | struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); |
166 | ||
c5e7cbaf | 167 | if (apb->init_disabled || apb->state == ARCHE_PLATFORM_STATE_OFF) |
af3aae10 VH |
168 | return; |
169 | ||
5667ab17 VH |
170 | /* disable the clock */ |
171 | if (gpio_is_valid(apb->clk_en_gpio)) | |
172 | gpio_set_value(apb->clk_en_gpio, 0); | |
173 | ||
174 | if (!IS_ERR(apb->vcore) && regulator_is_enabled(apb->vcore) > 0) | |
175 | regulator_disable(apb->vcore); | |
176 | ||
177 | if (!IS_ERR(apb->vio) && regulator_is_enabled(apb->vio) > 0) | |
178 | regulator_disable(apb->vio); | |
179 | ||
180 | /* As part of exit, put APB back in reset state */ | |
181 | assert_reset(apb->resetn_gpio); | |
182 | apb->state = ARCHE_PLATFORM_STATE_OFF; | |
183 | ||
184 | /* TODO: May have to send an event to SVC about this exit */ | |
185 | } | |
186 | ||
65fd5a50 VH |
187 | int apb_ctrl_coldboot(struct device *dev) |
188 | { | |
189 | return coldboot_seq(to_platform_device(dev)); | |
190 | } | |
191 | ||
192 | int apb_ctrl_fw_flashing(struct device *dev) | |
193 | { | |
194 | return fw_flashing_seq(to_platform_device(dev)); | |
195 | } | |
196 | ||
197 | int apb_ctrl_standby_boot(struct device *dev) | |
198 | { | |
199 | return standby_boot_seq(to_platform_device(dev)); | |
200 | } | |
201 | ||
202 | void apb_ctrl_poweroff(struct device *dev) | |
203 | { | |
204 | poweroff_seq(to_platform_device(dev)); | |
205 | } | |
206 | ||
33d76291 VH |
207 | static ssize_t state_store(struct device *dev, |
208 | struct device_attribute *attr, const char *buf, size_t count) | |
209 | { | |
210 | struct platform_device *pdev = to_platform_device(dev); | |
211 | struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); | |
212 | int ret = 0; | |
af3aae10 | 213 | bool is_disabled; |
33d76291 VH |
214 | |
215 | if (sysfs_streq(buf, "off")) { | |
216 | if (apb->state == ARCHE_PLATFORM_STATE_OFF) | |
217 | return count; | |
218 | ||
80a057aa | 219 | poweroff_seq(pdev); |
33d76291 VH |
220 | } else if (sysfs_streq(buf, "active")) { |
221 | if (apb->state == ARCHE_PLATFORM_STATE_ACTIVE) | |
222 | return count; | |
223 | ||
80a057aa | 224 | poweroff_seq(pdev); |
af3aae10 VH |
225 | is_disabled = apb->init_disabled; |
226 | apb->init_disabled = false; | |
80a057aa | 227 | ret = coldboot_seq(pdev); |
af3aae10 VH |
228 | if (ret) |
229 | apb->init_disabled = is_disabled; | |
33d76291 VH |
230 | } else if (sysfs_streq(buf, "standby")) { |
231 | if (apb->state == ARCHE_PLATFORM_STATE_STANDBY) | |
232 | return count; | |
233 | ||
80a057aa | 234 | ret = standby_boot_seq(pdev); |
33d76291 VH |
235 | } else if (sysfs_streq(buf, "fw_flashing")) { |
236 | if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING) | |
237 | return count; | |
238 | ||
239 | /* First we want to make sure we power off everything | |
240 | * and then enter FW flashing state */ | |
80a057aa VH |
241 | poweroff_seq(pdev); |
242 | ret = fw_flashing_seq(pdev); | |
33d76291 VH |
243 | } else { |
244 | dev_err(dev, "unknown state\n"); | |
245 | ret = -EINVAL; | |
246 | } | |
247 | ||
248 | return ret ? ret : count; | |
249 | } | |
250 | ||
251 | static ssize_t state_show(struct device *dev, | |
252 | struct device_attribute *attr, char *buf) | |
253 | { | |
254 | struct arche_apb_ctrl_drvdata *apb = dev_get_drvdata(dev); | |
255 | ||
256 | switch (apb->state) { | |
257 | case ARCHE_PLATFORM_STATE_OFF: | |
af3aae10 VH |
258 | return sprintf(buf, "off%s\n", |
259 | apb->init_disabled ? ",disabled" : ""); | |
33d76291 VH |
260 | case ARCHE_PLATFORM_STATE_ACTIVE: |
261 | return sprintf(buf, "active\n"); | |
262 | case ARCHE_PLATFORM_STATE_STANDBY: | |
263 | return sprintf(buf, "standby\n"); | |
264 | case ARCHE_PLATFORM_STATE_FW_FLASHING: | |
265 | return sprintf(buf, "fw_flashing\n"); | |
266 | default: | |
267 | return sprintf(buf, "unknown state\n"); | |
268 | } | |
269 | } | |
270 | ||
271 | static DEVICE_ATTR_RW(state); | |
272 | ||
5a781787 VH |
273 | static int apb_ctrl_get_devtree_data(struct platform_device *pdev, |
274 | struct arche_apb_ctrl_drvdata *apb) | |
275 | { | |
276 | struct device *dev = &pdev->dev; | |
277 | struct device_node *np = dev->of_node; | |
6ceed512 | 278 | int ret; |
5a781787 | 279 | |
5a781787 | 280 | apb->resetn_gpio = of_get_named_gpio(np, "reset-gpios", 0); |
7541c1a1 | 281 | if (apb->resetn_gpio < 0) { |
5a781787 VH |
282 | dev_err(dev, "failed to get reset gpio\n"); |
283 | return apb->resetn_gpio; | |
284 | } | |
6ceed512 VH |
285 | ret = devm_gpio_request_one(dev, apb->resetn_gpio, |
286 | GPIOF_OUT_INIT_LOW, "apb-reset"); | |
287 | if (ret) { | |
288 | dev_err(dev, "Failed requesting reset gpio %d\n", | |
289 | apb->resetn_gpio); | |
290 | return ret; | |
291 | } | |
5a781787 VH |
292 | |
293 | apb->boot_ret_gpio = of_get_named_gpio(np, "boot-ret-gpios", 0); | |
7541c1a1 | 294 | if (apb->boot_ret_gpio < 0) { |
5a781787 VH |
295 | dev_err(dev, "failed to get boot retention gpio\n"); |
296 | return apb->boot_ret_gpio; | |
297 | } | |
6ceed512 VH |
298 | ret = devm_gpio_request_one(dev, apb->boot_ret_gpio, |
299 | GPIOF_OUT_INIT_LOW, "boot retention"); | |
300 | if (ret) { | |
301 | dev_err(dev, "Failed requesting bootret gpio %d\n", | |
302 | apb->boot_ret_gpio); | |
303 | return ret; | |
304 | } | |
5a781787 VH |
305 | |
306 | /* It's not mandatory to support power management interface */ | |
307 | apb->pwroff_gpio = of_get_named_gpio(np, "pwr-off-gpios", 0); | |
7541c1a1 | 308 | if (apb->pwroff_gpio < 0) { |
db0cff55 | 309 | dev_err(dev, "failed to get power off gpio\n"); |
5a781787 VH |
310 | return apb->pwroff_gpio; |
311 | } | |
6ceed512 VH |
312 | ret = devm_gpio_request_one(dev, apb->pwroff_gpio, |
313 | GPIOF_IN, "pwroff_n"); | |
314 | if (ret) { | |
315 | dev_err(dev, "Failed requesting pwroff_n gpio %d\n", | |
316 | apb->pwroff_gpio); | |
317 | return ret; | |
318 | } | |
5a781787 VH |
319 | |
320 | /* Do not make clock mandatory as of now (for DB3) */ | |
321 | apb->clk_en_gpio = of_get_named_gpio(np, "clock-en-gpio", 0); | |
6ceed512 | 322 | if (apb->clk_en_gpio < 0) { |
db0cff55 | 323 | dev_warn(dev, "failed to get clock en gpio\n"); |
6ceed512 VH |
324 | } else if (gpio_is_valid(apb->clk_en_gpio)) { |
325 | ret = devm_gpio_request_one(dev, apb->clk_en_gpio, | |
326 | GPIOF_OUT_INIT_LOW, "apb_clk_en"); | |
327 | if (ret) { | |
328 | dev_warn(dev, "Failed requesting APB clock en gpio %d\n", | |
329 | apb->clk_en_gpio); | |
330 | return ret; | |
331 | } | |
332 | } | |
5a781787 VH |
333 | |
334 | apb->pwrdn_gpio = of_get_named_gpio(np, "pwr-down-gpios", 0); | |
7541c1a1 | 335 | if (apb->pwrdn_gpio < 0) |
db0cff55 | 336 | dev_warn(dev, "failed to get power down gpio\n"); |
5a781787 VH |
337 | |
338 | /* Regulators are optional, as we may have fixed supply coming in */ | |
339 | apb->vcore = devm_regulator_get(dev, "vcore"); | |
dcf77c39 | 340 | if (IS_ERR(apb->vcore)) |
db0cff55 | 341 | dev_warn(dev, "no core regulator found\n"); |
5a781787 VH |
342 | |
343 | apb->vio = devm_regulator_get(dev, "vio"); | |
dcf77c39 | 344 | if (IS_ERR(apb->vio)) |
db0cff55 | 345 | dev_warn(dev, "no IO regulator found\n"); |
5a781787 VH |
346 | |
347 | apb->pinctrl = devm_pinctrl_get(&pdev->dev); | |
348 | if (IS_ERR(apb->pinctrl)) { | |
349 | dev_err(&pdev->dev, "could not get pinctrl handle\n"); | |
350 | return PTR_ERR(apb->pinctrl); | |
351 | } | |
352 | apb->pin_default = pinctrl_lookup_state(apb->pinctrl, "default"); | |
353 | if (IS_ERR(apb->pin_default)) { | |
354 | dev_err(&pdev->dev, "could not get default pin state\n"); | |
355 | return PTR_ERR(apb->pin_default); | |
356 | } | |
357 | ||
358 | return 0; | |
359 | } | |
360 | ||
1e5dd1f8 | 361 | int arche_apb_ctrl_probe(struct platform_device *pdev) |
5a781787 VH |
362 | { |
363 | int ret; | |
364 | struct arche_apb_ctrl_drvdata *apb; | |
365 | struct device *dev = &pdev->dev; | |
366 | ||
367 | apb = devm_kzalloc(&pdev->dev, sizeof(*apb), GFP_KERNEL); | |
368 | if (!apb) | |
369 | return -ENOMEM; | |
370 | ||
371 | ret = apb_ctrl_get_devtree_data(pdev, apb); | |
372 | if (ret) { | |
373 | dev_err(dev, "failed to get apb devicetree data %d\n", ret); | |
374 | return ret; | |
375 | } | |
376 | ||
f2222a41 VH |
377 | /* Initially set APB to OFF state */ |
378 | apb->state = ARCHE_PLATFORM_STATE_OFF; | |
af3aae10 VH |
379 | /* Check whether device needs to be enabled on boot */ |
380 | if (of_property_read_bool(pdev->dev.of_node, "ara,init-disable")) | |
381 | apb->init_disabled = true; | |
f2222a41 | 382 | |
6961e046 VH |
383 | platform_set_drvdata(pdev, apb); |
384 | ||
33d76291 VH |
385 | /* Create sysfs interface to allow user to change state dynamically */ |
386 | ret = device_create_file(dev, &dev_attr_state); | |
387 | if (ret) { | |
388 | dev_err(dev, "failed to create state file in sysfs\n"); | |
389 | return ret; | |
390 | } | |
391 | ||
5a781787 VH |
392 | dev_info(&pdev->dev, "Device registered successfully\n"); |
393 | return 0; | |
5a781787 VH |
394 | } |
395 | ||
1e5dd1f8 | 396 | int arche_apb_ctrl_remove(struct platform_device *pdev) |
5a781787 | 397 | { |
33d76291 | 398 | device_remove_file(&pdev->dev, &dev_attr_state); |
80a057aa | 399 | poweroff_seq(pdev); |
5a781787 VH |
400 | platform_set_drvdata(pdev, NULL); |
401 | ||
402 | return 0; | |
403 | } | |
404 | ||
405 | static int arche_apb_ctrl_suspend(struct device *dev) | |
406 | { | |
407 | /* | |
3b538c39 | 408 | * If timing profile permits, we may shutdown bridge |
5a781787 VH |
409 | * completely |
410 | * | |
411 | * TODO: sequence ?? | |
412 | * | |
413 | * Also, need to make sure we meet precondition for unipro suspend | |
414 | * Precondition: Definition ??? | |
415 | */ | |
416 | return 0; | |
417 | } | |
418 | ||
419 | static int arche_apb_ctrl_resume(struct device *dev) | |
420 | { | |
421 | /* | |
422 | * Atleast for ES2 we have to meet the delay requirement between | |
423 | * unipro switch and AP bridge init, depending on whether bridge is in | |
424 | * OFF state or standby state. | |
425 | * | |
426 | * Based on whether bridge is in standby or OFF state we may have to | |
427 | * assert multiple signals. Please refer to WDM spec, for more info. | |
428 | * | |
429 | */ | |
430 | return 0; | |
431 | } | |
432 | ||
1e5dd1f8 GKH |
433 | SIMPLE_DEV_PM_OPS(arche_apb_ctrl_pm_ops, |
434 | arche_apb_ctrl_suspend, | |
435 | arche_apb_ctrl_resume); |