1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Arche Platform driver to enable Unipro link. |
4 | * |
5 | * Copyright 2014-2015 Google Inc. |
6 | * Copyright 2014-2015 Linaro Ltd. |
7 | */ |
8 | |
9 | #include <linux/clk.h> |
10 | #include <linux/delay.h> |
11 | #include <linux/gpio/consumer.h> |
12 | #include <linux/init.h> |
13 | #include <linux/module.h> |
14 | #include <linux/of_platform.h> |
15 | #include <linux/pinctrl/consumer.h> |
16 | #include <linux/platform_device.h> |
17 | #include <linux/pm.h> |
18 | #include <linux/interrupt.h> |
19 | #include <linux/irq.h> |
20 | #include <linux/suspend.h> |
21 | #include <linux/time.h> |
22 | #include <linux/greybus.h> |
23 | #include <linux/of.h> |
24 | #include "arche_platform.h" |
25 | |
26 | #if IS_ENABLED(CONFIG_USB_HSIC_USB3613) |
27 | #include <linux/usb/usb3613.h> |
28 | #else |
29 | static inline int usb3613_hub_mode_ctrl(bool unused) |
30 | { |
31 | return 0; |
32 | } |
33 | #endif |
34 | |
35 | #define WD_COLDBOOT_PULSE_WIDTH_MS 30 |
36 | |
37 | enum svc_wakedetect_state { |
38 | WD_STATE_IDLE, /* Default state = pulled high/low */ |
39 | WD_STATE_BOOT_INIT, /* WD = falling edge (low) */ |
40 | WD_STATE_COLDBOOT_TRIG, /* WD = rising edge (high), > 30msec */ |
41 | WD_STATE_STANDBYBOOT_TRIG, /* As of now not used ?? */ |
42 | WD_STATE_COLDBOOT_START, /* Cold boot process started */ |
43 | WD_STATE_STANDBYBOOT_START, /* Not used */ |
44 | }; |
45 | |
46 | struct arche_platform_drvdata { |
47 | /* Control GPIO signals to and from AP <=> SVC */ |
48 | struct gpio_desc *svc_reset; |
49 | bool is_reset_act_hi; |
50 | struct gpio_desc *svc_sysboot; |
51 | struct gpio_desc *wake_detect; /* bi-dir,maps to WAKE_MOD & WAKE_FRAME signals */ |
52 | |
53 | enum arche_platform_state state; |
54 | |
55 | struct gpio_desc *svc_refclk_req; |
56 | struct clk *svc_ref_clk; |
57 | |
58 | struct pinctrl *pinctrl; |
59 | struct pinctrl_state *pin_default; |
60 | |
61 | int num_apbs; |
62 | |
63 | enum svc_wakedetect_state wake_detect_state; |
64 | int wake_detect_irq; |
65 | spinlock_t wake_lock; /* Protect wake_detect_state */ |
66 | struct mutex platform_state_mutex; /* Protect state */ |
67 | unsigned long wake_detect_start; |
68 | struct notifier_block pm_notifier; |
69 | |
70 | struct device *dev; |
71 | }; |
72 | |
73 | /* Requires calling context to hold arche_pdata->platform_state_mutex */ |
74 | static void arche_platform_set_state(struct arche_platform_drvdata *arche_pdata, |
75 | enum arche_platform_state state) |
76 | { |
77 | arche_pdata->state = state; |
78 | } |
79 | |
80 | /* Requires arche_pdata->wake_lock is held by calling context */ |
81 | static void arche_platform_set_wake_detect_state(struct arche_platform_drvdata *arche_pdata, |
82 | enum svc_wakedetect_state state) |
83 | { |
84 | arche_pdata->wake_detect_state = state; |
85 | } |
86 | |
87 | static inline void svc_reset_onoff(struct gpio_desc *gpio, bool onoff) |
88 | { |
89 | gpiod_set_raw_value(desc: gpio, value: onoff); |
90 | } |
91 | |
92 | static int apb_cold_boot(struct device *dev, void *data) |
93 | { |
94 | int ret; |
95 | |
96 | ret = apb_ctrl_coldboot(dev); |
97 | if (ret) |
98 | dev_warn(dev, "failed to coldboot\n" ); |
99 | |
100 | /*Child nodes are independent, so do not exit coldboot operation */ |
101 | return 0; |
102 | } |
103 | |
104 | static int apb_poweroff(struct device *dev, void *data) |
105 | { |
106 | apb_ctrl_poweroff(dev); |
107 | |
108 | /* Enable HUB3613 into HUB mode. */ |
109 | if (usb3613_hub_mode_ctrl(unused: false)) |
110 | dev_warn(dev, "failed to control hub device\n" ); |
111 | |
112 | return 0; |
113 | } |
114 | |
115 | static void arche_platform_wd_irq_en(struct arche_platform_drvdata *arche_pdata) |
116 | { |
117 | /* Enable interrupt here, to read event back from SVC */ |
118 | enable_irq(irq: arche_pdata->wake_detect_irq); |
119 | } |
120 | |
121 | static irqreturn_t arche_platform_wd_irq_thread(int irq, void *devid) |
122 | { |
123 | struct arche_platform_drvdata *arche_pdata = devid; |
124 | unsigned long flags; |
125 | |
126 | spin_lock_irqsave(&arche_pdata->wake_lock, flags); |
127 | if (arche_pdata->wake_detect_state != WD_STATE_COLDBOOT_TRIG) { |
128 | /* Something is wrong */ |
129 | spin_unlock_irqrestore(lock: &arche_pdata->wake_lock, flags); |
130 | return IRQ_HANDLED; |
131 | } |
132 | |
133 | arche_platform_set_wake_detect_state(arche_pdata, |
134 | state: WD_STATE_COLDBOOT_START); |
135 | spin_unlock_irqrestore(lock: &arche_pdata->wake_lock, flags); |
136 | |
137 | /* It should complete power cycle, so first make sure it is poweroff */ |
138 | device_for_each_child(dev: arche_pdata->dev, NULL, fn: apb_poweroff); |
139 | |
140 | /* Bring APB out of reset: cold boot sequence */ |
141 | device_for_each_child(dev: arche_pdata->dev, NULL, fn: apb_cold_boot); |
142 | |
143 | /* Enable HUB3613 into HUB mode. */ |
144 | if (usb3613_hub_mode_ctrl(unused: true)) |
145 | dev_warn(arche_pdata->dev, "failed to control hub device\n" ); |
146 | |
147 | spin_lock_irqsave(&arche_pdata->wake_lock, flags); |
148 | arche_platform_set_wake_detect_state(arche_pdata, state: WD_STATE_IDLE); |
149 | spin_unlock_irqrestore(lock: &arche_pdata->wake_lock, flags); |
150 | |
151 | return IRQ_HANDLED; |
152 | } |
153 | |
154 | static irqreturn_t arche_platform_wd_irq(int irq, void *devid) |
155 | { |
156 | struct arche_platform_drvdata *arche_pdata = devid; |
157 | unsigned long flags; |
158 | |
159 | spin_lock_irqsave(&arche_pdata->wake_lock, flags); |
160 | |
161 | if (gpiod_get_value(desc: arche_pdata->wake_detect)) { |
162 | /* wake/detect rising */ |
163 | |
164 | /* |
165 | * If wake/detect line goes high after low, within less than |
166 | * 30msec, then standby boot sequence is initiated, which is not |
167 | * supported/implemented as of now. So ignore it. |
168 | */ |
169 | if (arche_pdata->wake_detect_state == WD_STATE_BOOT_INIT) { |
170 | if (time_before(jiffies, |
171 | arche_pdata->wake_detect_start + |
172 | msecs_to_jiffies(WD_COLDBOOT_PULSE_WIDTH_MS))) { |
173 | arche_platform_set_wake_detect_state(arche_pdata, |
174 | state: WD_STATE_IDLE); |
175 | } else { |
176 | /* |
177 | * Check we are not in middle of irq thread |
178 | * already |
179 | */ |
180 | if (arche_pdata->wake_detect_state != |
181 | WD_STATE_COLDBOOT_START) { |
182 | arche_platform_set_wake_detect_state(arche_pdata, |
183 | state: WD_STATE_COLDBOOT_TRIG); |
184 | spin_unlock_irqrestore(lock: &arche_pdata->wake_lock, |
185 | flags); |
186 | return IRQ_WAKE_THREAD; |
187 | } |
188 | } |
189 | } |
190 | } else { |
191 | /* wake/detect falling */ |
192 | if (arche_pdata->wake_detect_state == WD_STATE_IDLE) { |
193 | arche_pdata->wake_detect_start = jiffies; |
194 | /* |
195 | * In the beginning, when wake/detect goes low |
196 | * (first time), we assume it is meant for coldboot |
197 | * and set the flag. If wake/detect line stays low |
198 | * beyond 30msec, then it is coldboot else fallback |
199 | * to standby boot. |
200 | */ |
201 | arche_platform_set_wake_detect_state(arche_pdata, |
202 | state: WD_STATE_BOOT_INIT); |
203 | } |
204 | } |
205 | |
206 | spin_unlock_irqrestore(lock: &arche_pdata->wake_lock, flags); |
207 | |
208 | return IRQ_HANDLED; |
209 | } |
210 | |
211 | /* |
212 | * Requires arche_pdata->platform_state_mutex to be held |
213 | */ |
214 | static int |
215 | arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata) |
216 | { |
217 | int ret; |
218 | |
219 | if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) |
220 | return 0; |
221 | |
222 | dev_info(arche_pdata->dev, "Booting from cold boot state\n" ); |
223 | |
224 | svc_reset_onoff(gpio: arche_pdata->svc_reset, onoff: arche_pdata->is_reset_act_hi); |
225 | |
226 | gpiod_set_value(desc: arche_pdata->svc_sysboot, value: 0); |
227 | usleep_range(min: 100, max: 200); |
228 | |
229 | ret = clk_prepare_enable(clk: arche_pdata->svc_ref_clk); |
230 | if (ret) { |
231 | dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n" , |
232 | ret); |
233 | return ret; |
234 | } |
235 | |
236 | /* bring SVC out of reset */ |
237 | svc_reset_onoff(gpio: arche_pdata->svc_reset, onoff: !arche_pdata->is_reset_act_hi); |
238 | |
239 | arche_platform_set_state(arche_pdata, state: ARCHE_PLATFORM_STATE_ACTIVE); |
240 | |
241 | return 0; |
242 | } |
243 | |
244 | /* |
245 | * Requires arche_pdata->platform_state_mutex to be held |
246 | */ |
247 | static int |
248 | arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata) |
249 | { |
250 | int ret; |
251 | |
252 | if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) |
253 | return 0; |
254 | |
255 | dev_info(arche_pdata->dev, "Switching to FW flashing state\n" ); |
256 | |
257 | svc_reset_onoff(gpio: arche_pdata->svc_reset, onoff: arche_pdata->is_reset_act_hi); |
258 | |
259 | gpiod_set_value(desc: arche_pdata->svc_sysboot, value: 1); |
260 | |
261 | usleep_range(min: 100, max: 200); |
262 | |
263 | ret = clk_prepare_enable(clk: arche_pdata->svc_ref_clk); |
264 | if (ret) { |
265 | dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n" , |
266 | ret); |
267 | return ret; |
268 | } |
269 | |
270 | svc_reset_onoff(gpio: arche_pdata->svc_reset, onoff: !arche_pdata->is_reset_act_hi); |
271 | |
272 | arche_platform_set_state(arche_pdata, state: ARCHE_PLATFORM_STATE_FW_FLASHING); |
273 | |
274 | return 0; |
275 | } |
276 | |
277 | /* |
278 | * Requires arche_pdata->platform_state_mutex to be held |
279 | */ |
280 | static void |
281 | arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata) |
282 | { |
283 | unsigned long flags; |
284 | |
285 | if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) |
286 | return; |
287 | |
288 | /* If in fw_flashing mode, then no need to repeate things again */ |
289 | if (arche_pdata->state != ARCHE_PLATFORM_STATE_FW_FLASHING) { |
290 | disable_irq(irq: arche_pdata->wake_detect_irq); |
291 | |
292 | spin_lock_irqsave(&arche_pdata->wake_lock, flags); |
293 | arche_platform_set_wake_detect_state(arche_pdata, |
294 | state: WD_STATE_IDLE); |
295 | spin_unlock_irqrestore(lock: &arche_pdata->wake_lock, flags); |
296 | } |
297 | |
298 | clk_disable_unprepare(clk: arche_pdata->svc_ref_clk); |
299 | |
300 | /* As part of exit, put APB back in reset state */ |
301 | svc_reset_onoff(gpio: arche_pdata->svc_reset, onoff: arche_pdata->is_reset_act_hi); |
302 | |
303 | arche_platform_set_state(arche_pdata, state: ARCHE_PLATFORM_STATE_OFF); |
304 | } |
305 | |
306 | static ssize_t state_store(struct device *dev, |
307 | struct device_attribute *attr, |
308 | const char *buf, size_t count) |
309 | { |
310 | struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev); |
311 | int ret = 0; |
312 | |
313 | mutex_lock(&arche_pdata->platform_state_mutex); |
314 | |
315 | if (sysfs_streq(s1: buf, s2: "off" )) { |
316 | if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) |
317 | goto exit; |
318 | |
319 | /* If SVC goes down, bring down APB's as well */ |
320 | device_for_each_child(dev: arche_pdata->dev, NULL, fn: apb_poweroff); |
321 | |
322 | arche_platform_poweroff_seq(arche_pdata); |
323 | |
324 | } else if (sysfs_streq(s1: buf, s2: "active" )) { |
325 | if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) |
326 | goto exit; |
327 | |
328 | /* First we want to make sure we power off everything |
329 | * and then activate back again |
330 | */ |
331 | device_for_each_child(dev: arche_pdata->dev, NULL, fn: apb_poweroff); |
332 | arche_platform_poweroff_seq(arche_pdata); |
333 | |
334 | arche_platform_wd_irq_en(arche_pdata); |
335 | ret = arche_platform_coldboot_seq(arche_pdata); |
336 | if (ret) |
337 | goto exit; |
338 | |
339 | } else if (sysfs_streq(s1: buf, s2: "standby" )) { |
340 | if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY) |
341 | goto exit; |
342 | |
343 | dev_warn(arche_pdata->dev, "standby state not supported\n" ); |
344 | } else if (sysfs_streq(s1: buf, s2: "fw_flashing" )) { |
345 | if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) |
346 | goto exit; |
347 | |
348 | /* |
349 | * Here we only control SVC. |
350 | * |
351 | * In case of FW_FLASHING mode we do not want to control |
352 | * APBs, as in case of V2, SPI bus is shared between both |
353 | * the APBs. So let user chose which APB he wants to flash. |
354 | */ |
355 | arche_platform_poweroff_seq(arche_pdata); |
356 | |
357 | ret = arche_platform_fw_flashing_seq(arche_pdata); |
358 | if (ret) |
359 | goto exit; |
360 | } else { |
361 | dev_err(arche_pdata->dev, "unknown state\n" ); |
362 | ret = -EINVAL; |
363 | } |
364 | |
365 | exit: |
366 | mutex_unlock(lock: &arche_pdata->platform_state_mutex); |
367 | return ret ? ret : count; |
368 | } |
369 | |
370 | static ssize_t state_show(struct device *dev, |
371 | struct device_attribute *attr, char *buf) |
372 | { |
373 | struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev); |
374 | |
375 | switch (arche_pdata->state) { |
376 | case ARCHE_PLATFORM_STATE_OFF: |
377 | return sprintf(buf, fmt: "off\n" ); |
378 | case ARCHE_PLATFORM_STATE_ACTIVE: |
379 | return sprintf(buf, fmt: "active\n" ); |
380 | case ARCHE_PLATFORM_STATE_STANDBY: |
381 | return sprintf(buf, fmt: "standby\n" ); |
382 | case ARCHE_PLATFORM_STATE_FW_FLASHING: |
383 | return sprintf(buf, fmt: "fw_flashing\n" ); |
384 | default: |
385 | return sprintf(buf, fmt: "unknown state\n" ); |
386 | } |
387 | } |
388 | |
389 | static DEVICE_ATTR_RW(state); |
390 | |
391 | static int arche_platform_pm_notifier(struct notifier_block *notifier, |
392 | unsigned long pm_event, void *unused) |
393 | { |
394 | struct arche_platform_drvdata *arche_pdata = |
395 | container_of(notifier, struct arche_platform_drvdata, |
396 | pm_notifier); |
397 | int ret = NOTIFY_DONE; |
398 | |
399 | mutex_lock(&arche_pdata->platform_state_mutex); |
400 | switch (pm_event) { |
401 | case PM_SUSPEND_PREPARE: |
402 | if (arche_pdata->state != ARCHE_PLATFORM_STATE_ACTIVE) { |
403 | ret = NOTIFY_STOP; |
404 | break; |
405 | } |
406 | device_for_each_child(dev: arche_pdata->dev, NULL, fn: apb_poweroff); |
407 | arche_platform_poweroff_seq(arche_pdata); |
408 | break; |
409 | case PM_POST_SUSPEND: |
410 | if (arche_pdata->state != ARCHE_PLATFORM_STATE_OFF) |
411 | break; |
412 | |
413 | arche_platform_wd_irq_en(arche_pdata); |
414 | arche_platform_coldboot_seq(arche_pdata); |
415 | break; |
416 | default: |
417 | break; |
418 | } |
419 | mutex_unlock(lock: &arche_pdata->platform_state_mutex); |
420 | |
421 | return ret; |
422 | } |
423 | |
424 | static int arche_platform_probe(struct platform_device *pdev) |
425 | { |
426 | struct arche_platform_drvdata *arche_pdata; |
427 | struct device *dev = &pdev->dev; |
428 | struct device_node *np = dev->of_node; |
429 | int ret; |
430 | unsigned int flags; |
431 | |
432 | arche_pdata = devm_kzalloc(dev: &pdev->dev, size: sizeof(*arche_pdata), |
433 | GFP_KERNEL); |
434 | if (!arche_pdata) |
435 | return -ENOMEM; |
436 | |
437 | /* setup svc reset gpio */ |
438 | arche_pdata->is_reset_act_hi = of_property_read_bool(np, |
439 | propname: "svc,reset-active-high" ); |
440 | if (arche_pdata->is_reset_act_hi) |
441 | flags = GPIOD_OUT_HIGH; |
442 | else |
443 | flags = GPIOD_OUT_LOW; |
444 | |
445 | arche_pdata->svc_reset = devm_gpiod_get(dev, con_id: "svc,reset" , flags); |
446 | if (IS_ERR(ptr: arche_pdata->svc_reset)) { |
447 | ret = PTR_ERR(ptr: arche_pdata->svc_reset); |
448 | dev_err(dev, "failed to request svc-reset GPIO: %d\n" , ret); |
449 | return ret; |
450 | } |
451 | arche_platform_set_state(arche_pdata, state: ARCHE_PLATFORM_STATE_OFF); |
452 | |
453 | arche_pdata->svc_sysboot = devm_gpiod_get(dev, con_id: "svc,sysboot" , |
454 | flags: GPIOD_OUT_LOW); |
455 | if (IS_ERR(ptr: arche_pdata->svc_sysboot)) { |
456 | ret = PTR_ERR(ptr: arche_pdata->svc_sysboot); |
457 | dev_err(dev, "failed to request sysboot0 GPIO: %d\n" , ret); |
458 | return ret; |
459 | } |
460 | |
461 | /* setup the clock request gpio first */ |
462 | arche_pdata->svc_refclk_req = devm_gpiod_get(dev, con_id: "svc,refclk-req" , |
463 | flags: GPIOD_IN); |
464 | if (IS_ERR(ptr: arche_pdata->svc_refclk_req)) { |
465 | ret = PTR_ERR(ptr: arche_pdata->svc_refclk_req); |
466 | dev_err(dev, "failed to request svc-clk-req GPIO: %d\n" , ret); |
467 | return ret; |
468 | } |
469 | |
470 | /* setup refclk2 to follow the pin */ |
471 | arche_pdata->svc_ref_clk = devm_clk_get(dev, id: "svc_ref_clk" ); |
472 | if (IS_ERR(ptr: arche_pdata->svc_ref_clk)) { |
473 | ret = PTR_ERR(ptr: arche_pdata->svc_ref_clk); |
474 | dev_err(dev, "failed to get svc_ref_clk: %d\n" , ret); |
475 | return ret; |
476 | } |
477 | |
478 | platform_set_drvdata(pdev, data: arche_pdata); |
479 | |
480 | arche_pdata->num_apbs = of_get_child_count(np); |
481 | dev_dbg(dev, "Number of APB's available - %d\n" , arche_pdata->num_apbs); |
482 | |
483 | arche_pdata->wake_detect = devm_gpiod_get(dev, con_id: "svc,wake-detect" , |
484 | flags: GPIOD_IN); |
485 | if (IS_ERR(ptr: arche_pdata->wake_detect)) { |
486 | ret = PTR_ERR(ptr: arche_pdata->wake_detect); |
487 | dev_err(dev, "Failed requesting wake_detect GPIO: %d\n" , ret); |
488 | return ret; |
489 | } |
490 | |
491 | arche_platform_set_wake_detect_state(arche_pdata, state: WD_STATE_IDLE); |
492 | |
493 | arche_pdata->dev = &pdev->dev; |
494 | |
495 | spin_lock_init(&arche_pdata->wake_lock); |
496 | mutex_init(&arche_pdata->platform_state_mutex); |
497 | arche_pdata->wake_detect_irq = |
498 | gpiod_to_irq(desc: arche_pdata->wake_detect); |
499 | |
500 | ret = devm_request_threaded_irq(dev, irq: arche_pdata->wake_detect_irq, |
501 | handler: arche_platform_wd_irq, |
502 | thread_fn: arche_platform_wd_irq_thread, |
503 | IRQF_TRIGGER_FALLING | |
504 | IRQF_TRIGGER_RISING | IRQF_ONESHOT, |
505 | devname: dev_name(dev), dev_id: arche_pdata); |
506 | if (ret) { |
507 | dev_err(dev, "failed to request wake detect IRQ %d\n" , ret); |
508 | return ret; |
509 | } |
510 | disable_irq(irq: arche_pdata->wake_detect_irq); |
511 | |
512 | ret = device_create_file(device: dev, entry: &dev_attr_state); |
513 | if (ret) { |
514 | dev_err(dev, "failed to create state file in sysfs\n" ); |
515 | return ret; |
516 | } |
517 | |
518 | ret = of_platform_populate(root: np, NULL, NULL, parent: dev); |
519 | if (ret) { |
520 | dev_err(dev, "failed to populate child nodes %d\n" , ret); |
521 | goto err_device_remove; |
522 | } |
523 | |
524 | arche_pdata->pm_notifier.notifier_call = arche_platform_pm_notifier; |
525 | ret = register_pm_notifier(nb: &arche_pdata->pm_notifier); |
526 | |
527 | if (ret) { |
528 | dev_err(dev, "failed to register pm notifier %d\n" , ret); |
529 | goto err_device_remove; |
530 | } |
531 | |
532 | /* Explicitly power off if requested */ |
533 | if (!of_property_read_bool(np: pdev->dev.of_node, propname: "arche,init-off" )) { |
534 | mutex_lock(&arche_pdata->platform_state_mutex); |
535 | ret = arche_platform_coldboot_seq(arche_pdata); |
536 | if (ret) { |
537 | dev_err(dev, "Failed to cold boot svc %d\n" , ret); |
538 | goto err_coldboot; |
539 | } |
540 | arche_platform_wd_irq_en(arche_pdata); |
541 | mutex_unlock(lock: &arche_pdata->platform_state_mutex); |
542 | } |
543 | |
544 | dev_info(dev, "Device registered successfully\n" ); |
545 | return 0; |
546 | |
547 | err_coldboot: |
548 | mutex_unlock(lock: &arche_pdata->platform_state_mutex); |
549 | err_device_remove: |
550 | device_remove_file(dev: &pdev->dev, attr: &dev_attr_state); |
551 | return ret; |
552 | } |
553 | |
554 | static int arche_remove_child(struct device *dev, void *unused) |
555 | { |
556 | struct platform_device *pdev = to_platform_device(dev); |
557 | |
558 | platform_device_unregister(pdev); |
559 | |
560 | return 0; |
561 | } |
562 | |
563 | static void arche_platform_remove(struct platform_device *pdev) |
564 | { |
565 | struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); |
566 | |
567 | unregister_pm_notifier(nb: &arche_pdata->pm_notifier); |
568 | device_remove_file(dev: &pdev->dev, attr: &dev_attr_state); |
569 | device_for_each_child(dev: &pdev->dev, NULL, fn: arche_remove_child); |
570 | arche_platform_poweroff_seq(arche_pdata); |
571 | |
572 | if (usb3613_hub_mode_ctrl(unused: false)) |
573 | dev_warn(arche_pdata->dev, "failed to control hub device\n" ); |
574 | } |
575 | |
576 | static __maybe_unused int arche_platform_suspend(struct device *dev) |
577 | { |
578 | /* |
579 | * If timing profile premits, we may shutdown bridge |
580 | * completely |
581 | * |
582 | * TODO: sequence ?? |
583 | * |
584 | * Also, need to make sure we meet precondition for unipro suspend |
585 | * Precondition: Definition ??? |
586 | */ |
587 | return 0; |
588 | } |
589 | |
590 | static __maybe_unused int arche_platform_resume(struct device *dev) |
591 | { |
592 | /* |
593 | * At least for ES2 we have to meet the delay requirement between |
594 | * unipro switch and AP bridge init, depending on whether bridge is in |
595 | * OFF state or standby state. |
596 | * |
597 | * Based on whether bridge is in standby or OFF state we may have to |
598 | * assert multiple signals. Please refer to WDM spec, for more info. |
599 | * |
600 | */ |
601 | return 0; |
602 | } |
603 | |
604 | static void arche_platform_shutdown(struct platform_device *pdev) |
605 | { |
606 | struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); |
607 | |
608 | arche_platform_poweroff_seq(arche_pdata); |
609 | |
610 | usb3613_hub_mode_ctrl(unused: false); |
611 | } |
612 | |
613 | static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops, |
614 | arche_platform_suspend, |
615 | arche_platform_resume); |
616 | |
617 | static const struct of_device_id arche_platform_of_match[] = { |
618 | /* Use PID/VID of SVC device */ |
619 | { .compatible = "google,arche-platform" , }, |
620 | { }, |
621 | }; |
622 | |
623 | static const struct of_device_id arche_combined_id[] = { |
624 | /* Use PID/VID of SVC device */ |
625 | { .compatible = "google,arche-platform" , }, |
626 | { .compatible = "usbffff,2" , }, |
627 | { }, |
628 | }; |
629 | MODULE_DEVICE_TABLE(of, arche_combined_id); |
630 | |
631 | static struct platform_driver arche_platform_device_driver = { |
632 | .probe = arche_platform_probe, |
633 | .remove_new = arche_platform_remove, |
634 | .shutdown = arche_platform_shutdown, |
635 | .driver = { |
636 | .name = "arche-platform-ctrl" , |
637 | .pm = &arche_platform_pm_ops, |
638 | .of_match_table = arche_platform_of_match, |
639 | } |
640 | }; |
641 | |
642 | static int __init arche_init(void) |
643 | { |
644 | int retval; |
645 | |
646 | retval = platform_driver_register(&arche_platform_device_driver); |
647 | if (retval) |
648 | return retval; |
649 | |
650 | retval = arche_apb_init(); |
651 | if (retval) |
652 | platform_driver_unregister(&arche_platform_device_driver); |
653 | |
654 | return retval; |
655 | } |
656 | module_init(arche_init); |
657 | |
658 | static void __exit arche_exit(void) |
659 | { |
660 | arche_apb_exit(); |
661 | platform_driver_unregister(&arche_platform_device_driver); |
662 | } |
663 | module_exit(arche_exit); |
664 | |
665 | MODULE_LICENSE("GPL v2" ); |
666 | MODULE_AUTHOR("Vaibhav Hiremath <vaibhav.hiremath@linaro.org>" ); |
667 | MODULE_DESCRIPTION("Arche Platform Driver" ); |
668 | |