From: Sascha Hauer <s.hauer@pengutronix.de>
To: "open list:BAREBOX" <barebox@lists.infradead.org>
Subject: [PATCH 09/20] pmdomain: add K3 driver
Date: Fri, 29 Nov 2024 12:44:24 +0100 [thread overview]
Message-ID: <20241129-k3-r5-v1-9-67c4bb42a5c7@pengutronix.de> (raw)
In-Reply-To: <20241129-k3-r5-v1-0-67c4bb42a5c7@pengutronix.de>
The power domain support for K3 SoCs is implemented on the Cortex-R5
boot processor by the ti-dm firmware binary. The A53 cores access the
power domains via mailboxes. However, during early boot we are running
barebox on the Cortex-R5 processor and the ti-dm firmware is not yet
running, so we must implement our own driver for the power domains.
Code is based on U-Boot-2025.01-rc1.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
drivers/pmdomain/ti/Kconfig | 4 +
drivers/pmdomain/ti/Makefile | 1 +
drivers/pmdomain/ti/ti-k3.c | 479 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 484 insertions(+)
diff --git a/drivers/pmdomain/ti/Kconfig b/drivers/pmdomain/ti/Kconfig
index f34a5146c1..fc65e34118 100644
--- a/drivers/pmdomain/ti/Kconfig
+++ b/drivers/pmdomain/ti/Kconfig
@@ -6,3 +6,7 @@ config TI_SCI_PM_DOMAINS
help
Generic power domain implementation for TI device implementing
the TI SCI protocol.
+
+config TI_K3_PM_DOMAINS
+ bool "TI K3 PM Domains Driver"
+ depends on MACH_K3_CORTEX_R5 || COMPILE_TEST
diff --git a/drivers/pmdomain/ti/Makefile b/drivers/pmdomain/ti/Makefile
index ab582e04a8..e5d56da052 100644
--- a/drivers/pmdomain/ti/Makefile
+++ b/drivers/pmdomain/ti/Makefile
@@ -1,2 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_TI_SCI_PM_DOMAINS) += ti_sci_pm_domains.o
+obj-$(CONFIG_TI_K3_PM_DOMAINS) += ti-k3.o
diff --git a/drivers/pmdomain/ti/ti-k3.c b/drivers/pmdomain/ti/ti-k3.c
new file mode 100644
index 0000000000..33bffeaca0
--- /dev/null
+++ b/drivers/pmdomain/ti/ti-k3.c
@@ -0,0 +1,479 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Texas Instruments power domain driver
+ *
+ * Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com/
+ * Tero Kristo <t-kristo@ti.com>
+ */
+
+#define pr_fmt(fmt) "ti-k3-pm-domain: " fmt
+
+#include <io.h>
+#include <of_device.h>
+#include <malloc.h>
+#include <init.h>
+#include <pm_domain.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+
+#define PSC_PTCMD 0x120
+#define PSC_PTCMD_H 0x124
+#define PSC_PTSTAT 0x128
+#define PSC_PTSTAT_H 0x12C
+#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
+
+#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)
+
+#define MDSTAT_STATE_MASK 0x3f
+#define MDSTAT_BUSY_MASK 0x30
+#define MDSTAT_STATE_SWRSTDISABLE 0x0
+#define MDSTAT_STATE_ENABLE 0x3
+
+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 {
+ int 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;
+};
+
+struct ti_k3_pm_domain {
+ struct generic_pm_domain genpd;
+ struct ti_lpsc *lpsc;
+};
+
+struct ti_k3_priv {
+ const struct ti_k3_pd_platdata *data;
+ struct genpd_onecell_data pd_data;
+ struct ti_k3_pm_domain *pd;
+};
+
+static u32 psc_read(struct ti_psc *psc, u32 reg)
+{
+ return readl(psc->base + reg);
+}
+
+static void psc_write(u32 val, struct ti_psc *psc, u32 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 int ti_pd_wait(struct ti_pd *pd)
+{
+ u32 ptstat;
+ u32 pdoffset = 0;
+ u32 ptstatreg = PSC_PTSTAT;
+ int ret;
+
+ if (pd->id > 31) {
+ pdoffset = 32;
+ ptstatreg = PSC_PTSTAT_H;
+ }
+
+ ret = readl_poll_timeout(pd->psc->base + ptstatreg, ptstat,
+ !(ptstat & BIT(pd->id - pdoffset)), PD_TIMEOUT);
+
+ if (ret)
+ pr_err("%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)
+{
+ u32 pdoffset = 0;
+ u32 ptcmdreg = PSC_PTCMD;
+
+ if (pd->id > 31) {
+ pdoffset = 32;
+ ptcmdreg = PSC_PTCMD_H;
+ }
+
+ psc_write(BIT(pd->id - pdoffset), pd->psc, ptcmdreg);
+}
+
+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;
+
+ pr_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;
+
+ pr_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)
+ pr_err("%s: module %d failed to transition.\n", __func__,
+ lpsc->id);
+
+ return ret;
+}
+
+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;
+ }
+
+ pr_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 inline struct ti_k3_pm_domain *to_ti_k3_pd(struct generic_pm_domain *gpd)
+{
+ return container_of(gpd, struct ti_k3_pm_domain, genpd);
+}
+
+static int ti_k3_pm_domain_on(struct generic_pm_domain *domain)
+{
+ struct ti_k3_pm_domain *pd = to_ti_k3_pd(domain);
+
+ return ti_lpsc_transition(pd->lpsc, MDSTAT_STATE_ENABLE);
+}
+
+static int ti_k3_pm_domain_off(struct generic_pm_domain *domain)
+{
+ struct ti_k3_pm_domain *pd = to_ti_k3_pd(domain);
+
+ return ti_lpsc_transition(pd->lpsc, MDSTAT_STATE_SWRSTDISABLE);
+}
+
+static struct ti_psc am625_psc[] = {
+ [0] = { .id = 0, .base = (void *)0x04000000 },
+ [1] = { .id = 1, .base = (void *)0x00400000 },
+};
+
+static struct ti_pd am625_pd[] = {
+ [0] = { .id = 0, .psc = &am625_psc[1], },
+ [1] = { .id = 2, .psc = &am625_psc[1], .depend = &am625_pd[0] },
+ [2] = { .id = 3, .psc = &am625_psc[1], .depend = &am625_pd[0] },
+ [3] = { .id = 4, .psc = &am625_psc[1], .depend = &am625_pd[2] },
+ [4] = { .id = 5, .psc = &am625_psc[1], .depend = &am625_pd[2] },
+};
+
+static struct ti_lpsc am625_lpsc[] = {
+ [0] = { .id = 0, .psc = &am625_psc[1], .pd = &am625_pd[0], },
+ [1] = { .id = 9, .psc = &am625_psc[1], .pd = &am625_pd[0], .depend = &am625_lpsc[11] },
+ [2] = { .id = 10, .psc = &am625_psc[1], .pd = &am625_pd[0], .depend = &am625_lpsc[1] },
+ [3] = { .id = 11, .psc = &am625_psc[1], .pd = &am625_pd[0], .depend = &am625_lpsc[2] },
+ [4] = { .id = 12, .psc = &am625_psc[1], .pd = &am625_pd[0], .depend = &am625_lpsc[8] },
+ [5] = { .id = 13, .psc = &am625_psc[1], .pd = &am625_pd[0], .depend = &am625_lpsc[9] },
+ [6] = { .id = 20, .psc = &am625_psc[1], .pd = &am625_pd[0], .depend = &am625_lpsc[11] },
+ [7] = { .id = 21, .psc = &am625_psc[1], .pd = &am625_pd[0], .depend = &am625_lpsc[11] },
+ [8] = { .id = 23, .psc = &am625_psc[1], .pd = &am625_pd[0], .depend = &am625_lpsc[11] },
+ [9] = { .id = 24, .psc = &am625_psc[1], .pd = &am625_pd[0], .depend = &am625_lpsc[11] },
+ [10] = { .id = 28, .psc = &am625_psc[1], .pd = &am625_pd[0], .depend = &am625_lpsc[11] },
+ [11] = { .id = 34, .psc = &am625_psc[1], .pd = &am625_pd[0], },
+ [12] = { .id = 41, .psc = &am625_psc[1], .pd = &am625_pd[1], .depend = &am625_lpsc[11] },
+ [13] = { .id = 42, .psc = &am625_psc[1], .pd = &am625_pd[2], .depend = &am625_lpsc[11] },
+ [14] = { .id = 45, .psc = &am625_psc[1], .pd = &am625_pd[3], .depend = &am625_lpsc[13] },
+ [15] = { .id = 46, .psc = &am625_psc[1], .pd = &am625_pd[4], .depend = &am625_lpsc[13] },
+};
+
+static struct ti_dev am625_dev[] = {
+ { .id = 16, .lpsc = 0 },
+ { .id = 77, .lpsc = 0 },
+ { .id = 61, .lpsc = 0 },
+ { .id = 95, .lpsc = 0 },
+ { .id = 107, .lpsc = 0 },
+ { .id = 170, .lpsc = 1 },
+ { .id = 177, .lpsc = 2 },
+ { .id = 55, .lpsc = 3 },
+ { .id = 178, .lpsc = 4 },
+ { .id = 179, .lpsc = 5 },
+ { .id = 57, .lpsc = 6 },
+ { .id = 58, .lpsc = 7 },
+ { .id = 161, .lpsc = 8 },
+ { .id = 162, .lpsc = 9 },
+ { .id = 75, .lpsc = 10 },
+ { .id = 36, .lpsc = 11 },
+ { .id = 102, .lpsc = 11 },
+ { .id = 146, .lpsc = 11 },
+ { .id = 13, .lpsc = 12 },
+ { .id = 166, .lpsc = 13 },
+ { .id = 135, .lpsc = 14 },
+ { .id = 136, .lpsc = 15 },
+};
+
+const struct ti_k3_pd_platdata am62x_pd_platdata = {
+ .psc = am625_psc,
+ .pd = am625_pd,
+ .lpsc = am625_lpsc,
+ .devs = am625_dev,
+ .num_psc = ARRAY_SIZE(am625_psc),
+ .num_pd = ARRAY_SIZE(am625_pd),
+ .num_lpsc = ARRAY_SIZE(am625_lpsc),
+ .num_devs = ARRAY_SIZE(am625_dev),
+};
+
+static struct generic_pm_domain *ti_k3_pd_xlate(struct of_phandle_args *args,
+ void *data)
+{
+ struct genpd_onecell_data *genpd_data = data;
+ struct ti_k3_priv *priv = container_of(genpd_data, struct ti_k3_priv, pd_data);
+ unsigned int idx = args->args[0];
+ int i;
+
+ for (i = 0; i < priv->data->num_devs; i++)
+ if (priv->data->devs[i].id == idx) {
+ unsigned int lpsc = priv->data->devs[i].lpsc;
+ pr_debug("Translate: %d -> %d\n", idx, lpsc);
+ return &priv->pd[lpsc].genpd;
+ }
+
+ return 0;
+}
+
+static int ti_k3_pm_domain_probe(struct device *dev)
+{
+ struct ti_k3_priv *priv;
+ const struct ti_k3_pd_platdata *data;
+ struct ti_k3_pm_domain *pd;
+ struct generic_pm_domain **domains;
+ struct genpd_onecell_data *pd_data;
+ int num_domains;
+ int i;
+
+ priv = xzalloc(sizeof(*priv));
+
+ if (of_machine_is_compatible("ti,am625"))
+ data = &am62x_pd_platdata;
+ else
+ return dev_err_probe(dev, -EINVAL, "Unknown SoC\n");
+
+ priv->data = data;
+
+ num_domains = data->num_lpsc;
+
+ pd = devm_kcalloc(dev, num_domains, sizeof(*pd), GFP_KERNEL);
+ if (!pd)
+ return -ENOMEM;
+
+ domains = devm_kcalloc(dev, num_domains, sizeof(*domains), GFP_KERNEL);
+ if (!domains)
+ return -ENOMEM;
+
+ priv->pd = pd;
+
+ for (i = 0; i < num_domains; i++, pd++) {
+ pd->genpd.name = basprintf("pd:%d", i);
+ pd->lpsc = &data->lpsc[i];
+ pd->genpd.power_off = ti_k3_pm_domain_off;
+ pd->genpd.power_on = ti_k3_pm_domain_on;
+
+ pm_genpd_init(&pd->genpd, NULL, true);
+
+ domains[i] = &pd->genpd;
+ }
+
+ pd_data = &priv->pd_data;
+
+ pd_data->domains = domains;
+ pd_data->num_domains = num_domains;
+ pd_data->xlate = ti_k3_pd_xlate;
+
+ return of_genpd_add_provider_onecell(dev->of_node, pd_data);
+}
+
+static const struct of_device_id ti_k3_power_domain_of_match[] = {
+ { .compatible = "ti,sci-pm-domain" },
+ { /* sentinel */ }
+};
+
+static struct driver ti_k3_pm_domains_driver = {
+ .probe = ti_k3_pm_domain_probe,
+ .name = "ti_k3_pm_domains",
+ .of_match_table = ti_k3_power_domain_of_match,
+};
+core_platform_driver(ti_k3_pm_domains_driver);
--
2.39.5
next prev parent reply other threads:[~2024-11-29 11:45 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-11-29 11:44 [PATCH 00/20] ARM: K3: Add R5 boot support Sascha Hauer
2024-11-29 11:44 ` [PATCH 01/20] ARM: add ARMv7R MPU support Sascha Hauer
2024-11-29 11:44 ` [PATCH 02/20] lib/rationale: compile for pbl Sascha Hauer
2024-11-29 11:44 ` [PATCH 03/20] DDR: Add k3 DDR driver Sascha Hauer
2024-11-29 11:44 ` [PATCH 04/20] ARM: move ARM_CPU_PART_* defines to header Sascha Hauer
2024-11-29 11:44 ` [PATCH 05/20] nommu_v7_vectors_init: disable for r5 Sascha Hauer
2024-11-29 11:44 ` [PATCH 06/20] clocksource: timer-ti-dm: add support for K3 SoCs Sascha Hauer
2024-11-29 11:44 ` [PATCH 07/20] ARM: K3: mount /boot even with env handling disabled Sascha Hauer
2024-11-29 11:44 ` [PATCH 08/20] clk: add K3 clk driver Sascha Hauer
2024-11-29 11:44 ` Sascha Hauer [this message]
2024-11-29 11:44 ` [PATCH 10/20] rproc: add K3 arm64 rproc driver Sascha Hauer
2024-11-29 11:44 ` [PATCH 11/20] ARM: k3: add k3_debug_ll_init() Sascha Hauer
2024-11-29 11:44 ` [PATCH 12/20] ARM: K3: use debug_ll code for regular PBL console Sascha Hauer
2024-11-29 11:44 ` [PATCH 13/20] elf: use iomem regions as fallback when loading to non-sdram memory Sascha Hauer
2024-11-29 11:44 ` [PATCH 14/20] rproc: add K3 system_controller Sascha Hauer
2024-11-29 11:44 ` [PATCH 15/20] firmware: ti_sci: add function to get global handle Sascha Hauer
2024-11-29 11:44 ` [PATCH 16/20] ARM: k3: Add initial r5 support Sascha Hauer
2024-11-29 11:44 ` [PATCH 17/20] ARM: k3: Add k3img tool Sascha Hauer
2024-11-29 11:44 ` [PATCH 18/20] ARM: beagleplay: add binary files Sascha Hauer
2024-11-29 11:44 ` [PATCH 19/20] ARM: beagleplay: add Cortex-R5 boot support Sascha Hauer
2024-11-29 11:44 ` [PATCH 20/20] Documentation: add build documentation for TI K3 SoCs Sascha Hauer
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20241129-k3-r5-v1-9-67c4bb42a5c7@pengutronix.de \
--to=s.hauer@pengutronix.de \
--cc=barebox@lists.infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox