:p
atchew
Login
Hi, This series adds support for the power domains on Google GS101. It's fairly similar to SoCs already supported by this driver, except that register acces does not work via plain ioremap() / readl() / writel(). Instead, the regmap created by the PMU driver must be used (which uses Arm SMCC calls under the hood). The DT update to add the new required properties on gs101 will be posted separately. Signed-off-by: André Draszik <andre.draszik@linaro.org> --- André Draszik (10): dt-bindings: power: samsung: add google,gs101-pd dt-bindings: soc: samsung: exynos-pmu: allow power domains as child on g101 pmdomain: samsung: use to devm_kstrdup_const() to simplify error handling pmdomain: samsung: convert to using regmap pmdomain: samsung: convert to regmap_read_poll_timeout() pmdomain: samsung: don't hardcode offset for registers to 0 and 4 pmdomain: samsung: selectively handle enforced sync_state pmdomain: samsung: try to get PMU (syscon) regmap pmdomain: samsung: use dev_err() instead of pr_err() pmdomain: samsung: add support for google,gs101-pd .../devicetree/bindings/power/pd-samsung.yaml | 1 + .../bindings/soc/samsung/exynos-pmu.yaml | 53 ++++++++- drivers/pmdomain/samsung/exynos-pm-domains.c | 126 +++++++++++++++------ 3 files changed, 145 insertions(+), 35 deletions(-) --- base-commit: a5f97c90e75f09f24ece2dca34168722b140a798 change-id: 20251001-gs101-pd-d4dc97d70a84 Best regards, -- André Draszik <andre.draszik@linaro.org>
Add support for the Google gs101 version of the Exynos power domains. A new compatible is needed because register fields have changed. Signed-off-by: André Draszik <andre.draszik@linaro.org> --- Documentation/devicetree/bindings/power/pd-samsung.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/power/pd-samsung.yaml b/Documentation/devicetree/bindings/power/pd-samsung.yaml index XXXXXXX..XXXXXXX 100644 --- a/Documentation/devicetree/bindings/power/pd-samsung.yaml +++ b/Documentation/devicetree/bindings/power/pd-samsung.yaml @@ -XXX,XX +XXX,XX @@ allOf: properties: compatible: enum: + - google,gs101-pd - samsung,exynos4210-pd - samsung,exynos5433-pd -- 2.51.0.618.g983fd99d29-goog
The power domains are a property of / implemented in the PMU. As such, they should be modelled as child nodes of the PMU. Update the example while at it. Signed-off-by: André Draszik <andre.draszik@linaro.org> --- Note: Ideally, the newly added properties (ranges, etc.) should only be 'required' if "^power-domain@[0-9a-f]+$" exists as a patternProperty, as they're needed only in that case. As-is, this patch now causes warnings for existing DTs as they don't specify the new properties (and they shouldn't need to). Only if DTs are updated to include power-domains, such an update should also add the new properties. I've not been able to come up with the correct schema syntax to achieve that. dependencies, dependentRequired, and dependentSchemas don't seem to support patterns. Similarly, - if: required: - ... then: required: - ... doesn't allow patterns in the 'if' block (or I didn't get the syntax right). --- .../bindings/soc/samsung/exynos-pmu.yaml | 53 +++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml b/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml index XXXXXXX..XXXXXXX 100644 --- a/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml +++ b/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml @@ -XXX,XX +XXX,XX @@ properties: minItems: 1 maxItems: 32 + '#address-cells': + const: 1 + + '#size-cells': + const: 1 + + ranges: true + dp-phy: $ref: /schemas/phy/samsung,dp-video-phy.yaml unevaluatedProperties: false @@ -XXX,XX +XXX,XX @@ required: - compatible - reg -additionalProperties: false +unevaluatedProperties: false allOf: - if: @@ -XXX,XX +XXX,XX @@ allOf: enum: - google,gs101-pmu then: + patternProperties: + "^power-domain@[0-9a-f]+$": + type: object + description: Child node describing one power domain within the PMU + + additionalProperties: true + + properties: + compatible: + const: google,gs101-pd + required: + - '#address-cells' + - '#size-cells' + - ranges - google,pmu-intr-gen-syscon else: properties: + '#address-cells': false + '#size-cells': false + ranges: false google,pmu-intr-gen-syscon: false examples: @@ -XXX,XX +XXX,XX @@ examples: #phy-cells = <1>; }; }; + + - | + system-controller@17460000 { + compatible = "google,gs101-pmu", "syscon"; + reg = <0x17460000 0x10000>; + ranges; + #address-cells = <1>; + #size-cells = <1>; + + google,pmu-intr-gen-syscon = <&pmu_intr_gen>; + + pd_g3d: power-domain@1e00 { + compatible = "google,gs101-pd"; + reg = <0x1e00 0x80>; + #power-domain-cells = <0>; + label = "g3d"; + }; + + power-domain@2000 { + compatible = "google,gs101-pd"; + reg = <0x2000 0x80>; + #power-domain-cells = <0>; + power-domains = <&pd_g3d>; + label = "embedded_g3d"; + }; + }; -- 2.51.0.618.g983fd99d29-goog
Convert to using devm_kstrdup_const() so as to simplify cleanup during error handling. Signed-off-by: André Draszik <andre.draszik@linaro.org> --- drivers/pmdomain/samsung/exynos-pm-domains.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/pmdomain/samsung/exynos-pm-domains.c b/drivers/pmdomain/samsung/exynos-pm-domains.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/pmdomain/samsung/exynos-pm-domains.c +++ b/drivers/pmdomain/samsung/exynos-pm-domains.c @@ -XXX,XX +XXX,XX @@ static const struct of_device_id exynos_pm_domain_of_match[] = { { }, }; -static const char *exynos_get_domain_name(struct device_node *node) +static const char *exynos_get_domain_name(struct device *dev, + struct device_node *node) { const char *name; if (of_property_read_string(node, "label", &name) < 0) name = kbasename(node->full_name); - return kstrdup_const(name, GFP_KERNEL); + return devm_kstrdup_const(dev, name, GFP_KERNEL); } static int exynos_pd_probe(struct platform_device *pdev) @@ -XXX,XX +XXX,XX @@ static int exynos_pd_probe(struct platform_device *pdev) if (!pd) return -ENOMEM; - pd->pd.name = exynos_get_domain_name(np); + pd->pd.name = exynos_get_domain_name(dev, np); if (!pd->pd.name) return -ENOMEM; pd->base = of_iomap(np, 0); - if (!pd->base) { - kfree_const(pd->pd.name); + if (!pd->base) return -ENODEV; - } pd->pd.power_off = exynos_pd_power_off; pd->pd.power_on = exynos_pd_power_on; -- 2.51.0.618.g983fd99d29-goog
On platforms such as Google gs101, direct mmio register access to the PMU registers doesn't necessarily work and access must happen via a (syscon) regmap created by the PMU driver instead. In preparation for supporting such SoCs convert the existing mmio accesses to using a regmap wrapper. With this change in place, a follow-up patch can update the driver to optionally acquire the PMU-created regmap without having to change the rest of the code. Signed-off-by: André Draszik <andre.draszik@linaro.org> --- There is one checkpatch warning, relating to the non-const regmap_config. It can a) not be made const without resorting to having two copies and copying, and b) will go away in a follow-up patch anyway. --- drivers/pmdomain/samsung/exynos-pm-domains.c | 78 ++++++++++++++++++++-------- 1 file changed, 57 insertions(+), 21 deletions(-) diff --git a/drivers/pmdomain/samsung/exynos-pm-domains.c b/drivers/pmdomain/samsung/exynos-pm-domains.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/pmdomain/samsung/exynos-pm-domains.c +++ b/drivers/pmdomain/samsung/exynos-pm-domains.c @@ -XXX,XX +XXX,XX @@ // conjunction with runtime-pm. Support for both device-tree and non-device-tree // based power domain support is included. -#include <linux/io.h> #include <linux/err.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/pm_domain.h> #include <linux/delay.h> #include <linux/of.h> -#include <linux/of_address.h> #include <linux/pm_runtime.h> +#include <linux/regmap.h> struct exynos_pm_domain_config { /* Value for LOCAL_PWR_CFG and STATUS fields for each domain */ @@ -XXX,XX +XXX,XX @@ struct exynos_pm_domain_config { * Exynos specific wrapper around the generic power domain */ struct exynos_pm_domain { - void __iomem *base; + struct regmap *regmap; struct generic_pm_domain pd; u32 local_pwr_cfg; }; @@ -XXX,XX +XXX,XX @@ struct exynos_pm_domain { static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on) { struct exynos_pm_domain *pd; - void __iomem *base; u32 timeout, pwr; - char *op; + int err; pd = container_of(domain, struct exynos_pm_domain, pd); - base = pd->base; pwr = power_on ? pd->local_pwr_cfg : 0; - writel_relaxed(pwr, base); + err = regmap_write(pd->regmap, 0, pwr); + if (err) + return err; /* Wait max 1ms */ timeout = 10; - - while ((readl_relaxed(base + 0x4) & pd->local_pwr_cfg) != pwr) { - if (!timeout) { - op = (power_on) ? "enable" : "disable"; - pr_err("Power domain %s %s failed\n", domain->name, op); - return -ETIMEDOUT; + while (timeout-- > 0) { + unsigned int val; + + err = regmap_read(pd->regmap, 0x4, &val); + if (err || ((val & pd->local_pwr_cfg) != pwr)) { + cpu_relax(); + usleep_range(80, 100); + continue; } - timeout--; - cpu_relax(); - usleep_range(80, 100); + + return 0; } - return 0; + if (!err) + err = -ETIMEDOUT; + pr_err("Power domain %s %sable failed: %d\n", domain->name, + power_on ? "en" : "dis", err); + + return err; } static int exynos_pd_power_on(struct generic_pm_domain *domain) @@ -XXX,XX +XXX,XX @@ static int exynos_pd_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; struct of_phandle_args child, parent; struct exynos_pm_domain *pd; + struct resource *res; + void __iomem *base; + unsigned int val; int on, ret; + struct regmap_config reg_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .use_relaxed_mmio = true, + }; + pm_domain_cfg = of_device_get_match_data(dev); pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); if (!pd) @@ -XXX,XX +XXX,XX @@ static int exynos_pd_probe(struct platform_device *pdev) if (!pd->pd.name) return -ENOMEM; - pd->base = of_iomap(np, 0); - if (!pd->base) - return -ENODEV; + /* + * The resource typically points into the address space of the PMU. + * Therefore, avoid using devm_platform_get_and_ioremap_resource() and + * instead use platform_get_resource() and devm_ioremap() to avoid + * conflicts due to address space overlap. + */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return dev_err_probe(dev, -ENXIO, "missing IO resources"); + + base = devm_ioremap(dev, res->start, resource_size(res)); + if (!base) + return dev_err_probe(dev, -ENOMEM, + "failed to ioremap PMU registers"); + + reg_config.max_register = resource_size(res) - reg_config.reg_stride; + pd->regmap = devm_regmap_init_mmio(dev, base, ®_config); + if (IS_ERR(pd->regmap)) + return dev_err_probe(dev, PTR_ERR(base), + "failed to init regmap"); pd->pd.power_off = exynos_pd_power_off; pd->pd.power_on = exynos_pd_power_on; pd->local_pwr_cfg = pm_domain_cfg->local_pwr_cfg; - on = readl_relaxed(pd->base + 0x4) & pd->local_pwr_cfg; + ret = regmap_read(pd->regmap, 0x4, &val); + if (ret) + return dev_err_probe(dev, ret, "failed to read status"); + + on = val & pd->local_pwr_cfg; pm_genpd_init(&pd->pd, NULL, !on); ret = of_genpd_add_provider_simple(np, &pd->pd); -- 2.51.0.618.g983fd99d29-goog
Replace the open-coded PD status polling with regmap_read_poll_timeout(). This change simplifies the code without altering functionality. Signed-off-by: André Draszik <andre.draszik@linaro.org> --- drivers/pmdomain/samsung/exynos-pm-domains.c | 29 ++++++++-------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/drivers/pmdomain/samsung/exynos-pm-domains.c b/drivers/pmdomain/samsung/exynos-pm-domains.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/pmdomain/samsung/exynos-pm-domains.c +++ b/drivers/pmdomain/samsung/exynos-pm-domains.c @@ -XXX,XX +XXX,XX @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/pm_domain.h> -#include <linux/delay.h> #include <linux/of.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> @@ -XXX,XX +XXX,XX @@ struct exynos_pm_domain { static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on) { struct exynos_pm_domain *pd; - u32 timeout, pwr; + unsigned int val; + u32 pwr; int err; pd = container_of(domain, struct exynos_pm_domain, pd); @@ -XXX,XX +XXX,XX @@ static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on) if (err) return err; - /* Wait max 1ms */ - timeout = 10; - while (timeout-- > 0) { - unsigned int val; - - err = regmap_read(pd->regmap, 0x4, &val); - if (err || ((val & pd->local_pwr_cfg) != pwr)) { - cpu_relax(); - usleep_range(80, 100); - continue; - } - - return 0; - } - - if (!err) - err = -ETIMEDOUT; - pr_err("Power domain %s %sable failed: %d\n", domain->name, - power_on ? "en" : "dis", err); + err = regmap_read_poll_timeout(pd->regmap, 0x4, val, + (val & pd->local_pwr_cfg) == pwr, + 100, 1 * USEC_PER_MSEC); + if (err) + pr_err("Power domain %s %sable failed: %d (%#.2x)\n", + domain->name, power_on ? "en" : "dis", err, val); return err; } -- 2.51.0.618.g983fd99d29-goog
On platforms such as Google gs101, direct mmio register access to the PMU registers doesn't necessarily work and access must happen via a (syscon) regmap created by the PMU driver instead. When such a regmap is used it will cover the complete PMU memory region rather than individual power domains. This means the register offsets for the configuration and status registers will have to take the power domain offsets into account, rather than unconditionally hardcoding 0 and 4 respectively. Update the code to allow that. Signed-off-by: André Draszik <andre.draszik@linaro.org> --- drivers/pmdomain/samsung/exynos-pm-domains.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/pmdomain/samsung/exynos-pm-domains.c b/drivers/pmdomain/samsung/exynos-pm-domains.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/pmdomain/samsung/exynos-pm-domains.c +++ b/drivers/pmdomain/samsung/exynos-pm-domains.c @@ -XXX,XX +XXX,XX @@ struct exynos_pm_domain { struct regmap *regmap; struct generic_pm_domain pd; u32 local_pwr_cfg; + u32 configuration_reg; + u32 status_reg; }; static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on) @@ -XXX,XX +XXX,XX @@ static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on) pd = container_of(domain, struct exynos_pm_domain, pd); pwr = power_on ? pd->local_pwr_cfg : 0; - err = regmap_write(pd->regmap, 0, pwr); + err = regmap_write(pd->regmap, pd->configuration_reg, pwr); if (err) return err; - err = regmap_read_poll_timeout(pd->regmap, 0x4, val, + err = regmap_read_poll_timeout(pd->regmap, pd->status_reg, val, (val & pd->local_pwr_cfg) == pwr, 100, 1 * USEC_PER_MSEC); if (err) @@ -XXX,XX +XXX,XX @@ static int exynos_pd_probe(struct platform_device *pdev) pd->pd.power_off = exynos_pd_power_off; pd->pd.power_on = exynos_pd_power_on; pd->local_pwr_cfg = pm_domain_cfg->local_pwr_cfg; + pd->configuration_reg += 0; + pd->status_reg += 4; - ret = regmap_read(pd->regmap, 0x4, &val); + ret = regmap_read(pd->regmap, pd->status_reg, &val); if (ret) return dev_err_probe(dev, ret, "failed to read status"); -- 2.51.0.618.g983fd99d29-goog
Unconditionally calling of_genpd_sync_state() causes issues on platforms with child domains as the parent domain will be turned off before the child domain was even registered during boot. This in particular is an issue for the upcoming Google gs101 support - all operations on child domains registered after the parent domain misbehave. Add a flag to the probe data to be able to sync_state conditionally only, and enable that flag on the two platforms currently supported by this driver. Signed-off-by: André Draszik <andre.draszik@linaro.org> --- drivers/pmdomain/samsung/exynos-pm-domains.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/pmdomain/samsung/exynos-pm-domains.c b/drivers/pmdomain/samsung/exynos-pm-domains.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/pmdomain/samsung/exynos-pm-domains.c +++ b/drivers/pmdomain/samsung/exynos-pm-domains.c @@ -XXX,XX +XXX,XX @@ struct exynos_pm_domain_config { /* Value for LOCAL_PWR_CFG and STATUS fields for each domain */ u32 local_pwr_cfg; + unsigned int need_early_sync_state:1; }; /* @@ -XXX,XX +XXX,XX @@ static int exynos_pd_power_off(struct generic_pm_domain *domain) static const struct exynos_pm_domain_config exynos4210_cfg = { .local_pwr_cfg = 0x7, + .need_early_sync_state = true, }; static const struct exynos_pm_domain_config exynos5433_cfg = { .local_pwr_cfg = 0xf, + .need_early_sync_state = true, }; static const struct of_device_id exynos_pm_domain_of_match[] = { @@ -XXX,XX +XXX,XX @@ static int exynos_pd_probe(struct platform_device *pdev) * reset during boot. As a temporary hack to manage this, let's enforce * a sync_state. */ - if (!ret) + if (pm_domain_cfg->need_early_sync_state && !ret) of_genpd_sync_state(np); pm_runtime_enable(dev); -- 2.51.0.618.g983fd99d29-goog
On platforms such as Google gs101, direct mmio register access to the PMU registers doesn't necessarily work and access must happen via a (syscon) regmap created by the PMU driver instead. Try to obtain this regmap using the parent node in DT in case this PD is a child of the PMU and fall back to the traditional direct mmio regmap otherwise. Signed-off-by: André Draszik <andre.draszik@linaro.org> --- drivers/pmdomain/samsung/exynos-pm-domains.c | 58 ++++++++++++++++++---------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/drivers/pmdomain/samsung/exynos-pm-domains.c b/drivers/pmdomain/samsung/exynos-pm-domains.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/pmdomain/samsung/exynos-pm-domains.c +++ b/drivers/pmdomain/samsung/exynos-pm-domains.c @@ -XXX,XX +XXX,XX @@ #include <linux/err.h> #include <linux/platform_device.h> #include <linux/slab.h> +#include <linux/mfd/syscon.h> #include <linux/pm_domain.h> #include <linux/of.h> #include <linux/pm_runtime.h> @@ -XXX,XX +XXX,XX @@ static int exynos_pd_probe(struct platform_device *pdev) struct of_phandle_args child, parent; struct exynos_pm_domain *pd; struct resource *res; - void __iomem *base; unsigned int val; int on, ret; - struct regmap_config reg_config = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, - .use_relaxed_mmio = true, - }; - pm_domain_cfg = of_device_get_match_data(dev); pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); if (!pd) @@ -XXX,XX +XXX,XX @@ static int exynos_pd_probe(struct platform_device *pdev) return -ENOMEM; /* - * The resource typically points into the address space of the PMU. + * The resource typically points into the address space of the PMU and + * we have to consider two cases: + * 1) some implementations require a custom syscon regmap + * 2) this driver might map the same addresses as the PMU driver * Therefore, avoid using devm_platform_get_and_ioremap_resource() and - * instead use platform_get_resource() and devm_ioremap() to avoid + * instead use platform_get_resource() here, and below for case 1) use + * syscon_node_to_regmap() while for case 2) use devm_ioremap() to avoid * conflicts due to address space overlap. */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return dev_err_probe(dev, -ENXIO, "missing IO resources"); - base = devm_ioremap(dev, res->start, resource_size(res)); - if (!base) - return dev_err_probe(dev, -ENOMEM, - "failed to ioremap PMU registers"); - - reg_config.max_register = resource_size(res) - reg_config.reg_stride; - pd->regmap = devm_regmap_init_mmio(dev, base, ®_config); - if (IS_ERR(pd->regmap)) - return dev_err_probe(dev, PTR_ERR(base), - "failed to init regmap"); + if (dev->parent && + of_device_is_compatible(dev->parent->of_node, "syscon")) { + pd->regmap = syscon_node_to_regmap(dev->parent->of_node); + if (IS_ERR(pd->regmap)) + return dev_err_probe(dev, PTR_ERR(pd->regmap), + "failed to acquire PMU regmap"); + + pd->configuration_reg = res->start; + pd->status_reg = res->start; + } else { + void __iomem *base; + + const struct regmap_config reg_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .use_relaxed_mmio = true, + .max_register = (resource_size(res) + - reg_config.reg_stride), + }; + + base = devm_ioremap(dev, res->start, resource_size(res)); + if (!base) + return dev_err_probe(dev, -ENOMEM, + "failed to ioremap PMU registers"); + + pd->regmap = devm_regmap_init_mmio(dev, base, ®_config); + if (IS_ERR(pd->regmap)) + return dev_err_probe(dev, PTR_ERR(base), + "failed to init regmap"); + } pd->pd.power_off = exynos_pd_power_off; pd->pd.power_on = exynos_pd_power_on; -- 2.51.0.618.g983fd99d29-goog
dev_err() gives us more consistent error messages, which include the device. Switch to using dev_err(). Signed-off-by: André Draszik <andre.draszik@linaro.org> --- drivers/pmdomain/samsung/exynos-pm-domains.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/pmdomain/samsung/exynos-pm-domains.c b/drivers/pmdomain/samsung/exynos-pm-domains.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/pmdomain/samsung/exynos-pm-domains.c +++ b/drivers/pmdomain/samsung/exynos-pm-domains.c @@ -XXX,XX +XXX,XX @@ struct exynos_pm_domain_config { */ struct exynos_pm_domain { struct regmap *regmap; + struct device *dev; struct generic_pm_domain pd; u32 local_pwr_cfg; u32 configuration_reg; @@ -XXX,XX +XXX,XX @@ static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on) (val & pd->local_pwr_cfg) == pwr, 100, 1 * USEC_PER_MSEC); if (err) - pr_err("Power domain %s %sable failed: %d (%#.2x)\n", - domain->name, power_on ? "en" : "dis", err, val); + dev_err(pd->dev, + "Power domain %s %sable failed: %d (%#.2x)\n", + domain->name, power_on ? "en" : "dis", err, val); return err; } @@ -XXX,XX +XXX,XX @@ static int exynos_pd_probe(struct platform_device *pdev) if (!pd) return -ENOMEM; + pd->dev = dev; + pd->pd.name = exynos_get_domain_name(dev, np); if (!pd->pd.name) return -ENOMEM; -- 2.51.0.618.g983fd99d29-goog
Compared to other previous designs supported by this driver, the status is just one bit. There is nothing unusual here. Signed-off-by: André Draszik <andre.draszik@linaro.org> --- drivers/pmdomain/samsung/exynos-pm-domains.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/pmdomain/samsung/exynos-pm-domains.c b/drivers/pmdomain/samsung/exynos-pm-domains.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/pmdomain/samsung/exynos-pm-domains.c +++ b/drivers/pmdomain/samsung/exynos-pm-domains.c @@ -XXX,XX +XXX,XX @@ static const struct exynos_pm_domain_config exynos5433_cfg = { .need_early_sync_state = true, }; +static const struct exynos_pm_domain_config gs101_cfg = { + .local_pwr_cfg = BIT(0), +}; + static const struct of_device_id exynos_pm_domain_of_match[] = { { + .compatible = "google,gs101-pd", + .data = &gs101_cfg, + }, { .compatible = "samsung,exynos4210-pd", .data = &exynos4210_cfg, }, { -- 2.51.0.618.g983fd99d29-goog
Hi, This series adds support for the power domains on Google GS101. It's fairly similar to SoCs already supported by this driver, except that register acces does not work via plain ioremap() / readl() / writel(). Instead, the regmap created by the PMU driver must be used (which uses Arm SMCC calls under the hood). The DT update to add the new required properties on gs101 will be posted separately. Signed-off-by: André Draszik <andre.draszik@linaro.org> --- Changes in v3: - use additionalProperties, not unevaluatedProperties in patch 2 - fix path in $id in patch 2 (Rob) - drop comment around 'select' in patch 2 (Rob) - collect tags - Link to v2: https://lore.kernel.org/r/20251009-gs101-pd-v2-0-3f4a6db2af39@linaro.org Changes in v2: - Krzysztof: - move google,gs101-pmu binding into separate file - mark devm_kstrdup_const() patch as fix - use bool for need_early_sync_state - merge patches 8 and 10 from v1 series into one patch - collect tags - Link to v1: https://lore.kernel.org/r/20251006-gs101-pd-v1-0-f0cb0c01ea7b@linaro.org --- André Draszik (10): dt-bindings: power: samsung: add google,gs101-pd dt-bindings: soc: samsung: exynos-pmu: move gs101-pmu into separate binding dt-bindings: soc: samsung: gs101-pmu: allow power domains as children pmdomain: samsung: plug potential memleak during probe pmdomain: samsung: convert to using regmap pmdomain: samsung: convert to regmap_read_poll_timeout() pmdomain: samsung: don't hardcode offset for registers to 0 and 4 pmdomain: samsung: selectively handle enforced sync_state pmdomain: samsung: add support for google,gs101-pd pmdomain: samsung: use dev_err() instead of pr_err() .../devicetree/bindings/power/pd-samsung.yaml | 1 + .../bindings/soc/google/google,gs101-pmu.yaml | 106 +++++++++++++++++ .../bindings/soc/samsung/exynos-pmu.yaml | 20 ---- MAINTAINERS | 1 + drivers/pmdomain/samsung/exynos-pm-domains.c | 126 +++++++++++++++------ 5 files changed, 200 insertions(+), 54 deletions(-) --- base-commit: 58e817956925fdc12c61f1cb86915b82ae1603c1 change-id: 20251001-gs101-pd-d4dc97d70a84 Best regards, -- André Draszik <andre.draszik@linaro.org>
Add support for the Google gs101 version of the Exynos power domains. A new compatible is needed because register fields have changed. Reviewed-by: Peter Griffin <peter.griffin@linaro.org> Signed-off-by: André Draszik <andre.draszik@linaro.org> --- Documentation/devicetree/bindings/power/pd-samsung.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/power/pd-samsung.yaml b/Documentation/devicetree/bindings/power/pd-samsung.yaml index XXXXXXX..XXXXXXX 100644 --- a/Documentation/devicetree/bindings/power/pd-samsung.yaml +++ b/Documentation/devicetree/bindings/power/pd-samsung.yaml @@ -XXX,XX +XXX,XX @@ allOf: properties: compatible: enum: + - google,gs101-pd - samsung,exynos4210-pd - samsung,exynos5433-pd -- 2.51.0.788.g6d19910ace-goog
The gs101-pmu binding is going to acquire various additional (pattern) properties that don't apply to other PMUs supported by this binding. To enable this, move google,gs101-pmu into a separate binding. Signed-off-by: André Draszik <andre.draszik@linaro.org> --- v3: - use additionalProperties, not unevaluatedProperties - fix path in $id (Rob) - drop comment around 'select' (Rob) --- .../bindings/soc/google/google,gs101-pmu.yaml | 66 ++++++++++++++++++++++ .../bindings/soc/samsung/exynos-pmu.yaml | 20 ------- MAINTAINERS | 1 + 3 files changed, 67 insertions(+), 20 deletions(-) diff --git a/Documentation/devicetree/bindings/soc/google/google,gs101-pmu.yaml b/Documentation/devicetree/bindings/soc/google/google,gs101-pmu.yaml new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/Documentation/devicetree/bindings/soc/google/google,gs101-pmu.yaml @@ -XXX,XX +XXX,XX @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/google/google,gs101-pmu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Google GS101 Power Management Unit (PMU) + +maintainers: + - André Draszik <andre.draszik@linaro.org> + +select: + properties: + compatible: + contains: + const: google,gs101-pmu + required: + - compatible + +properties: + compatible: + items: + - const: google,gs101-pmu + - const: syscon + + reg: + maxItems: 1 + + reboot-mode: + $ref: /schemas/power/reset/syscon-reboot-mode.yaml + type: object + description: + Reboot mode to alter bootloader behavior for the next boot + + syscon-poweroff: + $ref: /schemas/power/reset/syscon-poweroff.yaml# + type: object + description: + Node for power off method + + syscon-reboot: + $ref: /schemas/power/reset/syscon-reboot.yaml# + type: object + description: + Node for reboot method + + google,pmu-intr-gen-syscon: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Phandle to PMU interrupt generation interface. + +required: + - compatible + - reg + - google,pmu-intr-gen-syscon + +additionalProperties: false + +examples: + - | + system-controller@17460000 { + compatible = "google,gs101-pmu", "syscon"; + reg = <0x17460000 0x10000>; + + google,pmu-intr-gen-syscon = <&pmu_intr_gen>; + }; diff --git a/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml b/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml index XXXXXXX..XXXXXXX 100644 --- a/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml +++ b/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml @@ -XXX,XX +XXX,XX @@ select: compatible: contains: enum: - - google,gs101-pmu - samsung,exynos3250-pmu - samsung,exynos4210-pmu - samsung,exynos4212-pmu @@ -XXX,XX +XXX,XX @@ properties: oneOf: - items: - enum: - - google,gs101-pmu - samsung,exynos3250-pmu - samsung,exynos4210-pmu - samsung,exynos4212-pmu @@ -XXX,XX +XXX,XX @@ properties: description: Node for reboot method - google,pmu-intr-gen-syscon: - $ref: /schemas/types.yaml#/definitions/phandle - description: - Phandle to PMU interrupt generation interface. - required: - compatible - reg @@ -XXX,XX +XXX,XX @@ allOf: properties: dp-phy: false - - if: - properties: - compatible: - contains: - enum: - - google,gs101-pmu - then: - required: - - google,pmu-intr-gen-syscon - else: - properties: - google,pmu-intr-gen-syscon: false - examples: - | #include <dt-bindings/clock/exynos5250.h> diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ S: Maintained P: Documentation/process/maintainer-soc-clean-dts.rst C: irc://irc.oftc.net/pixel6-kernel-dev F: Documentation/devicetree/bindings/clock/google,gs101-clock.yaml +F: Documentation/devicetree/bindings/soc/google/google,gs101-pmu.yaml F: Documentation/devicetree/bindings/soc/google/google,gs101-pmu-intr-gen.yaml F: arch/arm64/boot/dts/exynos/google/ F: drivers/clk/samsung/clk-gs101.c -- 2.51.0.788.g6d19910ace-goog
The power domains are a property of / implemented in the PMU. As such, they should be modelled as child nodes of the PMU. Signed-off-by: André Draszik <andre.draszik@linaro.org> --- Note: Ideally, the newly added properties (ranges, etc.) should only be 'required' if "^power-domain@[0-9a-f]+$" exists as a patternProperty, as they're needed only in that case. As-is, this patch now causes warnings for existing DTs as they don't specify the new properties (and they shouldn't need to). Only if DTs are updated to include power-domains, such an update should also add the new properties. I've not been able to come up with the correct schema syntax to achieve that. dependencies, dependentRequired, and dependentSchemas don't seem to support patterns. Similarly, - if: required: - ... then: required: - ... doesn't allow patterns in the 'if' block (or I didn't get the syntax right). Rob said in https://lore.kernel.org/all/20251010141357.GA219719-robh@kernel.org/ that this is a known limitation in json-schema. --- .../bindings/soc/google/google,gs101-pmu.yaml | 40 ++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/Documentation/devicetree/bindings/soc/google/google,gs101-pmu.yaml b/Documentation/devicetree/bindings/soc/google/google,gs101-pmu.yaml index XXXXXXX..XXXXXXX 100644 --- a/Documentation/devicetree/bindings/soc/google/google,gs101-pmu.yaml +++ b/Documentation/devicetree/bindings/soc/google/google,gs101-pmu.yaml @@ -XXX,XX +XXX,XX @@ properties: reg: maxItems: 1 + '#address-cells': + const: 1 + + '#size-cells': + const: 1 + + ranges: true + reboot-mode: $ref: /schemas/power/reset/syscon-reboot-mode.yaml type: object @@ -XXX,XX +XXX,XX @@ properties: description: Phandle to PMU interrupt generation interface. +patternProperties: + "^power-domain@[0-9a-f]+$": + type: object + description: Child node describing one power domain within the PMU + + additionalProperties: true + + properties: + compatible: + const: google,gs101-pd + required: - compatible - reg + - '#address-cells' + - '#size-cells' + - ranges - google,pmu-intr-gen-syscon additionalProperties: false @@ -XXX,XX +XXX,XX @@ examples: system-controller@17460000 { compatible = "google,gs101-pmu", "syscon"; reg = <0x17460000 0x10000>; + #address-cells = <1>; + #size-cells = <1>; + ranges; google,pmu-intr-gen-syscon = <&pmu_intr_gen>; + + pd_g3d: power-domain@1e00 { + compatible = "google,gs101-pd"; + reg = <0x1e00 0x80>; + #power-domain-cells = <0>; + label = "g3d"; + }; + + power-domain@2000 { + compatible = "google,gs101-pd"; + reg = <0x2000 0x80>; + #power-domain-cells = <0>; + power-domains = <&pd_g3d>; + label = "embedded_g3d"; + }; }; -- 2.51.0.788.g6d19910ace-goog
of_genpd_add_provider_simple() could fail, in which case this code leaks the domain name, pd->pd.name. Use devm_kstrdup_const() to plug this leak. As a side-effect, we can simplify existing error handling. Fixes: c09a3e6c97f0 ("soc: samsung: pm_domains: Convert to regular platform driver") Cc: stable@vger.kernel.org Reviewed-by: Peter Griffin <peter.griffin@linaro.org> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Signed-off-by: André Draszik <andre.draszik@linaro.org> --- v2: mark as fix, as this isn't a pure simplification --- drivers/pmdomain/samsung/exynos-pm-domains.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/pmdomain/samsung/exynos-pm-domains.c b/drivers/pmdomain/samsung/exynos-pm-domains.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/pmdomain/samsung/exynos-pm-domains.c +++ b/drivers/pmdomain/samsung/exynos-pm-domains.c @@ -XXX,XX +XXX,XX @@ static const struct of_device_id exynos_pm_domain_of_match[] = { { }, }; -static const char *exynos_get_domain_name(struct device_node *node) +static const char *exynos_get_domain_name(struct device *dev, + struct device_node *node) { const char *name; if (of_property_read_string(node, "label", &name) < 0) name = kbasename(node->full_name); - return kstrdup_const(name, GFP_KERNEL); + return devm_kstrdup_const(dev, name, GFP_KERNEL); } static int exynos_pd_probe(struct platform_device *pdev) @@ -XXX,XX +XXX,XX @@ static int exynos_pd_probe(struct platform_device *pdev) if (!pd) return -ENOMEM; - pd->pd.name = exynos_get_domain_name(np); + pd->pd.name = exynos_get_domain_name(dev, np); if (!pd->pd.name) return -ENOMEM; pd->base = of_iomap(np, 0); - if (!pd->base) { - kfree_const(pd->pd.name); + if (!pd->base) return -ENODEV; - } pd->pd.power_off = exynos_pd_power_off; pd->pd.power_on = exynos_pd_power_on; -- 2.51.0.788.g6d19910ace-goog
On platforms such as Google gs101, direct mmio register access to the PMU registers doesn't necessarily work and access must happen via a (syscon) regmap created by the PMU driver instead. In preparation for supporting such SoCs convert the existing mmio accesses to using a regmap wrapper. With this change in place, a follow-up patch can update the driver to optionally acquire the PMU-created regmap without having to change the rest of the code. Signed-off-by: André Draszik <andre.draszik@linaro.org> --- There is one checkpatch warning, relating to the non-const regmap_config. It can not easily be made const at this stage, but a follow-up patch allows us to make it const and the warning will go away anyway. --- drivers/pmdomain/samsung/exynos-pm-domains.c | 78 ++++++++++++++++++++-------- 1 file changed, 57 insertions(+), 21 deletions(-) diff --git a/drivers/pmdomain/samsung/exynos-pm-domains.c b/drivers/pmdomain/samsung/exynos-pm-domains.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/pmdomain/samsung/exynos-pm-domains.c +++ b/drivers/pmdomain/samsung/exynos-pm-domains.c @@ -XXX,XX +XXX,XX @@ // conjunction with runtime-pm. Support for both device-tree and non-device-tree // based power domain support is included. -#include <linux/io.h> #include <linux/err.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/pm_domain.h> #include <linux/delay.h> #include <linux/of.h> -#include <linux/of_address.h> #include <linux/pm_runtime.h> +#include <linux/regmap.h> struct exynos_pm_domain_config { /* Value for LOCAL_PWR_CFG and STATUS fields for each domain */ @@ -XXX,XX +XXX,XX @@ struct exynos_pm_domain_config { * Exynos specific wrapper around the generic power domain */ struct exynos_pm_domain { - void __iomem *base; + struct regmap *regmap; struct generic_pm_domain pd; u32 local_pwr_cfg; }; @@ -XXX,XX +XXX,XX @@ struct exynos_pm_domain { static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on) { struct exynos_pm_domain *pd; - void __iomem *base; u32 timeout, pwr; - char *op; + int err; pd = container_of(domain, struct exynos_pm_domain, pd); - base = pd->base; pwr = power_on ? pd->local_pwr_cfg : 0; - writel_relaxed(pwr, base); + err = regmap_write(pd->regmap, 0, pwr); + if (err) + return err; /* Wait max 1ms */ timeout = 10; - - while ((readl_relaxed(base + 0x4) & pd->local_pwr_cfg) != pwr) { - if (!timeout) { - op = (power_on) ? "enable" : "disable"; - pr_err("Power domain %s %s failed\n", domain->name, op); - return -ETIMEDOUT; + while (timeout-- > 0) { + unsigned int val; + + err = regmap_read(pd->regmap, 0x4, &val); + if (err || ((val & pd->local_pwr_cfg) != pwr)) { + cpu_relax(); + usleep_range(80, 100); + continue; } - timeout--; - cpu_relax(); - usleep_range(80, 100); + + return 0; } - return 0; + if (!err) + err = -ETIMEDOUT; + pr_err("Power domain %s %sable failed: %d\n", domain->name, + power_on ? "en" : "dis", err); + + return err; } static int exynos_pd_power_on(struct generic_pm_domain *domain) @@ -XXX,XX +XXX,XX @@ static int exynos_pd_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; struct of_phandle_args child, parent; struct exynos_pm_domain *pd; + struct resource *res; + void __iomem *base; + unsigned int val; int on, ret; + struct regmap_config reg_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .use_relaxed_mmio = true, + }; + pm_domain_cfg = of_device_get_match_data(dev); pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); if (!pd) @@ -XXX,XX +XXX,XX @@ static int exynos_pd_probe(struct platform_device *pdev) if (!pd->pd.name) return -ENOMEM; - pd->base = of_iomap(np, 0); - if (!pd->base) - return -ENODEV; + /* + * The resource typically points into the address space of the PMU. + * Therefore, avoid using devm_platform_get_and_ioremap_resource() and + * instead use platform_get_resource() and devm_ioremap() to avoid + * conflicts due to address space overlap. + */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return dev_err_probe(dev, -ENXIO, "missing IO resources"); + + base = devm_ioremap(dev, res->start, resource_size(res)); + if (!base) + return dev_err_probe(dev, -ENOMEM, + "failed to ioremap PMU registers"); + + reg_config.max_register = resource_size(res) - reg_config.reg_stride; + pd->regmap = devm_regmap_init_mmio(dev, base, ®_config); + if (IS_ERR(pd->regmap)) + return dev_err_probe(dev, PTR_ERR(base), + "failed to init regmap"); pd->pd.power_off = exynos_pd_power_off; pd->pd.power_on = exynos_pd_power_on; pd->local_pwr_cfg = pm_domain_cfg->local_pwr_cfg; - on = readl_relaxed(pd->base + 0x4) & pd->local_pwr_cfg; + ret = regmap_read(pd->regmap, 0x4, &val); + if (ret) + return dev_err_probe(dev, ret, "failed to read status"); + + on = val & pd->local_pwr_cfg; pm_genpd_init(&pd->pd, NULL, !on); ret = of_genpd_add_provider_simple(np, &pd->pd); -- 2.51.0.788.g6d19910ace-goog
Replace the open-coded PD status polling with regmap_read_poll_timeout(). This change simplifies the code without altering functionality. Signed-off-by: André Draszik <andre.draszik@linaro.org> --- drivers/pmdomain/samsung/exynos-pm-domains.c | 29 ++++++++-------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/drivers/pmdomain/samsung/exynos-pm-domains.c b/drivers/pmdomain/samsung/exynos-pm-domains.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/pmdomain/samsung/exynos-pm-domains.c +++ b/drivers/pmdomain/samsung/exynos-pm-domains.c @@ -XXX,XX +XXX,XX @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/pm_domain.h> -#include <linux/delay.h> #include <linux/of.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> @@ -XXX,XX +XXX,XX @@ struct exynos_pm_domain { static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on) { struct exynos_pm_domain *pd; - u32 timeout, pwr; + unsigned int val; + u32 pwr; int err; pd = container_of(domain, struct exynos_pm_domain, pd); @@ -XXX,XX +XXX,XX @@ static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on) if (err) return err; - /* Wait max 1ms */ - timeout = 10; - while (timeout-- > 0) { - unsigned int val; - - err = regmap_read(pd->regmap, 0x4, &val); - if (err || ((val & pd->local_pwr_cfg) != pwr)) { - cpu_relax(); - usleep_range(80, 100); - continue; - } - - return 0; - } - - if (!err) - err = -ETIMEDOUT; - pr_err("Power domain %s %sable failed: %d\n", domain->name, - power_on ? "en" : "dis", err); + err = regmap_read_poll_timeout(pd->regmap, 0x4, val, + (val & pd->local_pwr_cfg) == pwr, + 100, 1 * USEC_PER_MSEC); + if (err) + pr_err("Power domain %s %sable failed: %d (%#.2x)\n", + domain->name, power_on ? "en" : "dis", err, val); return err; } -- 2.51.0.788.g6d19910ace-goog
On platforms such as Google gs101, direct mmio register access to the PMU registers doesn't necessarily work and access must happen via a (syscon) regmap created by the PMU driver instead. When such a regmap is used it will cover the complete PMU memory region rather than individual power domains. This means the register offsets for the configuration and status registers will have to take the power domain offsets into account, rather than unconditionally hardcoding 0 and 4 respectively. Update the code to allow that. Signed-off-by: André Draszik <andre.draszik@linaro.org> --- drivers/pmdomain/samsung/exynos-pm-domains.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/pmdomain/samsung/exynos-pm-domains.c b/drivers/pmdomain/samsung/exynos-pm-domains.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/pmdomain/samsung/exynos-pm-domains.c +++ b/drivers/pmdomain/samsung/exynos-pm-domains.c @@ -XXX,XX +XXX,XX @@ struct exynos_pm_domain { struct regmap *regmap; struct generic_pm_domain pd; u32 local_pwr_cfg; + u32 configuration_reg; + u32 status_reg; }; static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on) @@ -XXX,XX +XXX,XX @@ static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on) pd = container_of(domain, struct exynos_pm_domain, pd); pwr = power_on ? pd->local_pwr_cfg : 0; - err = regmap_write(pd->regmap, 0, pwr); + err = regmap_write(pd->regmap, pd->configuration_reg, pwr); if (err) return err; - err = regmap_read_poll_timeout(pd->regmap, 0x4, val, + err = regmap_read_poll_timeout(pd->regmap, pd->status_reg, val, (val & pd->local_pwr_cfg) == pwr, 100, 1 * USEC_PER_MSEC); if (err) @@ -XXX,XX +XXX,XX @@ static int exynos_pd_probe(struct platform_device *pdev) pd->pd.power_off = exynos_pd_power_off; pd->pd.power_on = exynos_pd_power_on; pd->local_pwr_cfg = pm_domain_cfg->local_pwr_cfg; + pd->configuration_reg += 0; + pd->status_reg += 4; - ret = regmap_read(pd->regmap, 0x4, &val); + ret = regmap_read(pd->regmap, pd->status_reg, &val); if (ret) return dev_err_probe(dev, ret, "failed to read status"); -- 2.51.0.788.g6d19910ace-goog
Unconditionally calling of_genpd_sync_state() causes issues on platforms with child domains as the parent domain will be turned off before the child domain was even registered during boot. This in particular is an issue for the upcoming Google gs101 support - all operations on child domains registered after the parent domain misbehave. Add a flag to the probe data to be able to sync_state conditionally only, and enable that flag on the two platforms currently supported by this driver. Signed-off-by: André Draszik <andre.draszik@linaro.org> --- v2: * use bool for need_early_sync_state (Krzysztof) --- drivers/pmdomain/samsung/exynos-pm-domains.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/pmdomain/samsung/exynos-pm-domains.c b/drivers/pmdomain/samsung/exynos-pm-domains.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/pmdomain/samsung/exynos-pm-domains.c +++ b/drivers/pmdomain/samsung/exynos-pm-domains.c @@ -XXX,XX +XXX,XX @@ struct exynos_pm_domain_config { /* Value for LOCAL_PWR_CFG and STATUS fields for each domain */ u32 local_pwr_cfg; + bool need_early_sync_state; }; /* @@ -XXX,XX +XXX,XX @@ static int exynos_pd_power_off(struct generic_pm_domain *domain) static const struct exynos_pm_domain_config exynos4210_cfg = { .local_pwr_cfg = 0x7, + .need_early_sync_state = true, }; static const struct exynos_pm_domain_config exynos5433_cfg = { .local_pwr_cfg = 0xf, + .need_early_sync_state = true, }; static const struct of_device_id exynos_pm_domain_of_match[] = { @@ -XXX,XX +XXX,XX @@ static int exynos_pd_probe(struct platform_device *pdev) * reset during boot. As a temporary hack to manage this, let's enforce * a sync_state. */ - if (!ret) + if (pm_domain_cfg->need_early_sync_state && !ret) of_genpd_sync_state(np); pm_runtime_enable(dev); -- 2.51.0.788.g6d19910ace-goog
On Google gs101, direct mmio register access to the PMU registers doesn't work and access must happen via a (syscon) regmap created by the PMU driver instead. Try to obtain this regmap using the parent node in DT in case this PD is a child of the PMU and fall back to the traditional direct mmio regmap otherwise. Additionally, the status is just one bit on gs101. Signed-off-by: André Draszik <andre.draszik@linaro.org> --- drivers/pmdomain/samsung/exynos-pm-domains.c | 65 +++++++++++++++++++--------- 1 file changed, 45 insertions(+), 20 deletions(-) diff --git a/drivers/pmdomain/samsung/exynos-pm-domains.c b/drivers/pmdomain/samsung/exynos-pm-domains.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/pmdomain/samsung/exynos-pm-domains.c +++ b/drivers/pmdomain/samsung/exynos-pm-domains.c @@ -XXX,XX +XXX,XX @@ #include <linux/err.h> #include <linux/platform_device.h> #include <linux/slab.h> +#include <linux/mfd/syscon.h> #include <linux/pm_domain.h> #include <linux/of.h> #include <linux/pm_runtime.h> @@ -XXX,XX +XXX,XX @@ static const struct exynos_pm_domain_config exynos5433_cfg = { .need_early_sync_state = true, }; +static const struct exynos_pm_domain_config gs101_cfg = { + .local_pwr_cfg = BIT(0), +}; + static const struct of_device_id exynos_pm_domain_of_match[] = { { + .compatible = "google,gs101-pd", + .data = &gs101_cfg, + }, { .compatible = "samsung,exynos4210-pd", .data = &exynos4210_cfg, }, { @@ -XXX,XX +XXX,XX @@ static int exynos_pd_probe(struct platform_device *pdev) struct of_phandle_args child, parent; struct exynos_pm_domain *pd; struct resource *res; - void __iomem *base; unsigned int val; int on, ret; - struct regmap_config reg_config = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, - .use_relaxed_mmio = true, - }; - pm_domain_cfg = of_device_get_match_data(dev); pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); if (!pd) @@ -XXX,XX +XXX,XX @@ static int exynos_pd_probe(struct platform_device *pdev) return -ENOMEM; /* - * The resource typically points into the address space of the PMU. + * The resource typically points into the address space of the PMU and + * we have to consider two cases: + * 1) some implementations require a custom syscon regmap + * 2) this driver might map the same addresses as the PMU driver * Therefore, avoid using devm_platform_get_and_ioremap_resource() and - * instead use platform_get_resource() and devm_ioremap() to avoid + * instead use platform_get_resource() here, and below for case 1) use + * syscon_node_to_regmap() while for case 2) use devm_ioremap() to avoid * conflicts due to address space overlap. */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return dev_err_probe(dev, -ENXIO, "missing IO resources"); - base = devm_ioremap(dev, res->start, resource_size(res)); - if (!base) - return dev_err_probe(dev, -ENOMEM, - "failed to ioremap PMU registers"); - - reg_config.max_register = resource_size(res) - reg_config.reg_stride; - pd->regmap = devm_regmap_init_mmio(dev, base, ®_config); - if (IS_ERR(pd->regmap)) - return dev_err_probe(dev, PTR_ERR(base), - "failed to init regmap"); + if (dev->parent && + of_device_is_compatible(dev->parent->of_node, "syscon")) { + pd->regmap = syscon_node_to_regmap(dev->parent->of_node); + if (IS_ERR(pd->regmap)) + return dev_err_probe(dev, PTR_ERR(pd->regmap), + "failed to acquire PMU regmap"); + + pd->configuration_reg = res->start; + pd->status_reg = res->start; + } else { + void __iomem *base; + + const struct regmap_config reg_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .use_relaxed_mmio = true, + .max_register = (resource_size(res) + - reg_config.reg_stride), + }; + + base = devm_ioremap(dev, res->start, resource_size(res)); + if (!base) + return dev_err_probe(dev, -ENOMEM, + "failed to ioremap PMU registers"); + + pd->regmap = devm_regmap_init_mmio(dev, base, ®_config); + if (IS_ERR(pd->regmap)) + return dev_err_probe(dev, PTR_ERR(base), + "failed to init regmap"); + } pd->pd.power_off = exynos_pd_power_off; pd->pd.power_on = exynos_pd_power_on; -- 2.51.0.788.g6d19910ace-goog
dev_err() gives us more consistent error messages, which include the device. Switch to using dev_err(). Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Signed-off-by: André Draszik <andre.draszik@linaro.org> --- drivers/pmdomain/samsung/exynos-pm-domains.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/pmdomain/samsung/exynos-pm-domains.c b/drivers/pmdomain/samsung/exynos-pm-domains.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/pmdomain/samsung/exynos-pm-domains.c +++ b/drivers/pmdomain/samsung/exynos-pm-domains.c @@ -XXX,XX +XXX,XX @@ struct exynos_pm_domain_config { */ struct exynos_pm_domain { struct regmap *regmap; + struct device *dev; struct generic_pm_domain pd; u32 local_pwr_cfg; u32 configuration_reg; @@ -XXX,XX +XXX,XX @@ static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on) (val & pd->local_pwr_cfg) == pwr, 100, 1 * USEC_PER_MSEC); if (err) - pr_err("Power domain %s %sable failed: %d (%#.2x)\n", - domain->name, power_on ? "en" : "dis", err, val); + dev_err(pd->dev, + "Power domain %s %sable failed: %d (%#.2x)\n", + domain->name, power_on ? "en" : "dis", err, val); return err; } @@ -XXX,XX +XXX,XX @@ static int exynos_pd_probe(struct platform_device *pdev) if (!pd) return -ENOMEM; + pd->dev = dev; + pd->pd.name = exynos_get_domain_name(dev, np); if (!pd->pd.name) return -ENOMEM; -- 2.51.0.788.g6d19910ace-goog