From bbe379fc7ca5cae09b1bc44fe5ee4173ad883fbf Mon Sep 17 00:00:00 2001 From: Henry Chen Date: Fri, 11 Sep 2015 21:05:20 +0800 Subject: [PATCH 1/5] soc: mediatek: Move the initial setting of pmic wrap interrupt before requesting irq. The watchdog may not be initialized by the bootloader, even if the rest of the pwrap is. Move the watchdog initialization out of pwrap_init() to make sure the watchdog is always initialized and not only when the pwrap is uninitialized. Signed-off-by: Henry Chen Acked-by: Sascha Hauer Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-pmic-wrap.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c index 8bc7b41b09fd..105597a885cb 100644 --- a/drivers/soc/mediatek/mtk-pmic-wrap.c +++ b/drivers/soc/mediatek/mtk-pmic-wrap.c @@ -725,10 +725,6 @@ static int pwrap_init(struct pmic_wrapper *wrp) pwrap_writel(wrp, 0x1, PWRAP_WACS2_EN); pwrap_writel(wrp, 0x5, PWRAP_STAUPD_PRD); pwrap_writel(wrp, 0xff, PWRAP_STAUPD_GRPEN); - pwrap_writel(wrp, 0xf, PWRAP_WDT_UNIT); - pwrap_writel(wrp, 0xffffffff, PWRAP_WDT_SRC_EN); - pwrap_writel(wrp, 0x1, PWRAP_TIMER_EN); - pwrap_writel(wrp, ~((1 << 31) | (1 << 1)), PWRAP_INT_EN); if (pwrap_is_mt8135(wrp)) { /* enable pwrap events and pwrap bridge in AP side */ @@ -896,6 +892,12 @@ static int pwrap_probe(struct platform_device *pdev) return -ENODEV; } + /* Initialize watchdog, may not be done by the bootloader */ + pwrap_writel(wrp, 0xf, PWRAP_WDT_UNIT); + pwrap_writel(wrp, 0xffffffff, PWRAP_WDT_SRC_EN); + pwrap_writel(wrp, 0x1, PWRAP_TIMER_EN); + pwrap_writel(wrp, ~((1 << 31) | (1 << 1)), PWRAP_INT_EN); + irq = platform_get_irq(pdev, 0); ret = devm_request_irq(wrp->dev, irq, pwrap_interrupt, IRQF_TRIGGER_HIGH, "mt-pmic-pwrap", wrp); From 47e90154fafd1a1310e3c5baed77d8f4c33ab271 Mon Sep 17 00:00:00 2001 From: Eddie Huang Date: Wed, 26 Aug 2015 15:14:41 +0800 Subject: [PATCH 2/5] soc: mediatek: add scpsys support active_wakeup Register gpd_dev_ops.active_wakeup function to support keep power during suspend state. And add flag to each power domain to decide whether keep power during suspend or not. Signed-off-by: Chunfeng Yun Signed-off-by: Eddie Huang Acked-by: Sascha Hauer Reviewed-by: Daniel Kurtz Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-scpsys.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/soc/mediatek/mtk-scpsys.c b/drivers/soc/mediatek/mtk-scpsys.c index 164a7d8439b1..55ab08ffc010 100644 --- a/drivers/soc/mediatek/mtk-scpsys.c +++ b/drivers/soc/mediatek/mtk-scpsys.c @@ -68,6 +68,7 @@ struct scp_domain_data { u32 sram_pdn_ack_bits; u32 bus_prot_mask; enum clk_id clk_id; + bool active_wakeup; }; static const struct scp_domain_data scp_domain_data[] __initconst = { @@ -128,6 +129,7 @@ static const struct scp_domain_data scp_domain_data[] __initconst = { .sram_pdn_bits = GENMASK(11, 8), .sram_pdn_ack_bits = GENMASK(15, 12), .clk_id = MT8173_CLK_NONE, + .active_wakeup = true, }, [MT8173_POWER_DOMAIN_MFG_ASYNC] = { .name = "mfg_async", @@ -172,6 +174,7 @@ struct scp_domain { u32 sram_pdn_bits; u32 sram_pdn_ack_bits; u32 bus_prot_mask; + bool active_wakeup; }; struct scp { @@ -371,6 +374,17 @@ out: return ret; } +static bool scpsys_active_wakeup(struct device *dev) +{ + struct generic_pm_domain *genpd; + struct scp_domain *scpd; + + genpd = pd_to_genpd(dev->pm_domain); + scpd = container_of(genpd, struct scp_domain, genpd); + + return scpd->active_wakeup; +} + static int __init scpsys_probe(struct platform_device *pdev) { struct genpd_onecell_data *pd_data; @@ -428,12 +442,14 @@ static int __init scpsys_probe(struct platform_device *pdev) scpd->sram_pdn_bits = data->sram_pdn_bits; scpd->sram_pdn_ack_bits = data->sram_pdn_ack_bits; scpd->bus_prot_mask = data->bus_prot_mask; + scpd->active_wakeup = data->active_wakeup; if (data->clk_id != MT8173_CLK_NONE) scpd->clk = clk[data->clk_id]; genpd->name = data->name; genpd->power_off = scpsys_power_off; genpd->power_on = scpsys_power_on; + genpd->dev_ops.active_wakeup = scpsys_active_wakeup; /* * Initially turn on all domains to make the domains usable From 41b3e0f067c26dd17837aa49fba13fcb5c6319e1 Mon Sep 17 00:00:00 2001 From: James Liao Date: Wed, 7 Oct 2015 17:14:40 +0800 Subject: [PATCH 3/5] soc: mediatek: Fix random hang up issue while kernel init In kernel late init, it turns off all unused clocks, which needs to access subsystem registers such as VENC and VENC_LT. Accessing MT8173 VENC registers needs two top clocks, mm_sel and venc_sel. Accessing VENC_LT registers needs mm_sel and venclt_sel. So we need to keep these clocks on before accessing their registers. This patch keeps venc_sel / venclt_sel clock on when VENC / VENC_LT's power is on, to prevent system hang up while accessing its registeres. Signed-off-by: James Liao Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-scpsys.c | 67 ++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 23 deletions(-) diff --git a/drivers/soc/mediatek/mtk-scpsys.c b/drivers/soc/mediatek/mtk-scpsys.c index 55ab08ffc010..4d4203c896c4 100644 --- a/drivers/soc/mediatek/mtk-scpsys.c +++ b/drivers/soc/mediatek/mtk-scpsys.c @@ -54,12 +54,16 @@ #define PWR_STATUS_USB BIT(25) enum clk_id { + MT8173_CLK_NONE, MT8173_CLK_MM, MT8173_CLK_MFG, - MT8173_CLK_NONE, - MT8173_CLK_MAX = MT8173_CLK_NONE, + MT8173_CLK_VENC, + MT8173_CLK_VENC_LT, + MT8173_CLK_MAX, }; +#define MAX_CLKS 2 + struct scp_domain_data { const char *name; u32 sta_mask; @@ -67,7 +71,7 @@ struct scp_domain_data { u32 sram_pdn_bits; u32 sram_pdn_ack_bits; u32 bus_prot_mask; - enum clk_id clk_id; + enum clk_id clk_id[MAX_CLKS]; bool active_wakeup; }; @@ -78,7 +82,7 @@ static const struct scp_domain_data scp_domain_data[] __initconst = { .ctl_offs = SPM_VDE_PWR_CON, .sram_pdn_bits = GENMASK(11, 8), .sram_pdn_ack_bits = GENMASK(12, 12), - .clk_id = MT8173_CLK_MM, + .clk_id = {MT8173_CLK_MM}, }, [MT8173_POWER_DOMAIN_VENC] = { .name = "venc", @@ -86,7 +90,7 @@ static const struct scp_domain_data scp_domain_data[] __initconst = { .ctl_offs = SPM_VEN_PWR_CON, .sram_pdn_bits = GENMASK(11, 8), .sram_pdn_ack_bits = GENMASK(15, 12), - .clk_id = MT8173_CLK_MM, + .clk_id = {MT8173_CLK_MM, MT8173_CLK_VENC}, }, [MT8173_POWER_DOMAIN_ISP] = { .name = "isp", @@ -94,7 +98,7 @@ static const struct scp_domain_data scp_domain_data[] __initconst = { .ctl_offs = SPM_ISP_PWR_CON, .sram_pdn_bits = GENMASK(11, 8), .sram_pdn_ack_bits = GENMASK(13, 12), - .clk_id = MT8173_CLK_MM, + .clk_id = {MT8173_CLK_MM}, }, [MT8173_POWER_DOMAIN_MM] = { .name = "mm", @@ -102,7 +106,7 @@ static const struct scp_domain_data scp_domain_data[] __initconst = { .ctl_offs = SPM_DIS_PWR_CON, .sram_pdn_bits = GENMASK(11, 8), .sram_pdn_ack_bits = GENMASK(12, 12), - .clk_id = MT8173_CLK_MM, + .clk_id = {MT8173_CLK_MM}, .bus_prot_mask = MT8173_TOP_AXI_PROT_EN_MM_M0 | MT8173_TOP_AXI_PROT_EN_MM_M1, }, @@ -112,7 +116,7 @@ static const struct scp_domain_data scp_domain_data[] __initconst = { .ctl_offs = SPM_VEN2_PWR_CON, .sram_pdn_bits = GENMASK(11, 8), .sram_pdn_ack_bits = GENMASK(15, 12), - .clk_id = MT8173_CLK_MM, + .clk_id = {MT8173_CLK_MM, MT8173_CLK_VENC_LT}, }, [MT8173_POWER_DOMAIN_AUDIO] = { .name = "audio", @@ -120,7 +124,7 @@ static const struct scp_domain_data scp_domain_data[] __initconst = { .ctl_offs = SPM_AUDIO_PWR_CON, .sram_pdn_bits = GENMASK(11, 8), .sram_pdn_ack_bits = GENMASK(15, 12), - .clk_id = MT8173_CLK_NONE, + .clk_id = {MT8173_CLK_NONE}, }, [MT8173_POWER_DOMAIN_USB] = { .name = "usb", @@ -128,7 +132,7 @@ static const struct scp_domain_data scp_domain_data[] __initconst = { .ctl_offs = SPM_USB_PWR_CON, .sram_pdn_bits = GENMASK(11, 8), .sram_pdn_ack_bits = GENMASK(15, 12), - .clk_id = MT8173_CLK_NONE, + .clk_id = {MT8173_CLK_NONE}, .active_wakeup = true, }, [MT8173_POWER_DOMAIN_MFG_ASYNC] = { @@ -137,7 +141,7 @@ static const struct scp_domain_data scp_domain_data[] __initconst = { .ctl_offs = SPM_MFG_ASYNC_PWR_CON, .sram_pdn_bits = GENMASK(11, 8), .sram_pdn_ack_bits = 0, - .clk_id = MT8173_CLK_MFG, + .clk_id = {MT8173_CLK_MFG}, }, [MT8173_POWER_DOMAIN_MFG_2D] = { .name = "mfg_2d", @@ -145,7 +149,7 @@ static const struct scp_domain_data scp_domain_data[] __initconst = { .ctl_offs = SPM_MFG_2D_PWR_CON, .sram_pdn_bits = GENMASK(11, 8), .sram_pdn_ack_bits = GENMASK(13, 12), - .clk_id = MT8173_CLK_NONE, + .clk_id = {MT8173_CLK_NONE}, }, [MT8173_POWER_DOMAIN_MFG] = { .name = "mfg", @@ -153,7 +157,7 @@ static const struct scp_domain_data scp_domain_data[] __initconst = { .ctl_offs = SPM_MFG_PWR_CON, .sram_pdn_bits = GENMASK(13, 8), .sram_pdn_ack_bits = GENMASK(21, 16), - .clk_id = MT8173_CLK_NONE, + .clk_id = {MT8173_CLK_NONE}, .bus_prot_mask = MT8173_TOP_AXI_PROT_EN_MFG_S | MT8173_TOP_AXI_PROT_EN_MFG_M0 | MT8173_TOP_AXI_PROT_EN_MFG_M1 | @@ -168,7 +172,7 @@ struct scp; struct scp_domain { struct generic_pm_domain genpd; struct scp *scp; - struct clk *clk; + struct clk *clk[MAX_CLKS]; u32 sta_mask; void __iomem *ctl_addr; u32 sram_pdn_bits; @@ -215,11 +219,16 @@ static int scpsys_power_on(struct generic_pm_domain *genpd) u32 sram_pdn_ack = scpd->sram_pdn_ack_bits; u32 val; int ret; + int i; + + for (i = 0; i < MAX_CLKS && scpd->clk[i]; i++) { + ret = clk_prepare_enable(scpd->clk[i]); + if (ret) { + for (--i; i >= 0; i--) + clk_disable_unprepare(scpd->clk[i]); - if (scpd->clk) { - ret = clk_prepare_enable(scpd->clk); - if (ret) goto err_clk; + } } val = readl(ctl_addr); @@ -285,7 +294,10 @@ static int scpsys_power_on(struct generic_pm_domain *genpd) return 0; err_pwr_ack: - clk_disable_unprepare(scpd->clk); + for (i = MAX_CLKS - 1; i >= 0; i--) { + if (scpd->clk[i]) + clk_disable_unprepare(scpd->clk[i]); + } err_clk: dev_err(scp->dev, "Failed to power on domain %s\n", genpd->name); @@ -302,6 +314,7 @@ static int scpsys_power_off(struct generic_pm_domain *genpd) u32 pdn_ack = scpd->sram_pdn_ack_bits; u32 val; int ret; + int i; if (scpd->bus_prot_mask) { ret = mtk_infracfg_set_bus_protection(scp->infracfg, @@ -363,8 +376,8 @@ static int scpsys_power_off(struct generic_pm_domain *genpd) expired = true; } - if (scpd->clk) - clk_disable_unprepare(scpd->clk); + for (i = 0; i < MAX_CLKS && scpd->clk[i]; i++) + clk_disable_unprepare(scpd->clk[i]); return 0; @@ -389,7 +402,7 @@ static int __init scpsys_probe(struct platform_device *pdev) { struct genpd_onecell_data *pd_data; struct resource *res; - int i, ret; + int i, j, ret; struct scp *scp; struct clk *clk[MT8173_CLK_MAX]; @@ -419,6 +432,14 @@ static int __init scpsys_probe(struct platform_device *pdev) if (IS_ERR(clk[MT8173_CLK_MFG])) return PTR_ERR(clk[MT8173_CLK_MFG]); + clk[MT8173_CLK_VENC] = devm_clk_get(&pdev->dev, "venc"); + if (IS_ERR(clk[MT8173_CLK_VENC])) + return PTR_ERR(clk[MT8173_CLK_VENC]); + + clk[MT8173_CLK_VENC_LT] = devm_clk_get(&pdev->dev, "venc_lt"); + if (IS_ERR(clk[MT8173_CLK_VENC_LT])) + return PTR_ERR(clk[MT8173_CLK_VENC_LT]); + scp->infracfg = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "infracfg"); if (IS_ERR(scp->infracfg)) { @@ -443,8 +464,8 @@ static int __init scpsys_probe(struct platform_device *pdev) scpd->sram_pdn_ack_bits = data->sram_pdn_ack_bits; scpd->bus_prot_mask = data->bus_prot_mask; scpd->active_wakeup = data->active_wakeup; - if (data->clk_id != MT8173_CLK_NONE) - scpd->clk = clk[data->clk_id]; + for (j = 0; j < MAX_CLKS && data->clk_id[j]; j++) + scpd->clk[j] = clk[data->clk_id[j]]; genpd->name = data->name; genpd->power_off = scpsys_power_off; From 9821e545c5e02ac28af7c595ce71d037a1f74dc9 Mon Sep 17 00:00:00 2001 From: Matthias Brugger Date: Fri, 2 Oct 2015 23:19:37 +0800 Subject: [PATCH 4/5] ARM: mediatek: enable gpt6 on boot up to make arch timer working We enable GTP6 which ungates the arch timer clock. In the future this should be done in the bootloader. Signed-off-by: Matthias Brugger Signed-off-by: Yingjoe Chen --- arch/arm/mach-mediatek/mediatek.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/arch/arm/mach-mediatek/mediatek.c b/arch/arm/mach-mediatek/mediatek.c index a9549005097e..19dc738c1abc 100644 --- a/arch/arm/mach-mediatek/mediatek.c +++ b/arch/arm/mach-mediatek/mediatek.c @@ -16,6 +16,32 @@ */ #include #include +#include +#include +#include + + +#define GPT6_CON_MT65xx 0x10008060 +#define GPT_ENABLE 0x31 + +static void __init mediatek_timer_init(void) +{ + void __iomem *gpt_base; + + if (of_machine_is_compatible("mediatek,mt6589") || + of_machine_is_compatible("mediatek,mt8135") || + of_machine_is_compatible("mediatek,mt8127")) { + /* turn on GPT6 which ungates arch timer clocks */ + gpt_base = ioremap(GPT6_CON_MT65xx, 0x04); + + /* enable clock and set to free-run */ + writel(GPT_ENABLE, gpt_base); + iounmap(gpt_base); + } + + of_clk_init(NULL); + clocksource_of_init(); +}; static const char * const mediatek_board_dt_compat[] = { "mediatek,mt6589", @@ -27,4 +53,5 @@ static const char * const mediatek_board_dt_compat[] = { DT_MACHINE_START(MEDIATEK_DT, "Mediatek Cortex-A7 (Device Tree)") .dt_compat = mediatek_board_dt_compat, + .init_time = mediatek_timer_init, MACHINE_END From 0cda07001a9454f371b7a7edabad55d99ef91157 Mon Sep 17 00:00:00 2001 From: Yingjoe Chen Date: Fri, 2 Oct 2015 23:19:39 +0800 Subject: [PATCH 5/5] ARM: mediatek: add smp bringup code Add support for booting secondary CPUs on mt6589, mt8127 and mt8135. Signed-off-by: Yingjoe Chen Signed-off-by: Matthias Brugger --- arch/arm/mach-mediatek/Makefile | 3 + arch/arm/mach-mediatek/platsmp.c | 141 +++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 arch/arm/mach-mediatek/platsmp.c diff --git a/arch/arm/mach-mediatek/Makefile b/arch/arm/mach-mediatek/Makefile index 43e619f56172..21164605b83f 100644 --- a/arch/arm/mach-mediatek/Makefile +++ b/arch/arm/mach-mediatek/Makefile @@ -1 +1,4 @@ +ifeq ($(CONFIG_SMP),y) +obj-$(CONFIG_ARCH_MEDIATEK) += platsmp.o +endif obj-$(CONFIG_ARCH_MEDIATEK) += mediatek.o diff --git a/arch/arm/mach-mediatek/platsmp.c b/arch/arm/mach-mediatek/platsmp.c new file mode 100644 index 000000000000..8141f3f8afed --- /dev/null +++ b/arch/arm/mach-mediatek/platsmp.c @@ -0,0 +1,141 @@ +/* + * arch/arm/mach-mediatek/platsmp.c + * + * Copyright (c) 2014 Mediatek Inc. + * Author: Shunli Wang + * Yingjoe Chen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include + +#define MTK_MAX_CPU 8 +#define MTK_SMP_REG_SIZE 0x1000 + +struct mtk_smp_boot_info { + unsigned long smp_base; + unsigned int jump_reg; + unsigned int core_keys[MTK_MAX_CPU - 1]; + unsigned int core_regs[MTK_MAX_CPU - 1]; +}; + +static const struct mtk_smp_boot_info mtk_mt8135_tz_boot = { + 0x80002000, 0x3fc, + { 0x534c4131, 0x4c415332, 0x41534c33 }, + { 0x3f8, 0x3f8, 0x3f8 }, +}; + +static const struct mtk_smp_boot_info mtk_mt6589_boot = { + 0x10002000, 0x34, + { 0x534c4131, 0x4c415332, 0x41534c33 }, + { 0x38, 0x3c, 0x40 }, +}; + +static const struct of_device_id mtk_tz_smp_boot_infos[] __initconst = { + { .compatible = "mediatek,mt8135", .data = &mtk_mt8135_tz_boot }, + { .compatible = "mediatek,mt8127", .data = &mtk_mt8135_tz_boot }, +}; + +static const struct of_device_id mtk_smp_boot_infos[] __initconst = { + { .compatible = "mediatek,mt6589", .data = &mtk_mt6589_boot }, +}; + +static void __iomem *mtk_smp_base; +static const struct mtk_smp_boot_info *mtk_smp_info; + +static int mtk_boot_secondary(unsigned int cpu, struct task_struct *idle) +{ + if (!mtk_smp_base) + return -EINVAL; + + if (!mtk_smp_info->core_keys[cpu-1]) + return -EINVAL; + + writel_relaxed(mtk_smp_info->core_keys[cpu-1], + mtk_smp_base + mtk_smp_info->core_regs[cpu-1]); + + arch_send_wakeup_ipi_mask(cpumask_of(cpu)); + + return 0; +} + +static void __init __mtk_smp_prepare_cpus(unsigned int max_cpus, int trustzone) +{ + int i, num; + const struct of_device_id *infos; + + if (trustzone) { + num = ARRAY_SIZE(mtk_tz_smp_boot_infos); + infos = mtk_tz_smp_boot_infos; + } else { + num = ARRAY_SIZE(mtk_smp_boot_infos); + infos = mtk_smp_boot_infos; + } + + /* Find smp boot info for this SoC */ + for (i = 0; i < num; i++) { + if (of_machine_is_compatible(infos[i].compatible)) { + mtk_smp_info = infos[i].data; + break; + } + } + + if (!mtk_smp_info) { + pr_err("%s: Device is not supported\n", __func__); + return; + } + + if (trustzone) { + /* smp_base(trustzone-bootinfo) is reserved by device tree */ + mtk_smp_base = phys_to_virt(mtk_smp_info->smp_base); + } else { + mtk_smp_base = ioremap(mtk_smp_info->smp_base, MTK_SMP_REG_SIZE); + if (!mtk_smp_base) { + pr_err("%s: Can't remap %lx\n", __func__, + mtk_smp_info->smp_base); + return; + } + } + + /* + * write the address of slave startup address into the system-wide + * jump register + */ + writel_relaxed(virt_to_phys(secondary_startup_arm), + mtk_smp_base + mtk_smp_info->jump_reg); +} + +static void __init mtk_tz_smp_prepare_cpus(unsigned int max_cpus) +{ + __mtk_smp_prepare_cpus(max_cpus, 1); +} + +static void __init mtk_smp_prepare_cpus(unsigned int max_cpus) +{ + __mtk_smp_prepare_cpus(max_cpus, 0); +} + +static struct smp_operations mt81xx_tz_smp_ops __initdata = { + .smp_prepare_cpus = mtk_tz_smp_prepare_cpus, + .smp_boot_secondary = mtk_boot_secondary, +}; +CPU_METHOD_OF_DECLARE(mt81xx_tz_smp, "mediatek,mt81xx-tz-smp", &mt81xx_tz_smp_ops); + +static struct smp_operations mt6589_smp_ops __initdata = { + .smp_prepare_cpus = mtk_smp_prepare_cpus, + .smp_boot_secondary = mtk_boot_secondary, +}; +CPU_METHOD_OF_DECLARE(mt6589_smp, "mediatek,mt6589-smp", &mt6589_smp_ops);