Merge tag 'for-v5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux...
[platform/kernel/linux-starfive.git] / drivers / power / reset / atc260x-poweroff.c
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         struct notifier_block restart_nb;
20         int (*do_poweroff)(const struct atc260x_pwrc *pwrc, bool restart);
21 };
22
23 /* Global variable needed only for pm_power_off */
24 static struct atc260x_pwrc *atc260x_pwrc_data;
25
26 static int atc2603c_do_poweroff(const struct atc260x_pwrc *pwrc, bool restart)
27 {
28         int ret, deep_sleep = 0;
29         uint reg_mask, reg_val;
30
31         /* S4-Deep Sleep Mode is NOT available for WALL/USB power */
32         if (!restart && !power_supply_is_system_supplied()) {
33                 deep_sleep = 1;
34                 dev_info(pwrc->dev, "Enabling S4-Deep Sleep Mode");
35         }
36
37         /* Update wakeup sources */
38         reg_val = ATC2603C_PMU_SYS_CTL0_ONOFF_LONG_WK_EN |
39                   (restart ? ATC2603C_PMU_SYS_CTL0_RESET_WK_EN
40                            : ATC2603C_PMU_SYS_CTL0_ONOFF_SHORT_WK_EN);
41
42         ret = regmap_update_bits(pwrc->regmap, ATC2603C_PMU_SYS_CTL0,
43                                  ATC2603C_PMU_SYS_CTL0_WK_ALL, reg_val);
44         if (ret)
45                 dev_warn(pwrc->dev, "failed to write SYS_CTL0: %d\n", ret);
46
47         /* Update power mode */
48         reg_mask = ATC2603C_PMU_SYS_CTL3_EN_S2 | ATC2603C_PMU_SYS_CTL3_EN_S3;
49
50         ret = regmap_update_bits(pwrc->regmap, ATC2603C_PMU_SYS_CTL3, reg_mask,
51                                  deep_sleep ? 0 : ATC2603C_PMU_SYS_CTL3_EN_S3);
52         if (ret) {
53                 dev_err(pwrc->dev, "failed to write SYS_CTL3: %d\n", ret);
54                 return ret;
55         }
56
57         /* Trigger poweroff / restart sequence */
58         reg_mask = restart ? ATC2603C_PMU_SYS_CTL0_RESTART_EN
59                            : ATC2603C_PMU_SYS_CTL1_EN_S1;
60         reg_val = restart ? ATC2603C_PMU_SYS_CTL0_RESTART_EN : 0;
61
62         ret = regmap_update_bits(pwrc->regmap,
63                                  restart ? ATC2603C_PMU_SYS_CTL0 : ATC2603C_PMU_SYS_CTL1,
64                                  reg_mask, reg_val);
65         if (ret) {
66                 dev_err(pwrc->dev, "failed to write SYS_CTL%d: %d\n",
67                         restart ? 0 : 1, ret);
68                 return ret;
69         }
70
71         /* Wait for trigger completion */
72         mdelay(200);
73
74         return 0;
75 }
76
77 static int atc2609a_do_poweroff(const struct atc260x_pwrc *pwrc, bool restart)
78 {
79         int ret, deep_sleep = 0;
80         uint reg_mask, reg_val;
81
82         /* S4-Deep Sleep Mode is NOT available for WALL/USB power */
83         if (!restart && !power_supply_is_system_supplied()) {
84                 deep_sleep = 1;
85                 dev_info(pwrc->dev, "Enabling S4-Deep Sleep Mode");
86         }
87
88         /* Update wakeup sources */
89         reg_val = ATC2609A_PMU_SYS_CTL0_ONOFF_LONG_WK_EN |
90                   (restart ? ATC2609A_PMU_SYS_CTL0_RESET_WK_EN
91                            : ATC2609A_PMU_SYS_CTL0_ONOFF_SHORT_WK_EN);
92
93         ret = regmap_update_bits(pwrc->regmap, ATC2609A_PMU_SYS_CTL0,
94                                  ATC2609A_PMU_SYS_CTL0_WK_ALL, reg_val);
95         if (ret)
96                 dev_warn(pwrc->dev, "failed to write SYS_CTL0: %d\n", ret);
97
98         /* Update power mode */
99         reg_mask = ATC2609A_PMU_SYS_CTL3_EN_S2 | ATC2609A_PMU_SYS_CTL3_EN_S3;
100
101         ret = regmap_update_bits(pwrc->regmap, ATC2609A_PMU_SYS_CTL3, reg_mask,
102                                  deep_sleep ? 0 : ATC2609A_PMU_SYS_CTL3_EN_S3);
103         if (ret) {
104                 dev_err(pwrc->dev, "failed to write SYS_CTL3: %d\n", ret);
105                 return ret;
106         }
107
108         /* Trigger poweroff / restart sequence */
109         reg_mask = restart ? ATC2609A_PMU_SYS_CTL0_RESTART_EN
110                            : ATC2609A_PMU_SYS_CTL1_EN_S1;
111         reg_val = restart ? ATC2609A_PMU_SYS_CTL0_RESTART_EN : 0;
112
113         ret = regmap_update_bits(pwrc->regmap,
114                                  restart ? ATC2609A_PMU_SYS_CTL0 : ATC2609A_PMU_SYS_CTL1,
115                                  reg_mask, reg_val);
116         if (ret) {
117                 dev_err(pwrc->dev, "failed to write SYS_CTL%d: %d\n",
118                         restart ? 0 : 1, ret);
119                 return ret;
120         }
121
122         /* Wait for trigger completion */
123         mdelay(200);
124
125         return 0;
126 }
127
128 static int atc2603c_init(const struct atc260x_pwrc *pwrc)
129 {
130         int ret;
131
132         /*
133          * Delay transition from S2/S3 to S1 in order to avoid
134          * DDR init failure in Bootloader.
135          */
136         ret = regmap_update_bits(pwrc->regmap, ATC2603C_PMU_SYS_CTL3,
137                                  ATC2603C_PMU_SYS_CTL3_S2S3TOS1_TIMER_EN,
138                                  ATC2603C_PMU_SYS_CTL3_S2S3TOS1_TIMER_EN);
139         if (ret)
140                 dev_warn(pwrc->dev, "failed to write SYS_CTL3: %d\n", ret);
141
142         /* Set wakeup sources */
143         ret = regmap_update_bits(pwrc->regmap, ATC2603C_PMU_SYS_CTL0,
144                                  ATC2603C_PMU_SYS_CTL0_WK_ALL,
145                                  ATC2603C_PMU_SYS_CTL0_HDSW_WK_EN |
146                                  ATC2603C_PMU_SYS_CTL0_ONOFF_LONG_WK_EN);
147         if (ret)
148                 dev_warn(pwrc->dev, "failed to write SYS_CTL0: %d\n", ret);
149
150         return ret;
151 }
152
153 static int atc2609a_init(const struct atc260x_pwrc *pwrc)
154 {
155         int ret;
156
157         /* Set wakeup sources */
158         ret = regmap_update_bits(pwrc->regmap, ATC2609A_PMU_SYS_CTL0,
159                                  ATC2609A_PMU_SYS_CTL0_WK_ALL,
160                                  ATC2609A_PMU_SYS_CTL0_HDSW_WK_EN |
161                                  ATC2609A_PMU_SYS_CTL0_ONOFF_LONG_WK_EN);
162         if (ret)
163                 dev_warn(pwrc->dev, "failed to write SYS_CTL0: %d\n", ret);
164
165         return ret;
166 }
167
168 static void atc260x_pwrc_pm_handler(void)
169 {
170         atc260x_pwrc_data->do_poweroff(atc260x_pwrc_data, false);
171
172         WARN_ONCE(1, "Unable to power off system\n");
173 }
174
175 static int atc260x_pwrc_restart_handler(struct notifier_block *nb,
176                                         unsigned long mode, void *cmd)
177 {
178         struct atc260x_pwrc *pwrc = container_of(nb, struct atc260x_pwrc,
179                                                  restart_nb);
180         pwrc->do_poweroff(pwrc, true);
181
182         return NOTIFY_DONE;
183 }
184
185 static int atc260x_pwrc_probe(struct platform_device *pdev)
186 {
187         struct atc260x *atc260x = dev_get_drvdata(pdev->dev.parent);
188         struct atc260x_pwrc *priv;
189         int ret;
190
191         priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
192         if (!priv)
193                 return -ENOMEM;
194
195         priv->dev = &pdev->dev;
196         priv->regmap = atc260x->regmap;
197         priv->restart_nb.notifier_call = atc260x_pwrc_restart_handler;
198         priv->restart_nb.priority = 192;
199
200         switch (atc260x->ic_type) {
201         case ATC2603C:
202                 priv->do_poweroff = atc2603c_do_poweroff;
203                 ret = atc2603c_init(priv);
204                 break;
205         case ATC2609A:
206                 priv->do_poweroff = atc2609a_do_poweroff;
207                 ret = atc2609a_init(priv);
208                 break;
209         default:
210                 dev_err(priv->dev,
211                         "Poweroff not supported for ATC260x PMIC type: %u\n",
212                         atc260x->ic_type);
213                 return -EINVAL;
214         }
215
216         if (ret)
217                 return ret;
218
219         platform_set_drvdata(pdev, priv);
220
221         if (!pm_power_off) {
222                 atc260x_pwrc_data = priv;
223                 pm_power_off = atc260x_pwrc_pm_handler;
224         } else {
225                 dev_warn(priv->dev, "Poweroff callback already assigned\n");
226         }
227
228         ret = register_restart_handler(&priv->restart_nb);
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 int atc260x_pwrc_remove(struct platform_device *pdev)
237 {
238         struct atc260x_pwrc *priv = platform_get_drvdata(pdev);
239
240         if (atc260x_pwrc_data == priv) {
241                 pm_power_off = NULL;
242                 atc260x_pwrc_data = NULL;
243         }
244
245         unregister_restart_handler(&priv->restart_nb);
246
247         return 0;
248 }
249
250 static struct platform_driver atc260x_pwrc_driver = {
251         .probe = atc260x_pwrc_probe,
252         .remove = atc260x_pwrc_remove,
253         .driver = {
254                 .name = "atc260x-pwrc",
255         },
256 };
257
258 module_platform_driver(atc260x_pwrc_driver);
259
260 MODULE_DESCRIPTION("Poweroff & reset driver for ATC260x PMICs");
261 MODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@gmail.com>");
262 MODULE_LICENSE("GPL");