From patchwork Thu Nov 15 02:08:00 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ryder Lee X-Patchwork-Id: 998045 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=mediatek.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 42wQ8T3ZxLz9s47 for ; Thu, 15 Nov 2018 13:21:13 +1100 (AEDT) Received: by lists.denx.de (Postfix, from userid 105) id 4E523C22186; Thu, 15 Nov 2018 02:20:38 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: * X-Spam-Status: No, score=1.3 required=5.0 tests=RCVD_IN_DNSWL_BLOCKED, RDNS_NONE, UNPARSEABLE_RELAY autolearn=no autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 0F4B5C224E5; Thu, 15 Nov 2018 02:08:54 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 855BAC221A4; Thu, 15 Nov 2018 02:08:43 +0000 (UTC) Received: from mailgw01.mediatek.com (unknown [210.61.82.183]) by lists.denx.de (Postfix) with ESMTPS id 2F976C224AB for ; Thu, 15 Nov 2018 02:08:30 +0000 (UTC) X-UUID: 9fe497482bfb45c1ba8b9a5d8b76f29c-20181115 X-UUID: 9fe497482bfb45c1ba8b9a5d8b76f29c-20181115 Received: from mtkcas08.mediatek.inc [(172.21.101.126)] by mailgw01.mediatek.com (envelope-from ) (mhqrelay.mediatek.com ESMTP with TLS) with ESMTP id 1838970665; Thu, 15 Nov 2018 10:08:21 +0800 Received: from MTKCAS06.mediatek.inc (172.21.101.30) by mtkmbs08n2.mediatek.inc (172.21.101.56) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Thu, 15 Nov 2018 10:08:19 +0800 Received: from mtkslt306.mediatek.inc (10.21.14.136) by MTKCAS06.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1395.4 via Frontend Transport; Thu, 15 Nov 2018 10:08:20 +0800 From: Ryder Lee To: Tom Rini , Simon Glass , Albert Aribaud Date: Thu, 15 Nov 2018 10:08:00 +0800 Message-ID: <667fc8962ebec324f1fa17ab57b533e4c6bbf701.1542246777.git.ryder.lee@mediatek.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: References: MIME-Version: 1.0 X-TM-SNTS-SMTP: 60882E5A021AAC871895C2A8399B4849E83DBF65A4448FB18274D5B0800C93BD2000:8 X-MTK: N Cc: Ryder Lee , Steven Liu , Roy Luo , u-boot@lists.denx.de Subject: [U-Boot] [PATCH v5 12/18] power domain: MediaTek: add power domain driver for MT7629 SoC X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 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" This adds a power domain driver for the Mediatek SCPSYS unit. The System Control Processor System (SCPSYS) has several power management related tasks in the system. The tasks include thermal measurement, dynamic voltage frequency scaling (DVFS), interrupt filter and lowlevel sleep control. The System Power Manager (SPM) inside the SCPSYS is for the MTCMOS power domain control. For now this driver only adds power domain support. Signed-off-by: Ryder Lee Reviewed-by: Simon Glass --- Changes since v5: None Changes since v4: None --- drivers/power/domain/Kconfig | 7 + drivers/power/domain/Makefile | 1 + drivers/power/domain/mtk-power-domain.c | 326 ++++++++++++++++++++++++++++++++ 3 files changed, 334 insertions(+) create mode 100644 drivers/power/domain/mtk-power-domain.c diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig index a08b428..93deaef 100644 --- a/drivers/power/domain/Kconfig +++ b/drivers/power/domain/Kconfig @@ -23,6 +23,13 @@ config IMX8_POWER_DOMAIN Enable support for manipulating NXP i.MX8 on-SoC power domains via IPC requests to the SCU. +config MTK_POWER_DOMAIN + bool "Enable the MediaTek power domain driver" + depends on POWER_DOMAIN && ARCH_MEDIATEK + help + Enable support for manipulating MediaTek power domains via MMIO + mapped registers. + config MESON_GX_VPU_POWER_DOMAIN bool "Enable Amlogic Meson GX VPU power domain driver" depends on ARCH_MESON diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile index b08d18f..695aafe 100644 --- a/drivers/power/domain/Makefile +++ b/drivers/power/domain/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_$(SPL_)POWER_DOMAIN) += power-domain-uclass.o obj-$(CONFIG_BCM6328_POWER_DOMAIN) += bcm6328-power-domain.o obj-$(CONFIG_IMX8_POWER_DOMAIN) += imx8-power-domain.o +obj-$(CONFIG_MTK_POWER_DOMAIN) += mtk-power-domain.o obj-$(CONFIG_MESON_GX_VPU_POWER_DOMAIN) += meson-gx-pwrc-vpu.o obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain.o obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain-test.o diff --git a/drivers/power/domain/mtk-power-domain.c b/drivers/power/domain/mtk-power-domain.c new file mode 100644 index 0000000..ed4a718 --- /dev/null +++ b/drivers/power/domain/mtk-power-domain.c @@ -0,0 +1,326 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 MediaTek Inc. + * Author: Ryder Lee + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define SPM_EN (0xb16 << 16 | 0x1) +#define SPM_ETHSYS_PWR_CON 0x2e0 +#define SPM_HIF0_PWR_CON 0x2e4 +#define SPM_HIF1_PWR_CON 0x2e8 +#define SPM_PWR_STATUS 0x60c +#define SPM_PWR_STATUS_2ND 0x610 + +#define PWR_RST_B_BIT BIT(0) +#define PWR_ISO_BIT BIT(1) +#define PWR_ON_BIT BIT(2) +#define PWR_ON_2ND_BIT BIT(3) +#define PWR_CLK_DIS_BIT BIT(4) + +#define PWR_STATUS_ETHSYS BIT(24) +#define PWR_STATUS_HIF0 BIT(25) +#define PWR_STATUS_HIF1 BIT(26) + +/* Infrasys configuration */ +#define INFRA_TOPDCM_CTRL 0x10 +#define INFRA_TOPAXI_PROT_EN 0x220 +#define INFRA_TOPAXI_PROT_STA1 0x228 + +#define DCM_TOP_EN BIT(0) + +enum scp_domain_type { + SCPSYS_MT7629, +}; + +struct scp_domain; + +struct scp_domain_data { + struct scp_domain *scpd; + u32 sta_mask; + int ctl_offs; + u32 sram_pdn_bits; + u32 sram_pdn_ack_bits; + u32 bus_prot_mask; +}; + +struct scp_domain { + void __iomem *base; + void __iomem *infracfg; + enum scp_domain_type type; + struct scp_domain_data *data; +}; + +static struct scp_domain_data scp_domain_mt7629[] = { + [MT7629_POWER_DOMAIN_ETHSYS] = { + .sta_mask = PWR_STATUS_ETHSYS, + .ctl_offs = SPM_ETHSYS_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + .bus_prot_mask = (BIT(3) | BIT(17)), + }, + [MT7629_POWER_DOMAIN_HIF0] = { + .sta_mask = PWR_STATUS_HIF0, + .ctl_offs = SPM_HIF0_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + .bus_prot_mask = GENMASK(25, 24), + }, + [MT7629_POWER_DOMAIN_HIF1] = { + .sta_mask = PWR_STATUS_HIF1, + .ctl_offs = SPM_HIF1_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + .bus_prot_mask = GENMASK(28, 26), + }, +}; + +/** + * This function enables the bus protection bits for disabled power + * domains so that the system does not hang when some unit accesses the + * bus while in power down. + */ +static int mtk_infracfg_set_bus_protection(void __iomem *infracfg, + u32 mask) +{ + u32 val; + + clrsetbits_le32(infracfg + INFRA_TOPAXI_PROT_EN, mask, mask); + + return readl_poll_timeout(infracfg + INFRA_TOPAXI_PROT_STA1, val, + (val & mask) == mask, 100); +} + +static int mtk_infracfg_clear_bus_protection(void __iomem *infracfg, + u32 mask) +{ + u32 val; + + clrbits_le32(infracfg + INFRA_TOPAXI_PROT_EN, mask); + + return readl_poll_timeout(infracfg + INFRA_TOPAXI_PROT_STA1, val, + !(val & mask), 100); +} + +static int scpsys_domain_is_on(struct scp_domain_data *data) +{ + struct scp_domain *scpd = data->scpd; + u32 sta = readl(scpd->base + SPM_PWR_STATUS) & + data->sta_mask; + u32 sta2 = readl(scpd->base + SPM_PWR_STATUS_2ND) & + data->sta_mask; + + /* + * A domain is on when both status bits are set. If only one is set + * return an error. This happens while powering up a domain + */ + if (sta && sta2) + return true; + if (!sta && !sta2) + return false; + + return -EINVAL; +} + +static int scpsys_power_on(struct power_domain *power_domain) +{ + struct scp_domain *scpd = dev_get_priv(power_domain->dev); + struct scp_domain_data *data = &scpd->data[power_domain->id]; + void __iomem *ctl_addr = scpd->base + data->ctl_offs; + u32 pdn_ack = data->sram_pdn_ack_bits; + u32 val; + int ret, tmp; + + writel(SPM_EN, scpd->base); + + val = readl(ctl_addr); + val |= PWR_ON_BIT; + writel(val, ctl_addr); + + val |= PWR_ON_2ND_BIT; + writel(val, ctl_addr); + + ret = readx_poll_timeout(scpsys_domain_is_on, data, tmp, tmp > 0, + 100); + if (ret < 0) + return ret; + + val &= ~PWR_CLK_DIS_BIT; + writel(val, ctl_addr); + + val &= ~PWR_ISO_BIT; + writel(val, ctl_addr); + + val |= PWR_RST_B_BIT; + writel(val, ctl_addr); + + val &= ~data->sram_pdn_bits; + writel(val, ctl_addr); + + ret = readl_poll_timeout(ctl_addr, tmp, !(tmp & pdn_ack), 100); + if (ret < 0) + return ret; + + if (data->bus_prot_mask) { + ret = mtk_infracfg_clear_bus_protection(scpd->infracfg, + data->bus_prot_mask); + if (ret) + return ret; + } + + return 0; +} + +static int scpsys_power_off(struct power_domain *power_domain) +{ + struct scp_domain *scpd = dev_get_priv(power_domain->dev); + struct scp_domain_data *data = &scpd->data[power_domain->id]; + void __iomem *ctl_addr = scpd->base + data->ctl_offs; + u32 pdn_ack = data->sram_pdn_ack_bits; + u32 val; + int ret, tmp; + + if (data->bus_prot_mask) { + ret = mtk_infracfg_set_bus_protection(scpd->infracfg, + data->bus_prot_mask); + if (ret) + return ret; + } + + val = readl(ctl_addr); + val |= data->sram_pdn_bits; + writel(val, ctl_addr); + + ret = readl_poll_timeout(ctl_addr, tmp, (tmp & pdn_ack) == pdn_ack, + 100); + if (ret < 0) + return ret; + + val |= PWR_ISO_BIT; + writel(val, ctl_addr); + + val &= ~PWR_RST_B_BIT; + writel(val, ctl_addr); + + val |= PWR_CLK_DIS_BIT; + writel(val, ctl_addr); + + val &= ~PWR_ON_BIT; + writel(val, ctl_addr); + + val &= ~PWR_ON_2ND_BIT; + writel(val, ctl_addr); + + ret = readx_poll_timeout(scpsys_domain_is_on, data, tmp, !tmp, 100); + if (ret < 0) + return ret; + + return 0; +} + +static int scpsys_power_request(struct power_domain *power_domain) +{ + struct scp_domain *scpd = dev_get_priv(power_domain->dev); + struct scp_domain_data *data; + + data = &scpd->data[power_domain->id]; + data->scpd = scpd; + + return 0; +} + +static int scpsys_power_free(struct power_domain *power_domain) +{ + return 0; +} + +static int mtk_power_domain_hook(struct udevice *dev) +{ + struct scp_domain *scpd = dev_get_priv(dev); + + scpd->type = (enum scp_domain_type)dev_get_driver_data(dev); + + switch (scpd->type) { + case SCPSYS_MT7629: + scpd->data = scp_domain_mt7629; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int mtk_power_domain_probe(struct udevice *dev) +{ + struct ofnode_phandle_args args; + struct scp_domain *scpd = dev_get_priv(dev); + struct regmap *regmap; + struct clk_bulk bulk; + int err; + + scpd->base = dev_read_addr_ptr(dev); + if (!scpd->base) + return -ENOENT; + + err = mtk_power_domain_hook(dev); + if (err) + return err; + + /* get corresponding syscon phandle */ + err = dev_read_phandle_with_args(dev, "infracfg", NULL, 0, 0, &args); + if (err) + return err; + + regmap = syscon_node_to_regmap(args.node); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + scpd->infracfg = regmap_get_range(regmap, 0); + if (!scpd->infracfg) + return -ENOENT; + + /* enable Infra DCM */ + setbits_le32(scpd->infracfg + INFRA_TOPDCM_CTRL, DCM_TOP_EN); + + err = clk_get_bulk(dev, &bulk); + if (err) + return err; + + return clk_enable_bulk(&bulk); +} + +static const struct udevice_id mtk_power_domain_ids[] = { + { + .compatible = "mediatek,mt7629-scpsys", + .data = SCPSYS_MT7629, + }, + { /* sentinel */ } +}; + +struct power_domain_ops mtk_power_domain_ops = { + .free = scpsys_power_free, + .off = scpsys_power_off, + .on = scpsys_power_on, + .request = scpsys_power_request, +}; + +U_BOOT_DRIVER(mtk_power_domain) = { + .name = "mtk_power_domain", + .id = UCLASS_POWER_DOMAIN, + .ops = &mtk_power_domain_ops, + .probe = mtk_power_domain_probe, + .of_match = mtk_power_domain_ids, + .priv_auto_alloc_size = sizeof(struct scp_domain), +};