1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Poweroff & reset driver for Actions Semi ATC260x PMICs |
4 | * |
5 | * Copyright (c) 2020 Cristian Ciocaltea <cristian.ciocaltea@gmail.com> |
6 | */ |
7 | |
8 | #include <linux/delay.h> |
9 | #include <linux/mfd/atc260x/core.h> |
10 | #include <linux/module.h> |
11 | #include <linux/platform_device.h> |
12 | #include <linux/power_supply.h> |
13 | #include <linux/reboot.h> |
14 | #include <linux/regmap.h> |
15 | |
16 | struct atc260x_pwrc { |
17 | struct device *dev; |
18 | struct regmap *regmap; |
19 | int (*do_poweroff)(const struct atc260x_pwrc *pwrc, bool restart); |
20 | }; |
21 | |
22 | static int atc2603c_do_poweroff(const struct atc260x_pwrc *pwrc, bool restart) |
23 | { |
24 | int ret, deep_sleep = 0; |
25 | uint reg_mask, reg_val; |
26 | |
27 | /* S4-Deep Sleep Mode is NOT available for WALL/USB power */ |
28 | if (!restart && !power_supply_is_system_supplied()) { |
29 | deep_sleep = 1; |
30 | dev_info(pwrc->dev, "Enabling S4-Deep Sleep Mode" ); |
31 | } |
32 | |
33 | /* Update wakeup sources */ |
34 | reg_val = ATC2603C_PMU_SYS_CTL0_ONOFF_LONG_WK_EN | |
35 | (restart ? ATC2603C_PMU_SYS_CTL0_RESET_WK_EN |
36 | : ATC2603C_PMU_SYS_CTL0_ONOFF_SHORT_WK_EN); |
37 | |
38 | ret = regmap_update_bits(map: pwrc->regmap, ATC2603C_PMU_SYS_CTL0, |
39 | ATC2603C_PMU_SYS_CTL0_WK_ALL, val: reg_val); |
40 | if (ret) |
41 | dev_warn(pwrc->dev, "failed to write SYS_CTL0: %d\n" , ret); |
42 | |
43 | /* Update power mode */ |
44 | reg_mask = ATC2603C_PMU_SYS_CTL3_EN_S2 | ATC2603C_PMU_SYS_CTL3_EN_S3; |
45 | |
46 | ret = regmap_update_bits(map: pwrc->regmap, ATC2603C_PMU_SYS_CTL3, mask: reg_mask, |
47 | val: deep_sleep ? 0 : ATC2603C_PMU_SYS_CTL3_EN_S3); |
48 | if (ret) { |
49 | dev_err(pwrc->dev, "failed to write SYS_CTL3: %d\n" , ret); |
50 | return ret; |
51 | } |
52 | |
53 | /* Trigger poweroff / restart sequence */ |
54 | reg_mask = restart ? ATC2603C_PMU_SYS_CTL0_RESTART_EN |
55 | : ATC2603C_PMU_SYS_CTL1_EN_S1; |
56 | reg_val = restart ? ATC2603C_PMU_SYS_CTL0_RESTART_EN : 0; |
57 | |
58 | ret = regmap_update_bits(map: pwrc->regmap, |
59 | reg: restart ? ATC2603C_PMU_SYS_CTL0 : ATC2603C_PMU_SYS_CTL1, |
60 | mask: reg_mask, val: reg_val); |
61 | if (ret) { |
62 | dev_err(pwrc->dev, "failed to write SYS_CTL%d: %d\n" , |
63 | restart ? 0 : 1, ret); |
64 | return ret; |
65 | } |
66 | |
67 | /* Wait for trigger completion */ |
68 | mdelay(200); |
69 | |
70 | return 0; |
71 | } |
72 | |
73 | static int atc2609a_do_poweroff(const struct atc260x_pwrc *pwrc, bool restart) |
74 | { |
75 | int ret, deep_sleep = 0; |
76 | uint reg_mask, reg_val; |
77 | |
78 | /* S4-Deep Sleep Mode is NOT available for WALL/USB power */ |
79 | if (!restart && !power_supply_is_system_supplied()) { |
80 | deep_sleep = 1; |
81 | dev_info(pwrc->dev, "Enabling S4-Deep Sleep Mode" ); |
82 | } |
83 | |
84 | /* Update wakeup sources */ |
85 | reg_val = ATC2609A_PMU_SYS_CTL0_ONOFF_LONG_WK_EN | |
86 | (restart ? ATC2609A_PMU_SYS_CTL0_RESET_WK_EN |
87 | : ATC2609A_PMU_SYS_CTL0_ONOFF_SHORT_WK_EN); |
88 | |
89 | ret = regmap_update_bits(map: pwrc->regmap, ATC2609A_PMU_SYS_CTL0, |
90 | ATC2609A_PMU_SYS_CTL0_WK_ALL, val: reg_val); |
91 | if (ret) |
92 | dev_warn(pwrc->dev, "failed to write SYS_CTL0: %d\n" , ret); |
93 | |
94 | /* Update power mode */ |
95 | reg_mask = ATC2609A_PMU_SYS_CTL3_EN_S2 | ATC2609A_PMU_SYS_CTL3_EN_S3; |
96 | |
97 | ret = regmap_update_bits(map: pwrc->regmap, ATC2609A_PMU_SYS_CTL3, mask: reg_mask, |
98 | val: deep_sleep ? 0 : ATC2609A_PMU_SYS_CTL3_EN_S3); |
99 | if (ret) { |
100 | dev_err(pwrc->dev, "failed to write SYS_CTL3: %d\n" , ret); |
101 | return ret; |
102 | } |
103 | |
104 | /* Trigger poweroff / restart sequence */ |
105 | reg_mask = restart ? ATC2609A_PMU_SYS_CTL0_RESTART_EN |
106 | : ATC2609A_PMU_SYS_CTL1_EN_S1; |
107 | reg_val = restart ? ATC2609A_PMU_SYS_CTL0_RESTART_EN : 0; |
108 | |
109 | ret = regmap_update_bits(map: pwrc->regmap, |
110 | reg: restart ? ATC2609A_PMU_SYS_CTL0 : ATC2609A_PMU_SYS_CTL1, |
111 | mask: reg_mask, val: reg_val); |
112 | if (ret) { |
113 | dev_err(pwrc->dev, "failed to write SYS_CTL%d: %d\n" , |
114 | restart ? 0 : 1, ret); |
115 | return ret; |
116 | } |
117 | |
118 | /* Wait for trigger completion */ |
119 | mdelay(200); |
120 | |
121 | return 0; |
122 | } |
123 | |
124 | static int atc2603c_init(const struct atc260x_pwrc *pwrc) |
125 | { |
126 | int ret; |
127 | |
128 | /* |
129 | * Delay transition from S2/S3 to S1 in order to avoid |
130 | * DDR init failure in Bootloader. |
131 | */ |
132 | ret = regmap_update_bits(map: pwrc->regmap, ATC2603C_PMU_SYS_CTL3, |
133 | ATC2603C_PMU_SYS_CTL3_S2S3TOS1_TIMER_EN, |
134 | ATC2603C_PMU_SYS_CTL3_S2S3TOS1_TIMER_EN); |
135 | if (ret) |
136 | dev_warn(pwrc->dev, "failed to write SYS_CTL3: %d\n" , ret); |
137 | |
138 | /* Set wakeup sources */ |
139 | ret = regmap_update_bits(map: pwrc->regmap, ATC2603C_PMU_SYS_CTL0, |
140 | ATC2603C_PMU_SYS_CTL0_WK_ALL, |
141 | ATC2603C_PMU_SYS_CTL0_HDSW_WK_EN | |
142 | ATC2603C_PMU_SYS_CTL0_ONOFF_LONG_WK_EN); |
143 | if (ret) |
144 | dev_warn(pwrc->dev, "failed to write SYS_CTL0: %d\n" , ret); |
145 | |
146 | return ret; |
147 | } |
148 | |
149 | static int atc2609a_init(const struct atc260x_pwrc *pwrc) |
150 | { |
151 | int ret; |
152 | |
153 | /* Set wakeup sources */ |
154 | ret = regmap_update_bits(map: pwrc->regmap, ATC2609A_PMU_SYS_CTL0, |
155 | ATC2609A_PMU_SYS_CTL0_WK_ALL, |
156 | ATC2609A_PMU_SYS_CTL0_HDSW_WK_EN | |
157 | ATC2609A_PMU_SYS_CTL0_ONOFF_LONG_WK_EN); |
158 | if (ret) |
159 | dev_warn(pwrc->dev, "failed to write SYS_CTL0: %d\n" , ret); |
160 | |
161 | return ret; |
162 | } |
163 | |
164 | static int atc260x_pwrc_pm_handler(struct sys_off_data *data) |
165 | { |
166 | struct atc260x_pwrc *pwrc = data->cb_data; |
167 | |
168 | pwrc->do_poweroff(pwrc, false); |
169 | |
170 | WARN_ONCE(1, "Unable to power off system\n" ); |
171 | |
172 | return NOTIFY_DONE; |
173 | } |
174 | |
175 | static int atc260x_pwrc_restart_handler(struct sys_off_data *data) |
176 | { |
177 | struct atc260x_pwrc *pwrc = data->cb_data; |
178 | pwrc->do_poweroff(pwrc, true); |
179 | |
180 | return NOTIFY_DONE; |
181 | } |
182 | |
183 | static int atc260x_pwrc_probe(struct platform_device *pdev) |
184 | { |
185 | struct atc260x *atc260x = dev_get_drvdata(dev: pdev->dev.parent); |
186 | struct atc260x_pwrc *priv; |
187 | int ret; |
188 | |
189 | priv = devm_kzalloc(dev: &pdev->dev, size: sizeof(*priv), GFP_KERNEL); |
190 | if (!priv) |
191 | return -ENOMEM; |
192 | |
193 | priv->dev = &pdev->dev; |
194 | priv->regmap = atc260x->regmap; |
195 | |
196 | switch (atc260x->ic_type) { |
197 | case ATC2603C: |
198 | priv->do_poweroff = atc2603c_do_poweroff; |
199 | ret = atc2603c_init(pwrc: priv); |
200 | break; |
201 | case ATC2609A: |
202 | priv->do_poweroff = atc2609a_do_poweroff; |
203 | ret = atc2609a_init(pwrc: priv); |
204 | break; |
205 | default: |
206 | dev_err(priv->dev, |
207 | "Poweroff not supported for ATC260x PMIC type: %u\n" , |
208 | atc260x->ic_type); |
209 | return -EINVAL; |
210 | } |
211 | |
212 | if (ret) |
213 | return ret; |
214 | |
215 | ret = devm_register_sys_off_handler(dev: priv->dev, |
216 | mode: SYS_OFF_MODE_POWER_OFF, |
217 | SYS_OFF_PRIO_DEFAULT, |
218 | callback: atc260x_pwrc_pm_handler, |
219 | cb_data: priv); |
220 | if (ret) |
221 | dev_err(priv->dev, "failed to register power-off handler: %d\n" , |
222 | ret); |
223 | |
224 | ret = devm_register_sys_off_handler(dev: priv->dev, |
225 | mode: SYS_OFF_MODE_RESTART, |
226 | SYS_OFF_PRIO_HIGH, |
227 | callback: atc260x_pwrc_restart_handler, |
228 | cb_data: priv); |
229 | if (ret) |
230 | dev_err(priv->dev, "failed to register restart handler: %d\n" , |
231 | ret); |
232 | |
233 | return ret; |
234 | } |
235 | |
236 | static struct platform_driver atc260x_pwrc_driver = { |
237 | .probe = atc260x_pwrc_probe, |
238 | .driver = { |
239 | .name = "atc260x-pwrc" , |
240 | }, |
241 | }; |
242 | |
243 | module_platform_driver(atc260x_pwrc_driver); |
244 | |
245 | MODULE_DESCRIPTION("Poweroff & reset driver for ATC260x PMICs" ); |
246 | MODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@gmail.com>" ); |
247 | MODULE_LICENSE("GPL" ); |
248 | |