Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

soc: apple: Add driver for Apple PMGR power state controls

Implements genpd and reset providers for downstream devices. Each
instance of the driver binds to a single register and represents a
single SoC power domain.

The driver does not currently implement all features (clockgate-only
state, misc flags), but we declare the respective registers for
documentation purposes. These features will be added as they become
useful for downstream devices.

This also creates the apple/soc tree and Kconfig submenu.

Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Hector Martin <marcan@marcan.st>

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