Commit | Line | Data |
---|---|---|
ec66096b MK |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* | |
3 | * Force-disables a regulator to power down a device | |
4 | * | |
5 | * Michael Klein <michael@fossekall.de> | |
6 | * | |
7 | * Copyright (C) 2020 Michael Klein | |
8 | * | |
9 | * Based on the gpio-poweroff driver. | |
10 | */ | |
11 | #include <linux/delay.h> | |
12 | #include <linux/module.h> | |
13 | #include <linux/of.h> | |
14 | #include <linux/platform_device.h> | |
15 | #include <linux/pm.h> | |
16 | #include <linux/regulator/consumer.h> | |
17 | ||
18 | #define TIMEOUT_MS 3000 | |
19 | ||
20 | /* | |
21 | * Hold configuration here, cannot be more than one instance of the driver | |
22 | * since pm_power_off itself is global. | |
23 | */ | |
24 | static struct regulator *cpu_regulator; | |
25 | ||
26 | static void regulator_poweroff_do_poweroff(void) | |
27 | { | |
28 | if (cpu_regulator && regulator_is_enabled(cpu_regulator)) | |
29 | regulator_force_disable(cpu_regulator); | |
30 | ||
31 | /* give it some time */ | |
32 | mdelay(TIMEOUT_MS); | |
33 | ||
34 | WARN_ON(1); | |
35 | } | |
36 | ||
37 | static int regulator_poweroff_probe(struct platform_device *pdev) | |
38 | { | |
39 | /* If a pm_power_off function has already been added, leave it alone */ | |
40 | if (pm_power_off != NULL) { | |
41 | dev_err(&pdev->dev, | |
42 | "%s: pm_power_off function already registered\n", | |
43 | __func__); | |
44 | return -EBUSY; | |
45 | } | |
46 | ||
47 | cpu_regulator = devm_regulator_get(&pdev->dev, "cpu"); | |
48 | if (IS_ERR(cpu_regulator)) | |
49 | return PTR_ERR(cpu_regulator); | |
50 | ||
51 | pm_power_off = ®ulator_poweroff_do_poweroff; | |
52 | return 0; | |
53 | } | |
54 | ||
55 | static int regulator_poweroff_remove(__maybe_unused struct platform_device *pdev) | |
56 | { | |
57 | if (pm_power_off == ®ulator_poweroff_do_poweroff) | |
58 | pm_power_off = NULL; | |
59 | ||
60 | return 0; | |
61 | } | |
62 | ||
63 | static const struct of_device_id of_regulator_poweroff_match[] = { | |
64 | { .compatible = "regulator-poweroff", }, | |
65 | {}, | |
66 | }; | |
67 | ||
68 | static struct platform_driver regulator_poweroff_driver = { | |
69 | .probe = regulator_poweroff_probe, | |
70 | .remove = regulator_poweroff_remove, | |
71 | .driver = { | |
72 | .name = "poweroff-regulator", | |
73 | .of_match_table = of_regulator_poweroff_match, | |
74 | }, | |
75 | }; | |
76 | ||
77 | module_platform_driver(regulator_poweroff_driver); | |
78 | ||
79 | MODULE_AUTHOR("Michael Klein <michael@fossekall.de>"); | |
80 | MODULE_DESCRIPTION("Regulator poweroff driver"); | |
81 | MODULE_LICENSE("GPL v2"); | |
82 | MODULE_ALIAS("platform:poweroff-regulator"); |