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

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

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



Reply via email to