diff options
| -rw-r--r-- | arch/arm/kernel/process.c | 12 | ||||
| -rw-r--r-- | arch/arm64/kernel/process.c | 3 | ||||
| -rw-r--r-- | drivers/clk/rockchip/clk-rk3188.c | 2 | ||||
| -rw-r--r-- | drivers/clk/rockchip/clk-rk3288.c | 10 | ||||
| -rw-r--r-- | drivers/clk/rockchip/clk.c | 25 | ||||
| -rw-r--r-- | drivers/clk/rockchip/clk.h | 1 | ||||
| -rw-r--r-- | drivers/clk/samsung/clk-s3c2412.c | 29 | ||||
| -rw-r--r-- | drivers/clk/samsung/clk-s3c2443.c | 19 | ||||
| -rw-r--r-- | drivers/power/reset/restart-poweroff.c | 3 | ||||
| -rw-r--r-- | drivers/watchdog/alim7101_wdt.c | 42 | ||||
| -rw-r--r-- | drivers/watchdog/moxart_wdt.c | 32 | ||||
| -rw-r--r-- | drivers/watchdog/sunxi_wdt.c | 31 | ||||
| -rw-r--r-- | include/linux/reboot.h | 3 | ||||
| -rw-r--r-- | kernel/reboot.c | 81 |
14 files changed, 247 insertions, 46 deletions
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 81ef686a91ca..250b6f652afc 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c | |||
| @@ -114,18 +114,13 @@ void soft_restart(unsigned long addr) | |||
| 114 | BUG(); | 114 | BUG(); |
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | static void null_restart(enum reboot_mode reboot_mode, const char *cmd) | ||
| 118 | { | ||
| 119 | } | ||
| 120 | |||
| 121 | /* | 117 | /* |
| 122 | * Function pointers to optional machine specific functions | 118 | * Function pointers to optional machine specific functions |
| 123 | */ | 119 | */ |
| 124 | void (*pm_power_off)(void); | 120 | void (*pm_power_off)(void); |
| 125 | EXPORT_SYMBOL(pm_power_off); | 121 | EXPORT_SYMBOL(pm_power_off); |
| 126 | 122 | ||
| 127 | void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd) = null_restart; | 123 | void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd); |
| 128 | EXPORT_SYMBOL_GPL(arm_pm_restart); | ||
| 129 | 124 | ||
| 130 | /* | 125 | /* |
| 131 | * This is our default idle handler. | 126 | * This is our default idle handler. |
| @@ -230,7 +225,10 @@ void machine_restart(char *cmd) | |||
| 230 | local_irq_disable(); | 225 | local_irq_disable(); |
| 231 | smp_send_stop(); | 226 | smp_send_stop(); |
| 232 | 227 | ||
| 233 | arm_pm_restart(reboot_mode, cmd); | 228 | if (arm_pm_restart) |
| 229 | arm_pm_restart(reboot_mode, cmd); | ||
| 230 | else | ||
| 231 | do_kernel_restart(cmd); | ||
| 234 | 232 | ||
| 235 | /* Give a grace period for failure to restart of 1s */ | 233 | /* Give a grace period for failure to restart of 1s */ |
| 236 | mdelay(1000); | 234 | mdelay(1000); |
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index 29d48690f2ac..e0ef8ba4fdb9 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c | |||
| @@ -98,7 +98,6 @@ void (*pm_power_off)(void); | |||
| 98 | EXPORT_SYMBOL_GPL(pm_power_off); | 98 | EXPORT_SYMBOL_GPL(pm_power_off); |
| 99 | 99 | ||
| 100 | void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd); | 100 | void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd); |
| 101 | EXPORT_SYMBOL_GPL(arm_pm_restart); | ||
| 102 | 101 | ||
| 103 | /* | 102 | /* |
| 104 | * This is our default idle handler. | 103 | * This is our default idle handler. |
| @@ -180,6 +179,8 @@ void machine_restart(char *cmd) | |||
| 180 | /* Now call the architecture specific reboot code. */ | 179 | /* Now call the architecture specific reboot code. */ |
| 181 | if (arm_pm_restart) | 180 | if (arm_pm_restart) |
| 182 | arm_pm_restart(reboot_mode, cmd); | 181 | arm_pm_restart(reboot_mode, cmd); |
| 182 | else | ||
| 183 | do_kernel_restart(cmd); | ||
| 183 | 184 | ||
| 184 | /* | 185 | /* |
| 185 | * Whoops - the architecture was unable to reboot. | 186 | * Whoops - the architecture was unable to reboot. |
diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c index ceabce595498..beed49c79126 100644 --- a/drivers/clk/rockchip/clk-rk3188.c +++ b/drivers/clk/rockchip/clk-rk3188.c | |||
| @@ -735,6 +735,8 @@ static void __init rk3188_common_clk_init(struct device_node *np) | |||
| 735 | 735 | ||
| 736 | rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0), | 736 | rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0), |
| 737 | ROCKCHIP_SOFTRST_HIWORD_MASK); | 737 | ROCKCHIP_SOFTRST_HIWORD_MASK); |
| 738 | |||
| 739 | rockchip_register_restart_notifier(RK2928_GLB_SRST_FST); | ||
| 738 | } | 740 | } |
| 739 | 741 | ||
| 740 | static void __init rk3066a_clk_init(struct device_node *np) | 742 | static void __init rk3066a_clk_init(struct device_node *np) |
diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c index d053529113f8..23278291da44 100644 --- a/drivers/clk/rockchip/clk-rk3288.c +++ b/drivers/clk/rockchip/clk-rk3288.c | |||
| @@ -300,15 +300,15 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { | |||
| 300 | COMPOSITE(0, "i2s_src", mux_pll_src_cpll_gpll_p, 0, | 300 | COMPOSITE(0, "i2s_src", mux_pll_src_cpll_gpll_p, 0, |
| 301 | RK3288_CLKSEL_CON(4), 15, 1, MFLAGS, 0, 7, DFLAGS, | 301 | RK3288_CLKSEL_CON(4), 15, 1, MFLAGS, 0, 7, DFLAGS, |
| 302 | RK3288_CLKGATE_CON(4), 1, GFLAGS), | 302 | RK3288_CLKGATE_CON(4), 1, GFLAGS), |
| 303 | COMPOSITE_FRAC(0, "i2s_frac", "i2s_src", 0, | 303 | COMPOSITE_FRAC(0, "i2s_frac", "i2s_src", CLK_SET_RATE_PARENT, |
| 304 | RK3288_CLKSEL_CON(8), 0, | 304 | RK3288_CLKSEL_CON(8), 0, |
| 305 | RK3288_CLKGATE_CON(4), 2, GFLAGS), | 305 | RK3288_CLKGATE_CON(4), 2, GFLAGS), |
| 306 | MUX(0, "i2s_pre", mux_i2s_pre_p, 0, | 306 | MUX(0, "i2s_pre", mux_i2s_pre_p, CLK_SET_RATE_PARENT, |
| 307 | RK3288_CLKSEL_CON(4), 8, 2, MFLAGS), | 307 | RK3288_CLKSEL_CON(4), 8, 2, MFLAGS), |
| 308 | COMPOSITE_NODIV(0, "i2s0_clkout", mux_i2s_clkout_p, 0, | 308 | COMPOSITE_NODIV(0, "i2s0_clkout", mux_i2s_clkout_p, CLK_SET_RATE_PARENT, |
| 309 | RK3288_CLKSEL_CON(4), 12, 1, MFLAGS, | 309 | RK3288_CLKSEL_CON(4), 12, 1, MFLAGS, |
| 310 | RK3288_CLKGATE_CON(4), 0, GFLAGS), | 310 | RK3288_CLKGATE_CON(4), 0, GFLAGS), |
| 311 | GATE(SCLK_I2S0, "sclk_i2s0", "i2s_pre", 0, | 311 | GATE(SCLK_I2S0, "sclk_i2s0", "i2s_pre", CLK_SET_RATE_PARENT, |
| 312 | RK3288_CLKGATE_CON(4), 3, GFLAGS), | 312 | RK3288_CLKGATE_CON(4), 3, GFLAGS), |
| 313 | 313 | ||
| 314 | MUX(0, "spdif_src", mux_pll_src_cpll_gpll_p, 0, | 314 | MUX(0, "spdif_src", mux_pll_src_cpll_gpll_p, 0, |
| @@ -808,5 +808,7 @@ static void __init rk3288_clk_init(struct device_node *np) | |||
| 808 | 808 | ||
| 809 | rockchip_register_softrst(np, 12, reg_base + RK3288_SOFTRST_CON(0), | 809 | rockchip_register_softrst(np, 12, reg_base + RK3288_SOFTRST_CON(0), |
| 810 | ROCKCHIP_SOFTRST_HIWORD_MASK); | 810 | ROCKCHIP_SOFTRST_HIWORD_MASK); |
| 811 | |||
| 812 | rockchip_register_restart_notifier(RK3288_GLB_SRST_FST); | ||
| 811 | } | 813 | } |
| 812 | CLK_OF_DECLARE(rk3288_cru, "rockchip,rk3288-cru", rk3288_clk_init); | 814 | CLK_OF_DECLARE(rk3288_cru, "rockchip,rk3288-cru", rk3288_clk_init); |
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index fd3b5ef87e29..1e68bff481b8 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c | |||
| @@ -25,6 +25,7 @@ | |||
| 25 | #include <linux/clk-provider.h> | 25 | #include <linux/clk-provider.h> |
| 26 | #include <linux/mfd/syscon.h> | 26 | #include <linux/mfd/syscon.h> |
| 27 | #include <linux/regmap.h> | 27 | #include <linux/regmap.h> |
| 28 | #include <linux/reboot.h> | ||
| 28 | #include "clk.h" | 29 | #include "clk.h" |
| 29 | 30 | ||
| 30 | /** | 31 | /** |
| @@ -330,3 +331,27 @@ void __init rockchip_clk_protect_critical(const char *clocks[], int nclocks) | |||
| 330 | clk_prepare_enable(clk); | 331 | clk_prepare_enable(clk); |
| 331 | } | 332 | } |
| 332 | } | 333 | } |
| 334 | |||
| 335 | static unsigned int reg_restart; | ||
| 336 | static int rockchip_restart_notify(struct notifier_block *this, | ||
| 337 | unsigned long mode, void *cmd) | ||
| 338 | { | ||
| 339 | writel(0xfdb9, reg_base + reg_restart); | ||
| 340 | return NOTIFY_DONE; | ||
| 341 | } | ||
| 342 | |||
| 343 | static struct notifier_block rockchip_restart_handler = { | ||
| 344 | .notifier_call = rockchip_restart_notify, | ||
| 345 | .priority = 128, | ||
| 346 | }; | ||
| 347 | |||
| 348 | void __init rockchip_register_restart_notifier(unsigned int reg) | ||
| 349 | { | ||
| 350 | int ret; | ||
| 351 | |||
| 352 | reg_restart = reg; | ||
| 353 | ret = register_restart_handler(&rockchip_restart_handler); | ||
| 354 | if (ret) | ||
| 355 | pr_err("%s: cannot register restart handler, %d\n", | ||
| 356 | __func__, ret); | ||
| 357 | } | ||
diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index f4791fbb3da9..ca009ab0a33a 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h | |||
| @@ -367,6 +367,7 @@ void rockchip_clk_register_armclk(unsigned int lookup_id, const char *name, | |||
| 367 | const struct rockchip_cpuclk_rate_table *rates, | 367 | const struct rockchip_cpuclk_rate_table *rates, |
| 368 | int nrates); | 368 | int nrates); |
| 369 | void rockchip_clk_protect_critical(const char *clocks[], int nclocks); | 369 | void rockchip_clk_protect_critical(const char *clocks[], int nclocks); |
| 370 | void rockchip_register_restart_notifier(unsigned int reg); | ||
| 370 | 371 | ||
| 371 | #define ROCKCHIP_SOFTRST_HIWORD_MASK BIT(0) | 372 | #define ROCKCHIP_SOFTRST_HIWORD_MASK BIT(0) |
| 372 | 373 | ||
diff --git a/drivers/clk/samsung/clk-s3c2412.c b/drivers/clk/samsung/clk-s3c2412.c index 34af09f6a155..2ceedaf8ce18 100644 --- a/drivers/clk/samsung/clk-s3c2412.c +++ b/drivers/clk/samsung/clk-s3c2412.c | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include <linux/of.h> | 14 | #include <linux/of.h> |
| 15 | #include <linux/of_address.h> | 15 | #include <linux/of_address.h> |
| 16 | #include <linux/syscore_ops.h> | 16 | #include <linux/syscore_ops.h> |
| 17 | #include <linux/reboot.h> | ||
| 17 | 18 | ||
| 18 | #include <dt-bindings/clock/s3c2412.h> | 19 | #include <dt-bindings/clock/s3c2412.h> |
| 19 | 20 | ||
| @@ -26,6 +27,7 @@ | |||
| 26 | #define CLKCON 0x0c | 27 | #define CLKCON 0x0c |
| 27 | #define CLKDIVN 0x14 | 28 | #define CLKDIVN 0x14 |
| 28 | #define CLKSRC 0x1c | 29 | #define CLKSRC 0x1c |
| 30 | #define SWRST 0x30 | ||
| 29 | 31 | ||
| 30 | /* list of PLLs to be registered */ | 32 | /* list of PLLs to be registered */ |
| 31 | enum s3c2412_plls { | 33 | enum s3c2412_plls { |
| @@ -204,6 +206,28 @@ struct samsung_clock_alias s3c2412_aliases[] __initdata = { | |||
| 204 | ALIAS(MSYSCLK, NULL, "fclk"), | 206 | ALIAS(MSYSCLK, NULL, "fclk"), |
| 205 | }; | 207 | }; |
| 206 | 208 | ||
| 209 | static int s3c2412_restart(struct notifier_block *this, | ||
| 210 | unsigned long mode, void *cmd) | ||
| 211 | { | ||
| 212 | /* errata "Watch-dog/Software Reset Problem" specifies that | ||
| 213 | * this reset must be done with the SYSCLK sourced from | ||
| 214 | * EXTCLK instead of FOUT to avoid a glitch in the reset | ||
| 215 | * mechanism. | ||
| 216 | * | ||
| 217 | * See the watchdog section of the S3C2412 manual for more | ||
| 218 | * information on this fix. | ||
| 219 | */ | ||
| 220 | |||
| 221 | __raw_writel(0x00, reg_base + CLKSRC); | ||
| 222 | __raw_writel(0x533C2412, reg_base + SWRST); | ||
| 223 | return NOTIFY_DONE; | ||
| 224 | } | ||
| 225 | |||
| 226 | static struct notifier_block s3c2412_restart_handler = { | ||
| 227 | .notifier_call = s3c2412_restart, | ||
| 228 | .priority = 129, | ||
| 229 | }; | ||
| 230 | |||
| 207 | /* | 231 | /* |
| 208 | * fixed rate clocks generated outside the soc | 232 | * fixed rate clocks generated outside the soc |
| 209 | * Only necessary until the devicetree-move is complete | 233 | * Only necessary until the devicetree-move is complete |
| @@ -233,6 +257,7 @@ void __init s3c2412_common_clk_init(struct device_node *np, unsigned long xti_f, | |||
| 233 | unsigned long ext_f, void __iomem *base) | 257 | unsigned long ext_f, void __iomem *base) |
| 234 | { | 258 | { |
| 235 | struct samsung_clk_provider *ctx; | 259 | struct samsung_clk_provider *ctx; |
| 260 | int ret; | ||
| 236 | reg_base = base; | 261 | reg_base = base; |
| 237 | 262 | ||
| 238 | if (np) { | 263 | if (np) { |
| @@ -267,6 +292,10 @@ void __init s3c2412_common_clk_init(struct device_node *np, unsigned long xti_f, | |||
| 267 | s3c2412_clk_sleep_init(); | 292 | s3c2412_clk_sleep_init(); |
| 268 | 293 | ||
| 269 | samsung_clk_of_add_provider(np, ctx); | 294 | samsung_clk_of_add_provider(np, ctx); |
| 295 | |||
| 296 | ret = register_restart_handler(&s3c2412_restart_handler); | ||
| 297 | if (ret) | ||
| 298 | pr_warn("cannot register restart handler, %d\n", ret); | ||
| 270 | } | 299 | } |
| 271 | 300 | ||
| 272 | static void __init s3c2412_clk_init(struct device_node *np) | 301 | static void __init s3c2412_clk_init(struct device_node *np) |
diff --git a/drivers/clk/samsung/clk-s3c2443.c b/drivers/clk/samsung/clk-s3c2443.c index c92f853fca9f..0c3c182b902a 100644 --- a/drivers/clk/samsung/clk-s3c2443.c +++ b/drivers/clk/samsung/clk-s3c2443.c | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include <linux/of.h> | 14 | #include <linux/of.h> |
| 15 | #include <linux/of_address.h> | 15 | #include <linux/of_address.h> |
| 16 | #include <linux/syscore_ops.h> | 16 | #include <linux/syscore_ops.h> |
| 17 | #include <linux/reboot.h> | ||
| 17 | 18 | ||
| 18 | #include <dt-bindings/clock/s3c2443.h> | 19 | #include <dt-bindings/clock/s3c2443.h> |
| 19 | 20 | ||
| @@ -33,6 +34,7 @@ | |||
| 33 | #define HCLKCON 0x30 | 34 | #define HCLKCON 0x30 |
| 34 | #define PCLKCON 0x34 | 35 | #define PCLKCON 0x34 |
| 35 | #define SCLKCON 0x38 | 36 | #define SCLKCON 0x38 |
| 37 | #define SWRST 0x44 | ||
| 36 | 38 | ||
| 37 | /* the soc types */ | 39 | /* the soc types */ |
| 38 | enum supported_socs { | 40 | enum supported_socs { |
| @@ -354,6 +356,18 @@ struct samsung_clock_alias s3c2450_aliases[] __initdata = { | |||
| 354 | ALIAS(PCLK_I2C1, "s3c2410-i2c.1", "i2c"), | 356 | ALIAS(PCLK_I2C1, "s3c2410-i2c.1", "i2c"), |
| 355 | }; | 357 | }; |
| 356 | 358 | ||
| 359 | static int s3c2443_restart(struct notifier_block *this, | ||
| 360 | unsigned long mode, void *cmd) | ||
| 361 | { | ||
| 362 | __raw_writel(0x533c2443, reg_base + SWRST); | ||
| 363 | return NOTIFY_DONE; | ||
| 364 | } | ||
| 365 | |||
| 366 | static struct notifier_block s3c2443_restart_handler = { | ||
| 367 | .notifier_call = s3c2443_restart, | ||
| 368 | .priority = 129, | ||
| 369 | }; | ||
| 370 | |||
| 357 | /* | 371 | /* |
| 358 | * fixed rate clocks generated outside the soc | 372 | * fixed rate clocks generated outside the soc |
| 359 | * Only necessary until the devicetree-move is complete | 373 | * Only necessary until the devicetree-move is complete |
| @@ -378,6 +392,7 @@ void __init s3c2443_common_clk_init(struct device_node *np, unsigned long xti_f, | |||
| 378 | void __iomem *base) | 392 | void __iomem *base) |
| 379 | { | 393 | { |
| 380 | struct samsung_clk_provider *ctx; | 394 | struct samsung_clk_provider *ctx; |
| 395 | int ret; | ||
| 381 | reg_base = base; | 396 | reg_base = base; |
| 382 | 397 | ||
| 383 | if (np) { | 398 | if (np) { |
| @@ -447,6 +462,10 @@ void __init s3c2443_common_clk_init(struct device_node *np, unsigned long xti_f, | |||
| 447 | s3c2443_clk_sleep_init(); | 462 | s3c2443_clk_sleep_init(); |
| 448 | 463 | ||
| 449 | samsung_clk_of_add_provider(np, ctx); | 464 | samsung_clk_of_add_provider(np, ctx); |
| 465 | |||
| 466 | ret = register_restart_handler(&s3c2443_restart_handler); | ||
| 467 | if (ret) | ||
| 468 | pr_warn("cannot register restart handler, %d\n", ret); | ||
| 450 | } | 469 | } |
| 451 | 470 | ||
| 452 | static void __init s3c2416_clk_init(struct device_node *np) | 471 | static void __init s3c2416_clk_init(struct device_node *np) |
diff --git a/drivers/power/reset/restart-poweroff.c b/drivers/power/reset/restart-poweroff.c index 3e51f8d29bfe..edd707ee7281 100644 --- a/drivers/power/reset/restart-poweroff.c +++ b/drivers/power/reset/restart-poweroff.c | |||
| @@ -20,7 +20,8 @@ | |||
| 20 | 20 | ||
| 21 | static void restart_poweroff_do_poweroff(void) | 21 | static void restart_poweroff_do_poweroff(void) |
| 22 | { | 22 | { |
| 23 | arm_pm_restart(REBOOT_HARD, NULL); | 23 | reboot_mode = REBOOT_HARD; |
| 24 | machine_restart(NULL); | ||
| 24 | } | 25 | } |
| 25 | 26 | ||
| 26 | static int restart_poweroff_probe(struct platform_device *pdev) | 27 | static int restart_poweroff_probe(struct platform_device *pdev) |
diff --git a/drivers/watchdog/alim7101_wdt.c b/drivers/watchdog/alim7101_wdt.c index 996b2f7d330e..665e0e7dfe1e 100644 --- a/drivers/watchdog/alim7101_wdt.c +++ b/drivers/watchdog/alim7101_wdt.c | |||
| @@ -301,6 +301,28 @@ static struct miscdevice wdt_miscdev = { | |||
| 301 | .fops = &wdt_fops, | 301 | .fops = &wdt_fops, |
| 302 | }; | 302 | }; |
| 303 | 303 | ||
| 304 | static int wdt_restart_handle(struct notifier_block *this, unsigned long mode, | ||
| 305 | void *cmd) | ||
| 306 | { | ||
| 307 | /* | ||
| 308 | * Cobalt devices have no way of rebooting themselves other | ||
| 309 | * than getting the watchdog to pull reset, so we restart the | ||
| 310 | * watchdog on reboot with no heartbeat. | ||
| 311 | */ | ||
| 312 | wdt_change(WDT_ENABLE); | ||
| 313 | |||
| 314 | /* loop until the watchdog fires */ | ||
| 315 | while (true) | ||
| 316 | ; | ||
| 317 | |||
| 318 | return NOTIFY_DONE; | ||
| 319 | } | ||
| 320 | |||
| 321 | static struct notifier_block wdt_restart_handler = { | ||
| 322 | .notifier_call = wdt_restart_handle, | ||
| 323 | .priority = 128, | ||
| 324 | }; | ||
| 325 | |||
| 304 | /* | 326 | /* |
| 305 | * Notifier for system down | 327 | * Notifier for system down |
| 306 | */ | 328 | */ |
| @@ -311,15 +333,6 @@ static int wdt_notify_sys(struct notifier_block *this, | |||
| 311 | if (code == SYS_DOWN || code == SYS_HALT) | 333 | if (code == SYS_DOWN || code == SYS_HALT) |
| 312 | wdt_turnoff(); | 334 | wdt_turnoff(); |
| 313 | 335 | ||
| 314 | if (code == SYS_RESTART) { | ||
| 315 | /* | ||
| 316 | * Cobalt devices have no way of rebooting themselves other | ||
| 317 | * than getting the watchdog to pull reset, so we restart the | ||
| 318 | * watchdog on reboot with no heartbeat | ||
| 319 | */ | ||
| 320 | wdt_change(WDT_ENABLE); | ||
| 321 | pr_info("Watchdog timer is now enabled with no heartbeat - should reboot in ~1 second\n"); | ||
| 322 | } | ||
| 323 | return NOTIFY_DONE; | 336 | return NOTIFY_DONE; |
| 324 | } | 337 | } |
| 325 | 338 | ||
| @@ -338,6 +351,7 @@ static void __exit alim7101_wdt_unload(void) | |||
| 338 | /* Deregister */ | 351 | /* Deregister */ |
| 339 | misc_deregister(&wdt_miscdev); | 352 | misc_deregister(&wdt_miscdev); |
| 340 | unregister_reboot_notifier(&wdt_notifier); | 353 | unregister_reboot_notifier(&wdt_notifier); |
| 354 | unregister_restart_handler(&wdt_restart_handler); | ||
| 341 | pci_dev_put(alim7101_pmu); | 355 | pci_dev_put(alim7101_pmu); |
| 342 | } | 356 | } |
| 343 | 357 | ||
| @@ -390,11 +404,17 @@ static int __init alim7101_wdt_init(void) | |||
| 390 | goto err_out; | 404 | goto err_out; |
| 391 | } | 405 | } |
| 392 | 406 | ||
| 407 | rc = register_restart_handler(&wdt_restart_handler); | ||
| 408 | if (rc) { | ||
| 409 | pr_err("cannot register restart handler (err=%d)\n", rc); | ||
| 410 | goto err_out_reboot; | ||
| 411 | } | ||
| 412 | |||
| 393 | rc = misc_register(&wdt_miscdev); | 413 | rc = misc_register(&wdt_miscdev); |
| 394 | if (rc) { | 414 | if (rc) { |
| 395 | pr_err("cannot register miscdev on minor=%d (err=%d)\n", | 415 | pr_err("cannot register miscdev on minor=%d (err=%d)\n", |
| 396 | wdt_miscdev.minor, rc); | 416 | wdt_miscdev.minor, rc); |
| 397 | goto err_out_reboot; | 417 | goto err_out_restart; |
| 398 | } | 418 | } |
| 399 | 419 | ||
| 400 | if (nowayout) | 420 | if (nowayout) |
| @@ -404,6 +424,8 @@ static int __init alim7101_wdt_init(void) | |||
| 404 | timeout, nowayout); | 424 | timeout, nowayout); |
| 405 | return 0; | 425 | return 0; |
| 406 | 426 | ||
| 427 | err_out_restart: | ||
| 428 | unregister_restart_handler(&wdt_restart_handler); | ||
| 407 | err_out_reboot: | 429 | err_out_reboot: |
| 408 | unregister_reboot_notifier(&wdt_notifier); | 430 | unregister_reboot_notifier(&wdt_notifier); |
| 409 | err_out: | 431 | err_out: |
diff --git a/drivers/watchdog/moxart_wdt.c b/drivers/watchdog/moxart_wdt.c index 4aa3a8a876fe..a64405b82596 100644 --- a/drivers/watchdog/moxart_wdt.c +++ b/drivers/watchdog/moxart_wdt.c | |||
| @@ -15,12 +15,12 @@ | |||
| 15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
| 16 | #include <linux/err.h> | 16 | #include <linux/err.h> |
| 17 | #include <linux/kernel.h> | 17 | #include <linux/kernel.h> |
| 18 | #include <linux/notifier.h> | ||
| 18 | #include <linux/platform_device.h> | 19 | #include <linux/platform_device.h> |
| 20 | #include <linux/reboot.h> | ||
| 19 | #include <linux/watchdog.h> | 21 | #include <linux/watchdog.h> |
| 20 | #include <linux/moduleparam.h> | 22 | #include <linux/moduleparam.h> |
| 21 | 23 | ||
| 22 | #include <asm/system_misc.h> | ||
| 23 | |||
| 24 | #define REG_COUNT 0x4 | 24 | #define REG_COUNT 0x4 |
| 25 | #define REG_MODE 0x8 | 25 | #define REG_MODE 0x8 |
| 26 | #define REG_ENABLE 0xC | 26 | #define REG_ENABLE 0xC |
| @@ -29,17 +29,22 @@ struct moxart_wdt_dev { | |||
| 29 | struct watchdog_device dev; | 29 | struct watchdog_device dev; |
| 30 | void __iomem *base; | 30 | void __iomem *base; |
| 31 | unsigned int clock_frequency; | 31 | unsigned int clock_frequency; |
| 32 | struct notifier_block restart_handler; | ||
| 32 | }; | 33 | }; |
| 33 | 34 | ||
| 34 | static struct moxart_wdt_dev *moxart_restart_ctx; | ||
| 35 | |||
| 36 | static int heartbeat; | 35 | static int heartbeat; |
| 37 | 36 | ||
| 38 | static void moxart_wdt_restart(enum reboot_mode reboot_mode, const char *cmd) | 37 | static int moxart_restart_handle(struct notifier_block *this, |
| 38 | unsigned long mode, void *cmd) | ||
| 39 | { | 39 | { |
| 40 | writel(1, moxart_restart_ctx->base + REG_COUNT); | 40 | struct moxart_wdt_dev *moxart_wdt = container_of(this, |
| 41 | writel(0x5ab9, moxart_restart_ctx->base + REG_MODE); | 41 | struct moxart_wdt_dev, |
| 42 | writel(0x03, moxart_restart_ctx->base + REG_ENABLE); | 42 | restart_handler); |
| 43 | writel(1, moxart_wdt->base + REG_COUNT); | ||
| 44 | writel(0x5ab9, moxart_wdt->base + REG_MODE); | ||
| 45 | writel(0x03, moxart_wdt->base + REG_ENABLE); | ||
| 46 | |||
| 47 | return NOTIFY_DONE; | ||
| 43 | } | 48 | } |
| 44 | 49 | ||
| 45 | static int moxart_wdt_stop(struct watchdog_device *wdt_dev) | 50 | static int moxart_wdt_stop(struct watchdog_device *wdt_dev) |
| @@ -136,8 +141,12 @@ static int moxart_wdt_probe(struct platform_device *pdev) | |||
| 136 | if (err) | 141 | if (err) |
| 137 | return err; | 142 | return err; |
| 138 | 143 | ||
| 139 | moxart_restart_ctx = moxart_wdt; | 144 | moxart_wdt->restart_handler.notifier_call = moxart_restart_handle; |
| 140 | arm_pm_restart = moxart_wdt_restart; | 145 | moxart_wdt->restart_handler.priority = 128; |
| 146 | err = register_restart_handler(&moxart_wdt->restart_handler); | ||
| 147 | if (err) | ||
| 148 | dev_err(dev, "cannot register restart notifier (err=%d)\n", | ||
| 149 | err); | ||
| 141 | 150 | ||
| 142 | dev_dbg(dev, "Watchdog enabled (heartbeat=%d sec, nowayout=%d)\n", | 151 | dev_dbg(dev, "Watchdog enabled (heartbeat=%d sec, nowayout=%d)\n", |
| 143 | moxart_wdt->dev.timeout, nowayout); | 152 | moxart_wdt->dev.timeout, nowayout); |
| @@ -149,9 +158,8 @@ static int moxart_wdt_remove(struct platform_device *pdev) | |||
| 149 | { | 158 | { |
| 150 | struct moxart_wdt_dev *moxart_wdt = platform_get_drvdata(pdev); | 159 | struct moxart_wdt_dev *moxart_wdt = platform_get_drvdata(pdev); |
| 151 | 160 | ||
| 152 | arm_pm_restart = NULL; | 161 | unregister_restart_handler(&moxart_wdt->restart_handler); |
| 153 | moxart_wdt_stop(&moxart_wdt->dev); | 162 | moxart_wdt_stop(&moxart_wdt->dev); |
| 154 | watchdog_unregister_device(&moxart_wdt->dev); | ||
| 155 | 163 | ||
| 156 | return 0; | 164 | return 0; |
| 157 | } | 165 | } |
diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c index 60deb9d304c0..480bb557f353 100644 --- a/drivers/watchdog/sunxi_wdt.c +++ b/drivers/watchdog/sunxi_wdt.c | |||
| @@ -21,14 +21,13 @@ | |||
| 21 | #include <linux/kernel.h> | 21 | #include <linux/kernel.h> |
| 22 | #include <linux/module.h> | 22 | #include <linux/module.h> |
| 23 | #include <linux/moduleparam.h> | 23 | #include <linux/moduleparam.h> |
| 24 | #include <linux/notifier.h> | ||
| 24 | #include <linux/of.h> | 25 | #include <linux/of.h> |
| 25 | #include <linux/platform_device.h> | 26 | #include <linux/platform_device.h> |
| 26 | #include <linux/reboot.h> | 27 | #include <linux/reboot.h> |
| 27 | #include <linux/types.h> | 28 | #include <linux/types.h> |
| 28 | #include <linux/watchdog.h> | 29 | #include <linux/watchdog.h> |
| 29 | 30 | ||
| 30 | #include <asm/system_misc.h> | ||
| 31 | |||
| 32 | #define WDT_MAX_TIMEOUT 16 | 31 | #define WDT_MAX_TIMEOUT 16 |
| 33 | #define WDT_MIN_TIMEOUT 1 | 32 | #define WDT_MIN_TIMEOUT 1 |
| 34 | #define WDT_MODE_TIMEOUT(n) ((n) << 3) | 33 | #define WDT_MODE_TIMEOUT(n) ((n) << 3) |
| @@ -50,6 +49,7 @@ static unsigned int timeout = WDT_MAX_TIMEOUT; | |||
| 50 | struct sunxi_wdt_dev { | 49 | struct sunxi_wdt_dev { |
| 51 | struct watchdog_device wdt_dev; | 50 | struct watchdog_device wdt_dev; |
| 52 | void __iomem *wdt_base; | 51 | void __iomem *wdt_base; |
| 52 | struct notifier_block restart_handler; | ||
| 53 | }; | 53 | }; |
| 54 | 54 | ||
| 55 | /* | 55 | /* |
| @@ -74,24 +74,29 @@ static const int wdt_timeout_map[] = { | |||
| 74 | [16] = 0xB, /* 16s */ | 74 | [16] = 0xB, /* 16s */ |
| 75 | }; | 75 | }; |
| 76 | 76 | ||
| 77 | static void __iomem *reboot_wdt_base; | ||
| 78 | 77 | ||
| 79 | static void sun4i_wdt_restart(enum reboot_mode mode, const char *cmd) | 78 | static int sunxi_restart_handle(struct notifier_block *this, unsigned long mode, |
| 79 | void *cmd) | ||
| 80 | { | 80 | { |
| 81 | struct sunxi_wdt_dev *sunxi_wdt = container_of(this, | ||
| 82 | struct sunxi_wdt_dev, | ||
| 83 | restart_handler); | ||
| 84 | void __iomem *wdt_base = sunxi_wdt->wdt_base; | ||
| 85 | |||
| 81 | /* Enable timer and set reset bit in the watchdog */ | 86 | /* Enable timer and set reset bit in the watchdog */ |
| 82 | writel(WDT_MODE_EN | WDT_MODE_RST_EN, reboot_wdt_base + WDT_MODE); | 87 | writel(WDT_MODE_EN | WDT_MODE_RST_EN, wdt_base + WDT_MODE); |
| 83 | 88 | ||
| 84 | /* | 89 | /* |
| 85 | * Restart the watchdog. The default (and lowest) interval | 90 | * Restart the watchdog. The default (and lowest) interval |
| 86 | * value for the watchdog is 0.5s. | 91 | * value for the watchdog is 0.5s. |
| 87 | */ | 92 | */ |
| 88 | writel(WDT_CTRL_RELOAD, reboot_wdt_base + WDT_CTRL); | 93 | writel(WDT_CTRL_RELOAD, wdt_base + WDT_CTRL); |
| 89 | 94 | ||
| 90 | while (1) { | 95 | while (1) { |
| 91 | mdelay(5); | 96 | mdelay(5); |
| 92 | writel(WDT_MODE_EN | WDT_MODE_RST_EN, | 97 | writel(WDT_MODE_EN | WDT_MODE_RST_EN, wdt_base + WDT_MODE); |
| 93 | reboot_wdt_base + WDT_MODE); | ||
| 94 | } | 98 | } |
| 99 | return NOTIFY_DONE; | ||
| 95 | } | 100 | } |
| 96 | 101 | ||
| 97 | static int sunxi_wdt_ping(struct watchdog_device *wdt_dev) | 102 | static int sunxi_wdt_ping(struct watchdog_device *wdt_dev) |
| @@ -205,8 +210,12 @@ static int sunxi_wdt_probe(struct platform_device *pdev) | |||
| 205 | if (unlikely(err)) | 210 | if (unlikely(err)) |
| 206 | return err; | 211 | return err; |
| 207 | 212 | ||
| 208 | reboot_wdt_base = sunxi_wdt->wdt_base; | 213 | sunxi_wdt->restart_handler.notifier_call = sunxi_restart_handle; |
| 209 | arm_pm_restart = sun4i_wdt_restart; | 214 | sunxi_wdt->restart_handler.priority = 128; |
| 215 | err = register_restart_handler(&sunxi_wdt->restart_handler); | ||
| 216 | if (err) | ||
| 217 | dev_err(&pdev->dev, | ||
| 218 | "cannot register restart handler (err=%d)\n", err); | ||
| 210 | 219 | ||
| 211 | dev_info(&pdev->dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)", | 220 | dev_info(&pdev->dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)", |
| 212 | sunxi_wdt->wdt_dev.timeout, nowayout); | 221 | sunxi_wdt->wdt_dev.timeout, nowayout); |
| @@ -218,7 +227,7 @@ static int sunxi_wdt_remove(struct platform_device *pdev) | |||
| 218 | { | 227 | { |
| 219 | struct sunxi_wdt_dev *sunxi_wdt = platform_get_drvdata(pdev); | 228 | struct sunxi_wdt_dev *sunxi_wdt = platform_get_drvdata(pdev); |
| 220 | 229 | ||
| 221 | arm_pm_restart = NULL; | 230 | unregister_restart_handler(&sunxi_wdt->restart_handler); |
| 222 | 231 | ||
| 223 | watchdog_unregister_device(&sunxi_wdt->wdt_dev); | 232 | watchdog_unregister_device(&sunxi_wdt->wdt_dev); |
| 224 | watchdog_set_drvdata(&sunxi_wdt->wdt_dev, NULL); | 233 | watchdog_set_drvdata(&sunxi_wdt->wdt_dev, NULL); |
diff --git a/include/linux/reboot.h b/include/linux/reboot.h index 48bf152761c7..67fc8fcdc4b0 100644 --- a/include/linux/reboot.h +++ b/include/linux/reboot.h | |||
| @@ -38,6 +38,9 @@ extern int reboot_force; | |||
| 38 | extern int register_reboot_notifier(struct notifier_block *); | 38 | extern int register_reboot_notifier(struct notifier_block *); |
| 39 | extern int unregister_reboot_notifier(struct notifier_block *); | 39 | extern int unregister_reboot_notifier(struct notifier_block *); |
| 40 | 40 | ||
| 41 | extern int register_restart_handler(struct notifier_block *); | ||
| 42 | extern int unregister_restart_handler(struct notifier_block *); | ||
| 43 | extern void do_kernel_restart(char *cmd); | ||
| 41 | 44 | ||
| 42 | /* | 45 | /* |
| 43 | * Architecture-specific implementations of sys_reboot commands. | 46 | * Architecture-specific implementations of sys_reboot commands. |
diff --git a/kernel/reboot.c b/kernel/reboot.c index a3a9e240fcdb..5925f5ae8dff 100644 --- a/kernel/reboot.c +++ b/kernel/reboot.c | |||
| @@ -104,6 +104,87 @@ int unregister_reboot_notifier(struct notifier_block *nb) | |||
| 104 | } | 104 | } |
| 105 | EXPORT_SYMBOL(unregister_reboot_notifier); | 105 | EXPORT_SYMBOL(unregister_reboot_notifier); |
| 106 | 106 | ||
| 107 | /* | ||
| 108 | * Notifier list for kernel code which wants to be called | ||
| 109 | * to restart the system. | ||
| 110 | */ | ||
| 111 | static ATOMIC_NOTIFIER_HEAD(restart_handler_list); | ||
| 112 | |||
| 113 | /** | ||
| 114 | * register_restart_handler - Register function to be called to reset | ||
| 115 | * the system | ||
| 116 | * @nb: Info about handler function to be called | ||
| 117 | * @nb->priority: Handler priority. Handlers should follow the | ||
| 118 | * following guidelines for setting priorities. | ||
| 119 | * 0: Restart handler of last resort, | ||
| 120 | * with limited restart capabilities | ||
| 121 | * 128: Default restart handler; use if no other | ||
| 122 | * restart handler is expected to be available, | ||
| 123 | * and/or if restart functionality is | ||
| 124 | * sufficient to restart the entire system | ||
| 125 | * 255: Highest priority restart handler, will | ||
| 126 | * preempt all other restart handlers | ||
| 127 | * | ||
| 128 | * Registers a function with code to be called to restart the | ||
| 129 | * system. | ||
| 130 | * | ||
| 131 | * Registered functions will be called from machine_restart as last | ||
| 132 | * step of the restart sequence (if the architecture specific | ||
| 133 | * machine_restart function calls do_kernel_restart - see below | ||
| 134 | * for details). | ||
| 135 | * Registered functions are expected to restart the system immediately. | ||
| 136 | * If more than one function is registered, the restart handler priority | ||
| 137 | * selects which function will be called first. | ||
| 138 | * | ||
| 139 | * Restart handlers are expected to be registered from non-architecture | ||
| 140 | * code, typically from drivers. A typical use case would be a system | ||
| 141 | * where restart functionality is provided through a watchdog. Multiple | ||
| 142 | * restart handlers may exist; for example, one restart handler might | ||
| 143 | * restart the entire system, while another only restarts the CPU. | ||
| 144 | * In such cases, the restart handler which only restarts part of the | ||
| 145 | * hardware is expected to register with low priority to ensure that | ||
| 146 | * it only runs if no other means to restart the system is available. | ||
| 147 | * | ||
| 148 | * Currently always returns zero, as atomic_notifier_chain_register() | ||
| 149 | * always returns zero. | ||
| 150 | */ | ||
| 151 | int register_restart_handler(struct notifier_block *nb) | ||
| 152 | { | ||
| 153 | return atomic_notifier_chain_register(&restart_handler_list, nb); | ||
| 154 | } | ||
| 155 | EXPORT_SYMBOL(register_restart_handler); | ||
| 156 | |||
| 157 | /** | ||
| 158 | * unregister_restart_handler - Unregister previously registered | ||
| 159 | * restart handler | ||
| 160 | * @nb: Hook to be unregistered | ||
| 161 | * | ||
| 162 | * Unregisters a previously registered restart handler function. | ||
| 163 | * | ||
| 164 | * Returns zero on success, or %-ENOENT on failure. | ||
| 165 | */ | ||
| 166 | int unregister_restart_handler(struct notifier_block *nb) | ||
| 167 | { | ||
| 168 | return atomic_notifier_chain_unregister(&restart_handler_list, nb); | ||
| 169 | } | ||
| 170 | EXPORT_SYMBOL(unregister_restart_handler); | ||
| 171 | |||
| 172 | /** | ||
| 173 | * do_kernel_restart - Execute kernel restart handler call chain | ||
| 174 | * | ||
| 175 | * Calls functions registered with register_restart_handler. | ||
| 176 | * | ||
| 177 | * Expected to be called from machine_restart as last step of the restart | ||
| 178 | * sequence. | ||
| 179 | * | ||
| 180 | * Restarts the system immediately if a restart handler function has been | ||
| 181 | * registered. Otherwise does nothing. | ||
| 182 | */ | ||
| 183 | void do_kernel_restart(char *cmd) | ||
| 184 | { | ||
| 185 | atomic_notifier_call_chain(&restart_handler_list, reboot_mode, cmd); | ||
| 186 | } | ||
| 187 | |||
| 107 | void migrate_to_reboot_cpu(void) | 188 | void migrate_to_reboot_cpu(void) |
| 108 | { | 189 | { |
| 109 | /* The boot cpu is always logical cpu 0 */ | 190 | /* The boot cpu is always logical cpu 0 */ |
