On 5/11/21 5:30 PM, 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>
Reviewed-by: Jaehoon Chung <jh80.ch...@samsung.com> Best Regards, Jaehoon Chung > --- > drivers/power/domain/Kconfig | 7 + > drivers/power/domain/Makefile | 1 + > drivers/power/domain/ti-power-domain.c | 368 +++++++++++++++++++++++++ > include/k3-dev.h | 76 +++++ > 4 files changed, 452 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..99b3f9ae71 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 && ARCH_K3 > + 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..e418a7b996 > --- /dev/null > +++ b/drivers/power/domain/ti-power-domain.c > @@ -0,0 +1,368 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Texas Instruments power domain driver > + * > + * Copyright (C) 2020-2021 Texas Instruments Incorporated - > http://www.ti.com/ > + * 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> > +#include <linux/iopoll.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 1000 > +#define PD_TIMEOUT 1000 > + > +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 ret; > + > + ret = readl_poll_timeout(pd->psc->base + PSC_PTSTAT, ptstat, > + !(ptstat & BIT(pd->id)), PD_TIMEOUT); > + > + if (ret) > + printf("%s: psc%d, pd%d failed to transition.\n", __func__, > + pd->psc->id, pd->id); > + > + return ret; > +} > + > +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) > +{ > + return pd_read(pd, PSC_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 ret; > + > + ret = readl_poll_timeout(lpsc->psc->base + PSC_MDSTAT + lpsc->id * 4, > + mdstat, > + !(mdstat & MDSTAT_BUSY_MASK), LPSC_TIMEOUT); > + > + if (ret) > + printf("%s: module %d failed to transition.\n", __func__, > + lpsc->id); > + > + return ret; > +} > + > +static u8 lpsc_get_state(struct ti_lpsc *lpsc) > +{ > + return lpsc_read(lpsc, PSC_MDCTL) & MDSTAT_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) { > + printf("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]); > + 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 >