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>
---
 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
+       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..a2d8983540
--- /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/
+ *     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--;
+       }
+
+       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;
+
+       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;
+}
+
+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]);
+               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_alloc_size = 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
-- 
2.17.1

--
Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki. 
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki

Reply via email to