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