Hi Tero,

On 5/6/21 2:55 AM, Tero Kristo wrote:
> From: Tero Kristo <t-kri...@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-kri...@ti.com>
> Signed-off-by: Tero Kristo <kri...@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.

> +     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?

> + *   Tero Kristo <t-kri...@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?

> +
> +     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;

> +
> +     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--;
> +     }
> +
> +     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;

> +}
> +
> +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?

> +             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
> 

Reply via email to