327 lines · c
1// SPDX-License-Identifier: GPL-2.0-only OR MIT2/*3 * Apple SoC PMGR device power state driver4 *5 * Copyright The Asahi Linux Contributors6 */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 0xf34#define APPLE_PMGR_PS_CLKGATE 0x435#define APPLE_PMGR_PS_PWRGATE 0x036 37#define APPLE_PMGR_PS_SET_TIMEOUT 10038#define APPLE_PMGR_RESET_TIME 139 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;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 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, ®);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, ®);97 /*98 * We consider domains as active if they are actually on, or if they have auto-PM99 * 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);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(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 133 spin_unlock_irqrestore(&ps->genpd.slock, flags);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);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(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 152 spin_unlock_irqrestore(&ps->genpd.slock, flags);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, ®);176 177 return !!(reg & APPLE_PMGR_RESET);178}179 180static 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 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 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(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 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 the286 * 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");325 326module_platform_driver(apple_pmgr_ps_driver);327