From patchwork Fri Nov 20 11:23:16 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tero Kristo X-Patchwork-Id: 1403640 X-Patchwork-Delegate: lokeshvutla@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=ti.com header.i=@ti.com header.a=rsa-sha256 header.s=ti-com-17Q1 header.b=HyepWwsR; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4CcvSd5048z9sVC for ; Fri, 20 Nov 2020 22:28:49 +1100 (AEDT) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id D98C682607; Fri, 20 Nov 2020 12:25:36 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (1024-bit key; unprotected) header.d=ti.com header.i=@ti.com header.b="HyepWwsR"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 30A17825C3; Fri, 20 Nov 2020 12:24:23 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-1.7 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL,T_SPF_HELO_TEMPERROR, URIBL_BLOCKED autolearn=no autolearn_force=no version=3.4.2 Received: from fllv0016.ext.ti.com (fllv0016.ext.ti.com [198.47.19.142]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 3F316825C3 for ; Fri, 20 Nov 2020 12:24:08 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=t-kristo@ti.com Received: from lelv0265.itg.ti.com ([10.180.67.224]) by fllv0016.ext.ti.com (8.15.2/8.15.2) with ESMTP id 0AKBO5wS127801; Fri, 20 Nov 2020 05:24:05 -0600 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1605871445; bh=0MdkLTL8FJgYvLHncoOjlZtO4nt/R2al3Jc6hMf4X3w=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=HyepWwsR0gRpfz2OonectYUc00jfn3fnKLejrQmL73OfB+LaJ5cuMAf22Do6TVpoE +/B5JdMsvnnr0hWwe4FIHiWjQvU5gjfMNw9xwrCKHq8XbddkpSuM5jZX3OLNOqknGL 0HilBBPmo8vhIGX9DSJtPWs/jLEOT9bW0SUeGhAo= Received: from DFLE100.ent.ti.com (dfle100.ent.ti.com [10.64.6.21]) by lelv0265.itg.ti.com (8.15.2/8.15.2) with ESMTPS id 0AKBO5I7050676 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=FAIL); Fri, 20 Nov 2020 05:24:05 -0600 Received: from DFLE107.ent.ti.com (10.64.6.28) by DFLE100.ent.ti.com (10.64.6.21) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.1979.3; Fri, 20 Nov 2020 05:24:04 -0600 Received: from fllv0039.itg.ti.com (10.64.41.19) by DFLE107.ent.ti.com (10.64.6.28) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.1979.3 via Frontend Transport; Fri, 20 Nov 2020 05:24:04 -0600 Received: from sokoban.bb.dnainternet.fi (ileax41-snat.itg.ti.com [10.172.224.153]) by fllv0039.itg.ti.com (8.15.2/8.15.2) with ESMTP id 0AKBNZmw076324; Fri, 20 Nov 2020 05:24:03 -0600 From: Tero Kristo To: , CC: , , , Subject: [PATCHv2 15/25] power: domain: Introduce driver for raw TI K3 PDs Date: Fri, 20 Nov 2020 13:23:16 +0200 Message-ID: <20201120112326.10350-16-t-kristo@ti.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20201120112326.10350-1-t-kristo@ti.com> References: <20201120112326.10350-1-t-kristo@ti.com> MIME-Version: 1.0 X-EXCLAIMER-MD-CONFIG: e1e8a2fd-e40a-4ac6-ac9b-f7e9cc9ee180 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.102.3 at phobos.denx.de X-Virus-Status: Clean 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 --- 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 + */ + +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include + +#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