brintos

brintos / linux-shallow public Read only

0
0
Text · 9.1 KiB · 9467235 Raw
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, &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-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, &reg);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