diff options
author | Maxime Ripard <maxime.ripard@free-electrons.com> | 2014-07-03 08:07:18 -0400 |
---|---|---|
committer | Maxime Ripard <maxime.ripard@free-electrons.com> | 2014-07-15 07:40:34 -0400 |
commit | ae499f0fadaf28bf3138676fa2d3f6cf7d57556a (patch) | |
tree | db1cb132271288f5dd347746c85d1f0614fca08a | |
parent | ecfe64d8c55f8f210a609cd2eabfcc03f03672a9 (diff) |
power: reset: Add AT91 poweroff driver
Add a driver to handle the shutdown of the Atmel SoCs. This code used to be
(and still is) in arch/arm/mach-at91. We didn't remove it yet so that we can
convert all the boards to using this driver, before removing it entirely in a
separate patch.
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Acked-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
-rw-r--r-- | drivers/power/reset/Kconfig | 8 | ||||
-rw-r--r-- | drivers/power/reset/Makefile | 1 | ||||
-rw-r--r-- | drivers/power/reset/at91-poweroff.c | 156 |
3 files changed, 165 insertions, 0 deletions
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 360bac75a3da..1f9d0c53fc14 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig | |||
@@ -14,6 +14,14 @@ config POWER_RESET_AS3722 | |||
14 | help | 14 | help |
15 | 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. |
16 | 16 | ||
17 | config POWER_RESET_AT91_POWEROFF | ||
18 | bool "Atmel AT91 poweroff driver" | ||
19 | depends on MACH_AT91 | ||
20 | default SOC_AT91SAM9 || SOC_SAMA5 | ||
21 | help | ||
22 | This driver supports poweroff for Atmel AT91SAM9 and SAMA5 | ||
23 | SoCs | ||
24 | |||
17 | config POWER_RESET_AT91_RESET | 25 | config POWER_RESET_AT91_RESET |
18 | bool "Atmel AT91 reset driver" | 26 | bool "Atmel AT91 reset driver" |
19 | depends on MACH_AT91 | 27 | depends on MACH_AT91 |
diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index 1599214b91d9..8bf941bac3da 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile | |||
@@ -1,4 +1,5 @@ | |||
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 | ||
2 | obj-$(CONFIG_POWER_RESET_AT91_RESET) += at91-reset.o | 3 | obj-$(CONFIG_POWER_RESET_AT91_RESET) += at91-reset.o |
3 | obj-$(CONFIG_POWER_RESET_AXXIA) += axxia-reset.o | 4 | obj-$(CONFIG_POWER_RESET_AXXIA) += axxia-reset.o |
4 | obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o | 5 | obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o |
diff --git a/drivers/power/reset/at91-poweroff.c b/drivers/power/reset/at91-poweroff.c new file mode 100644 index 000000000000..40bf42d146f1 --- /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); | ||
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); | ||