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) |
23 | #define APPLE_PMGR_PS_MIN GENMASK(19, 16) |
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 | |
40 | struct 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; |
46 | u32 min_state; |
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 | |
52 | static 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(map: ps->regmap, reg: ps->offset, val: ®); |
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(map: ps->regmap, reg: ps->offset, val: 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(map: ps->regmap, reg: ps->offset, val: reg); |
87 | } |
88 | |
89 | return ret; |
90 | } |
91 | |
92 | static bool apple_pmgr_ps_is_active(struct apple_pmgr_ps *ps) |
93 | { |
94 | u32 reg = 0; |
95 | |
96 | regmap_read(map: ps->regmap, reg: ps->offset, val: ®); |
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 | |
106 | static int apple_pmgr_ps_power_on(struct generic_pm_domain *genpd) |
107 | { |
108 | return apple_pmgr_ps_set(genpd, APPLE_PMGR_PS_ACTIVE, auto_enable: true); |
109 | } |
110 | |
111 | static int apple_pmgr_ps_power_off(struct generic_pm_domain *genpd) |
112 | { |
113 | return apple_pmgr_ps_set(genpd, APPLE_PMGR_PS_PWRGATE, auto_enable: false); |
114 | } |
115 | |
116 | static 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); |
119 | unsigned long flags; |
120 | |
121 | spin_lock_irqsave(&ps->genpd.slock, flags); |
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(map: ps->regmap, reg: ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_DEV_DISABLE, |
129 | APPLE_PMGR_DEV_DISABLE); |
130 | regmap_update_bits(map: ps->regmap, reg: ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_RESET, |
131 | APPLE_PMGR_RESET); |
132 | |
133 | spin_unlock_irqrestore(lock: &ps->genpd.slock, flags); |
134 | |
135 | return 0; |
136 | } |
137 | |
138 | static 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); |
141 | unsigned long flags; |
142 | |
143 | spin_lock_irqsave(&ps->genpd.slock, flags); |
144 | |
145 | dev_dbg(ps->dev, "PS 0x%x: deassert reset\n" , ps->offset); |
146 | regmap_update_bits(map: ps->regmap, reg: ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_RESET, val: 0); |
147 | regmap_update_bits(map: ps->regmap, reg: ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_DEV_DISABLE, val: 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 | |
152 | spin_unlock_irqrestore(lock: &ps->genpd.slock, flags); |
153 | |
154 | return 0; |
155 | } |
156 | |
157 | static 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, max: 2 * APPLE_PMGR_RESET_TIME); |
166 | |
167 | return apple_pmgr_reset_deassert(rcdev, id); |
168 | } |
169 | |
170 | static 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(map: ps->regmap, reg: ps->offset, val: ®); |
176 | |
177 | return !!(reg & APPLE_PMGR_RESET); |
178 | } |
179 | |
180 | static const 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 | |
187 | static int apple_pmgr_reset_xlate(struct reset_controller_dev *rcdev, |
188 | const struct of_phandle_args *reset_spec) |
189 | { |
190 | return 0; |
191 | } |
192 | |
193 | static 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(np: node->parent); |
205 | if (IS_ERR(ptr: regmap)) |
206 | return PTR_ERR(ptr: regmap); |
207 | |
208 | ps = devm_kzalloc(dev, size: 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(np: node, propname: "label" , out_string: &name); |
216 | if (ret < 0) { |
217 | dev_err(dev, "missing label property\n" ); |
218 | return ret; |
219 | } |
220 | |
221 | ret = of_property_read_u32(np: node, propname: "reg" , out_value: &ps->offset); |
222 | if (ret < 0) { |
223 | dev_err(dev, "missing reg property\n" ); |
224 | return ret; |
225 | } |
226 | |
227 | ps->genpd.flags |= GENPD_FLAG_IRQ_SAFE; |
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 | |
232 | ret = of_property_read_u32(np: node, propname: "apple,min-state" , out_value: &ps->min_state); |
233 | if (ret == 0 && ps->min_state <= APPLE_PMGR_PS_ACTIVE) |
234 | regmap_update_bits(map: regmap, reg: ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_PS_MIN, |
235 | FIELD_PREP(APPLE_PMGR_PS_MIN, ps->min_state)); |
236 | |
237 | active = apple_pmgr_ps_is_active(ps); |
238 | if (of_property_read_bool(np: node, propname: "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(genpd: &ps->genpd) == 0; |
244 | } |
245 | } |
246 | |
247 | /* Turn on auto-PM if the domain is already on */ |
248 | if (active) |
249 | regmap_update_bits(map: regmap, reg: ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_AUTO_ENABLE, |
250 | APPLE_PMGR_AUTO_ENABLE); |
251 | |
252 | ret = pm_genpd_init(genpd: &ps->genpd, NULL, is_off: !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(np: node, genpd: &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: &it, args: parent.args, MAX_PHANDLE_ARGS); |
269 | child.np = node; |
270 | child.args_count = 0; |
271 | ret = of_genpd_add_subdomain(parent_spec: &parent, subdomain_spec: &child); |
272 | |
273 | if (ret == -EPROBE_DEFER) { |
274 | of_node_put(node: 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(node: 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, rcdev: &ps->rcdev); |
298 | if (ret < 0) |
299 | goto err_remove; |
300 | |
301 | return 0; |
302 | err_remove: |
303 | of_genpd_del_provider(np: node); |
304 | pm_genpd_remove(genpd: &ps->genpd); |
305 | return ret; |
306 | } |
307 | |
308 | static const struct of_device_id apple_pmgr_ps_of_match[] = { |
309 | { .compatible = "apple,pmgr-pwrstate" }, |
310 | {} |
311 | }; |
312 | |
313 | MODULE_DEVICE_TABLE(of, apple_pmgr_ps_of_match); |
314 | |
315 | static 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 | |
323 | MODULE_AUTHOR("Hector Martin <marcan@marcan.st>" ); |
324 | MODULE_DESCRIPTION("PMGR power state driver for Apple SoCs" ); |
325 | |
326 | module_platform_driver(apple_pmgr_ps_driver); |
327 | |