summaryrefslogtreecommitdiff
path: root/drivers/power/reset
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/power/reset')
-rw-r--r--drivers/power/reset/Kconfig26
-rw-r--r--drivers/power/reset/Makefile2
-rw-r--r--drivers/power/reset/arm-versatile-reboot.c49
-rw-r--r--drivers/power/reset/at91-poweroff.c7
-rw-r--r--drivers/power/reset/at91-reset.c28
-rw-r--r--drivers/power/reset/brcmstb-reboot.c41
-rw-r--r--drivers/power/reset/ltc2952-poweroff.c290
-rw-r--r--drivers/power/reset/restart-poweroff.c1
-rw-r--r--drivers/power/reset/rmobile-reset.c91
-rw-r--r--drivers/power/reset/st-poweroff.c17
-rw-r--r--drivers/power/reset/sun6i-reboot.c85
-rw-r--r--drivers/power/reset/vexpress-poweroff.c17
12 files changed, 338 insertions, 316 deletions
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
index 028e76504519..27f6646731b0 100644
--- a/drivers/power/reset/Kconfig
+++ b/drivers/power/reset/Kconfig
@@ -39,14 +39,13 @@ config POWER_RESET_AXXIA
Say Y if you have an Axxia family SoC.
config POWER_RESET_BRCMSTB
- bool "Broadcom STB reset driver" if COMPILE_TEST
- depends on ARM
+ bool "Broadcom STB reset driver"
+ depends on ARM || MIPS || COMPILE_TEST
default ARCH_BRCMSTB
help
- This driver provides restart support for ARM-based Broadcom STB
- boards.
+ This driver provides restart support for Broadcom STB boards.
- Say Y here if you have an ARM-based Broadcom STB board and you wish
+ Say Y here if you have a Broadcom STB board and you wish
to have restart support.
config POWER_RESET_GPIO
@@ -104,23 +103,16 @@ config POWER_RESET_QNAP
config POWER_RESET_RESTART
bool "Restart power-off driver"
- depends on ARM
help
Some boards don't actually have the ability to power off.
Instead they restart, and u-boot holds the SoC until the
user presses a key. u-boot then boots into Linux.
-config POWER_RESET_SUN6I
- bool "Allwinner A31 SoC reset driver"
- depends on ARCH_SUNXI
- help
- Reboot support for the Allwinner A31 SoCs.
-
config POWER_RESET_ST
- bool "ST restart power-off driver"
+ bool "ST restart driver"
depends on ARCH_STI
help
- Power off and reset support for STMicroelectronics boards.
+ Reset support for STMicroelectronics boards.
config POWER_RESET_VERSATILE
bool "ARM Versatile family reboot driver"
@@ -159,5 +151,11 @@ config POWER_RESET_SYSCON
help
Reboot support for generic SYSCON mapped register reset.
+config POWER_RESET_RMOBILE
+ tristate "Renesas R-Mobile reset driver"
+ depends on ARCH_RMOBILE || COMPILE_TEST
+ help
+ Reboot support for Renesas R-Mobile and SH-Mobile SoCs.
+
endif
diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile
index 1d4804d6b323..11de15bae52e 100644
--- a/drivers/power/reset/Makefile
+++ b/drivers/power/reset/Makefile
@@ -11,10 +11,10 @@ obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o
obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o
obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o
obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o
-obj-$(CONFIG_POWER_RESET_SUN6I) += sun6i-reboot.o
obj-$(CONFIG_POWER_RESET_ST) += st-poweroff.o
obj-$(CONFIG_POWER_RESET_VERSATILE) += arm-versatile-reboot.o
obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o
obj-$(CONFIG_POWER_RESET_XGENE) += xgene-reboot.o
obj-$(CONFIG_POWER_RESET_KEYSTONE) += keystone-reset.o
obj-$(CONFIG_POWER_RESET_SYSCON) += syscon-reboot.o
+obj-$(CONFIG_POWER_RESET_RMOBILE) += rmobile-reset.o
diff --git a/drivers/power/reset/arm-versatile-reboot.c b/drivers/power/reset/arm-versatile-reboot.c
index 5b08bffcf1a8..b208073c887d 100644
--- a/drivers/power/reset/arm-versatile-reboot.c
+++ b/drivers/power/reset/arm-versatile-reboot.c
@@ -13,16 +13,22 @@
#include <linux/reboot.h>
#include <linux/regmap.h>
#include <linux/of.h>
-#include <asm/system_misc.h>
+
+#define INTEGRATOR_HDR_CTRL_OFFSET 0x0C
+#define INTEGRATOR_HDR_LOCK_OFFSET 0x14
+#define INTEGRATOR_CM_CTRL_RESET (1 << 3)
#define REALVIEW_SYS_LOCK_OFFSET 0x20
-#define REALVIEW_SYS_LOCK_VAL 0xA05F
#define REALVIEW_SYS_RESETCTL_OFFSET 0x40
+/* Magic unlocking token used on all Versatile boards */
+#define VERSATILE_LOCK_VAL 0xA05F
+
/*
* We detect the different syscon types from the compatible strings.
*/
enum versatile_reboot {
+ INTEGRATOR_REBOOT_CM,
REALVIEW_REBOOT_EB,
REALVIEW_REBOOT_PB1176,
REALVIEW_REBOOT_PB11MP,
@@ -36,6 +42,10 @@ static enum versatile_reboot versatile_reboot_type;
static const struct of_device_id versatile_reboot_of_match[] = {
{
+ .compatible = "arm,core-module-integrator",
+ .data = (void *)INTEGRATOR_REBOOT_CM
+ },
+ {
.compatible = "arm,realview-eb-syscon",
.data = (void *)REALVIEW_REBOOT_EB,
},
@@ -55,31 +65,47 @@ static const struct of_device_id versatile_reboot_of_match[] = {
.compatible = "arm,realview-pbx-syscon",
.data = (void *)REALVIEW_REBOOT_PBX,
},
+ {},
};
-static void versatile_reboot(enum reboot_mode mode, const char *cmd)
+static int versatile_reboot(struct notifier_block *this, unsigned long mode,
+ void *cmd)
{
/* Unlock the reset register */
- regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET,
- REALVIEW_SYS_LOCK_VAL);
/* Then hit reset on the different machines */
switch (versatile_reboot_type) {
+ case INTEGRATOR_REBOOT_CM:
+ regmap_write(syscon_regmap, INTEGRATOR_HDR_LOCK_OFFSET,
+ VERSATILE_LOCK_VAL);
+ regmap_update_bits(syscon_regmap,
+ INTEGRATOR_HDR_CTRL_OFFSET,
+ INTEGRATOR_CM_CTRL_RESET,
+ INTEGRATOR_CM_CTRL_RESET);
+ break;
case REALVIEW_REBOOT_EB:
+ regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET,
+ VERSATILE_LOCK_VAL);
regmap_write(syscon_regmap,
REALVIEW_SYS_RESETCTL_OFFSET, 0x0008);
break;
case REALVIEW_REBOOT_PB1176:
+ regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET,
+ VERSATILE_LOCK_VAL);
regmap_write(syscon_regmap,
REALVIEW_SYS_RESETCTL_OFFSET, 0x0100);
break;
case REALVIEW_REBOOT_PB11MP:
case REALVIEW_REBOOT_PBA8:
+ regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET,
+ VERSATILE_LOCK_VAL);
regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET,
0x0000);
regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET,
0x0004);
break;
case REALVIEW_REBOOT_PBX:
+ regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET,
+ VERSATILE_LOCK_VAL);
regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET,
0x00f0);
regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET,
@@ -87,12 +113,20 @@ static void versatile_reboot(enum reboot_mode mode, const char *cmd)
break;
}
dsb();
+
+ return NOTIFY_DONE;
}
+static struct notifier_block versatile_reboot_nb = {
+ .notifier_call = versatile_reboot,
+ .priority = 192,
+};
+
static int __init versatile_reboot_probe(void)
{
const struct of_device_id *reboot_id;
struct device_node *np;
+ int err;
np = of_find_matching_node_and_match(NULL, versatile_reboot_of_match,
&reboot_id);
@@ -104,7 +138,10 @@ static int __init versatile_reboot_probe(void)
if (IS_ERR(syscon_regmap))
return PTR_ERR(syscon_regmap);
- arm_pm_restart = versatile_reboot;
+ err = register_restart_handler(&versatile_reboot_nb);
+ if (err)
+ return err;
+
pr_info("versatile reboot driver registered\n");
return 0;
}
diff --git a/drivers/power/reset/at91-poweroff.c b/drivers/power/reset/at91-poweroff.c
index c61000333bb9..4b72ea51c364 100644
--- a/drivers/power/reset/at91-poweroff.c
+++ b/drivers/power/reset/at91-poweroff.c
@@ -71,10 +71,11 @@ static void at91_poweroff(void)
writel(AT91_SHDW_KEY | AT91_SHDW_SHDW, at91_shdwc_base + AT91_SHDW_CR);
}
-const enum wakeup_type at91_poweroff_get_wakeup_mode(struct device_node *np)
+static int at91_poweroff_get_wakeup_mode(struct device_node *np)
{
const char *pm;
- int err, i;
+ unsigned int i;
+ int err;
err = of_property_read_string(np, "atmel,wakeup-mode", &pm);
if (err < 0)
@@ -90,7 +91,7 @@ const enum wakeup_type at91_poweroff_get_wakeup_mode(struct device_node *np)
static void at91_poweroff_dt_set_wakeup_mode(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
- enum wakeup_type wakeup_mode;
+ int wakeup_mode;
u32 mode = 0, tmp;
wakeup_mode = at91_poweroff_get_wakeup_mode(np);
diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c
index 69a75d99ae92..13584e24736a 100644
--- a/drivers/power/reset/at91-reset.c
+++ b/drivers/power/reset/at91-reset.c
@@ -17,8 +17,6 @@
#include <linux/platform_device.h>
#include <linux/reboot.h>
-#include <asm/system_misc.h>
-
#include <soc/at91/at91sam9_ddrsdr.h>
#include <soc/at91/at91sam9_sdramc.h>
@@ -54,7 +52,8 @@ static void __iomem *at91_ramc_base[2], *at91_rstc_base;
* reset register it can be left driving the data bus and
* killing the chance of a subsequent boot from NAND
*/
-static void at91sam9260_restart(enum reboot_mode mode, const char *cmd)
+static int at91sam9260_restart(struct notifier_block *this, unsigned long mode,
+ void *cmd)
{
asm volatile(
/* Align to cache lines */
@@ -76,9 +75,12 @@ static void at91sam9260_restart(enum reboot_mode mode, const char *cmd)
"r" (1),
"r" (AT91_SDRAMC_LPCB_POWER_DOWN),
"r" (AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST));
+
+ return NOTIFY_DONE;
}
-static void at91sam9g45_restart(enum reboot_mode mode, const char *cmd)
+static int at91sam9g45_restart(struct notifier_block *this, unsigned long mode,
+ void *cmd)
{
asm volatile(
/*
@@ -117,6 +119,8 @@ static void at91sam9g45_restart(enum reboot_mode mode, const char *cmd)
"r" (AT91_DDRSDRC_LPCB_POWER_DOWN),
"r" (AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST)
: "r0");
+
+ return NOTIFY_DONE;
}
static void __init at91_reset_status(struct platform_device *pdev)
@@ -161,6 +165,10 @@ static struct of_device_id at91_reset_of_match[] = {
{ /* sentinel */ }
};
+static struct notifier_block at91_restart_nb = {
+ .priority = 192,
+};
+
static int at91_reset_of_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
@@ -183,9 +191,8 @@ static int at91_reset_of_probe(struct platform_device *pdev)
}
match = of_match_node(at91_reset_of_match, pdev->dev.of_node);
- arm_pm_restart = match->data;
-
- return 0;
+ at91_restart_nb.notifier_call = match->data;
+ return register_restart_handler(&at91_restart_nb);
}
static int at91_reset_platform_probe(struct platform_device *pdev)
@@ -212,10 +219,11 @@ static int at91_reset_platform_probe(struct platform_device *pdev)
}
match = platform_get_device_id(pdev);
- arm_pm_restart = (void (*)(enum reboot_mode, const char*))
- match->driver_data;
+ at91_restart_nb.notifier_call =
+ (int (*)(struct notifier_block *,
+ unsigned long, void *)) match->driver_data;
- return 0;
+ return register_restart_handler(&at91_restart_nb);
}
static int at91_reset_probe(struct platform_device *pdev)
diff --git a/drivers/power/reset/brcmstb-reboot.c b/drivers/power/reset/brcmstb-reboot.c
index 100606f9b3dc..884b53c483c0 100644
--- a/drivers/power/reset/brcmstb-reboot.c
+++ b/drivers/power/reset/brcmstb-reboot.c
@@ -11,6 +11,7 @@
* GNU General Public License for more details.
*/
+#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/init.h>
@@ -34,13 +35,20 @@ static struct regmap *regmap;
static u32 rst_src_en;
static u32 sw_mstr_rst;
+struct reset_reg_mask {
+ u32 rst_src_en_mask;
+ u32 sw_mstr_rst_mask;
+};
+
+static const struct reset_reg_mask *reset_masks;
+
static int brcmstb_restart_handler(struct notifier_block *this,
unsigned long mode, void *cmd)
{
int rc;
u32 tmp;
- rc = regmap_write(regmap, rst_src_en, 1);
+ rc = regmap_write(regmap, rst_src_en, reset_masks->rst_src_en_mask);
if (rc) {
pr_err("failed to write rst_src_en (%d)\n", rc);
return NOTIFY_DONE;
@@ -52,7 +60,7 @@ static int brcmstb_restart_handler(struct notifier_block *this,
return NOTIFY_DONE;
}
- rc = regmap_write(regmap, sw_mstr_rst, 1);
+ rc = regmap_write(regmap, sw_mstr_rst, reset_masks->sw_mstr_rst_mask);
if (rc) {
pr_err("failed to write sw_mstr_rst (%d)\n", rc);
return NOTIFY_DONE;
@@ -75,10 +83,34 @@ static struct notifier_block brcmstb_restart_nb = {
.priority = 128,
};
+static const struct reset_reg_mask reset_bits_40nm = {
+ .rst_src_en_mask = BIT(0),
+ .sw_mstr_rst_mask = BIT(0),
+};
+
+static const struct reset_reg_mask reset_bits_65nm = {
+ .rst_src_en_mask = BIT(3),
+ .sw_mstr_rst_mask = BIT(31),
+};
+
+static const struct of_device_id of_match[] = {
+ { .compatible = "brcm,brcmstb-reboot", .data = &reset_bits_40nm },
+ { .compatible = "brcm,bcm7038-reboot", .data = &reset_bits_65nm },
+ {},
+};
+
static int brcmstb_reboot_probe(struct platform_device *pdev)
{
int rc;
struct device_node *np = pdev->dev.of_node;
+ const struct of_device_id *of_id;
+
+ of_id = of_match_node(of_match, np);
+ if (!of_id) {
+ pr_err("failed to look up compatible string\n");
+ return -EINVAL;
+ }
+ reset_masks = of_id->data;
regmap = syscon_regmap_lookup_by_phandle(np, "syscon");
if (IS_ERR(regmap)) {
@@ -108,11 +140,6 @@ static int brcmstb_reboot_probe(struct platform_device *pdev)
return rc;
}
-static const struct of_device_id of_match[] = {
- { .compatible = "brcm,brcmstb-reboot", },
- {},
-};
-
static struct platform_driver brcmstb_reboot_driver = {
.probe = brcmstb_reboot_probe,
.driver = {
diff --git a/drivers/power/reset/ltc2952-poweroff.c b/drivers/power/reset/ltc2952-poweroff.c
index 34f38a3dc3ff..7ef193b6f7fe 100644
--- a/drivers/power/reset/ltc2952-poweroff.c
+++ b/drivers/power/reset/ltc2952-poweroff.c
@@ -32,7 +32,9 @@
* - trigger (input)
* A level change indicates the shut-down trigger. If it's state reverts
* within the time-out defined by trigger_delay, the shut down is not
- * executed.
+ * executed. If no pin is assigned to this input, the driver will start the
+ * watchdog toggle immediately. The chip will only power off the system if
+ * it is requested to do so through the kill line.
*
* - watchdog (output)
* Once a shut down is triggered, the driver will toggle this signal,
@@ -63,7 +65,7 @@
#include <linux/gpio/consumer.h>
#include <linux/reboot.h>
-struct ltc2952_poweroff_data {
+struct ltc2952_poweroff {
struct hrtimer timer_trigger;
struct hrtimer timer_wde;
@@ -72,22 +74,21 @@ struct ltc2952_poweroff_data {
struct device *dev;
- unsigned int virq;
+ struct gpio_desc *gpio_trigger;
+ struct gpio_desc *gpio_watchdog;
+ struct gpio_desc *gpio_kill;
- /**
- * 0: trigger
- * 1: watchdog
- * 2: kill
- */
- struct gpio_desc *gpio[3];
+ bool kernel_panic;
+ struct notifier_block panic_notifier;
};
-static int ltc2952_poweroff_panic;
-static struct ltc2952_poweroff_data *ltc2952_data;
+#define to_ltc2952(p, m) container_of(p, struct ltc2952_poweroff, m)
-#define POWERPATH_IO_TRIGGER 0
-#define POWERPATH_IO_WATCHDOG 1
-#define POWERPATH_IO_KILL 2
+/*
+ * This global variable is only needed for pm_power_off. We should
+ * remove it entirely once we don't need the global state anymore.
+ */
+static struct ltc2952_poweroff *ltc2952_data;
/**
* ltc2952_poweroff_timer_wde - Timer callback
@@ -103,29 +104,24 @@ static enum hrtimer_restart ltc2952_poweroff_timer_wde(struct hrtimer *timer)
ktime_t now;
int state;
unsigned long overruns;
+ struct ltc2952_poweroff *data = to_ltc2952(timer, timer_wde);
- if (ltc2952_poweroff_panic)
+ if (data->kernel_panic)
return HRTIMER_NORESTART;
- state = gpiod_get_value(ltc2952_data->gpio[POWERPATH_IO_WATCHDOG]);
- gpiod_set_value(ltc2952_data->gpio[POWERPATH_IO_WATCHDOG], !state);
+ state = gpiod_get_value(data->gpio_watchdog);
+ gpiod_set_value(data->gpio_watchdog, !state);
now = hrtimer_cb_get_time(timer);
- overruns = hrtimer_forward(timer, now, ltc2952_data->wde_interval);
+ overruns = hrtimer_forward(timer, now, data->wde_interval);
return HRTIMER_RESTART;
}
-static enum hrtimer_restart ltc2952_poweroff_timer_trigger(
- struct hrtimer *timer)
+static void ltc2952_poweroff_start_wde(struct ltc2952_poweroff *data)
{
- int ret;
-
- ret = hrtimer_start(&ltc2952_data->timer_wde,
- ltc2952_data->wde_interval, HRTIMER_MODE_REL);
-
- if (ret) {
- dev_err(ltc2952_data->dev, "unable to start the timer\n");
+ if (hrtimer_start(&data->timer_wde, data->wde_interval,
+ HRTIMER_MODE_REL)) {
/*
* The device will not toggle the watchdog reset,
* thus shut down is only safe if the PowerPath controller
@@ -134,10 +130,17 @@ static enum hrtimer_restart ltc2952_poweroff_timer_trigger(
*
* Only sending a warning as the system will power-off anyway
*/
+ dev_err(data->dev, "unable to start the timer\n");
}
+}
- dev_info(ltc2952_data->dev, "executing shutdown\n");
+static enum hrtimer_restart
+ltc2952_poweroff_timer_trigger(struct hrtimer *timer)
+{
+ struct ltc2952_poweroff *data = to_ltc2952(timer, timer_trigger);
+ ltc2952_poweroff_start_wde(data);
+ dev_info(data->dev, "executing shutdown\n");
orderly_poweroff(true);
return HRTIMER_NORESTART;
@@ -154,180 +157,161 @@ static enum hrtimer_restart ltc2952_poweroff_timer_trigger(
*/
static irqreturn_t ltc2952_poweroff_handler(int irq, void *dev_id)
{
- int ret;
- struct ltc2952_poweroff_data *data = dev_id;
+ struct ltc2952_poweroff *data = dev_id;
- if (ltc2952_poweroff_panic)
- goto irq_ok;
-
- if (hrtimer_active(&data->timer_wde)) {
+ if (data->kernel_panic || hrtimer_active(&data->timer_wde)) {
/* shutdown is already triggered, nothing to do any more */
- goto irq_ok;
+ return IRQ_HANDLED;
}
- if (!hrtimer_active(&data->timer_trigger)) {
- ret = hrtimer_start(&data->timer_trigger, data->trigger_delay,
- HRTIMER_MODE_REL);
-
- if (ret)
+ if (gpiod_get_value(data->gpio_trigger)) {
+ if (hrtimer_start(&data->timer_trigger, data->trigger_delay,
+ HRTIMER_MODE_REL))
dev_err(data->dev, "unable to start the wait timer\n");
} else {
- ret = hrtimer_cancel(&data->timer_trigger);
+ hrtimer_cancel(&data->timer_trigger);
/* omitting return value check, timer should have been valid */
}
-
-irq_ok:
return IRQ_HANDLED;
}
static void ltc2952_poweroff_kill(void)
{
- gpiod_set_value(ltc2952_data->gpio[POWERPATH_IO_KILL], 1);
-}
-
-static int ltc2952_poweroff_suspend(struct platform_device *pdev,
- pm_message_t state)
-{
- return -ENOSYS;
-}
-
-static int ltc2952_poweroff_resume(struct platform_device *pdev)
-{
- return -ENOSYS;
+ gpiod_set_value(ltc2952_data->gpio_kill, 1);
}
-static void ltc2952_poweroff_default(struct ltc2952_poweroff_data *data)
+static void ltc2952_poweroff_default(struct ltc2952_poweroff *data)
{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(data->gpio); i++)
- data->gpio[i] = NULL;
-
data->wde_interval = ktime_set(0, 300L*1E6L);
data->trigger_delay = ktime_set(2, 500L*1E6L);
hrtimer_init(&data->timer_trigger, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- data->timer_trigger.function = &ltc2952_poweroff_timer_trigger;
+ data->timer_trigger.function = ltc2952_poweroff_timer_trigger;
hrtimer_init(&data->timer_wde, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- data->timer_wde.function = &ltc2952_poweroff_timer_wde;
+ data->timer_wde.function = ltc2952_poweroff_timer_wde;
}
static int ltc2952_poweroff_init(struct platform_device *pdev)
{
- int ret, virq;
- unsigned int i;
- struct ltc2952_poweroff_data *data;
-
- static char *name[] = {
- "trigger",
- "watchdog",
- "kill",
- NULL
- };
-
- data = ltc2952_data;
- ltc2952_poweroff_default(ltc2952_data);
-
- for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++) {
- ltc2952_data->gpio[i] = gpiod_get(&pdev->dev, name[i]);
+ int ret;
+ struct ltc2952_poweroff *data = platform_get_drvdata(pdev);
- if (IS_ERR(ltc2952_data->gpio[i])) {
- ret = PTR_ERR(ltc2952_data->gpio[i]);
- dev_err(&pdev->dev,
- "unable to claim the following gpio: %s\n",
- name[i]);
- goto err_io;
- }
- }
+ ltc2952_poweroff_default(data);
- ret = gpiod_direction_output(
- ltc2952_data->gpio[POWERPATH_IO_WATCHDOG], 0);
- if (ret) {
- dev_err(&pdev->dev, "unable to use watchdog-gpio as output\n");
- goto err_io;
+ data->gpio_watchdog = devm_gpiod_get(&pdev->dev, "watchdog",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(data->gpio_watchdog)) {
+ ret = PTR_ERR(data->gpio_watchdog);
+ dev_err(&pdev->dev, "unable to claim gpio \"watchdog\"\n");
+ return ret;
}
- ret = gpiod_direction_output(ltc2952_data->gpio[POWERPATH_IO_KILL], 0);
- if (ret) {
- dev_err(&pdev->dev, "unable to use kill-gpio as output\n");
- goto err_io;
+ data->gpio_kill = devm_gpiod_get(&pdev->dev, "kill", GPIOD_OUT_LOW);
+ if (IS_ERR(data->gpio_kill)) {
+ ret = PTR_ERR(data->gpio_kill);
+ dev_err(&pdev->dev, "unable to claim gpio \"kill\"\n");
+ return ret;
}
- virq = gpiod_to_irq(ltc2952_data->gpio[POWERPATH_IO_TRIGGER]);
- if (virq < 0) {
- dev_err(&pdev->dev, "cannot map GPIO as interrupt");
- goto err_io;
+ data->gpio_trigger = devm_gpiod_get(&pdev->dev, "trigger", GPIOD_IN);
+ if (IS_ERR(data->gpio_trigger)) {
+ /*
+ * It's not a problem if the trigger gpio isn't available, but
+ * it is worth a warning if its use was defined in the device
+ * tree.
+ */
+ if (PTR_ERR(data->gpio_trigger) != -ENOENT)
+ dev_err(&pdev->dev,
+ "unable to claim gpio \"trigger\"\n");
+ data->gpio_trigger = NULL;
}
- ltc2952_data->virq = virq;
- ret = request_irq(virq,
- ltc2952_poweroff_handler,
- (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING),
- "ltc2952-poweroff",
- ltc2952_data
- );
-
- if (ret) {
- dev_err(&pdev->dev, "cannot configure an interrupt handler\n");
- goto err_io;
+ if (devm_request_irq(&pdev->dev, gpiod_to_irq(data->gpio_trigger),
+ ltc2952_poweroff_handler,
+ (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING),
+ "ltc2952-poweroff",
+ data)) {
+ /*
+ * Some things may have happened:
+ * - No trigger input was defined
+ * - Claiming the GPIO failed
+ * - We could not map to an IRQ
+ * - We couldn't register an interrupt handler
+ *
+ * None of these really are problems, but all of them
+ * disqualify the push button from controlling the power.
+ *
+ * It is therefore important to note that if the ltc2952
+ * detects a button press for long enough, it will still start
+ * its own powerdown window and cut the power on us if we don't
+ * start the watchdog trigger.
+ */
+ if (data->gpio_trigger) {
+ dev_warn(&pdev->dev,
+ "unable to configure the trigger interrupt\n");
+ devm_gpiod_put(&pdev->dev, data->gpio_trigger);
+ data->gpio_trigger = NULL;
+ }
+ dev_info(&pdev->dev,
+ "power down trigger input will not be used\n");
+ ltc2952_poweroff_start_wde(data);
}
return 0;
+}
-err_io:
- for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++)
- if (ltc2952_data->gpio[i])
- gpiod_put(ltc2952_data->gpio[i]);
+static int ltc2952_poweroff_notify_panic(struct notifier_block *nb,
+ unsigned long code, void *unused)
+{
+ struct ltc2952_poweroff *data = to_ltc2952(nb, panic_notifier);
- return ret;
+ data->kernel_panic = true;
+ return NOTIFY_DONE;
}
static int ltc2952_poweroff_probe(struct platform_device *pdev)
{
int ret;
+ struct ltc2952_poweroff *data;
if (pm_power_off) {
dev_err(&pdev->dev, "pm_power_off already registered");
return -EBUSY;
}
- ltc2952_data = kzalloc(sizeof(*ltc2952_data), GFP_KERNEL);
- if (!ltc2952_data)
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
return -ENOMEM;
- ltc2952_data->dev = &pdev->dev;
+ data->dev = &pdev->dev;
+ platform_set_drvdata(pdev, data);
ret = ltc2952_poweroff_init(pdev);
if (ret)
- goto err;
+ return ret;
- pm_power_off = &ltc2952_poweroff_kill;
+ /* TODO: remove ltc2952_data */
+ ltc2952_data = data;
+ pm_power_off = ltc2952_poweroff_kill;
+ data->panic_notifier.notifier_call = ltc2952_poweroff_notify_panic;
+ atomic_notifier_chain_register(&panic_notifier_list,
+ &data->panic_notifier);
dev_info(&pdev->dev, "probe successful\n");
return 0;
-
-err:
- kfree(ltc2952_data);
- return ret;
}
static int ltc2952_poweroff_remove(struct platform_device *pdev)
{
- unsigned int i;
+ struct ltc2952_poweroff *data = platform_get_drvdata(pdev);
pm_power_off = NULL;
-
- if (ltc2952_data) {
- free_irq(ltc2952_data->virq, ltc2952_data);
-
- for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++)
- gpiod_put(ltc2952_data->gpio[i]);
-
- kfree(ltc2952_data);
- }
-
+ hrtimer_cancel(&data->timer_trigger);
+ hrtimer_cancel(&data->timer_wde);
+ atomic_notifier_chain_unregister(&panic_notifier_list,
+ &data->panic_notifier);
return 0;
}
@@ -344,41 +328,9 @@ static struct platform_driver ltc2952_poweroff_driver = {
.name = "ltc2952-poweroff",
.of_match_table = of_ltc2952_poweroff_match,
},
- .suspend = ltc2952_poweroff_suspend,
- .resume = ltc2952_poweroff_resume,
-};
-
-static int ltc2952_poweroff_notify_panic(struct notifier_block *nb,
- unsigned long code, void *unused)
-{
- ltc2952_poweroff_panic = 1;
- return NOTIFY_DONE;
-}
-
-static struct notifier_block ltc2952_poweroff_panic_nb = {
- .notifier_call = ltc2952_poweroff_notify_panic,
};
-static int __init ltc2952_poweroff_platform_init(void)
-{
- ltc2952_poweroff_panic = 0;
-
- atomic_notifier_chain_register(&panic_notifier_list,
- &ltc2952_poweroff_panic_nb);
-
- return platform_driver_register(&ltc2952_poweroff_driver);
-}
-
-static void __exit ltc2952_poweroff_platform_exit(void)
-{
- atomic_notifier_chain_unregister(&panic_notifier_list,
- &ltc2952_poweroff_panic_nb);
-
- platform_driver_unregister(&ltc2952_poweroff_driver);
-}
-
-module_init(ltc2952_poweroff_platform_init);
-module_exit(ltc2952_poweroff_platform_exit);
+module_platform_driver(ltc2952_poweroff_driver);
MODULE_AUTHOR("René Moll <rene.moll@xsens.com>");
MODULE_DESCRIPTION("LTC PowerPath power-off driver");
diff --git a/drivers/power/reset/restart-poweroff.c b/drivers/power/reset/restart-poweroff.c
index f46f2c2e4648..41b22c4d5236 100644
--- a/drivers/power/reset/restart-poweroff.c
+++ b/drivers/power/reset/restart-poweroff.c
@@ -16,7 +16,6 @@
#include <linux/of_platform.h>
#include <linux/module.h>
#include <linux/reboot.h>
-#include <asm/system_misc.h>
static void restart_poweroff_do_poweroff(void)
{
diff --git a/drivers/power/reset/rmobile-reset.c b/drivers/power/reset/rmobile-reset.c
new file mode 100644
index 000000000000..e6569df76941
--- /dev/null
+++ b/drivers/power/reset/rmobile-reset.c
@@ -0,0 +1,91 @@
+/*
+ * Renesas R-Mobile Reset Driver
+ *
+ * Copyright (C) 2014 Glider bvba
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/printk.h>
+#include <linux/reboot.h>
+
+/* SYSC Register Bank 2 */
+#define RESCNT2 0x20 /* Reset Control Register 2 */
+
+/* Reset Control Register 2 */
+#define RESCNT2_PRES 0x80000000 /* Soft power-on reset */
+
+static void __iomem *sysc_base2;
+
+static int rmobile_reset_handler(struct notifier_block *this,
+ unsigned long mode, void *cmd)
+{
+ pr_debug("%s %lu\n", __func__, mode);
+
+ /* Let's assume we have acquired the HPB semaphore */
+ writel(RESCNT2_PRES, sysc_base2 + RESCNT2);
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block rmobile_reset_nb = {
+ .notifier_call = rmobile_reset_handler,
+ .priority = 192,
+};
+
+static int rmobile_reset_probe(struct platform_device *pdev)
+{
+ int error;
+
+ sysc_base2 = of_iomap(pdev->dev.of_node, 1);
+ if (!sysc_base2)
+ return -ENODEV;
+
+ error = register_restart_handler(&rmobile_reset_nb);
+ if (error) {
+ dev_err(&pdev->dev,
+ "cannot register restart handler (err=%d)\n", error);
+ goto fail_unmap;
+ }
+
+ return 0;
+
+fail_unmap:
+ iounmap(sysc_base2);
+ return error;
+}
+
+static int rmobile_reset_remove(struct platform_device *pdev)
+{
+ unregister_restart_handler(&rmobile_reset_nb);
+ iounmap(sysc_base2);
+ return 0;
+}
+
+static const struct of_device_id rmobile_reset_of_match[] = {
+ { .compatible = "renesas,sysc-rmobile", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rmobile_reset_of_match);
+
+static struct platform_driver rmobile_reset_driver = {
+ .probe = rmobile_reset_probe,
+ .remove = rmobile_reset_remove,
+ .driver = {
+ .name = "rmobile_reset",
+ .of_match_table = rmobile_reset_of_match,
+ },
+};
+
+module_platform_driver(rmobile_reset_driver);
+
+MODULE_DESCRIPTION("Renesas R-Mobile Reset Driver");
+MODULE_AUTHOR("Geert Uytterhoeven <geert+renesas@glider.be>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/reset/st-poweroff.c b/drivers/power/reset/st-poweroff.c
index a0acf25ee2a2..27383de9caa8 100644
--- a/drivers/power/reset/st-poweroff.c
+++ b/drivers/power/reset/st-poweroff.c
@@ -15,10 +15,9 @@
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/mfd/syscon.h>
+#include <linux/reboot.h>
#include <linux/regmap.h>
-#include <asm/system_misc.h>
-
struct reset_syscfg {
struct regmap *regmap;
/* syscfg used for reset */
@@ -75,7 +74,8 @@ static struct reset_syscfg stid127_reset = {
static struct reset_syscfg *st_restart_syscfg;
-static void st_restart(enum reboot_mode reboot_mode, const char *cmd)
+static int st_restart(struct notifier_block *this, unsigned long mode,
+ void *cmd)
{
/* reset syscfg updated */
regmap_update_bits(st_restart_syscfg->regmap,
@@ -88,8 +88,15 @@ static void st_restart(enum reboot_mode reboot_mode, const char *cmd)
st_restart_syscfg->offset_rst_msk,
st_restart_syscfg->mask_rst_msk,
0);
+
+ return NOTIFY_DONE;
}
+static struct notifier_block st_restart_nb = {
+ .notifier_call = st_restart,
+ .priority = 192,
+};
+
static struct of_device_id st_reset_of_match[] = {
{
.compatible = "st,stih415-restart",
@@ -126,9 +133,7 @@ static int st_reset_probe(struct platform_device *pdev)
return PTR_ERR(st_restart_syscfg->regmap);
}
- arm_pm_restart = st_restart;
-
- return 0;
+ return register_restart_handler(&st_restart_nb);
}
static struct platform_driver st_reset_driver = {
diff --git a/drivers/power/reset/sun6i-reboot.c b/drivers/power/reset/sun6i-reboot.c
deleted file mode 100644
index af2cd7ff2fe8..000000000000
--- a/drivers/power/reset/sun6i-reboot.c
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Allwinner A31 SoCs reset code
- *
- * Copyright (C) 2012-2014 Maxime Ripard
- *
- * Maxime Ripard <maxime.ripard@free-electrons.com>
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-
-#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/of_address.h>
-#include <linux/platform_device.h>
-#include <linux/reboot.h>
-
-#include <asm/system_misc.h>
-
-#define SUN6I_WATCHDOG1_IRQ_REG 0x00
-#define SUN6I_WATCHDOG1_CTRL_REG 0x10
-#define SUN6I_WATCHDOG1_CTRL_RESTART BIT(0)
-#define SUN6I_WATCHDOG1_CONFIG_REG 0x14
-#define SUN6I_WATCHDOG1_CONFIG_RESTART BIT(0)
-#define SUN6I_WATCHDOG1_CONFIG_IRQ BIT(1)
-#define SUN6I_WATCHDOG1_MODE_REG 0x18
-#define SUN6I_WATCHDOG1_MODE_ENABLE BIT(0)
-
-static void __iomem *wdt_base;
-
-static void sun6i_wdt_restart(enum reboot_mode mode, const char *cmd)
-{
- if (!wdt_base)
- return;
-
- /* Disable interrupts */
- writel(0, wdt_base + SUN6I_WATCHDOG1_IRQ_REG);
-
- /* We want to disable the IRQ and just reset the whole system */
- writel(SUN6I_WATCHDOG1_CONFIG_RESTART,
- wdt_base + SUN6I_WATCHDOG1_CONFIG_REG);
-
- /* Enable timer. The default and lowest interval value is 0.5s */
- writel(SUN6I_WATCHDOG1_MODE_ENABLE,
- wdt_base + SUN6I_WATCHDOG1_MODE_REG);
-
- /* Restart the watchdog. */
- writel(SUN6I_WATCHDOG1_CTRL_RESTART,
- wdt_base + SUN6I_WATCHDOG1_CTRL_REG);
-
- while (1) {
- mdelay(5);
- writel(SUN6I_WATCHDOG1_MODE_ENABLE,
- wdt_base + SUN6I_WATCHDOG1_MODE_REG);
- }
-}
-
-static int sun6i_reboot_probe(struct platform_device *pdev)
-{
- wdt_base = of_iomap(pdev->dev.of_node, 0);
- if (!wdt_base) {
- WARN(1, "failed to map watchdog base address");
- return -ENODEV;
- }
-
- arm_pm_restart = sun6i_wdt_restart;
-
- return 0;
-}
-
-static struct of_device_id sun6i_reboot_of_match[] = {
- { .compatible = "allwinner,sun6i-a31-wdt" },
- {}
-};
-
-static struct platform_driver sun6i_reboot_driver = {
- .probe = sun6i_reboot_probe,
- .driver = {
- .name = "sun6i-reboot",
- .of_match_table = sun6i_reboot_of_match,
- },
-};
-module_platform_driver(sun6i_reboot_driver);
diff --git a/drivers/power/reset/vexpress-poweroff.c b/drivers/power/reset/vexpress-poweroff.c
index 9dfc9cee3232..be12d9b92957 100644
--- a/drivers/power/reset/vexpress-poweroff.c
+++ b/drivers/power/reset/vexpress-poweroff.c
@@ -111,23 +111,20 @@ static int _vexpress_register_restart_handler(struct device *dev)
static int vexpress_reset_probe(struct platform_device *pdev)
{
- enum vexpress_reset_func func;
const struct of_device_id *match =
of_match_device(vexpress_reset_of_match, &pdev->dev);
struct regmap *regmap;
int ret = 0;
- if (match)
- func = (enum vexpress_reset_func)match->data;
- else
- func = pdev->id_entry->driver_data;
+ if (!match)
+ return -EINVAL;
regmap = devm_regmap_init_vexpress_config(&pdev->dev);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
dev_set_drvdata(&pdev->dev, regmap);
- switch (func) {
+ switch ((enum vexpress_reset_func)match->data) {
case FUNC_SHUTDOWN:
vexpress_power_off_device = &pdev->dev;
pm_power_off = vexpress_power_off;
@@ -144,20 +141,12 @@ static int vexpress_reset_probe(struct platform_device *pdev)
return ret;
}
-static const struct platform_device_id vexpress_reset_id_table[] = {
- { .name = "vexpress-reset", .driver_data = FUNC_RESET, },
- { .name = "vexpress-shutdown", .driver_data = FUNC_SHUTDOWN, },
- { .name = "vexpress-reboot", .driver_data = FUNC_REBOOT, },
- {}
-};
-
static struct platform_driver vexpress_reset_driver = {
.probe = vexpress_reset_probe,
.driver = {
.name = "vexpress-reset",
.of_match_table = vexpress_reset_of_match,
},
- .id_table = vexpress_reset_id_table,
};
static int __init vexpress_reset_init(void)