Merge tag 'hyperv-fixes-signed-20230402' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-block.git] / drivers / soc / apple / apple-pmgr-pwrstate.c
CommitLineData
6df9d38f
HM
1// SPDX-License-Identifier: GPL-2.0-only OR MIT
2/*
3 * Apple SoC PMGR device power state driver
4 *
5 * Copyright The Asahi Linux Contributors
6 */
7
8#include <linux/bitops.h>
9#include <linux/bitfield.h>
10#include <linux/err.h>
11#include <linux/of.h>
12#include <linux/of_address.h>
13#include <linux/platform_device.h>
14#include <linux/pm_domain.h>
15#include <linux/regmap.h>
16#include <linux/mfd/syscon.h>
17#include <linux/reset-controller.h>
18#include <linux/module.h>
19
20#define APPLE_PMGR_RESET BIT(31)
21#define APPLE_PMGR_AUTO_ENABLE BIT(28)
22#define APPLE_PMGR_PS_AUTO GENMASK(27, 24)
cc1fe1e5 23#define APPLE_PMGR_PS_MIN GENMASK(19, 16)
6df9d38f
HM
24#define APPLE_PMGR_PARENT_OFF BIT(11)
25#define APPLE_PMGR_DEV_DISABLE BIT(10)
26#define APPLE_PMGR_WAS_CLKGATED BIT(9)
27#define APPLE_PMGR_WAS_PWRGATED BIT(8)
28#define APPLE_PMGR_PS_ACTUAL GENMASK(7, 4)
29#define APPLE_PMGR_PS_TARGET GENMASK(3, 0)
30
31#define APPLE_PMGR_FLAGS (APPLE_PMGR_WAS_CLKGATED | APPLE_PMGR_WAS_PWRGATED)
32
33#define APPLE_PMGR_PS_ACTIVE 0xf
34#define APPLE_PMGR_PS_CLKGATE 0x4
35#define APPLE_PMGR_PS_PWRGATE 0x0
36
37#define APPLE_PMGR_PS_SET_TIMEOUT 100
38#define APPLE_PMGR_RESET_TIME 1
39
40struct apple_pmgr_ps {
41 struct device *dev;
42 struct generic_pm_domain genpd;
43 struct reset_controller_dev rcdev;
44 struct regmap *regmap;
45 u32 offset;
cc1fe1e5 46 u32 min_state;
6df9d38f
HM
47};
48
49#define genpd_to_apple_pmgr_ps(_genpd) container_of(_genpd, struct apple_pmgr_ps, genpd)
50#define rcdev_to_apple_pmgr_ps(_rcdev) container_of(_rcdev, struct apple_pmgr_ps, rcdev)
51
52static int apple_pmgr_ps_set(struct generic_pm_domain *genpd, u32 pstate, bool auto_enable)
53{
54 int ret;
55 struct apple_pmgr_ps *ps = genpd_to_apple_pmgr_ps(genpd);
56 u32 reg;
57
58 ret = regmap_read(ps->regmap, ps->offset, &reg);
59 if (ret < 0)
60 return ret;
61
62 /* Resets are synchronous, and only work if the device is powered and clocked. */
63 if (reg & APPLE_PMGR_RESET && pstate != APPLE_PMGR_PS_ACTIVE)
64 dev_err(ps->dev, "PS %s: powering off with RESET active\n",
65 genpd->name);
66
67 reg &= ~(APPLE_PMGR_AUTO_ENABLE | APPLE_PMGR_FLAGS | APPLE_PMGR_PS_TARGET);
68 reg |= FIELD_PREP(APPLE_PMGR_PS_TARGET, pstate);
69
70 dev_dbg(ps->dev, "PS %s: pwrstate = 0x%x: 0x%x\n", genpd->name, pstate, reg);
71
72 regmap_write(ps->regmap, ps->offset, reg);
73
74 ret = regmap_read_poll_timeout_atomic(
75 ps->regmap, ps->offset, reg,
76 (FIELD_GET(APPLE_PMGR_PS_ACTUAL, reg) == pstate), 1,
77 APPLE_PMGR_PS_SET_TIMEOUT);
78 if (ret < 0)
79 dev_err(ps->dev, "PS %s: Failed to reach power state 0x%x (now: 0x%x)\n",
80 genpd->name, pstate, reg);
81
82 if (auto_enable) {
83 /* Not all devices implement this; this is a no-op where not implemented. */
84 reg &= ~APPLE_PMGR_FLAGS;
85 reg |= APPLE_PMGR_AUTO_ENABLE;
86 regmap_write(ps->regmap, ps->offset, reg);
87 }
88
89 return ret;
90}
91
92static bool apple_pmgr_ps_is_active(struct apple_pmgr_ps *ps)
93{
94 u32 reg = 0;
95
96 regmap_read(ps->regmap, ps->offset, &reg);
97 /*
98 * We consider domains as active if they are actually on, or if they have auto-PM
99 * enabled and the intended target is on.
100 */
101 return (FIELD_GET(APPLE_PMGR_PS_ACTUAL, reg) == APPLE_PMGR_PS_ACTIVE ||
102 (FIELD_GET(APPLE_PMGR_PS_TARGET, reg) == APPLE_PMGR_PS_ACTIVE &&
103 reg & APPLE_PMGR_AUTO_ENABLE));
104}
105
106static int apple_pmgr_ps_power_on(struct generic_pm_domain *genpd)
107{
108 return apple_pmgr_ps_set(genpd, APPLE_PMGR_PS_ACTIVE, true);
109}
110
111static int apple_pmgr_ps_power_off(struct generic_pm_domain *genpd)
112{
113 return apple_pmgr_ps_set(genpd, APPLE_PMGR_PS_PWRGATE, false);
114}
115
116static int apple_pmgr_reset_assert(struct reset_controller_dev *rcdev, unsigned long id)
117{
118 struct apple_pmgr_ps *ps = rcdev_to_apple_pmgr_ps(rcdev);
c289d5bc 119 unsigned long flags;
6df9d38f 120
c289d5bc 121 spin_lock_irqsave(&ps->genpd.slock, flags);
6df9d38f
HM
122
123 if (ps->genpd.status == GENPD_STATE_OFF)
124 dev_err(ps->dev, "PS 0x%x: asserting RESET while powered down\n", ps->offset);
125
126 dev_dbg(ps->dev, "PS 0x%x: assert reset\n", ps->offset);
127 /* Quiesce device before asserting reset */
128 regmap_update_bits(ps->regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_DEV_DISABLE,
129 APPLE_PMGR_DEV_DISABLE);
130 regmap_update_bits(ps->regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_RESET,
131 APPLE_PMGR_RESET);
132
c289d5bc 133 spin_unlock_irqrestore(&ps->genpd.slock, flags);
6df9d38f
HM
134
135 return 0;
136}
137
138static int apple_pmgr_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id)
139{
140 struct apple_pmgr_ps *ps = rcdev_to_apple_pmgr_ps(rcdev);
c289d5bc 141 unsigned long flags;
6df9d38f 142
c289d5bc 143 spin_lock_irqsave(&ps->genpd.slock, flags);
6df9d38f
HM
144
145 dev_dbg(ps->dev, "PS 0x%x: deassert reset\n", ps->offset);
146 regmap_update_bits(ps->regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_RESET, 0);
147 regmap_update_bits(ps->regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_DEV_DISABLE, 0);
148
149 if (ps->genpd.status == GENPD_STATE_OFF)
150 dev_err(ps->dev, "PS 0x%x: RESET was deasserted while powered down\n", ps->offset);
151
c289d5bc 152 spin_unlock_irqrestore(&ps->genpd.slock, flags);
6df9d38f
HM
153
154 return 0;
155}
156
157static int apple_pmgr_reset_reset(struct reset_controller_dev *rcdev, unsigned long id)
158{
159 int ret;
160
161 ret = apple_pmgr_reset_assert(rcdev, id);
162 if (ret)
163 return ret;
164
165 usleep_range(APPLE_PMGR_RESET_TIME, 2 * APPLE_PMGR_RESET_TIME);
166
167 return apple_pmgr_reset_deassert(rcdev, id);
168}
169
170static int apple_pmgr_reset_status(struct reset_controller_dev *rcdev, unsigned long id)
171{
172 struct apple_pmgr_ps *ps = rcdev_to_apple_pmgr_ps(rcdev);
173 u32 reg = 0;
174
175 regmap_read(ps->regmap, ps->offset, &reg);
176
177 return !!(reg & APPLE_PMGR_RESET);
178}
179
180const struct reset_control_ops apple_pmgr_reset_ops = {
181 .assert = apple_pmgr_reset_assert,
182 .deassert = apple_pmgr_reset_deassert,
183 .reset = apple_pmgr_reset_reset,
184 .status = apple_pmgr_reset_status,
185};
186
187static int apple_pmgr_reset_xlate(struct reset_controller_dev *rcdev,
188 const struct of_phandle_args *reset_spec)
189{
190 return 0;
191}
192
193static int apple_pmgr_ps_probe(struct platform_device *pdev)
194{
195 struct device *dev = &pdev->dev;
196 struct device_node *node = dev->of_node;
197 struct apple_pmgr_ps *ps;
198 struct regmap *regmap;
199 struct of_phandle_iterator it;
200 int ret;
201 const char *name;
202 bool active;
203
204 regmap = syscon_node_to_regmap(node->parent);
205 if (IS_ERR(regmap))
206 return PTR_ERR(regmap);
207
208 ps = devm_kzalloc(dev, sizeof(*ps), GFP_KERNEL);
209 if (!ps)
210 return -ENOMEM;
211
212 ps->dev = dev;
213 ps->regmap = regmap;
214
215 ret = of_property_read_string(node, "label", &name);
216 if (ret < 0) {
217 dev_err(dev, "missing label property\n");
218 return ret;
219 }
220
221 ret = of_property_read_u32(node, "reg", &ps->offset);
222 if (ret < 0) {
223 dev_err(dev, "missing reg property\n");
224 return ret;
225 }
226
c289d5bc 227 ps->genpd.flags |= GENPD_FLAG_IRQ_SAFE;
6df9d38f
HM
228 ps->genpd.name = name;
229 ps->genpd.power_on = apple_pmgr_ps_power_on;
230 ps->genpd.power_off = apple_pmgr_ps_power_off;
231
cc1fe1e5
HM
232 ret = of_property_read_u32(node, "apple,min-state", &ps->min_state);
233 if (ret == 0 && ps->min_state <= APPLE_PMGR_PS_ACTIVE)
234 regmap_update_bits(regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_PS_MIN,
235 FIELD_PREP(APPLE_PMGR_PS_MIN, ps->min_state));
236
6df9d38f
HM
237 active = apple_pmgr_ps_is_active(ps);
238 if (of_property_read_bool(node, "apple,always-on")) {
239 ps->genpd.flags |= GENPD_FLAG_ALWAYS_ON;
240 if (!active) {
241 dev_warn(dev, "always-on domain %s is not on at boot\n", name);
242 /* Turn it on so pm_genpd_init does not fail */
243 active = apple_pmgr_ps_power_on(&ps->genpd) == 0;
244 }
245 }
246
247 /* Turn on auto-PM if the domain is already on */
248 if (active)
249 regmap_update_bits(regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_AUTO_ENABLE,
250 APPLE_PMGR_AUTO_ENABLE);
251
252 ret = pm_genpd_init(&ps->genpd, NULL, !active);
253 if (ret < 0) {
254 dev_err(dev, "pm_genpd_init failed\n");
255 return ret;
256 }
257
258 ret = of_genpd_add_provider_simple(node, &ps->genpd);
259 if (ret < 0) {
260 dev_err(dev, "of_genpd_add_provider_simple failed\n");
261 return ret;
262 }
263
264 of_for_each_phandle(&it, ret, node, "power-domains", "#power-domain-cells", -1) {
265 struct of_phandle_args parent, child;
266
267 parent.np = it.node;
268 parent.args_count = of_phandle_iterator_args(&it, parent.args, MAX_PHANDLE_ARGS);
269 child.np = node;
270 child.args_count = 0;
271 ret = of_genpd_add_subdomain(&parent, &child);
272
273 if (ret == -EPROBE_DEFER) {
274 of_node_put(parent.np);
275 goto err_remove;
276 } else if (ret < 0) {
277 dev_err(dev, "failed to add to parent domain: %d (%s -> %s)\n",
278 ret, it.node->name, node->name);
279 of_node_put(parent.np);
280 goto err_remove;
281 }
282 }
283
284 /*
285 * Do not participate in regular PM; parent power domains are handled via the
286 * genpd hierarchy.
287 */
288 pm_genpd_remove_device(dev);
289
290 ps->rcdev.owner = THIS_MODULE;
291 ps->rcdev.nr_resets = 1;
292 ps->rcdev.ops = &apple_pmgr_reset_ops;
293 ps->rcdev.of_node = dev->of_node;
294 ps->rcdev.of_reset_n_cells = 0;
295 ps->rcdev.of_xlate = apple_pmgr_reset_xlate;
296
297 ret = devm_reset_controller_register(dev, &ps->rcdev);
298 if (ret < 0)
299 goto err_remove;
300
301 return 0;
302err_remove:
303 of_genpd_del_provider(node);
304 pm_genpd_remove(&ps->genpd);
305 return ret;
306}
307
308static const struct of_device_id apple_pmgr_ps_of_match[] = {
309 { .compatible = "apple,pmgr-pwrstate" },
310 {}
311};
312
313MODULE_DEVICE_TABLE(of, apple_pmgr_ps_of_match);
314
315static struct platform_driver apple_pmgr_ps_driver = {
316 .probe = apple_pmgr_ps_probe,
317 .driver = {
318 .name = "apple-pmgr-pwrstate",
319 .of_match_table = apple_pmgr_ps_of_match,
320 },
321};
322
323MODULE_AUTHOR("Hector Martin <marcan@marcan.st>");
324MODULE_DESCRIPTION("PMGR power state driver for Apple SoCs");
325MODULE_LICENSE("GPL v2");
326
327module_platform_driver(apple_pmgr_ps_driver);