diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-10-15 00:56:23 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-10-15 00:56:23 -0400 |
commit | 50fa86172bec2769979b5eb0cd1a244391ae4bb0 (patch) | |
tree | 5fd4949b031e1362af8b226d6372da2604de13ff /drivers/power/reset | |
parent | 6b0490816671b2f4126a99998c9bf3c8c0472de2 (diff) | |
parent | 7881c64716f3a7d60b325ed0ad4d15f49b474a43 (diff) |
Merge tag 'for-v3.18' of git://git.infradead.org/battery-2.6
Pull power supply and reset updates from Sebastian Reichel:
- Initial support for the following chips
* max77836 (charger)
* max14577 (charger)
* bq27742 (battery gauge)
* ltc2952 (poweroff)
* stih416 (restart)
* syscon-reboot (restart)
* gpio-restart (restart)
- cleanup of power supply core
- misc fixes in power supply and reset drivers
* tag 'for-v3.18' of git://git.infradead.org/battery-2.6: (48 commits)
power: ab8500_fg: Fix build warning
Documentation: charger: max14577: Update the date of introducing ABI
power: reset: corrections for simple syscon reboot driver
Documentation: power: reset: Add documentation for generic SYSCON reboot driver
power: reset: Add generic SYSCON register mapped reset
bq27x00_battery: Fix flag reading for bq27742
power: reset: use restart_notifier mechanism for msm-poweroff
power: Add simple gpio-restart driver
power: reset: st: Provide DT bindings for ST's Power Reset driver
power: reset: Add restart functionality for STiH41x platforms
power: charger-manager: Fix NULL pointer exception with missing cm-fuel-gauge
power: max14577: Fix circular config SYSFS dependency
power: gpio-charger: do not use gpio value directly
power: max8925: Use of_get_child_by_name
power: max8925: Fix NULL ptr dereference on memory allocation failure
bq27x00_battery: Add support to bq27742
Documentation: charger: max14577: Document exported sysfs entry
devicetree: mfd: max14577: Add device tree bindings document
power: max17040: Add ID for MAX77836 Fuel Gauge block
charger: max14577: Configure battery-dependent settings from DTS and sysfs
...
Conflicts:
drivers/power/reset/Kconfig
drivers/power/reset/Makefile
Diffstat (limited to 'drivers/power/reset')
-rw-r--r-- | drivers/power/reset/Kconfig | 33 | ||||
-rw-r--r-- | drivers/power/reset/Makefile | 4 | ||||
-rw-r--r-- | drivers/power/reset/gpio-restart.c | 149 | ||||
-rw-r--r-- | drivers/power/reset/ltc2952-poweroff.c | 386 | ||||
-rw-r--r-- | drivers/power/reset/msm-poweroff.c | 20 | ||||
-rw-r--r-- | drivers/power/reset/st-poweroff.c | 151 | ||||
-rw-r--r-- | drivers/power/reset/syscon-reboot.c | 91 | ||||
-rw-r--r-- | drivers/power/reset/xgene-reboot.c | 2 |
8 files changed, 827 insertions, 9 deletions
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 527a0f47ef44..f65ff49bb275 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig | |||
@@ -40,7 +40,7 @@ config POWER_RESET_AXXIA | |||
40 | 40 | ||
41 | config POWER_RESET_BRCMSTB | 41 | config POWER_RESET_BRCMSTB |
42 | bool "Broadcom STB reset driver" if COMPILE_TEST | 42 | bool "Broadcom STB reset driver" if COMPILE_TEST |
43 | depends on POWER_RESET && ARM | 43 | depends on ARM |
44 | default ARCH_BRCMSTB | 44 | default ARCH_BRCMSTB |
45 | help | 45 | help |
46 | This driver provides restart support for ARM-based Broadcom STB | 46 | This driver provides restart support for ARM-based Broadcom STB |
@@ -57,9 +57,17 @@ config POWER_RESET_GPIO | |||
57 | If your board needs a GPIO high/low to power down, say Y and | 57 | If your board needs a GPIO high/low to power down, say Y and |
58 | create a binding in your devicetree. | 58 | create a binding in your devicetree. |
59 | 59 | ||
60 | config POWER_RESET_GPIO_RESTART | ||
61 | bool "GPIO restart driver" | ||
62 | depends on OF_GPIO | ||
63 | help | ||
64 | This driver supports restarting your board via a GPIO line. | ||
65 | If your board needs a GPIO high/low to restart, say Y and | ||
66 | create a binding in your devicetree. | ||
67 | |||
60 | config POWER_RESET_HISI | 68 | config POWER_RESET_HISI |
61 | bool "Hisilicon power-off driver" | 69 | bool "Hisilicon power-off driver" |
62 | depends on POWER_RESET && ARCH_HISI | 70 | depends on ARCH_HISI |
63 | help | 71 | help |
64 | Reboot support for Hisilicon boards. | 72 | Reboot support for Hisilicon boards. |
65 | 73 | ||
@@ -69,6 +77,13 @@ config POWER_RESET_MSM | |||
69 | help | 77 | help |
70 | Power off and restart support for Qualcomm boards. | 78 | Power off and restart support for Qualcomm boards. |
71 | 79 | ||
80 | config POWER_RESET_LTC2952 | ||
81 | bool "LTC2952 PowerPath power-off driver" | ||
82 | depends on OF_GPIO | ||
83 | help | ||
84 | This driver supports an external powerdown trigger and board power | ||
85 | down via the LTC2952. Bindings are made in the device tree. | ||
86 | |||
72 | config POWER_RESET_QNAP | 87 | config POWER_RESET_QNAP |
73 | bool "QNAP power-off driver" | 88 | bool "QNAP power-off driver" |
74 | depends on OF_GPIO && PLAT_ORION | 89 | depends on OF_GPIO && PLAT_ORION |
@@ -92,6 +107,12 @@ config POWER_RESET_SUN6I | |||
92 | help | 107 | help |
93 | Reboot support for the Allwinner A31 SoCs. | 108 | Reboot support for the Allwinner A31 SoCs. |
94 | 109 | ||
110 | config POWER_RESET_ST | ||
111 | bool "ST restart power-off driver" | ||
112 | depends on ARCH_STI | ||
113 | help | ||
114 | Power off and reset support for STMicroelectronics boards. | ||
115 | |||
95 | config POWER_RESET_VERSATILE | 116 | config POWER_RESET_VERSATILE |
96 | bool "ARM Versatile family reboot driver" | 117 | bool "ARM Versatile family reboot driver" |
97 | depends on ARM | 118 | depends on ARM |
@@ -122,4 +143,12 @@ config POWER_RESET_KEYSTONE | |||
122 | help | 143 | help |
123 | Reboot support for the KEYSTONE SoCs. | 144 | Reboot support for the KEYSTONE SoCs. |
124 | 145 | ||
146 | config POWER_RESET_SYSCON | ||
147 | bool "Generic SYSCON regmap reset driver" | ||
148 | depends on OF | ||
149 | select MFD_SYSCON | ||
150 | help | ||
151 | Reboot support for generic SYSCON mapped register reset. | ||
152 | |||
125 | endif | 153 | endif |
154 | |||
diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index 73221009f2bf..76ce1c59469b 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile | |||
@@ -4,12 +4,16 @@ obj-$(CONFIG_POWER_RESET_AT91_RESET) += at91-reset.o | |||
4 | obj-$(CONFIG_POWER_RESET_AXXIA) += axxia-reset.o | 4 | obj-$(CONFIG_POWER_RESET_AXXIA) += axxia-reset.o |
5 | obj-$(CONFIG_POWER_RESET_BRCMSTB) += brcmstb-reboot.o | 5 | obj-$(CONFIG_POWER_RESET_BRCMSTB) += brcmstb-reboot.o |
6 | obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o | 6 | obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o |
7 | obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o | ||
7 | obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o | 8 | obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o |
8 | obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o | 9 | obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o |
10 | obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o | ||
9 | obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o | 11 | obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o |
10 | obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o | 12 | obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o |
11 | obj-$(CONFIG_POWER_RESET_SUN6I) += sun6i-reboot.o | 13 | obj-$(CONFIG_POWER_RESET_SUN6I) += sun6i-reboot.o |
14 | obj-$(CONFIG_POWER_RESET_ST) += st-poweroff.o | ||
12 | obj-$(CONFIG_POWER_RESET_VERSATILE) += arm-versatile-reboot.o | 15 | obj-$(CONFIG_POWER_RESET_VERSATILE) += arm-versatile-reboot.o |
13 | obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o | 16 | obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o |
14 | obj-$(CONFIG_POWER_RESET_XGENE) += xgene-reboot.o | 17 | obj-$(CONFIG_POWER_RESET_XGENE) += xgene-reboot.o |
15 | obj-$(CONFIG_POWER_RESET_KEYSTONE) += keystone-reset.o | 18 | obj-$(CONFIG_POWER_RESET_KEYSTONE) += keystone-reset.o |
19 | obj-$(CONFIG_POWER_RESET_SYSCON) += syscon-reboot.o | ||
diff --git a/drivers/power/reset/gpio-restart.c b/drivers/power/reset/gpio-restart.c new file mode 100644 index 000000000000..a76829b3f1cd --- /dev/null +++ b/drivers/power/reset/gpio-restart.c | |||
@@ -0,0 +1,149 @@ | |||
1 | /* | ||
2 | * Toggles a GPIO pin to restart a device | ||
3 | * | ||
4 | * Copyright (C) 2014 Google, Inc. | ||
5 | * | ||
6 | * This software is licensed under the terms of the GNU General Public | ||
7 | * License version 2, as published by the Free Software Foundation, and | ||
8 | * may be copied, distributed, and modified under those terms. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * Based on the gpio-poweroff driver. | ||
16 | */ | ||
17 | #include <linux/reboot.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/init.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/gpio/consumer.h> | ||
23 | #include <linux/of_platform.h> | ||
24 | #include <linux/module.h> | ||
25 | |||
26 | struct gpio_restart { | ||
27 | struct gpio_desc *reset_gpio; | ||
28 | struct notifier_block restart_handler; | ||
29 | u32 active_delay_ms; | ||
30 | u32 inactive_delay_ms; | ||
31 | u32 wait_delay_ms; | ||
32 | }; | ||
33 | |||
34 | static int gpio_restart_notify(struct notifier_block *this, | ||
35 | unsigned long mode, void *cmd) | ||
36 | { | ||
37 | struct gpio_restart *gpio_restart = | ||
38 | container_of(this, struct gpio_restart, restart_handler); | ||
39 | |||
40 | /* drive it active, also inactive->active edge */ | ||
41 | gpiod_direction_output(gpio_restart->reset_gpio, 1); | ||
42 | mdelay(gpio_restart->active_delay_ms); | ||
43 | |||
44 | /* drive inactive, also active->inactive edge */ | ||
45 | gpiod_set_value(gpio_restart->reset_gpio, 0); | ||
46 | mdelay(gpio_restart->inactive_delay_ms); | ||
47 | |||
48 | /* drive it active, also inactive->active edge */ | ||
49 | gpiod_set_value(gpio_restart->reset_gpio, 1); | ||
50 | |||
51 | /* give it some time */ | ||
52 | mdelay(gpio_restart->wait_delay_ms); | ||
53 | |||
54 | WARN_ON(1); | ||
55 | |||
56 | return NOTIFY_DONE; | ||
57 | } | ||
58 | |||
59 | static int gpio_restart_probe(struct platform_device *pdev) | ||
60 | { | ||
61 | struct gpio_restart *gpio_restart; | ||
62 | bool open_source = false; | ||
63 | u32 property; | ||
64 | int ret; | ||
65 | |||
66 | gpio_restart = devm_kzalloc(&pdev->dev, sizeof(*gpio_restart), | ||
67 | GFP_KERNEL); | ||
68 | if (!gpio_restart) | ||
69 | return -ENOMEM; | ||
70 | |||
71 | open_source = of_property_read_bool(pdev->dev.of_node, "open-source"); | ||
72 | |||
73 | gpio_restart->reset_gpio = devm_gpiod_get(&pdev->dev, NULL, | ||
74 | open_source ? GPIOD_IN : GPIOD_OUT_LOW); | ||
75 | if (IS_ERR(gpio_restart->reset_gpio)) { | ||
76 | dev_err(&pdev->dev, "Could net get reset GPIO\n"); | ||
77 | return PTR_ERR(gpio_restart->reset_gpio); | ||
78 | } | ||
79 | |||
80 | gpio_restart->restart_handler.notifier_call = gpio_restart_notify; | ||
81 | gpio_restart->restart_handler.priority = 128; | ||
82 | gpio_restart->active_delay_ms = 100; | ||
83 | gpio_restart->inactive_delay_ms = 100; | ||
84 | gpio_restart->wait_delay_ms = 3000; | ||
85 | |||
86 | ret = of_property_read_u32(pdev->dev.of_node, "priority", &property); | ||
87 | if (!ret) { | ||
88 | if (property > 255) | ||
89 | dev_err(&pdev->dev, "Invalid priority property: %u\n", | ||
90 | property); | ||
91 | else | ||
92 | gpio_restart->restart_handler.priority = property; | ||
93 | } | ||
94 | |||
95 | of_property_read_u32(pdev->dev.of_node, "active-delay", | ||
96 | &gpio_restart->active_delay_ms); | ||
97 | of_property_read_u32(pdev->dev.of_node, "inactive-delay", | ||
98 | &gpio_restart->inactive_delay_ms); | ||
99 | of_property_read_u32(pdev->dev.of_node, "wait-delay", | ||
100 | &gpio_restart->wait_delay_ms); | ||
101 | |||
102 | platform_set_drvdata(pdev, gpio_restart); | ||
103 | |||
104 | ret = register_restart_handler(&gpio_restart->restart_handler); | ||
105 | if (ret) { | ||
106 | dev_err(&pdev->dev, "%s: cannot register restart handler, %d\n", | ||
107 | __func__, ret); | ||
108 | return -ENODEV; | ||
109 | } | ||
110 | |||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | static int gpio_restart_remove(struct platform_device *pdev) | ||
115 | { | ||
116 | struct gpio_restart *gpio_restart = platform_get_drvdata(pdev); | ||
117 | int ret; | ||
118 | |||
119 | ret = unregister_restart_handler(&gpio_restart->restart_handler); | ||
120 | if (ret) { | ||
121 | dev_err(&pdev->dev, | ||
122 | "%s: cannot unregister restart handler, %d\n", | ||
123 | __func__, ret); | ||
124 | return -ENODEV; | ||
125 | } | ||
126 | |||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | static const struct of_device_id of_gpio_restart_match[] = { | ||
131 | { .compatible = "gpio-restart", }, | ||
132 | {}, | ||
133 | }; | ||
134 | |||
135 | static struct platform_driver gpio_restart_driver = { | ||
136 | .probe = gpio_restart_probe, | ||
137 | .remove = gpio_restart_remove, | ||
138 | .driver = { | ||
139 | .name = "restart-gpio", | ||
140 | .owner = THIS_MODULE, | ||
141 | .of_match_table = of_gpio_restart_match, | ||
142 | }, | ||
143 | }; | ||
144 | |||
145 | module_platform_driver(gpio_restart_driver); | ||
146 | |||
147 | MODULE_AUTHOR("David Riley <davidriley@chromium.org>"); | ||
148 | MODULE_DESCRIPTION("GPIO restart driver"); | ||
149 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/power/reset/ltc2952-poweroff.c b/drivers/power/reset/ltc2952-poweroff.c new file mode 100644 index 000000000000..116a1cef8f7b --- /dev/null +++ b/drivers/power/reset/ltc2952-poweroff.c | |||
@@ -0,0 +1,386 @@ | |||
1 | /* | ||
2 | * LTC2952 (PowerPath) driver | ||
3 | * | ||
4 | * Copyright (C) 2014, Xsens Technologies BV <info@xsens.com> | ||
5 | * Maintainer: René Moll <linux@r-moll.nl> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License | ||
9 | * as published by the Free Software Foundation; either version 2 | ||
10 | * of the License, or (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * ---------------------------------------- | ||
18 | * - Description | ||
19 | * ---------------------------------------- | ||
20 | * | ||
21 | * This driver is to be used with an external PowerPath Controller (LTC2952). | ||
22 | * Its function is to determine when a external shut down is triggered | ||
23 | * and react by properly shutting down the system. | ||
24 | * | ||
25 | * This driver expects a device tree with a ltc2952 entry for pin mapping. | ||
26 | * | ||
27 | * ---------------------------------------- | ||
28 | * - GPIO | ||
29 | * ---------------------------------------- | ||
30 | * | ||
31 | * The following GPIOs are used: | ||
32 | * - trigger (input) | ||
33 | * A level change indicates the shut-down trigger. If it's state reverts | ||
34 | * within the time-out defined by trigger_delay, the shut down is not | ||
35 | * executed. | ||
36 | * | ||
37 | * - watchdog (output) | ||
38 | * Once a shut down is triggered, the driver will toggle this signal, | ||
39 | * with an internal (wde_interval) to stall the hardware shut down. | ||
40 | * | ||
41 | * - kill (output) | ||
42 | * The last action during shut down is triggering this signalling, such | ||
43 | * that the PowerPath Control will power down the hardware. | ||
44 | * | ||
45 | * ---------------------------------------- | ||
46 | * - Interrupts | ||
47 | * ---------------------------------------- | ||
48 | * | ||
49 | * The driver requires a non-shared, edge-triggered interrupt on the trigger | ||
50 | * GPIO. | ||
51 | * | ||
52 | */ | ||
53 | |||
54 | #include <linux/kernel.h> | ||
55 | #include <linux/init.h> | ||
56 | #include <linux/interrupt.h> | ||
57 | #include <linux/device.h> | ||
58 | #include <linux/platform_device.h> | ||
59 | #include <linux/ktime.h> | ||
60 | #include <linux/slab.h> | ||
61 | #include <linux/kmod.h> | ||
62 | #include <linux/module.h> | ||
63 | #include <linux/gpio/consumer.h> | ||
64 | #include <linux/reboot.h> | ||
65 | |||
66 | struct ltc2952_poweroff_data { | ||
67 | struct hrtimer timer_trigger; | ||
68 | struct hrtimer timer_wde; | ||
69 | |||
70 | ktime_t trigger_delay; | ||
71 | ktime_t wde_interval; | ||
72 | |||
73 | struct device *dev; | ||
74 | |||
75 | unsigned int virq; | ||
76 | |||
77 | /** | ||
78 | * 0: trigger | ||
79 | * 1: watchdog | ||
80 | * 2: kill | ||
81 | */ | ||
82 | struct gpio_desc *gpio[3]; | ||
83 | }; | ||
84 | |||
85 | static int ltc2952_poweroff_panic; | ||
86 | static struct ltc2952_poweroff_data *ltc2952_data; | ||
87 | |||
88 | #define POWERPATH_IO_TRIGGER 0 | ||
89 | #define POWERPATH_IO_WATCHDOG 1 | ||
90 | #define POWERPATH_IO_KILL 2 | ||
91 | |||
92 | /** | ||
93 | * ltc2952_poweroff_timer_wde - Timer callback | ||
94 | * Toggles the watchdog reset signal each wde_interval | ||
95 | * | ||
96 | * @timer: corresponding timer | ||
97 | * | ||
98 | * Returns HRTIMER_RESTART for an infinite loop which will only stop when the | ||
99 | * machine actually shuts down | ||
100 | */ | ||
101 | static enum hrtimer_restart ltc2952_poweroff_timer_wde(struct hrtimer *timer) | ||
102 | { | ||
103 | ktime_t now; | ||
104 | int state; | ||
105 | unsigned long overruns; | ||
106 | |||
107 | if (ltc2952_poweroff_panic) | ||
108 | return HRTIMER_NORESTART; | ||
109 | |||
110 | state = gpiod_get_value(ltc2952_data->gpio[POWERPATH_IO_WATCHDOG]); | ||
111 | gpiod_set_value(ltc2952_data->gpio[POWERPATH_IO_WATCHDOG], !state); | ||
112 | |||
113 | now = hrtimer_cb_get_time(timer); | ||
114 | overruns = hrtimer_forward(timer, now, ltc2952_data->wde_interval); | ||
115 | |||
116 | return HRTIMER_RESTART; | ||
117 | } | ||
118 | |||
119 | static enum hrtimer_restart ltc2952_poweroff_timer_trigger( | ||
120 | struct hrtimer *timer) | ||
121 | { | ||
122 | int ret; | ||
123 | |||
124 | ret = hrtimer_start(<c2952_data->timer_wde, | ||
125 | ltc2952_data->wde_interval, HRTIMER_MODE_REL); | ||
126 | |||
127 | if (ret) { | ||
128 | dev_err(ltc2952_data->dev, "unable to start the timer\n"); | ||
129 | /* | ||
130 | * The device will not toggle the watchdog reset, | ||
131 | * thus shut down is only safe if the PowerPath controller | ||
132 | * has a long enough time-off before triggering a hardware | ||
133 | * power-off. | ||
134 | * | ||
135 | * Only sending a warning as the system will power-off anyway | ||
136 | */ | ||
137 | } | ||
138 | |||
139 | dev_info(ltc2952_data->dev, "executing shutdown\n"); | ||
140 | |||
141 | orderly_poweroff(true); | ||
142 | |||
143 | return HRTIMER_NORESTART; | ||
144 | } | ||
145 | |||
146 | /** | ||
147 | * ltc2952_poweroff_handler - Interrupt handler | ||
148 | * Triggered each time the trigger signal changes state and (de)activates a | ||
149 | * time-out (timer_trigger). Once the time-out is actually reached the shut | ||
150 | * down is executed. | ||
151 | * | ||
152 | * @irq: IRQ number | ||
153 | * @dev_id: pointer to the main data structure | ||
154 | */ | ||
155 | static irqreturn_t ltc2952_poweroff_handler(int irq, void *dev_id) | ||
156 | { | ||
157 | int ret; | ||
158 | struct ltc2952_poweroff_data *data = dev_id; | ||
159 | |||
160 | if (ltc2952_poweroff_panic) | ||
161 | goto irq_ok; | ||
162 | |||
163 | if (hrtimer_active(&data->timer_wde)) { | ||
164 | /* shutdown is already triggered, nothing to do any more */ | ||
165 | goto irq_ok; | ||
166 | } | ||
167 | |||
168 | if (!hrtimer_active(&data->timer_trigger)) { | ||
169 | ret = hrtimer_start(&data->timer_trigger, data->trigger_delay, | ||
170 | HRTIMER_MODE_REL); | ||
171 | |||
172 | if (ret) | ||
173 | dev_err(data->dev, "unable to start the wait timer\n"); | ||
174 | } else { | ||
175 | ret = hrtimer_cancel(&data->timer_trigger); | ||
176 | /* omitting return value check, timer should have been valid */ | ||
177 | } | ||
178 | |||
179 | irq_ok: | ||
180 | return IRQ_HANDLED; | ||
181 | } | ||
182 | |||
183 | static void ltc2952_poweroff_kill(void) | ||
184 | { | ||
185 | gpiod_set_value(ltc2952_data->gpio[POWERPATH_IO_KILL], 1); | ||
186 | } | ||
187 | |||
188 | static int ltc2952_poweroff_suspend(struct platform_device *pdev, | ||
189 | pm_message_t state) | ||
190 | { | ||
191 | return -ENOSYS; | ||
192 | } | ||
193 | |||
194 | static int ltc2952_poweroff_resume(struct platform_device *pdev) | ||
195 | { | ||
196 | return -ENOSYS; | ||
197 | } | ||
198 | |||
199 | static void ltc2952_poweroff_default(struct ltc2952_poweroff_data *data) | ||
200 | { | ||
201 | unsigned int i; | ||
202 | |||
203 | for (i = 0; i < ARRAY_SIZE(data->gpio); i++) | ||
204 | data->gpio[i] = NULL; | ||
205 | |||
206 | data->wde_interval = ktime_set(0, 300L*1E6L); | ||
207 | data->trigger_delay = ktime_set(2, 500L*1E6L); | ||
208 | |||
209 | hrtimer_init(&data->timer_trigger, CLOCK_MONOTONIC, HRTIMER_MODE_REL); | ||
210 | data->timer_trigger.function = <c2952_poweroff_timer_trigger; | ||
211 | |||
212 | hrtimer_init(&data->timer_wde, CLOCK_MONOTONIC, HRTIMER_MODE_REL); | ||
213 | data->timer_wde.function = <c2952_poweroff_timer_wde; | ||
214 | } | ||
215 | |||
216 | static int ltc2952_poweroff_init(struct platform_device *pdev) | ||
217 | { | ||
218 | int ret, virq; | ||
219 | unsigned int i; | ||
220 | struct ltc2952_poweroff_data *data; | ||
221 | |||
222 | static char *name[] = { | ||
223 | "trigger", | ||
224 | "watchdog", | ||
225 | "kill", | ||
226 | NULL | ||
227 | }; | ||
228 | |||
229 | data = ltc2952_data; | ||
230 | ltc2952_poweroff_default(ltc2952_data); | ||
231 | |||
232 | for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++) { | ||
233 | ltc2952_data->gpio[i] = gpiod_get(&pdev->dev, name[i]); | ||
234 | |||
235 | if (IS_ERR(ltc2952_data->gpio[i])) { | ||
236 | ret = PTR_ERR(ltc2952_data->gpio[i]); | ||
237 | dev_err(&pdev->dev, | ||
238 | "unable to claim the following gpio: %s\n", | ||
239 | name[i]); | ||
240 | goto err_io; | ||
241 | } | ||
242 | } | ||
243 | |||
244 | ret = gpiod_direction_output( | ||
245 | ltc2952_data->gpio[POWERPATH_IO_WATCHDOG], 0); | ||
246 | if (ret) { | ||
247 | dev_err(&pdev->dev, "unable to use watchdog-gpio as output\n"); | ||
248 | goto err_io; | ||
249 | } | ||
250 | |||
251 | ret = gpiod_direction_output(ltc2952_data->gpio[POWERPATH_IO_KILL], 0); | ||
252 | if (ret) { | ||
253 | dev_err(&pdev->dev, "unable to use kill-gpio as output\n"); | ||
254 | goto err_io; | ||
255 | } | ||
256 | |||
257 | virq = gpiod_to_irq(ltc2952_data->gpio[POWERPATH_IO_TRIGGER]); | ||
258 | if (virq < 0) { | ||
259 | dev_err(&pdev->dev, "cannot map GPIO as interrupt"); | ||
260 | goto err_io; | ||
261 | } | ||
262 | |||
263 | ltc2952_data->virq = virq; | ||
264 | ret = request_irq(virq, | ||
265 | ltc2952_poweroff_handler, | ||
266 | (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING), | ||
267 | "ltc2952-poweroff", | ||
268 | ltc2952_data | ||
269 | ); | ||
270 | |||
271 | if (ret) { | ||
272 | dev_err(&pdev->dev, "cannot configure an interrupt handler\n"); | ||
273 | goto err_io; | ||
274 | } | ||
275 | |||
276 | return 0; | ||
277 | |||
278 | err_io: | ||
279 | for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++) | ||
280 | if (ltc2952_data->gpio[i]) | ||
281 | gpiod_put(ltc2952_data->gpio[i]); | ||
282 | |||
283 | return ret; | ||
284 | } | ||
285 | |||
286 | static int ltc2952_poweroff_probe(struct platform_device *pdev) | ||
287 | { | ||
288 | int ret; | ||
289 | |||
290 | if (pm_power_off) { | ||
291 | dev_err(&pdev->dev, "pm_power_off already registered"); | ||
292 | return -EBUSY; | ||
293 | } | ||
294 | |||
295 | ltc2952_data = kzalloc(sizeof(*ltc2952_data), GFP_KERNEL); | ||
296 | if (!ltc2952_data) | ||
297 | return -ENOMEM; | ||
298 | |||
299 | ltc2952_data->dev = &pdev->dev; | ||
300 | |||
301 | ret = ltc2952_poweroff_init(pdev); | ||
302 | if (ret) | ||
303 | goto err; | ||
304 | |||
305 | pm_power_off = <c2952_poweroff_kill; | ||
306 | |||
307 | dev_info(&pdev->dev, "probe successful\n"); | ||
308 | |||
309 | return 0; | ||
310 | |||
311 | err: | ||
312 | kfree(ltc2952_data); | ||
313 | return ret; | ||
314 | } | ||
315 | |||
316 | static int ltc2952_poweroff_remove(struct platform_device *pdev) | ||
317 | { | ||
318 | unsigned int i; | ||
319 | |||
320 | pm_power_off = NULL; | ||
321 | |||
322 | if (ltc2952_data) { | ||
323 | free_irq(ltc2952_data->virq, ltc2952_data); | ||
324 | |||
325 | for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++) | ||
326 | gpiod_put(ltc2952_data->gpio[i]); | ||
327 | |||
328 | kfree(ltc2952_data); | ||
329 | } | ||
330 | |||
331 | return 0; | ||
332 | } | ||
333 | |||
334 | static const struct of_device_id of_ltc2952_poweroff_match[] = { | ||
335 | { .compatible = "lltc,ltc2952"}, | ||
336 | {}, | ||
337 | }; | ||
338 | MODULE_DEVICE_TABLE(of, of_ltc2952_poweroff_match); | ||
339 | |||
340 | static struct platform_driver ltc2952_poweroff_driver = { | ||
341 | .probe = ltc2952_poweroff_probe, | ||
342 | .remove = ltc2952_poweroff_remove, | ||
343 | .driver = { | ||
344 | .name = "ltc2952-poweroff", | ||
345 | .owner = THIS_MODULE, | ||
346 | .of_match_table = of_ltc2952_poweroff_match, | ||
347 | }, | ||
348 | .suspend = ltc2952_poweroff_suspend, | ||
349 | .resume = ltc2952_poweroff_resume, | ||
350 | }; | ||
351 | |||
352 | static int ltc2952_poweroff_notify_panic(struct notifier_block *nb, | ||
353 | unsigned long code, void *unused) | ||
354 | { | ||
355 | ltc2952_poweroff_panic = 1; | ||
356 | return NOTIFY_DONE; | ||
357 | } | ||
358 | |||
359 | static struct notifier_block ltc2952_poweroff_panic_nb = { | ||
360 | .notifier_call = ltc2952_poweroff_notify_panic, | ||
361 | }; | ||
362 | |||
363 | static int __init ltc2952_poweroff_platform_init(void) | ||
364 | { | ||
365 | ltc2952_poweroff_panic = 0; | ||
366 | |||
367 | atomic_notifier_chain_register(&panic_notifier_list, | ||
368 | <c2952_poweroff_panic_nb); | ||
369 | |||
370 | return platform_driver_register(<c2952_poweroff_driver); | ||
371 | } | ||
372 | |||
373 | static void __exit ltc2952_poweroff_platform_exit(void) | ||
374 | { | ||
375 | atomic_notifier_chain_unregister(&panic_notifier_list, | ||
376 | <c2952_poweroff_panic_nb); | ||
377 | |||
378 | platform_driver_unregister(<c2952_poweroff_driver); | ||
379 | } | ||
380 | |||
381 | module_init(ltc2952_poweroff_platform_init); | ||
382 | module_exit(ltc2952_poweroff_platform_exit); | ||
383 | |||
384 | MODULE_AUTHOR("René Moll <rene.moll@xsens.com>"); | ||
385 | MODULE_DESCRIPTION("LTC PowerPath power-off driver"); | ||
386 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c index 774f9a3b310d..4702efdfe466 100644 --- a/drivers/power/reset/msm-poweroff.c +++ b/drivers/power/reset/msm-poweroff.c | |||
@@ -20,21 +20,27 @@ | |||
20 | #include <linux/platform_device.h> | 20 | #include <linux/platform_device.h> |
21 | #include <linux/module.h> | 21 | #include <linux/module.h> |
22 | #include <linux/reboot.h> | 22 | #include <linux/reboot.h> |
23 | 23 | #include <linux/pm.h> | |
24 | #include <asm/system_misc.h> | ||
25 | 24 | ||
26 | static void __iomem *msm_ps_hold; | 25 | static void __iomem *msm_ps_hold; |
27 | 26 | static int do_msm_restart(struct notifier_block *nb, unsigned long action, | |
28 | static void do_msm_restart(enum reboot_mode reboot_mode, const char *cmd) | 27 | void *data) |
29 | { | 28 | { |
30 | writel(0, msm_ps_hold); | 29 | writel(0, msm_ps_hold); |
31 | mdelay(10000); | 30 | mdelay(10000); |
31 | |||
32 | return NOTIFY_DONE; | ||
32 | } | 33 | } |
33 | 34 | ||
35 | static struct notifier_block restart_nb = { | ||
36 | .notifier_call = do_msm_restart, | ||
37 | .priority = 128, | ||
38 | }; | ||
39 | |||
34 | static void do_msm_poweroff(void) | 40 | static void do_msm_poweroff(void) |
35 | { | 41 | { |
36 | /* TODO: Add poweroff capability */ | 42 | /* TODO: Add poweroff capability */ |
37 | do_msm_restart(REBOOT_HARD, NULL); | 43 | do_msm_restart(&restart_nb, 0, NULL); |
38 | } | 44 | } |
39 | 45 | ||
40 | static int msm_restart_probe(struct platform_device *pdev) | 46 | static int msm_restart_probe(struct platform_device *pdev) |
@@ -47,8 +53,10 @@ static int msm_restart_probe(struct platform_device *pdev) | |||
47 | if (IS_ERR(msm_ps_hold)) | 53 | if (IS_ERR(msm_ps_hold)) |
48 | return PTR_ERR(msm_ps_hold); | 54 | return PTR_ERR(msm_ps_hold); |
49 | 55 | ||
56 | register_restart_handler(&restart_nb); | ||
57 | |||
50 | pm_power_off = do_msm_poweroff; | 58 | pm_power_off = do_msm_poweroff; |
51 | arm_pm_restart = do_msm_restart; | 59 | |
52 | return 0; | 60 | return 0; |
53 | } | 61 | } |
54 | 62 | ||
diff --git a/drivers/power/reset/st-poweroff.c b/drivers/power/reset/st-poweroff.c new file mode 100644 index 000000000000..a0acf25ee2a2 --- /dev/null +++ b/drivers/power/reset/st-poweroff.c | |||
@@ -0,0 +1,151 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014 STMicroelectronics | ||
3 | * | ||
4 | * Power off Restart driver, used in STMicroelectronics devices. | ||
5 | * | ||
6 | * Author: Christophe Kerello <christophe.kerello@st.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2, as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/of.h> | ||
15 | #include <linux/of_platform.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | #include <linux/mfd/syscon.h> | ||
18 | #include <linux/regmap.h> | ||
19 | |||
20 | #include <asm/system_misc.h> | ||
21 | |||
22 | struct reset_syscfg { | ||
23 | struct regmap *regmap; | ||
24 | /* syscfg used for reset */ | ||
25 | unsigned int offset_rst; | ||
26 | unsigned int mask_rst; | ||
27 | /* syscfg used for unmask the reset */ | ||
28 | unsigned int offset_rst_msk; | ||
29 | unsigned int mask_rst_msk; | ||
30 | }; | ||
31 | |||
32 | /* STiH415 */ | ||
33 | #define STIH415_SYSCFG_11 0x2c | ||
34 | #define STIH415_SYSCFG_15 0x3c | ||
35 | |||
36 | static struct reset_syscfg stih415_reset = { | ||
37 | .offset_rst = STIH415_SYSCFG_11, | ||
38 | .mask_rst = BIT(0), | ||
39 | .offset_rst_msk = STIH415_SYSCFG_15, | ||
40 | .mask_rst_msk = BIT(0) | ||
41 | }; | ||
42 | |||
43 | /* STiH416 */ | ||
44 | #define STIH416_SYSCFG_500 0x7d0 | ||
45 | #define STIH416_SYSCFG_504 0x7e0 | ||
46 | |||
47 | static struct reset_syscfg stih416_reset = { | ||
48 | .offset_rst = STIH416_SYSCFG_500, | ||
49 | .mask_rst = BIT(0), | ||
50 | .offset_rst_msk = STIH416_SYSCFG_504, | ||
51 | .mask_rst_msk = BIT(0) | ||
52 | }; | ||
53 | |||
54 | /* STiH407 */ | ||
55 | #define STIH407_SYSCFG_4000 0x0 | ||
56 | #define STIH407_SYSCFG_4008 0x20 | ||
57 | |||
58 | static struct reset_syscfg stih407_reset = { | ||
59 | .offset_rst = STIH407_SYSCFG_4000, | ||
60 | .mask_rst = BIT(0), | ||
61 | .offset_rst_msk = STIH407_SYSCFG_4008, | ||
62 | .mask_rst_msk = BIT(0) | ||
63 | }; | ||
64 | |||
65 | /* STiD127 */ | ||
66 | #define STID127_SYSCFG_700 0x0 | ||
67 | #define STID127_SYSCFG_773 0x124 | ||
68 | |||
69 | static struct reset_syscfg stid127_reset = { | ||
70 | .offset_rst = STID127_SYSCFG_773, | ||
71 | .mask_rst = BIT(0), | ||
72 | .offset_rst_msk = STID127_SYSCFG_700, | ||
73 | .mask_rst_msk = BIT(8) | ||
74 | }; | ||
75 | |||
76 | static struct reset_syscfg *st_restart_syscfg; | ||
77 | |||
78 | static void st_restart(enum reboot_mode reboot_mode, const char *cmd) | ||
79 | { | ||
80 | /* reset syscfg updated */ | ||
81 | regmap_update_bits(st_restart_syscfg->regmap, | ||
82 | st_restart_syscfg->offset_rst, | ||
83 | st_restart_syscfg->mask_rst, | ||
84 | 0); | ||
85 | |||
86 | /* unmask the reset */ | ||
87 | regmap_update_bits(st_restart_syscfg->regmap, | ||
88 | st_restart_syscfg->offset_rst_msk, | ||
89 | st_restart_syscfg->mask_rst_msk, | ||
90 | 0); | ||
91 | } | ||
92 | |||
93 | static struct of_device_id st_reset_of_match[] = { | ||
94 | { | ||
95 | .compatible = "st,stih415-restart", | ||
96 | .data = (void *)&stih415_reset, | ||
97 | }, { | ||
98 | .compatible = "st,stih416-restart", | ||
99 | .data = (void *)&stih416_reset, | ||
100 | }, { | ||
101 | .compatible = "st,stih407-restart", | ||
102 | .data = (void *)&stih407_reset, | ||
103 | }, { | ||
104 | .compatible = "st,stid127-restart", | ||
105 | .data = (void *)&stid127_reset, | ||
106 | }, | ||
107 | {} | ||
108 | }; | ||
109 | |||
110 | static int st_reset_probe(struct platform_device *pdev) | ||
111 | { | ||
112 | struct device_node *np = pdev->dev.of_node; | ||
113 | const struct of_device_id *match; | ||
114 | struct device *dev = &pdev->dev; | ||
115 | |||
116 | match = of_match_device(st_reset_of_match, dev); | ||
117 | if (!match) | ||
118 | return -ENODEV; | ||
119 | |||
120 | st_restart_syscfg = (struct reset_syscfg *)match->data; | ||
121 | |||
122 | st_restart_syscfg->regmap = | ||
123 | syscon_regmap_lookup_by_phandle(np, "st,syscfg"); | ||
124 | if (IS_ERR(st_restart_syscfg->regmap)) { | ||
125 | dev_err(dev, "No syscfg phandle specified\n"); | ||
126 | return PTR_ERR(st_restart_syscfg->regmap); | ||
127 | } | ||
128 | |||
129 | arm_pm_restart = st_restart; | ||
130 | |||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | static struct platform_driver st_reset_driver = { | ||
135 | .probe = st_reset_probe, | ||
136 | .driver = { | ||
137 | .name = "st_reset", | ||
138 | .of_match_table = st_reset_of_match, | ||
139 | }, | ||
140 | }; | ||
141 | |||
142 | static int __init st_reset_init(void) | ||
143 | { | ||
144 | return platform_driver_register(&st_reset_driver); | ||
145 | } | ||
146 | |||
147 | device_initcall(st_reset_init); | ||
148 | |||
149 | MODULE_AUTHOR("Christophe Kerello <christophe.kerello@st.com>"); | ||
150 | MODULE_DESCRIPTION("STMicroelectronics Power off Restart driver"); | ||
151 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/power/reset/syscon-reboot.c b/drivers/power/reset/syscon-reboot.c new file mode 100644 index 000000000000..815b901822cf --- /dev/null +++ b/drivers/power/reset/syscon-reboot.c | |||
@@ -0,0 +1,91 @@ | |||
1 | /* | ||
2 | * Generic Syscon Reboot Driver | ||
3 | * | ||
4 | * Copyright (c) 2013, Applied Micro Circuits Corporation | ||
5 | * Author: Feng Kan <fkan@apm.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License as | ||
9 | * published by the Free Software Foundation; either version 2 of | ||
10 | * the License, or (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | */ | ||
17 | #include <linux/delay.h> | ||
18 | #include <linux/io.h> | ||
19 | #include <linux/notifier.h> | ||
20 | #include <linux/mfd/syscon.h> | ||
21 | #include <linux/of_address.h> | ||
22 | #include <linux/of_device.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | #include <linux/reboot.h> | ||
25 | #include <linux/regmap.h> | ||
26 | |||
27 | struct syscon_reboot_context { | ||
28 | struct regmap *map; | ||
29 | u32 offset; | ||
30 | u32 mask; | ||
31 | struct notifier_block restart_handler; | ||
32 | }; | ||
33 | |||
34 | static int syscon_restart_handle(struct notifier_block *this, | ||
35 | unsigned long mode, void *cmd) | ||
36 | { | ||
37 | struct syscon_reboot_context *ctx = | ||
38 | container_of(this, struct syscon_reboot_context, | ||
39 | restart_handler); | ||
40 | |||
41 | /* Issue the reboot */ | ||
42 | regmap_write(ctx->map, ctx->offset, ctx->mask); | ||
43 | |||
44 | mdelay(1000); | ||
45 | |||
46 | pr_emerg("Unable to restart system\n"); | ||
47 | return NOTIFY_DONE; | ||
48 | } | ||
49 | |||
50 | static int syscon_reboot_probe(struct platform_device *pdev) | ||
51 | { | ||
52 | struct syscon_reboot_context *ctx; | ||
53 | struct device *dev = &pdev->dev; | ||
54 | int err; | ||
55 | |||
56 | ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); | ||
57 | if (!ctx) | ||
58 | return -ENOMEM; | ||
59 | |||
60 | ctx->map = syscon_regmap_lookup_by_phandle(dev->of_node, "regmap"); | ||
61 | if (IS_ERR(ctx->map)) | ||
62 | return PTR_ERR(ctx->map); | ||
63 | |||
64 | if (of_property_read_u32(pdev->dev.of_node, "offset", &ctx->offset)) | ||
65 | return -EINVAL; | ||
66 | |||
67 | if (of_property_read_u32(pdev->dev.of_node, "mask", &ctx->mask)) | ||
68 | return -EINVAL; | ||
69 | |||
70 | ctx->restart_handler.notifier_call = syscon_restart_handle; | ||
71 | ctx->restart_handler.priority = 128; | ||
72 | err = register_restart_handler(&ctx->restart_handler); | ||
73 | if (err) | ||
74 | dev_err(dev, "can't register restart notifier (err=%d)\n", err); | ||
75 | |||
76 | return err; | ||
77 | } | ||
78 | |||
79 | static struct of_device_id syscon_reboot_of_match[] = { | ||
80 | { .compatible = "syscon-reboot" }, | ||
81 | {} | ||
82 | }; | ||
83 | |||
84 | static struct platform_driver syscon_reboot_driver = { | ||
85 | .probe = syscon_reboot_probe, | ||
86 | .driver = { | ||
87 | .name = "syscon-reboot", | ||
88 | .of_match_table = syscon_reboot_of_match, | ||
89 | }, | ||
90 | }; | ||
91 | module_platform_driver(syscon_reboot_driver); | ||
diff --git a/drivers/power/reset/xgene-reboot.c b/drivers/power/reset/xgene-reboot.c index ecd55f81b9d1..6b49be6867ab 100644 --- a/drivers/power/reset/xgene-reboot.c +++ b/drivers/power/reset/xgene-reboot.c | |||
@@ -40,7 +40,7 @@ struct xgene_reboot_context { | |||
40 | 40 | ||
41 | static struct xgene_reboot_context *xgene_restart_ctx; | 41 | static struct xgene_reboot_context *xgene_restart_ctx; |
42 | 42 | ||
43 | static void xgene_restart(char str, const char *cmd) | 43 | static void xgene_restart(enum reboot_mode mode, const char *cmd) |
44 | { | 44 | { |
45 | struct xgene_reboot_context *ctx = xgene_restart_ctx; | 45 | struct xgene_reboot_context *ctx = xgene_restart_ctx; |
46 | unsigned long timeout; | 46 | unsigned long timeout; |