diff options
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/reset/Kconfig | 42 | ||||
-rw-r--r-- | drivers/power/reset/Makefile | 3 | ||||
-rw-r--r-- | drivers/power/reset/arm-versatile-reboot.c | 111 | ||||
-rw-r--r-- | drivers/power/reset/at91-poweroff.c | 156 | ||||
-rw-r--r-- | drivers/power/reset/at91-reset.c | 252 |
5 files changed, 556 insertions, 8 deletions
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index ca41523bbebf..527a0f47ef44 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig | |||
@@ -6,15 +6,33 @@ menuconfig POWER_RESET | |||
6 | 6 | ||
7 | Say Y here to enable board reset and power off | 7 | Say Y here to enable board reset and power off |
8 | 8 | ||
9 | if POWER_RESET | ||
10 | |||
9 | config POWER_RESET_AS3722 | 11 | config POWER_RESET_AS3722 |
10 | bool "ams AS3722 power-off driver" | 12 | bool "ams AS3722 power-off driver" |
11 | depends on MFD_AS3722 && POWER_RESET | 13 | depends on MFD_AS3722 |
12 | help | 14 | help |
13 | This driver supports turning off board via a ams AS3722 power-off. | 15 | This driver supports turning off board via a ams AS3722 power-off. |
14 | 16 | ||
17 | config POWER_RESET_AT91_POWEROFF | ||
18 | bool "Atmel AT91 poweroff driver" | ||
19 | depends on ARCH_AT91 | ||
20 | default SOC_AT91SAM9 || SOC_SAMA5 | ||
21 | help | ||
22 | This driver supports poweroff for Atmel AT91SAM9 and SAMA5 | ||
23 | SoCs | ||
24 | |||
25 | config POWER_RESET_AT91_RESET | ||
26 | bool "Atmel AT91 reset driver" | ||
27 | depends on ARCH_AT91 | ||
28 | default SOC_AT91SAM9 || SOC_SAMA5 | ||
29 | help | ||
30 | This driver supports restart for Atmel AT91SAM9 and SAMA5 | ||
31 | SoCs | ||
32 | |||
15 | config POWER_RESET_AXXIA | 33 | config POWER_RESET_AXXIA |
16 | bool "LSI Axxia reset driver" | 34 | bool "LSI Axxia reset driver" |
17 | depends on POWER_RESET && ARCH_AXXIA | 35 | depends on ARCH_AXXIA |
18 | help | 36 | help |
19 | This driver supports restart for Axxia SoC. | 37 | This driver supports restart for Axxia SoC. |
20 | 38 | ||
@@ -33,7 +51,7 @@ config POWER_RESET_BRCMSTB | |||
33 | 51 | ||
34 | config POWER_RESET_GPIO | 52 | config POWER_RESET_GPIO |
35 | bool "GPIO power-off driver" | 53 | bool "GPIO power-off driver" |
36 | depends on OF_GPIO && POWER_RESET | 54 | depends on OF_GPIO |
37 | help | 55 | help |
38 | This driver supports turning off your board via a GPIO line. | 56 | This driver supports turning off your board via a GPIO line. |
39 | If your board needs a GPIO high/low to power down, say Y and | 57 | If your board needs a GPIO high/low to power down, say Y and |
@@ -47,13 +65,13 @@ config POWER_RESET_HISI | |||
47 | 65 | ||
48 | config POWER_RESET_MSM | 66 | config POWER_RESET_MSM |
49 | bool "Qualcomm MSM power-off driver" | 67 | bool "Qualcomm MSM power-off driver" |
50 | depends on POWER_RESET && ARCH_QCOM | 68 | depends on ARCH_QCOM |
51 | help | 69 | help |
52 | Power off and restart support for Qualcomm boards. | 70 | Power off and restart support for Qualcomm boards. |
53 | 71 | ||
54 | config POWER_RESET_QNAP | 72 | config POWER_RESET_QNAP |
55 | bool "QNAP power-off driver" | 73 | bool "QNAP power-off driver" |
56 | depends on OF_GPIO && POWER_RESET && PLAT_ORION | 74 | depends on OF_GPIO && PLAT_ORION |
57 | help | 75 | help |
58 | This driver supports turning off QNAP NAS devices by sending | 76 | This driver supports turning off QNAP NAS devices by sending |
59 | commands to the microcontroller which controls the main power. | 77 | commands to the microcontroller which controls the main power. |
@@ -71,14 +89,22 @@ config POWER_RESET_RESTART | |||
71 | config POWER_RESET_SUN6I | 89 | config POWER_RESET_SUN6I |
72 | bool "Allwinner A31 SoC reset driver" | 90 | bool "Allwinner A31 SoC reset driver" |
73 | depends on ARCH_SUNXI | 91 | depends on ARCH_SUNXI |
74 | depends on POWER_RESET | ||
75 | help | 92 | help |
76 | Reboot support for the Allwinner A31 SoCs. | 93 | Reboot support for the Allwinner A31 SoCs. |
77 | 94 | ||
95 | config POWER_RESET_VERSATILE | ||
96 | bool "ARM Versatile family reboot driver" | ||
97 | depends on ARM | ||
98 | depends on MFD_SYSCON | ||
99 | depends on OF | ||
100 | help | ||
101 | Power off and restart support for ARM Versatile family of | ||
102 | reference boards. | ||
103 | |||
78 | config POWER_RESET_VEXPRESS | 104 | config POWER_RESET_VEXPRESS |
79 | bool "ARM Versatile Express power-off and reset driver" | 105 | bool "ARM Versatile Express power-off and reset driver" |
80 | depends on ARM || ARM64 | 106 | depends on ARM || ARM64 |
81 | depends on POWER_RESET && VEXPRESS_CONFIG | 107 | depends on VEXPRESS_CONFIG |
82 | help | 108 | help |
83 | Power off and reset support for the ARM Ltd. Versatile | 109 | Power off and reset support for the ARM Ltd. Versatile |
84 | Express boards. | 110 | Express boards. |
@@ -86,7 +112,6 @@ config POWER_RESET_VEXPRESS | |||
86 | config POWER_RESET_XGENE | 112 | config POWER_RESET_XGENE |
87 | bool "APM SoC X-Gene reset driver" | 113 | bool "APM SoC X-Gene reset driver" |
88 | depends on ARM64 | 114 | depends on ARM64 |
89 | depends on POWER_RESET | ||
90 | help | 115 | help |
91 | Reboot support for the APM SoC X-Gene Eval boards. | 116 | Reboot support for the APM SoC X-Gene Eval boards. |
92 | 117 | ||
@@ -97,3 +122,4 @@ config POWER_RESET_KEYSTONE | |||
97 | help | 122 | help |
98 | Reboot support for the KEYSTONE SoCs. | 123 | Reboot support for the KEYSTONE SoCs. |
99 | 124 | ||
125 | endif | ||
diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index a42e70edd037..73221009f2bf 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile | |||
@@ -1,4 +1,6 @@ | |||
1 | obj-$(CONFIG_POWER_RESET_AS3722) += as3722-poweroff.o | 1 | obj-$(CONFIG_POWER_RESET_AS3722) += as3722-poweroff.o |
2 | obj-$(CONFIG_POWER_RESET_AT91_POWEROFF) += at91-poweroff.o | ||
3 | obj-$(CONFIG_POWER_RESET_AT91_RESET) += at91-reset.o | ||
2 | obj-$(CONFIG_POWER_RESET_AXXIA) += axxia-reset.o | 4 | obj-$(CONFIG_POWER_RESET_AXXIA) += axxia-reset.o |
3 | obj-$(CONFIG_POWER_RESET_BRCMSTB) += brcmstb-reboot.o | 5 | obj-$(CONFIG_POWER_RESET_BRCMSTB) += brcmstb-reboot.o |
4 | obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o | 6 | obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o |
@@ -7,6 +9,7 @@ obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o | |||
7 | obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o | 9 | obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o |
8 | obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o | 10 | obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o |
9 | obj-$(CONFIG_POWER_RESET_SUN6I) += sun6i-reboot.o | 11 | obj-$(CONFIG_POWER_RESET_SUN6I) += sun6i-reboot.o |
12 | obj-$(CONFIG_POWER_RESET_VERSATILE) += arm-versatile-reboot.o | ||
10 | obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o | 13 | obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o |
11 | obj-$(CONFIG_POWER_RESET_XGENE) += xgene-reboot.o | 14 | obj-$(CONFIG_POWER_RESET_XGENE) += xgene-reboot.o |
12 | obj-$(CONFIG_POWER_RESET_KEYSTONE) += keystone-reset.o | 15 | obj-$(CONFIG_POWER_RESET_KEYSTONE) += keystone-reset.o |
diff --git a/drivers/power/reset/arm-versatile-reboot.c b/drivers/power/reset/arm-versatile-reboot.c new file mode 100644 index 000000000000..5b08bffcf1a8 --- /dev/null +++ b/drivers/power/reset/arm-versatile-reboot.c | |||
@@ -0,0 +1,111 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014 Linaro Ltd. | ||
3 | * | ||
4 | * Author: Linus Walleij <linus.walleij@linaro.org> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2, as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | */ | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/mfd/syscon.h> | ||
13 | #include <linux/reboot.h> | ||
14 | #include <linux/regmap.h> | ||
15 | #include <linux/of.h> | ||
16 | #include <asm/system_misc.h> | ||
17 | |||
18 | #define REALVIEW_SYS_LOCK_OFFSET 0x20 | ||
19 | #define REALVIEW_SYS_LOCK_VAL 0xA05F | ||
20 | #define REALVIEW_SYS_RESETCTL_OFFSET 0x40 | ||
21 | |||
22 | /* | ||
23 | * We detect the different syscon types from the compatible strings. | ||
24 | */ | ||
25 | enum versatile_reboot { | ||
26 | REALVIEW_REBOOT_EB, | ||
27 | REALVIEW_REBOOT_PB1176, | ||
28 | REALVIEW_REBOOT_PB11MP, | ||
29 | REALVIEW_REBOOT_PBA8, | ||
30 | REALVIEW_REBOOT_PBX, | ||
31 | }; | ||
32 | |||
33 | /* Pointer to the system controller */ | ||
34 | static struct regmap *syscon_regmap; | ||
35 | static enum versatile_reboot versatile_reboot_type; | ||
36 | |||
37 | static const struct of_device_id versatile_reboot_of_match[] = { | ||
38 | { | ||
39 | .compatible = "arm,realview-eb-syscon", | ||
40 | .data = (void *)REALVIEW_REBOOT_EB, | ||
41 | }, | ||
42 | { | ||
43 | .compatible = "arm,realview-pb1176-syscon", | ||
44 | .data = (void *)REALVIEW_REBOOT_PB1176, | ||
45 | }, | ||
46 | { | ||
47 | .compatible = "arm,realview-pb11mp-syscon", | ||
48 | .data = (void *)REALVIEW_REBOOT_PB11MP, | ||
49 | }, | ||
50 | { | ||
51 | .compatible = "arm,realview-pba8-syscon", | ||
52 | .data = (void *)REALVIEW_REBOOT_PBA8, | ||
53 | }, | ||
54 | { | ||
55 | .compatible = "arm,realview-pbx-syscon", | ||
56 | .data = (void *)REALVIEW_REBOOT_PBX, | ||
57 | }, | ||
58 | }; | ||
59 | |||
60 | static void versatile_reboot(enum reboot_mode mode, const char *cmd) | ||
61 | { | ||
62 | /* Unlock the reset register */ | ||
63 | regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET, | ||
64 | REALVIEW_SYS_LOCK_VAL); | ||
65 | /* Then hit reset on the different machines */ | ||
66 | switch (versatile_reboot_type) { | ||
67 | case REALVIEW_REBOOT_EB: | ||
68 | regmap_write(syscon_regmap, | ||
69 | REALVIEW_SYS_RESETCTL_OFFSET, 0x0008); | ||
70 | break; | ||
71 | case REALVIEW_REBOOT_PB1176: | ||
72 | regmap_write(syscon_regmap, | ||
73 | REALVIEW_SYS_RESETCTL_OFFSET, 0x0100); | ||
74 | break; | ||
75 | case REALVIEW_REBOOT_PB11MP: | ||
76 | case REALVIEW_REBOOT_PBA8: | ||
77 | regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, | ||
78 | 0x0000); | ||
79 | regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, | ||
80 | 0x0004); | ||
81 | break; | ||
82 | case REALVIEW_REBOOT_PBX: | ||
83 | regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, | ||
84 | 0x00f0); | ||
85 | regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, | ||
86 | 0x00f4); | ||
87 | break; | ||
88 | } | ||
89 | dsb(); | ||
90 | } | ||
91 | |||
92 | static int __init versatile_reboot_probe(void) | ||
93 | { | ||
94 | const struct of_device_id *reboot_id; | ||
95 | struct device_node *np; | ||
96 | |||
97 | np = of_find_matching_node_and_match(NULL, versatile_reboot_of_match, | ||
98 | &reboot_id); | ||
99 | if (!np) | ||
100 | return -ENODEV; | ||
101 | versatile_reboot_type = (enum versatile_reboot)reboot_id->data; | ||
102 | |||
103 | syscon_regmap = syscon_node_to_regmap(np); | ||
104 | if (IS_ERR(syscon_regmap)) | ||
105 | return PTR_ERR(syscon_regmap); | ||
106 | |||
107 | arm_pm_restart = versatile_reboot; | ||
108 | pr_info("versatile reboot driver registered\n"); | ||
109 | return 0; | ||
110 | } | ||
111 | device_initcall(versatile_reboot_probe); | ||
diff --git a/drivers/power/reset/at91-poweroff.c b/drivers/power/reset/at91-poweroff.c new file mode 100644 index 000000000000..c61000333bb9 --- /dev/null +++ b/drivers/power/reset/at91-poweroff.c | |||
@@ -0,0 +1,156 @@ | |||
1 | /* | ||
2 | * Atmel AT91 SAM9 SoCs reset code | ||
3 | * | ||
4 | * Copyright (C) 2007 Atmel Corporation. | ||
5 | * Copyright (C) 2011 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> | ||
6 | * Copyright (C) 2014 Free Electrons | ||
7 | * | ||
8 | * This file is licensed under the terms of the GNU General Public | ||
9 | * License version 2. This program is licensed "as is" without any | ||
10 | * warranty of any kind, whether express or implied. | ||
11 | */ | ||
12 | |||
13 | #include <linux/io.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/of.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | #include <linux/printk.h> | ||
18 | |||
19 | #define AT91_SHDW_CR 0x00 /* Shut Down Control Register */ | ||
20 | #define AT91_SHDW_SHDW BIT(0) /* Shut Down command */ | ||
21 | #define AT91_SHDW_KEY (0xa5 << 24) /* KEY Password */ | ||
22 | |||
23 | #define AT91_SHDW_MR 0x04 /* Shut Down Mode Register */ | ||
24 | #define AT91_SHDW_WKMODE0 GENMASK(2, 0) /* Wake-up 0 Mode Selection */ | ||
25 | #define AT91_SHDW_CPTWK0_MAX 0xf /* Maximum Counter On Wake Up 0 */ | ||
26 | #define AT91_SHDW_CPTWK0 (AT91_SHDW_CPTWK0_MAX << 4) /* Counter On Wake Up 0 */ | ||
27 | #define AT91_SHDW_CPTWK0_(x) ((x) << 4) | ||
28 | #define AT91_SHDW_RTTWKEN BIT(16) /* Real Time Timer Wake-up Enable */ | ||
29 | #define AT91_SHDW_RTCWKEN BIT(17) /* Real Time Clock Wake-up Enable */ | ||
30 | |||
31 | #define AT91_SHDW_SR 0x08 /* Shut Down Status Register */ | ||
32 | #define AT91_SHDW_WAKEUP0 BIT(0) /* Wake-up 0 Status */ | ||
33 | #define AT91_SHDW_RTTWK BIT(16) /* Real-time Timer Wake-up */ | ||
34 | #define AT91_SHDW_RTCWK BIT(17) /* Real-time Clock Wake-up [SAM9RL] */ | ||
35 | |||
36 | enum wakeup_type { | ||
37 | AT91_SHDW_WKMODE0_NONE = 0, | ||
38 | AT91_SHDW_WKMODE0_HIGH = 1, | ||
39 | AT91_SHDW_WKMODE0_LOW = 2, | ||
40 | AT91_SHDW_WKMODE0_ANYLEVEL = 3, | ||
41 | }; | ||
42 | |||
43 | static const char *shdwc_wakeup_modes[] = { | ||
44 | [AT91_SHDW_WKMODE0_NONE] = "none", | ||
45 | [AT91_SHDW_WKMODE0_HIGH] = "high", | ||
46 | [AT91_SHDW_WKMODE0_LOW] = "low", | ||
47 | [AT91_SHDW_WKMODE0_ANYLEVEL] = "any", | ||
48 | }; | ||
49 | |||
50 | static void __iomem *at91_shdwc_base; | ||
51 | |||
52 | static void __init at91_wakeup_status(void) | ||
53 | { | ||
54 | u32 reg = readl(at91_shdwc_base + AT91_SHDW_SR); | ||
55 | char *reason = "unknown"; | ||
56 | |||
57 | /* Simple power-on, just bail out */ | ||
58 | if (!reg) | ||
59 | return; | ||
60 | |||
61 | if (reg & AT91_SHDW_RTTWK) | ||
62 | reason = "RTT"; | ||
63 | else if (reg & AT91_SHDW_RTCWK) | ||
64 | reason = "RTC"; | ||
65 | |||
66 | pr_info("AT91: Wake-Up source: %s\n", reason); | ||
67 | } | ||
68 | |||
69 | static void at91_poweroff(void) | ||
70 | { | ||
71 | writel(AT91_SHDW_KEY | AT91_SHDW_SHDW, at91_shdwc_base + AT91_SHDW_CR); | ||
72 | } | ||
73 | |||
74 | const enum wakeup_type at91_poweroff_get_wakeup_mode(struct device_node *np) | ||
75 | { | ||
76 | const char *pm; | ||
77 | int err, i; | ||
78 | |||
79 | err = of_property_read_string(np, "atmel,wakeup-mode", &pm); | ||
80 | if (err < 0) | ||
81 | return AT91_SHDW_WKMODE0_ANYLEVEL; | ||
82 | |||
83 | for (i = 0; i < ARRAY_SIZE(shdwc_wakeup_modes); i++) | ||
84 | if (!strcasecmp(pm, shdwc_wakeup_modes[i])) | ||
85 | return i; | ||
86 | |||
87 | return -ENODEV; | ||
88 | } | ||
89 | |||
90 | static void at91_poweroff_dt_set_wakeup_mode(struct platform_device *pdev) | ||
91 | { | ||
92 | struct device_node *np = pdev->dev.of_node; | ||
93 | enum wakeup_type wakeup_mode; | ||
94 | u32 mode = 0, tmp; | ||
95 | |||
96 | wakeup_mode = at91_poweroff_get_wakeup_mode(np); | ||
97 | if (wakeup_mode < 0) { | ||
98 | dev_warn(&pdev->dev, "shdwc unknown wakeup mode\n"); | ||
99 | return; | ||
100 | } | ||
101 | |||
102 | if (!of_property_read_u32(np, "atmel,wakeup-counter", &tmp)) { | ||
103 | if (tmp > AT91_SHDW_CPTWK0_MAX) { | ||
104 | dev_warn(&pdev->dev, | ||
105 | "shdwc wakeup counter 0x%x > 0x%x reduce it to 0x%x\n", | ||
106 | tmp, AT91_SHDW_CPTWK0_MAX, AT91_SHDW_CPTWK0_MAX); | ||
107 | tmp = AT91_SHDW_CPTWK0_MAX; | ||
108 | } | ||
109 | mode |= AT91_SHDW_CPTWK0_(tmp); | ||
110 | } | ||
111 | |||
112 | if (of_property_read_bool(np, "atmel,wakeup-rtc-timer")) | ||
113 | mode |= AT91_SHDW_RTCWKEN; | ||
114 | |||
115 | if (of_property_read_bool(np, "atmel,wakeup-rtt-timer")) | ||
116 | mode |= AT91_SHDW_RTTWKEN; | ||
117 | |||
118 | writel(wakeup_mode | mode, at91_shdwc_base + AT91_SHDW_MR); | ||
119 | } | ||
120 | |||
121 | static int at91_poweroff_probe(struct platform_device *pdev) | ||
122 | { | ||
123 | struct resource *res; | ||
124 | |||
125 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
126 | at91_shdwc_base = devm_ioremap_resource(&pdev->dev, res); | ||
127 | if (IS_ERR(at91_shdwc_base)) { | ||
128 | dev_err(&pdev->dev, "Could not map reset controller address\n"); | ||
129 | return PTR_ERR(at91_shdwc_base); | ||
130 | } | ||
131 | |||
132 | at91_wakeup_status(); | ||
133 | |||
134 | if (pdev->dev.of_node) | ||
135 | at91_poweroff_dt_set_wakeup_mode(pdev); | ||
136 | |||
137 | pm_power_off = at91_poweroff; | ||
138 | |||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | static struct of_device_id at91_poweroff_of_match[] = { | ||
143 | { .compatible = "atmel,at91sam9260-shdwc", }, | ||
144 | { .compatible = "atmel,at91sam9rl-shdwc", }, | ||
145 | { .compatible = "atmel,at91sam9x5-shdwc", }, | ||
146 | { /*sentinel*/ } | ||
147 | }; | ||
148 | |||
149 | static struct platform_driver at91_poweroff_driver = { | ||
150 | .probe = at91_poweroff_probe, | ||
151 | .driver = { | ||
152 | .name = "at91-poweroff", | ||
153 | .of_match_table = at91_poweroff_of_match, | ||
154 | }, | ||
155 | }; | ||
156 | module_platform_driver(at91_poweroff_driver); | ||
diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c new file mode 100644 index 000000000000..3611806c9cfd --- /dev/null +++ b/drivers/power/reset/at91-reset.c | |||
@@ -0,0 +1,252 @@ | |||
1 | /* | ||
2 | * Atmel AT91 SAM9 SoCs reset code | ||
3 | * | ||
4 | * Copyright (C) 2007 Atmel Corporation. | ||
5 | * Copyright (C) BitBox Ltd 2010 | ||
6 | * Copyright (C) 2011 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcosoft.com> | ||
7 | * Copyright (C) 2014 Free Electrons | ||
8 | * | ||
9 | * This file is licensed under the terms of the GNU General Public | ||
10 | * License version 2. This program is licensed "as is" without any | ||
11 | * warranty of any kind, whether express or implied. | ||
12 | */ | ||
13 | |||
14 | #include <linux/io.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/of_address.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/reboot.h> | ||
19 | |||
20 | #include <asm/system_misc.h> | ||
21 | |||
22 | #include <mach/at91sam9_ddrsdr.h> | ||
23 | #include <mach/at91sam9_sdramc.h> | ||
24 | |||
25 | #define AT91_RSTC_CR 0x00 /* Reset Controller Control Register */ | ||
26 | #define AT91_RSTC_PROCRST BIT(0) /* Processor Reset */ | ||
27 | #define AT91_RSTC_PERRST BIT(2) /* Peripheral Reset */ | ||
28 | #define AT91_RSTC_EXTRST BIT(3) /* External Reset */ | ||
29 | #define AT91_RSTC_KEY (0xa5 << 24) /* KEY Password */ | ||
30 | |||
31 | #define AT91_RSTC_SR 0x04 /* Reset Controller Status Register */ | ||
32 | #define AT91_RSTC_URSTS BIT(0) /* User Reset Status */ | ||
33 | #define AT91_RSTC_RSTTYP GENMASK(10, 8) /* Reset Type */ | ||
34 | #define AT91_RSTC_NRSTL BIT(16) /* NRST Pin Level */ | ||
35 | #define AT91_RSTC_SRCMP BIT(17) /* Software Reset Command in Progress */ | ||
36 | |||
37 | #define AT91_RSTC_MR 0x08 /* Reset Controller Mode Register */ | ||
38 | #define AT91_RSTC_URSTEN BIT(0) /* User Reset Enable */ | ||
39 | #define AT91_RSTC_URSTIEN BIT(4) /* User Reset Interrupt Enable */ | ||
40 | #define AT91_RSTC_ERSTL GENMASK(11, 8) /* External Reset Length */ | ||
41 | |||
42 | enum reset_type { | ||
43 | RESET_TYPE_GENERAL = 0, | ||
44 | RESET_TYPE_WAKEUP = 1, | ||
45 | RESET_TYPE_WATCHDOG = 2, | ||
46 | RESET_TYPE_SOFTWARE = 3, | ||
47 | RESET_TYPE_USER = 4, | ||
48 | }; | ||
49 | |||
50 | static void __iomem *at91_ramc_base[2], *at91_rstc_base; | ||
51 | |||
52 | /* | ||
53 | * unless the SDRAM is cleanly shutdown before we hit the | ||
54 | * reset register it can be left driving the data bus and | ||
55 | * killing the chance of a subsequent boot from NAND | ||
56 | */ | ||
57 | static void at91sam9260_restart(enum reboot_mode mode, const char *cmd) | ||
58 | { | ||
59 | asm volatile( | ||
60 | /* Align to cache lines */ | ||
61 | ".balign 32\n\t" | ||
62 | |||
63 | /* Disable SDRAM accesses */ | ||
64 | "str %2, [%0, #" __stringify(AT91_SDRAMC_TR) "]\n\t" | ||
65 | |||
66 | /* Power down SDRAM */ | ||
67 | "str %3, [%0, #" __stringify(AT91_SDRAMC_LPR) "]\n\t" | ||
68 | |||
69 | /* Reset CPU */ | ||
70 | "str %4, [%1, #" __stringify(AT91_RSTC_CR) "]\n\t" | ||
71 | |||
72 | "b .\n\t" | ||
73 | : | ||
74 | : "r" (at91_ramc_base[0]), | ||
75 | "r" (at91_rstc_base), | ||
76 | "r" (1), | ||
77 | "r" (AT91_SDRAMC_LPCB_POWER_DOWN), | ||
78 | "r" (AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST)); | ||
79 | } | ||
80 | |||
81 | static void at91sam9g45_restart(enum reboot_mode mode, const char *cmd) | ||
82 | { | ||
83 | asm volatile( | ||
84 | /* | ||
85 | * Test wether we have a second RAM controller to care | ||
86 | * about. | ||
87 | * | ||
88 | * First, test that we can dereference the virtual address. | ||
89 | */ | ||
90 | "cmp %1, #0\n\t" | ||
91 | "beq 1f\n\t" | ||
92 | |||
93 | /* Then, test that the RAM controller is enabled */ | ||
94 | "ldr r0, [%1]\n\t" | ||
95 | "cmp r0, #0\n\t" | ||
96 | |||
97 | /* Align to cache lines */ | ||
98 | ".balign 32\n\t" | ||
99 | |||
100 | /* Disable SDRAM0 accesses */ | ||
101 | "1: str %3, [%0, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t" | ||
102 | /* Power down SDRAM0 */ | ||
103 | " str %4, [%0, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t" | ||
104 | /* Disable SDRAM1 accesses */ | ||
105 | " strne %3, [%1, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t" | ||
106 | /* Power down SDRAM1 */ | ||
107 | " strne %4, [%1, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t" | ||
108 | /* Reset CPU */ | ||
109 | " str %5, [%2, #" __stringify(AT91_RSTC_CR) "]\n\t" | ||
110 | |||
111 | " b .\n\t" | ||
112 | : | ||
113 | : "r" (at91_ramc_base[0]), | ||
114 | "r" (at91_ramc_base[1]), | ||
115 | "r" (at91_rstc_base), | ||
116 | "r" (1), | ||
117 | "r" (AT91_DDRSDRC_LPCB_POWER_DOWN), | ||
118 | "r" (AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST) | ||
119 | : "r0"); | ||
120 | } | ||
121 | |||
122 | static void __init at91_reset_status(struct platform_device *pdev) | ||
123 | { | ||
124 | u32 reg = readl(at91_rstc_base + AT91_RSTC_SR); | ||
125 | char *reason; | ||
126 | |||
127 | switch ((reg & AT91_RSTC_RSTTYP) >> 8) { | ||
128 | case RESET_TYPE_GENERAL: | ||
129 | reason = "general reset"; | ||
130 | break; | ||
131 | case RESET_TYPE_WAKEUP: | ||
132 | reason = "wakeup"; | ||
133 | break; | ||
134 | case RESET_TYPE_WATCHDOG: | ||
135 | reason = "watchdog reset"; | ||
136 | break; | ||
137 | case RESET_TYPE_SOFTWARE: | ||
138 | reason = "software reset"; | ||
139 | break; | ||
140 | case RESET_TYPE_USER: | ||
141 | reason = "user reset"; | ||
142 | break; | ||
143 | default: | ||
144 | reason = "unknown reset"; | ||
145 | break; | ||
146 | } | ||
147 | |||
148 | pr_info("AT91: Starting after %s\n", reason); | ||
149 | } | ||
150 | |||
151 | static struct of_device_id at91_ramc_of_match[] = { | ||
152 | { .compatible = "atmel,at91sam9260-sdramc", }, | ||
153 | { .compatible = "atmel,at91sam9g45-ddramc", }, | ||
154 | { .compatible = "atmel,sama5d3-ddramc", }, | ||
155 | { /* sentinel */ } | ||
156 | }; | ||
157 | |||
158 | static struct of_device_id at91_reset_of_match[] = { | ||
159 | { .compatible = "atmel,at91sam9260-rstc", .data = at91sam9260_restart }, | ||
160 | { .compatible = "atmel,at91sam9g45-rstc", .data = at91sam9g45_restart }, | ||
161 | { /* sentinel */ } | ||
162 | }; | ||
163 | |||
164 | static int at91_reset_of_probe(struct platform_device *pdev) | ||
165 | { | ||
166 | const struct of_device_id *match; | ||
167 | struct device_node *np; | ||
168 | int idx = 0; | ||
169 | |||
170 | at91_rstc_base = of_iomap(pdev->dev.of_node, 0); | ||
171 | if (!at91_rstc_base) { | ||
172 | dev_err(&pdev->dev, "Could not map reset controller address\n"); | ||
173 | return -ENODEV; | ||
174 | } | ||
175 | |||
176 | for_each_matching_node(np, at91_ramc_of_match) { | ||
177 | at91_ramc_base[idx] = of_iomap(np, 0); | ||
178 | if (!at91_ramc_base[idx]) { | ||
179 | dev_err(&pdev->dev, "Could not map ram controller address\n"); | ||
180 | return -ENODEV; | ||
181 | } | ||
182 | idx++; | ||
183 | } | ||
184 | |||
185 | match = of_match_node(at91_reset_of_match, pdev->dev.of_node); | ||
186 | arm_pm_restart = match->data; | ||
187 | |||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | static int at91_reset_platform_probe(struct platform_device *pdev) | ||
192 | { | ||
193 | const struct platform_device_id *match; | ||
194 | struct resource *res; | ||
195 | int idx = 0; | ||
196 | |||
197 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
198 | at91_rstc_base = devm_ioremap_resource(&pdev->dev, res); | ||
199 | if (IS_ERR(at91_rstc_base)) { | ||
200 | dev_err(&pdev->dev, "Could not map reset controller address\n"); | ||
201 | return PTR_ERR(at91_rstc_base); | ||
202 | } | ||
203 | |||
204 | for (idx = 0; idx < 2; idx++) { | ||
205 | res = platform_get_resource(pdev, IORESOURCE_MEM, idx + 1 ); | ||
206 | at91_ramc_base[idx] = devm_ioremap(&pdev->dev, res->start, | ||
207 | resource_size(res)); | ||
208 | if (IS_ERR(at91_ramc_base[idx])) { | ||
209 | dev_err(&pdev->dev, "Could not map ram controller address\n"); | ||
210 | return PTR_ERR(at91_ramc_base[idx]); | ||
211 | } | ||
212 | } | ||
213 | |||
214 | match = platform_get_device_id(pdev); | ||
215 | arm_pm_restart = (void (*)(enum reboot_mode, const char*)) | ||
216 | match->driver_data; | ||
217 | |||
218 | return 0; | ||
219 | } | ||
220 | |||
221 | static int at91_reset_probe(struct platform_device *pdev) | ||
222 | { | ||
223 | int ret; | ||
224 | |||
225 | if (pdev->dev.of_node) | ||
226 | ret = at91_reset_of_probe(pdev); | ||
227 | else | ||
228 | ret = at91_reset_platform_probe(pdev); | ||
229 | |||
230 | if (ret) | ||
231 | return ret; | ||
232 | |||
233 | at91_reset_status(pdev); | ||
234 | |||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | static struct platform_device_id at91_reset_plat_match[] = { | ||
239 | { "at91-sam9260-reset", (unsigned long)at91sam9260_restart }, | ||
240 | { "at91-sam9g45-reset", (unsigned long)at91sam9g45_restart }, | ||
241 | { /* sentinel */ } | ||
242 | }; | ||
243 | |||
244 | static struct platform_driver at91_reset_driver = { | ||
245 | .probe = at91_reset_probe, | ||
246 | .driver = { | ||
247 | .name = "at91-reset", | ||
248 | .of_match_table = at91_reset_of_match, | ||
249 | }, | ||
250 | .id_table = at91_reset_plat_match, | ||
251 | }; | ||
252 | module_platform_driver(at91_reset_driver); | ||