diff options
author | Mike Turquette <mturquette@linaro.org> | 2014-10-01 14:19:10 -0400 |
---|---|---|
committer | Mike Turquette <mturquette@linaro.org> | 2014-10-01 14:19:10 -0400 |
commit | a7979009500df9766ba7d78554a6da9dea18dc26 (patch) | |
tree | 81bc6683d6a4c35761a64fb1c2eeaf59af1aff9f | |
parent | e156ee56cbe26c9e8df6619dac1a993245afc1d5 (diff) | |
parent | e317c19470f6d690122519bf9ed1c9f21ea11906 (diff) |
Merge tag 'v3.18-rockchip-clk2' of git://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip into clk-next
Allow parent rate changes for i2s on rk3288
and rockchip as well as s3c24xx restart handlers.
-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 */ |