lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CAPDyKFqTVUZn5F7ss7qCtNktCGfSaXCB+cGOnN9rH0hjTwJkhw@mail.gmail.com>
Date: Thu, 11 Sep 2025 16:31:55 +0200
From: Ulf Hansson <ulf.hansson@...aro.org>
To: Duje Mihanović <duje@...emihanovic.xyz>
Cc: Michael Turquette <mturquette@...libre.com>, Stephen Boyd <sboyd@...nel.org>, 
	Rob Herring <robh@...nel.org>, Krzysztof Kozlowski <krzk+dt@...nel.org>, Conor Dooley <conor+dt@...nel.org>, 
	David Wronek <david@...nlining.org>, Karel Balej <balejk@...fyz.cz>, phone-devel@...r.kernel.org, 
	~postmarketos/upstreaming@...ts.sr.ht, linux-arm-kernel@...ts.infradead.org, 
	linux-clk@...r.kernel.org, devicetree@...r.kernel.org, 
	linux-kernel@...r.kernel.org, linux-pm@...r.kernel.org
Subject: Re: [PATCH v3 2/4] pmdomain: marvell: Add PXA1908 power domains

On Fri, 29 Aug 2025 at 18:22, Duje Mihanović <duje@...emihanovic.xyz> wrote:
>
> Marvell's PXA1908 SoC has a few power domains for its VPU, GPU, image
> processor and DSI PHY. Add a driver to control these.
>
> Signed-off-by: Duje Mihanović <duje@...emihanovic.xyz>
> ---
> v3:
> - Move driver back to pmdomain subsystem, use auxiliary bus to
>   instantiate the driver
> - Drop redundant 'struct device' pointer in 'struct pxa1908_pd'
> - Fix pxa1908_pd_is_on() for DSI domain
> - Replace usleep_range() with fsleep()
> - Use dev_err_probe() where sensible
>
> v2:
> - Move to clk subsystem, instantiate the driver from the APMU clock
>   driver
> - Drop clock handling
> - Squash MAINTAINERS patch
> ---
>  MAINTAINERS                                        |   1 +
>  drivers/pmdomain/Kconfig                           |   1 +
>  drivers/pmdomain/Makefile                          |   1 +
>  drivers/pmdomain/marvell/Kconfig                   |  18 ++
>  drivers/pmdomain/marvell/Makefile                  |   3 +
>  .../pmdomain/marvell/pxa1908-power-controller.c    | 268 +++++++++++++++++++++
>  6 files changed, 292 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 88f7bd50686eb1f6bcd4f34c6827f27ad44ea4e8..34e5e218e83e0ed9882b111f5251601dd6549d4e 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -2871,6 +2871,7 @@ L:        linux-arm-kernel@...ts.infradead.org (moderated for non-subscribers)
>  S:     Maintained
>  F:     arch/arm64/boot/dts/marvell/mmp/
>  F:     drivers/clk/mmp/clk-pxa1908*.c
> +F:     drivers/pmdomain/marvell/
>  F:     include/dt-bindings/clock/marvell,pxa1908.h
>  F:     include/dt-bindings/power/marvell,pxa1908-power.h
>
> diff --git a/drivers/pmdomain/Kconfig b/drivers/pmdomain/Kconfig
> index 91f04ace35d4b024fafdf6af4e26a179640eb82f..23076ae90e6641dea8e5dbc851d041cd7929cee6 100644
> --- a/drivers/pmdomain/Kconfig
> +++ b/drivers/pmdomain/Kconfig
> @@ -7,6 +7,7 @@ source "drivers/pmdomain/apple/Kconfig"
>  source "drivers/pmdomain/arm/Kconfig"
>  source "drivers/pmdomain/bcm/Kconfig"
>  source "drivers/pmdomain/imx/Kconfig"
> +source "drivers/pmdomain/marvell/Kconfig"
>  source "drivers/pmdomain/mediatek/Kconfig"
>  source "drivers/pmdomain/qcom/Kconfig"
>  source "drivers/pmdomain/renesas/Kconfig"
> diff --git a/drivers/pmdomain/Makefile b/drivers/pmdomain/Makefile
> index 7030f44a49df9e91b1c9d1b6d12690a6248671fb..ebc802f13eb953db750f5a9507caa64c637a957a 100644
> --- a/drivers/pmdomain/Makefile
> +++ b/drivers/pmdomain/Makefile
> @@ -5,6 +5,7 @@ obj-y                                   += apple/
>  obj-y                                  += arm/
>  obj-y                                  += bcm/
>  obj-y                                  += imx/
> +obj-y                                  += marvell/
>  obj-y                                  += mediatek/
>  obj-y                                  += qcom/
>  obj-y                                  += renesas/
> diff --git a/drivers/pmdomain/marvell/Kconfig b/drivers/pmdomain/marvell/Kconfig
> new file mode 100644
> index 0000000000000000000000000000000000000000..6c4084c826670266b7d948438f6e6d76acb416e2
> --- /dev/null
> +++ b/drivers/pmdomain/marvell/Kconfig
> @@ -0,0 +1,18 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +menu "Marvell PM Domains"
> +       depends on ARCH_MMP || COMPILE_TEST
> +
> +config PXA1908_PM_DOMAINS
> +       tristate "Marvell PXA1908 power domains"
> +       depends on OF
> +       depends on PM
> +       default y if ARCH_MMP && ARM64
> +       select AUXILIARY_BUS
> +       select MFD_SYSCON
> +       select PM_GENERIC_DOMAINS
> +       select PM_GENERIC_DOMAINS_OF
> +       help
> +         Say Y here to enable support for Marvell PXA1908's power domanis.
> +
> +endmenu
> diff --git a/drivers/pmdomain/marvell/Makefile b/drivers/pmdomain/marvell/Makefile
> new file mode 100644
> index 0000000000000000000000000000000000000000..22c25013f6c856a2ca01a121e830279ee88eb0ed
> --- /dev/null
> +++ b/drivers/pmdomain/marvell/Makefile
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +obj-$(CONFIG_PXA1908_PM_DOMAINS) += pxa1908-power-controller.o
> diff --git a/drivers/pmdomain/marvell/pxa1908-power-controller.c b/drivers/pmdomain/marvell/pxa1908-power-controller.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..29134629861abcf46959f9dcc98d3f05a4cc5b72
> --- /dev/null
> +++ b/drivers/pmdomain/marvell/pxa1908-power-controller.c
> @@ -0,0 +1,268 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright 2025 Duje Mihanović <duje@...emihanovic.xyz>
> + */
> +
> +#include <linux/auxiliary_bus.h>
> +#include <linux/container_of.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_domain.h>
> +#include <linux/regmap.h>
> +#include <linux/units.h>
> +
> +#include <dt-bindings/power/marvell,pxa1908-power.h>
> +
> +/* VPU, GPU, ISP */
> +#define APMU_PWR_CTRL_REG      0xd8
> +#define APMU_PWR_BLK_TMR_REG   0xdc
> +#define APMU_PWR_STATUS_REG    0xf0
> +
> +/* DSI */
> +#define APMU_DEBUG             0x88
> +#define DSI_PHY_DVM_MASK       BIT(31)
> +
> +#define POWER_ON_LATENCY_US    300
> +#define POWER_OFF_LATENCY_US   20
> +
> +#define NR_DOMAINS     5
> +
> +struct pxa1908_pd_ctrl {
> +       struct generic_pm_domain *domains[NR_DOMAINS];
> +       struct genpd_onecell_data onecell_data;
> +       struct regmap *base;
> +};
> +
> +struct pxa1908_pd_data {
> +       u32 reg_clk_res_ctrl;
> +       u32 pwr_state;
> +       u32 hw_mode;
> +       bool keep_on;
> +       int id;
> +};
> +
> +struct pxa1908_pd {
> +       const struct pxa1908_pd_data data;
> +       struct generic_pm_domain genpd;
> +       bool initialized;
> +};
> +
> +static inline bool pxa1908_pd_is_on(struct pxa1908_pd *pd)
> +{
> +       struct pxa1908_pd_ctrl *ctrl = dev_get_drvdata(&pd->genpd.dev);

The genpd.dev is owned by the genpd core and must not be used like this.

Please find another way to derive the needed data.

> +
> +       return pd->data.id != PXA1908_POWER_DOMAIN_DSI
> +               ? regmap_test_bits(ctrl->base, APMU_PWR_STATUS_REG, pd->data.pwr_state)
> +               : regmap_test_bits(ctrl->base, APMU_DEBUG, DSI_PHY_DVM_MASK);
> +}
> +
> +static int pxa1908_pd_power_on(struct generic_pm_domain *genpd)
> +{
> +       struct pxa1908_pd *pd = container_of(genpd, struct pxa1908_pd, genpd);
> +       struct pxa1908_pd_ctrl *ctrl = dev_get_drvdata(&genpd->dev);

Ditto.


> +       const struct pxa1908_pd_data *data = &pd->data;
> +       unsigned int status;
> +       int ret = 0;
> +
> +       regmap_set_bits(ctrl->base, data->reg_clk_res_ctrl, data->hw_mode);
> +       if (data->id != PXA1908_POWER_DOMAIN_ISP)
> +               regmap_write(ctrl->base, APMU_PWR_BLK_TMR_REG, 0x20001fff);
> +       regmap_set_bits(ctrl->base, APMU_PWR_CTRL_REG, data->pwr_state);
> +
> +       fsleep(POWER_ON_LATENCY_US);

I assume the fsleep here is intentional and should be combined with
the poll-timeout below. Although I wonder why we can't just poll
immediately and by using some reasonable period+timeout instead?

> +
> +       ret = regmap_read_poll_timeout(ctrl->base, APMU_PWR_STATUS_REG, status,
> +                                      status & data->pwr_state, 6, 25 * USEC_PER_MSEC);

Please avoid hardcoding periods/timeouts here.

> +       if (ret == -ETIMEDOUT)
> +               dev_err(&genpd->dev, "timed out powering on domain '%s'\n", pd->genpd.name);
> +
> +       return ret;
> +}
> +
> +static int pxa1908_pd_power_off(struct generic_pm_domain *genpd)
> +{
> +       struct pxa1908_pd *pd = container_of(genpd, struct pxa1908_pd, genpd);
> +       struct pxa1908_pd_ctrl *ctrl = dev_get_drvdata(&genpd->dev);
> +       const struct pxa1908_pd_data *data = &pd->data;
> +       unsigned int status;
> +       int ret;
> +
> +       regmap_clear_bits(ctrl->base, APMU_PWR_CTRL_REG, data->pwr_state);
> +
> +       fsleep(POWER_OFF_LATENCY_US);

Ditto.

> +
> +       ret = regmap_read_poll_timeout(ctrl->base, APMU_PWR_STATUS_REG, status,
> +                                      !(status & data->pwr_state), 6, 25 * USEC_PER_MSEC);

Ditto.

> +       if (ret == -ETIMEDOUT) {
> +               dev_err(&genpd->dev, "timed out powering off domain '%s'\n", pd->genpd.name);
> +               return ret;
> +       }
> +
> +       return regmap_clear_bits(ctrl->base, data->reg_clk_res_ctrl, data->hw_mode);
> +}
> +
> +static inline int pxa1908_dsi_power_on(struct generic_pm_domain *genpd)
> +{
> +       struct pxa1908_pd_ctrl *ctrl = dev_get_drvdata(&genpd->dev);
> +
> +       return regmap_set_bits(ctrl->base, APMU_DEBUG, DSI_PHY_DVM_MASK);
> +}
> +
> +static inline int pxa1908_dsi_power_off(struct generic_pm_domain *genpd)
> +{
> +       struct pxa1908_pd_ctrl *ctrl = dev_get_drvdata(&genpd->dev);
> +
> +       return regmap_clear_bits(ctrl->base, APMU_DEBUG, DSI_PHY_DVM_MASK);
> +}
> +
> +#define DOMAIN(_id, _name, ctrl, mode, state) \
> +       [_id] = { \
> +               .data = { \
> +                       .reg_clk_res_ctrl = ctrl, \
> +                       .hw_mode = BIT(mode), \
> +                       .pwr_state = BIT(state), \
> +                       .id = _id, \
> +               }, \
> +               .genpd = { \
> +                       .name = _name, \
> +                       .power_on = pxa1908_pd_power_on, \
> +                       .power_off = pxa1908_pd_power_off, \
> +               }, \
> +       }
> +
> +static struct pxa1908_pd domains[NR_DOMAINS] = {
> +       DOMAIN(PXA1908_POWER_DOMAIN_VPU, "vpu", 0xa4, 19, 2),
> +       DOMAIN(PXA1908_POWER_DOMAIN_GPU, "gpu", 0xcc, 11, 0),
> +       DOMAIN(PXA1908_POWER_DOMAIN_GPU2D, "gpu2d", 0xf4, 11, 6),
> +       DOMAIN(PXA1908_POWER_DOMAIN_ISP, "isp", 0x38, 15, 4),
> +       [PXA1908_POWER_DOMAIN_DSI] = {
> +               .genpd = {
> +                       .name = "dsi",
> +                       .power_on = pxa1908_dsi_power_on,
> +                       .power_off = pxa1908_dsi_power_off,
> +                       /*
> +                        * TODO: There is no DSI driver written yet and until then we probably
> +                        * don't want to power off the DSI PHY ever.
> +                        */
> +                       .flags = GENPD_FLAG_ALWAYS_ON,
> +               },
> +               .data = {
> +                       /* See above. */
> +                       .keep_on = true,
> +               },
> +       },
> +};
> +
> +static void pxa1908_pd_remove(struct auxiliary_device *auxdev)
> +{
> +       struct pxa1908_pd *pd;
> +       int ret;
> +
> +       for (int i = NR_DOMAINS - 1; i >= 0; i--) {
> +               pd = &domains[i];
> +
> +               if (!pd->initialized)
> +                       continue;
> +
> +               if (pxa1908_pd_is_on(pd) && !pd->data.keep_on)
> +                       pxa1908_pd_power_off(&pd->genpd);
> +
> +               ret = pm_genpd_remove(&pd->genpd);
> +               if (ret)
> +                       dev_err(&pd->genpd.dev, "failed to remove domain '%s': %d\n",
> +                               pd->genpd.name, ret);
> +       }
> +}
> +
> +static int
> +pxa1908_pd_init(struct pxa1908_pd_ctrl *ctrl, int id, struct device *dev)
> +{
> +       struct pxa1908_pd *pd = &domains[id];
> +       int ret;
> +
> +       ctrl->domains[id] = &pd->genpd;
> +
> +       ret = pm_genpd_init(&pd->genpd, NULL, !pd->data.keep_on);
> +       if (ret)
> +               return dev_err_probe(dev, ret, "domain '%s' failed to initialize\n",
> +                                    pd->genpd.name);
> +
> +       dev_set_drvdata(&pd->genpd.dev, ctrl);

As stated above, please don't abuse the genpd.dev like this.

> +
> +       /* Make sure the state of the hardware is synced with the domain table above. */
> +       if (pd->data.keep_on) {
> +               ret = pd->genpd.power_on(&pd->genpd);

This should be managed before pm_genpd_init() is called.

> +               if (ret)
> +                       return dev_err_probe(dev, ret, "failed to power on domain '%s'\n",
> +                                            pd->genpd.name);
> +       } else {
> +               if (pxa1908_pd_is_on(pd)) {
> +                       dev_warn(dev,
> +                                "domain '%s' is on despite being default off; powering off\n",
> +                                pd->genpd.name);
> +
> +                       ret = pd->genpd.power_off(&pd->genpd);

Ditto.

> +                       if (ret)
> +                               return dev_err_probe(dev, ret,
> +                                                    "failed to power off domain '%s'\n",
> +                                                    pd->genpd.name);
> +               }
> +       }
> +
> +       pd->initialized = true;
> +
> +       return 0;
> +}
> +
> +static int
> +pxa1908_pd_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *aux_id)
> +{
> +       struct pxa1908_pd_ctrl *ctrl;
> +       struct device *dev = &auxdev->dev;
> +       int ret;
> +
> +       ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
> +       if (!ctrl)
> +               return -ENOMEM;
> +
> +       auxiliary_set_drvdata(auxdev, ctrl);
> +
> +       ctrl->base = syscon_node_to_regmap(dev->parent->of_node);
> +       if (IS_ERR(ctrl->base))
> +               return dev_err_probe(dev, PTR_ERR(ctrl->base), "no regmap available\n");
> +
> +       ctrl->onecell_data.domains = ctrl->domains;
> +       ctrl->onecell_data.num_domains = NR_DOMAINS;
> +
> +       for (int i = 0; i < NR_DOMAINS; i++) {
> +               ret = pxa1908_pd_init(ctrl, i, dev);
> +               if (ret)
> +                       goto err;
> +       }
> +
> +       return of_genpd_add_provider_onecell(dev->parent->of_node, &ctrl->onecell_data);
> +
> +err:
> +       pxa1908_pd_remove(auxdev);
> +       return ret;
> +}
> +
> +static const struct auxiliary_device_id pxa1908_pd_id[] = {
> +       { .name = "clk_pxa1908_apmu.power" },
> +       { }
> +};
> +MODULE_DEVICE_TABLE(auxiliary, pxa1908_pd_id);
> +
> +static struct auxiliary_driver pxa1908_pd_driver = {
> +       .probe = pxa1908_pd_probe,
> +       .remove = pxa1908_pd_remove,
> +       .id_table = pxa1908_pd_id,
> +};
> +module_auxiliary_driver(pxa1908_pd_driver);
> +
> +MODULE_AUTHOR("Duje Mihanović <duje@...emihanovic.xyz>");
> +MODULE_DESCRIPTION("Marvell PXA1908 power domain driver");
> +MODULE_LICENSE("GPL");
>
> --
> 2.51.0
>

Kind regards
Uffe

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ