[PATCHv3 14/26] power: domain: Introduce driver for raw TI K3 PDs
Tero Kristo
kristo at kernel.org
Fri May 7 09:19:36 CEST 2021
On 07/05/2021 03:03, Jaehoon Chung wrote:
> Hi Tero,
>
> On 5/6/21 2:55 AM, Tero Kristo wrote:
>> From: Tero Kristo <t-kristo at ti.com>
>>
>> Normally, power domains are handled via TI-SCI in K3 SoCs. However,
>> SPL is not going to have access to sysfw resources, so it must control
>> them directly. Add driver for supporting this.
>>
>> Signed-off-by: Tero Kristo <t-kristo at ti.com>
>> Signed-off-by: Tero Kristo <kristo at kernel.org>
>> ---
>> drivers/power/domain/Kconfig | 7 +
>> drivers/power/domain/Makefile | 1 +
>> drivers/power/domain/ti-power-domain.c | 377 +++++++++++++++++++++++++
>> include/k3-dev.h | 76 +++++
>> 4 files changed, 461 insertions(+)
>> create mode 100644 drivers/power/domain/ti-power-domain.c
>> create mode 100644 include/k3-dev.h
>>
>> diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig
>> index a0fd980752..b03a82d82c 100644
>> --- a/drivers/power/domain/Kconfig
>> +++ b/drivers/power/domain/Kconfig
>> @@ -72,4 +72,11 @@ config TI_SCI_POWER_DOMAIN
>> help
>> Generic power domain implementation for TI devices implementing the
>> TI SCI protocol.
>> +
>> +config TI_POWER_DOMAIN
>> + bool "Enable the TI K3 Power domain driver"
>> + depends on POWER_DOMAIN
>
> Add your ARCH config as "depends on". This is TI specific thing.
Right, will do that.
>
>> + help
>> + Generic power domain implementation for TI K3 devices.
>> +
>> endmenu
>> diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile
>> index 45bf9f6383..3d1e5f073c 100644
>> --- a/drivers/power/domain/Makefile
>> +++ b/drivers/power/domain/Makefile
>> @@ -14,3 +14,4 @@ obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain.o
>> obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain-test.o
>> obj-$(CONFIG_TEGRA186_POWER_DOMAIN) += tegra186-power-domain.o
>> obj-$(CONFIG_TI_SCI_POWER_DOMAIN) += ti-sci-power-domain.o
>> +obj-$(CONFIG_TI_POWER_DOMAIN) += ti-power-domain.o
>> diff --git a/drivers/power/domain/ti-power-domain.c b/drivers/power/domain/ti-power-domain.c
>> new file mode 100644
>> index 0000000000..ee2dc698ae
>> --- /dev/null
>> +++ b/drivers/power/domain/ti-power-domain.c
>> @@ -0,0 +1,377 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * Texas Instruments power domain driver
>> + *
>> + * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
>
> 2021?
Will change to 2020-2021.
>
>> + * Tero Kristo <t-kristo at ti.com>
>> + */
>> +
>> +#include <asm/io.h>
>> +#include <common.h>
>> +#include <dm.h>
>> +#include <errno.h>
>> +#include <power-domain-uclass.h>
>> +#include <soc.h>
>> +#include <k3-dev.h>
>> +
>> +#define PSC_PTCMD 0x120
>> +#define PSC_PTSTAT 0x128
>> +#define PSC_PDSTAT 0x200
>> +#define PSC_PDCTL 0x300
>> +#define PSC_MDSTAT 0x800
>> +#define PSC_MDCTL 0xa00
>> +
>> +#define PDCTL_STATE_MASK 0x1
>> +#define PDCTL_STATE_OFF 0x0
>> +#define PDCTL_STATE_ON 0x1
>> +
>> +#define MDSTAT_STATE_MASK 0x3f
>> +#define MDSTAT_BUSY_MASK 0x30
>> +#define MDSTAT_STATE_SWRSTDISABLE 0x0
>> +#define MDSTAT_STATE_ENABLE 0x3
>> +
>> +#define LPSC_TIMEOUT 100000
>> +#define PD_TIMEOUT 100000
>> +
>> +static u32 psc_read(struct ti_psc *psc, u32 reg)
>> +{
>> + u32 val;
>> +
>> + val = readl(psc->base + reg);
>> + debug("%s: 0x%x from %p\n", __func__, val, psc->base + reg);
>> + return val;
>> +}
>> +
>> +static void psc_write(u32 val, struct ti_psc *psc, u32 reg)
>> +{
>> + debug("%s: 0x%x to %p\n", __func__, val, psc->base + reg);
>> + writel(val, psc->base + reg);
>> +}
>> +
>> +static u32 pd_read(struct ti_pd *pd, u32 reg)
>> +{
>> + return psc_read(pd->psc, reg + 4 * pd->id);
>> +}
>> +
>> +static void pd_write(u32 val, struct ti_pd *pd, u32 reg)
>> +{
>> + psc_write(val, pd->psc, reg + 4 * pd->id);
>> +}
>> +
>> +static u32 lpsc_read(struct ti_lpsc *lpsc, u32 reg)
>> +{
>> + return psc_read(lpsc->psc, reg + 4 * lpsc->id);
>> +}
>> +
>> +static void lpsc_write(u32 val, struct ti_lpsc *lpsc, u32 reg)
>> +{
>> + psc_write(val, lpsc->psc, reg + 4 * lpsc->id);
>> +}
>> +
>> +static const struct soc_attr ti_k3_soc_pd_data[] = {
>> +#ifdef CONFIG_SOC_K3_J721E
>> + {
>> + .family = "J721E",
>> + .data = &j721e_pd_platdata,
>> + },
>> + {
>> + .family = "J7200",
>> + .data = &j7200_pd_platdata,
>> + },
>> +#endif
>> + { /* sentinel */ }
>> +};
>> +
>> +static int ti_power_domain_probe(struct udevice *dev)
>> +{
>> + struct ti_k3_pd_platdata *data = dev_get_priv(dev);
>> + const struct soc_attr *soc_match_data;
>> + const struct ti_k3_pd_platdata *pdata;
>> +
>> + printf("%s(dev=%p)\n", __func__, dev);
>> +
>> + if (!data)
>> + return -ENOMEM;
>> +
>> + soc_match_data = soc_device_match(ti_k3_soc_pd_data);
>> + if (!soc_match_data)
>> + return -ENODEV;
>> +
>> + pdata = (const struct ti_k3_pd_platdata *)soc_match_data->data;
>> +
>> + data->psc = pdata->psc;
>> + data->pd = pdata->pd;
>> + data->lpsc = pdata->lpsc;
>> + data->devs = pdata->devs;
>> + data->num_psc = pdata->num_psc;
>> + data->num_pd = pdata->num_pd;
>> + data->num_lpsc = pdata->num_lpsc;
>> + data->num_devs = pdata->num_devs;
>> +
>> + return 0;
>> +}
>> +
>> +static int ti_pd_wait(struct ti_pd *pd)
>> +{
>> + u32 ptstat;
>> + int i = PD_TIMEOUT;
>> +
>> + while (i) {
>> + ptstat = psc_read(pd->psc, PSC_PTSTAT);
>> + if (!(ptstat & BIT(pd->id)))
>> + return 0;
>> + i--;
>> + }
>
> doesn't use readl_pool_timeout() function?
This was based on assumption that the timer would not work very early in
boot, but seems I was mistaken. It does work so I'll replace this.
There is another poll in the file within the ti_lpsc_wait, will change
that also.
>
>> +
>> + debug("%s: psc%d, pd%d failed to transition.\n", __func__,
>> + pd->psc->id, pd->id);
>> +
>> + return -ETIMEDOUT;
>> +}
>> +
>> +static void ti_pd_transition(struct ti_pd *pd)
>> +{
>> + psc_write(BIT(pd->id), pd->psc, PSC_PTCMD);
>> +}
>> +
>> +static u8 ti_pd_state(struct ti_pd *pd)
>> +{
>> + u32 pdctl;
>
> Not need to use pdctl variable.
>
> return pd_read(pd, PSC_PDCTL) & PDCTL_STATE_MASK;
Sure, will change that.
>
>> +
>> + pdctl = pd_read(pd, PSC_PDCTL);
>> + return pdctl & PDCTL_STATE_MASK;
>> +}
>> +
>> +static int ti_pd_get(struct ti_pd *pd)
>> +{
>> + u32 pdctl;
>> + int ret;
>> +
>> + pd->usecount++;
>> +
>> + if (pd->usecount > 1)
>> + return 0;
>> +
>> + if (pd->depend) {
>> + ret = ti_pd_get(pd->depend);
>> + if (ret)
>> + return ret;
>> + ti_pd_transition(pd->depend);
>> + ret = ti_pd_wait(pd->depend);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + pdctl = pd_read(pd, PSC_PDCTL);
>> +
>> + if ((pdctl & PDCTL_STATE_MASK) == PDCTL_STATE_ON)
>> + return 0;
>> +
>> + debug("%s: enabling psc:%d, pd:%d\n", __func__, pd->psc->id, pd->id);
>> +
>> + pdctl &= ~PDCTL_STATE_MASK;
>> + pdctl |= PDCTL_STATE_ON;
>> +
>> + pd_write(pdctl, pd, PSC_PDCTL);
>> +
>> + return 0;
>> +}
>> +
>> +static int ti_pd_put(struct ti_pd *pd)
>> +{
>> + u32 pdctl;
>> + int ret;
>> +
>> + pd->usecount--;
>> +
>> + if (pd->usecount > 0)
>> + return 0;
>> +
>> + pdctl = pd_read(pd, PSC_PDCTL);
>> + if ((pdctl & PDCTL_STATE_MASK) == PDCTL_STATE_OFF)
>> + return 0;
>> +
>> + pdctl &= ~PDCTL_STATE_MASK;
>> + pdctl |= PDCTL_STATE_OFF;
>> +
>> + debug("%s: disabling psc:%d, pd:%d\n", __func__, pd->psc->id, pd->id);
>> +
>> + pd_write(pdctl, pd, PSC_PDCTL);
>> +
>> + if (pd->depend) {
>> + ti_pd_transition(pd);
>> + ret = ti_pd_wait(pd);
>> + if (ret)
>> + return ret;
>> +
>> + ret = ti_pd_put(pd->depend);
>> + if (ret)
>> + return ret;
>> + ti_pd_transition(pd->depend);
>> + ret = ti_pd_wait(pd->depend);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int ti_lpsc_wait(struct ti_lpsc *lpsc)
>> +{
>> + u32 mdstat;
>> + int i = LPSC_TIMEOUT;
>> +
>> + while (i) {
>> + mdstat = lpsc_read(lpsc, PSC_MDSTAT);
>> + if (!(mdstat & MDSTAT_BUSY_MASK))
>> + return 0;
>> + i--;
>> + }
Here is another manual iopoll to be replaced.
>> +
>> + printf("%s: module %d failed to transition.\n", __func__, lpsc->id);
>> +
>> + return -ETIMEDOUT;
>> +}
>> +
>> +static u8 lpsc_get_state(struct ti_lpsc *lpsc)
>> +{
>> + u32 mdctl;
>> +
>> + mdctl = lpsc_read(lpsc, PSC_MDCTL);
>> + return mdctl & MDSTAT_STATE_MASK;
>
> Ditto. return lpas_rad() & MDSTATE_STATE_MASK;
Will fix that also.
>
>> +}
>> +
>> +static int ti_lpsc_transition(struct ti_lpsc *lpsc, u8 state)
>> +{
>> + struct ti_pd *psc_pd;
>> + int ret;
>> + u32 mdctl;
>> +
>> + psc_pd = lpsc->pd;
>> +
>> + if (state == MDSTAT_STATE_ENABLE) {
>> + lpsc->usecount++;
>> + if (lpsc->usecount > 1)
>> + return 0;
>> + } else {
>> + lpsc->usecount--;
>> + if (lpsc->usecount >= 1)
>> + return 0;
>> + }
>> +
>> + debug("%s: transitioning psc:%d, lpsc:%d to %x\n", __func__,
>> + lpsc->psc->id, lpsc->id, state);
>> +
>> + if (lpsc->depend)
>> + ti_lpsc_transition(lpsc->depend, state);
>> +
>> + mdctl = lpsc_read(lpsc, PSC_MDCTL);
>> + if ((mdctl & MDSTAT_STATE_MASK) == state)
>> + return 0;
>> +
>> + if (state == MDSTAT_STATE_ENABLE)
>> + ti_pd_get(psc_pd);
>> + else
>> + ti_pd_put(psc_pd);
>> +
>> + mdctl &= ~MDSTAT_STATE_MASK;
>> + mdctl |= state;
>> +
>> + lpsc_write(mdctl, lpsc, PSC_MDCTL);
>> +
>> + ti_pd_transition(psc_pd);
>> + ret = ti_pd_wait(psc_pd);
>> + if (ret)
>> + return ret;
>> +
>> + return ti_lpsc_wait(lpsc);
>> +}
>> +
>> +static int ti_power_domain_transition(struct power_domain *pd, u8 state)
>> +{
>> + struct ti_lpsc *lpsc = pd->priv;
>> +
>> + return ti_lpsc_transition(lpsc, state);
>> +}
>> +
>> +static int ti_power_domain_on(struct power_domain *pd)
>> +{
>> + debug("%s(pd=%p, id=%lu)\n", __func__, pd, pd->id);
>> +
>> + return ti_power_domain_transition(pd, MDSTAT_STATE_ENABLE);
>> +}
>> +
>> +static int ti_power_domain_off(struct power_domain *pd)
>> +{
>> + debug("%s(pd=%p, id=%lu)\n", __func__, pd, pd->id);
>> +
>> + return ti_power_domain_transition(pd, MDSTAT_STATE_SWRSTDISABLE);
>> +}
>> +
>> +static struct ti_lpsc *lpsc_lookup(struct ti_k3_pd_platdata *data, int id)
>> +{
>> + int idx;
>> +
>> + for (idx = 0; idx < data->num_devs; idx++)
>> + if (data->devs[idx].id == id)
>> + return data->devs[idx].lpsc;
>> +
>> + return NULL;
>> +}
>> +
>> +static int ti_power_domain_of_xlate(struct power_domain *pd,
>> + struct ofnode_phandle_args *args)
>> +{
>> + struct ti_k3_pd_platdata *data = dev_get_priv(pd->dev);
>> + struct ti_lpsc *lpsc;
>> +
>> + debug("%s(power_domain=%p, id=%d)\n", __func__, pd, args->args[0]);
>> +
>> + if (args->args_count < 1) {
>> + debug("Invalid args_count: %d\n", args->args_count);
>> + return -EINVAL;
>> + }
>> +
>> + lpsc = lpsc_lookup(data, args->args[0]);
>> + if (!lpsc) {
>> + printf("%s: invalid dev-id: %d\n", __func__, args->args[0]);
>
> Well, you're using debug to display error message. but it's used printf at here.
> how about choosing one of them?
True, I'll change these both to be printfs as they are actual error
conditions.
Thanks for the review, I'll wait for additional comments on the series
and post updated version next week.
-Tero
>
>> + return -ENOENT;
>> + }
>> +
>> + pd->id = lpsc->id;
>> + pd->priv = lpsc;
>> +
>> + return 0;
>> +}
>> +
>> +static int ti_power_domain_request(struct power_domain *pd)
>> +{
>> + return 0;
>> +}
>> +
>> +static int ti_power_domain_free(struct power_domain *pd)
>> +{
>> + return 0;
>> +}
>> +
>> +static const struct udevice_id ti_power_domain_of_match[] = {
>> + { .compatible = "ti,sci-pm-domain" },
>> + { /* sentinel */ }
>> +};
>> +
>> +static struct power_domain_ops ti_power_domain_ops = {
>> + .on = ti_power_domain_on,
>> + .off = ti_power_domain_off,
>> + .of_xlate = ti_power_domain_of_xlate,
>> + .request = ti_power_domain_request,
>> + .rfree = ti_power_domain_free,
>> +};
>> +
>> +U_BOOT_DRIVER(ti_pm_domains) = {
>> + .name = "ti-pm-domains",
>> + .id = UCLASS_POWER_DOMAIN,
>> + .of_match = ti_power_domain_of_match,
>> + .probe = ti_power_domain_probe,
>> + .priv_auto = sizeof(struct ti_k3_pd_platdata),
>> + .ops = &ti_power_domain_ops,
>> +};
>> diff --git a/include/k3-dev.h b/include/k3-dev.h
>> new file mode 100644
>> index 0000000000..de3a8bdf9e
>> --- /dev/null
>> +++ b/include/k3-dev.h
>> @@ -0,0 +1,76 @@
>> +/* SPDX-License-Identifier: GPL-2.0+ */
>> +/*
>> + * Texas Instruments K3 Device Platform Data
>> + *
>> + * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
>> + */
>> +#ifndef __K3_DEV_H__
>> +#define __K3_DEV_H__
>> +
>> +#include <asm/io.h>
>> +#include <linux/types.h>
>> +#include <stdint.h>
>> +
>> +#define LPSC_MODULE_EXISTS BIT(0)
>> +#define LPSC_NO_CLOCK_GATING BIT(1)
>> +#define LPSC_DEPENDS BIT(2)
>> +#define LPSC_HAS_RESET_ISO BIT(3)
>> +#define LPSC_HAS_LOCAL_RESET BIT(4)
>> +#define LPSC_NO_MODULE_RESET BIT(5)
>> +
>> +#define PSC_PD_EXISTS BIT(0)
>> +#define PSC_PD_ALWAYSON BIT(1)
>> +#define PSC_PD_DEPENDS BIT(2)
>> +
>> +struct ti_psc {
>> + int id;
>> + void __iomem *base;
>> +};
>> +
>> +struct ti_pd;
>> +
>> +struct ti_pd {
>> + int id;
>> + int usecount;
>> + struct ti_psc *psc;
>> + struct ti_pd *depend;
>> +};
>> +
>> +struct ti_lpsc;
>> +
>> +struct ti_lpsc {
>> + int id;
>> + int usecount;
>> + struct ti_psc *psc;
>> + struct ti_pd *pd;
>> + struct ti_lpsc *depend;
>> +};
>> +
>> +struct ti_dev {
>> + struct ti_lpsc *lpsc;
>> + int id;
>> +};
>> +
>> +/**
>> + * struct ti_k3_pd_platdata - pm domain controller information structure
>> + */
>> +struct ti_k3_pd_platdata {
>> + struct ti_psc *psc;
>> + struct ti_pd *pd;
>> + struct ti_lpsc *lpsc;
>> + struct ti_dev *devs;
>> + int num_psc;
>> + int num_pd;
>> + int num_lpsc;
>> + int num_devs;
>> +};
>> +
>> +#define PSC(_id, _base) { .id = _id, .base = (void *)_base, }
>> +#define PSC_PD(_id, _psc, _depend) { .id = _id, .psc = _psc, .depend = _depend }
>> +#define PSC_LPSC(_id, _psc, _pd, _depend) { .id = _id, .psc = _psc, .pd = _pd, .depend = _depend }
>> +#define PSC_DEV(_id, _lpsc) { .id = _id, .lpsc = _lpsc }
>> +
>> +extern const struct ti_k3_pd_platdata j721e_pd_platdata;
>> +extern const struct ti_k3_pd_platdata j7200_pd_platdata;
>> +
>> +#endif
>>
>
More information about the U-Boot
mailing list