author | zhiqiang liang <zhiqiang.liang@amlogic.com> | 2019-05-10 05:34:49 (GMT) |
---|---|---|
committer | Tao Zeng <tao.zeng@amlogic.com> | 2019-09-17 02:41:42 (GMT) |
commit | b4ea53ce1f8ce21016a5d2b5ada70da76ad3f350 (patch) | |
tree | 14f0b367656f24b54194909a026052cbbbc0a7cd | |
parent | a29df37e41161e41f2cfa656111d6a2781b9f75e (diff) | |
download | common-b4ea53ce1f8ce21016a5d2b5ada70da76ad3f350.zip common-b4ea53ce1f8ce21016a5d2b5ada70da76ad3f350.tar.gz common-b4ea53ce1f8ce21016a5d2b5ada70da76ad3f350.tar.bz2 |
power: optimize the power consumption of vad wakeup [1/1]
PD#SWPL-3826
Problem:
optimize the power consumption of tl1 with vad wakeup
Solution:
optimize the power consumption when enter freeze mode
switch the clk81 to 24M
cpu and dsu clk switch to gp1 pll,frequency is 600M
closed the fixed pll
closed the vddio_3.3V
Verify:
TL1 revB
Change-Id: I39170bb8efb91b126b6a15faad3cefee19b13089
Signed-off-by: zhiqiang liang <zhiqiang.liang@amlogic.com>
Signed-off-by: Jian Hu <jian.hu@amlogic.com>
Signed-off-by: Hong Guo <hong.guo@amlogic.com>
-rw-r--r-- | MAINTAINERS | 5 | ||||
-rw-r--r-- | arch/arm/boot/dts/amlogic/mesontl1.dtsi | 10 | ||||
-rw-r--r-- | arch/arm/boot/dts/amlogic/tl1_t962x2_t309.dts | 5 | ||||
-rw-r--r-- | arch/arm/boot/dts/amlogic/tl1_t962x2_x301_1g.dts | 5 | ||||
-rw-r--r-- | arch/arm/boot/dts/amlogic/tl1_t962x2_x301_2g.dts | 5 | ||||
-rw-r--r-- | arch/arm64/boot/dts/amlogic/mesontl1.dtsi | 10 | ||||
-rw-r--r-- | arch/arm64/boot/dts/amlogic/tl1_t962x2_t309.dts | 5 | ||||
-rw-r--r-- | arch/arm64/boot/dts/amlogic/tl1_t962x2_x301_1g.dts | 6 | ||||
-rw-r--r-- | arch/arm64/boot/dts/amlogic/tl1_t962x2_x301_2g.dts | 6 | ||||
-rw-r--r-- | drivers/amlogic/clk/tl1/tl1.c | 5 | ||||
-rw-r--r-- | drivers/amlogic/clk/tl1/tl1_clk-pll.c | 3 | ||||
-rw-r--r-- | drivers/amlogic/pm/Makefile | 1 | ||||
-rw-r--r-- | drivers/amlogic/pm/gx_pm.c | 29 | ||||
-rw-r--r-- | drivers/amlogic/pm/vad_power.c | 217 | ||||
-rw-r--r-- | drivers/amlogic/pm/vad_power.h | 33 |
15 files changed, 345 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 9fc6c2a..2898117 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15153,3 +15153,8 @@ M: shunzhou jiang <shunzhou.jiang@amlogic.com> F: drivers/amlogic/firmware/bl40_module.c F: drivers/amlogic/firmware/Makefile F: drivers/amlogic/firmware/Kconfig + +AMLOGIC VAD WAKEUP POWER +M: Zhiqiang Liang <zhiqiang.liang@amlogic.com> +F: drivers/amlogic/pm/vad_power.c +F: drivers/amlogic/pm/vad_power.h diff --git a/arch/arm/boot/dts/amlogic/mesontl1.dtsi b/arch/arm/boot/dts/amlogic/mesontl1.dtsi index 87efd0a..b91c297 100644 --- a/arch/arm/boot/dts/amlogic/mesontl1.dtsi +++ b/arch/arm/boot/dts/amlogic/mesontl1.dtsi @@ -281,6 +281,16 @@ device_name = "aml_pm"; debug_reg = <0xff8000a8>; exit_reg = <0xff80023c>; + dmc_asr = <0xff638634>; + cpu_reg = <0xff63c19c>; + clocks = <&clkc CLKID_SWITCH_CLK81>, + <&clkc CLKID_CLK81>, + <&clkc CLKID_FIXED_PLL>, + <&xtal>; + clock-names = "switch_clk81", + "clk81", + "fixed_pll", + "xtal"; }; cpuinfo { diff --git a/arch/arm/boot/dts/amlogic/tl1_t962x2_t309.dts b/arch/arm/boot/dts/amlogic/tl1_t962x2_t309.dts index 5d03e01..3d62f88 100644 --- a/arch/arm/boot/dts/amlogic/tl1_t962x2_t309.dts +++ b/arch/arm/boot/dts/amlogic/tl1_t962x2_t309.dts @@ -1229,6 +1229,11 @@ }; }; + aml_pm { + vad_wakeup_disable = <0x0>; + vddio3v3_en = <&gpio_ao GPIOAO_2 0>; + }; + sd_emmc_b: sdio@ffe05000 { status = "okay"; compatible = "amlogic, meson-mmc-tl1"; diff --git a/arch/arm/boot/dts/amlogic/tl1_t962x2_x301_1g.dts b/arch/arm/boot/dts/amlogic/tl1_t962x2_x301_1g.dts index 525b488..4cbe11c 100644 --- a/arch/arm/boot/dts/amlogic/tl1_t962x2_x301_1g.dts +++ b/arch/arm/boot/dts/amlogic/tl1_t962x2_x301_1g.dts @@ -1330,6 +1330,11 @@ }; }; + aml_pm { + vad_wakeup_disable = <0x0>; + vddio3v3_en = <&gpio_ao GPIOAO_2 0>; + }; + /* sd_emmc_b: sd@ffe05000 { * status = "okay"; * compatible = "amlogic, meson-mmc-tl1"; diff --git a/arch/arm/boot/dts/amlogic/tl1_t962x2_x301_2g.dts b/arch/arm/boot/dts/amlogic/tl1_t962x2_x301_2g.dts index fe94a3f..3c595dc 100644 --- a/arch/arm/boot/dts/amlogic/tl1_t962x2_x301_2g.dts +++ b/arch/arm/boot/dts/amlogic/tl1_t962x2_x301_2g.dts @@ -1323,6 +1323,11 @@ }; }; + aml_pm { + vad_wakeup_disable = <0x0>; + vddio3v3_en = <&gpio_ao GPIOAO_2 0>; + }; + /* sd_emmc_b: sd@ffe05000 { * status = "okay"; * compatible = "amlogic, meson-mmc-tl1"; diff --git a/arch/arm64/boot/dts/amlogic/mesontl1.dtsi b/arch/arm64/boot/dts/amlogic/mesontl1.dtsi index 2d02cdd..605f609 100644 --- a/arch/arm64/boot/dts/amlogic/mesontl1.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesontl1.dtsi @@ -281,6 +281,16 @@ device_name = "aml_pm"; debug_reg = <0xff8000a8>; exit_reg = <0xff80023c>; + dmc_asr = <0xff638634>; + cpu_reg = <0xff63c19c>; + clocks = <&clkc CLKID_SWITCH_CLK81>, + <&clkc CLKID_CLK81>, + <&clkc CLKID_FIXED_PLL>, + <&xtal>; + clock-names = "switch_clk81", + "clk81", + "fixed_pll", + "xtal"; }; cpuinfo { diff --git a/arch/arm64/boot/dts/amlogic/tl1_t962x2_t309.dts b/arch/arm64/boot/dts/amlogic/tl1_t962x2_t309.dts index 8d62ed6..5aae92f 100644 --- a/arch/arm64/boot/dts/amlogic/tl1_t962x2_t309.dts +++ b/arch/arm64/boot/dts/amlogic/tl1_t962x2_t309.dts @@ -1227,6 +1227,11 @@ }; }; + aml_pm { + vad_wakeup_disable = <0x0>; + vddio3v3_en = <&gpio_ao GPIOAO_2 0>; + }; + sd_emmc_b: sdio@ffe05000 { status = "okay"; compatible = "amlogic, meson-mmc-tl1"; diff --git a/arch/arm64/boot/dts/amlogic/tl1_t962x2_x301_1g.dts b/arch/arm64/boot/dts/amlogic/tl1_t962x2_x301_1g.dts index c9dbfc8..6f5ad3c 100644 --- a/arch/arm64/boot/dts/amlogic/tl1_t962x2_x301_1g.dts +++ b/arch/arm64/boot/dts/amlogic/tl1_t962x2_x301_1g.dts @@ -1326,6 +1326,12 @@ */ }; }; + + aml_pm { + vad_wakeup_disable = <0x0>; + vddio3v3_en = <&gpio_ao GPIOAO_2 0>; + }; + /* sd_emmc_b: sd@ffe05000 { * status = "okay"; * compatible = "amlogic, meson-mmc-tl1"; diff --git a/arch/arm64/boot/dts/amlogic/tl1_t962x2_x301_2g.dts b/arch/arm64/boot/dts/amlogic/tl1_t962x2_x301_2g.dts index 085f7ec..5e7ef33 100644 --- a/arch/arm64/boot/dts/amlogic/tl1_t962x2_x301_2g.dts +++ b/arch/arm64/boot/dts/amlogic/tl1_t962x2_x301_2g.dts @@ -1318,6 +1318,12 @@ */ }; }; + + aml_pm { + vad_wakeup_disable = <0x0>; + vddio3v3_en = <&gpio_ao GPIOAO_2 0>; + }; + /* sd_emmc_b: sd@ffe05000 { * status = "okay"; * compatible = "amlogic, meson-mmc-tl1"; diff --git a/drivers/amlogic/clk/tl1/tl1.c b/drivers/amlogic/clk/tl1/tl1.c index ee90068..5d63053 100644 --- a/drivers/amlogic/clk/tl1/tl1.c +++ b/drivers/amlogic/clk/tl1/tl1.c @@ -1188,6 +1188,11 @@ static void __init tl1_clkc_init(struct device_node *np) goto iounmap; } + /* fixed pll init */ + ret = clk_prepare_enable(tl1_fixed_pll.hw.clk); + if (ret) + pr_err("%s, failed to init fixed pll\n", __func__); + ret = of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); if (ret < 0) { diff --git a/drivers/amlogic/clk/tl1/tl1_clk-pll.c b/drivers/amlogic/clk/tl1/tl1_clk-pll.c index b08c29f..637f4bc 100644 --- a/drivers/amlogic/clk/tl1/tl1_clk-pll.c +++ b/drivers/amlogic/clk/tl1/tl1_clk-pll.c @@ -499,6 +499,9 @@ static void meson_tl1_pll_disable(struct clk_hw *hw) if (pll->lock) spin_lock_irqsave(pll->lock, flags); + if (!strcmp(clk_hw_get_name(hw), "fixed_pll")) + pr_warn("Pay Attention, fixed pll will be disabled\n"); + writel(readl(pll->base + p->reg_off) | (MESON_PLL_RESET), pll->base + p->reg_off); writel(readl(pll->base + p->reg_off) & (~MESON_PLL_ENABLE), diff --git a/drivers/amlogic/pm/Makefile b/drivers/amlogic/pm/Makefile index 49582c2..87b8709 100644 --- a/drivers/amlogic/pm/Makefile +++ b/drivers/amlogic/pm/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND) += lgcy_early_suspend.o obj-$(CONFIG_AMLOGIC_GX_SUSPEND) += gx_pm.o +obj-$(CONFIG_AMLOGIC_GX_SUSPEND) += vad_power.o obj-$(CONFIG_AMLOGIC_M8B_SUSPEND) += m8b_pm.o diff --git a/drivers/amlogic/pm/gx_pm.c b/drivers/amlogic/pm/gx_pm.c index 951a3dc..0ea41c5 100644 --- a/drivers/amlogic/pm/gx_pm.c +++ b/drivers/amlogic/pm/gx_pm.c @@ -42,6 +42,7 @@ #include <linux/kobject.h> #include <../kernel/power/power.h> #include <linux/amlogic/scpi_protocol.h> +#include "vad_power.h" typedef unsigned long (psci_fn)(unsigned long, unsigned long, unsigned long, unsigned long); @@ -218,6 +219,8 @@ static int __init meson_pm_probe(struct platform_device *pdev) { struct device_node *cpu_node; struct device_node *state_node; + struct pm_data *p_data; + struct device *dev = &pdev->dev; int count = 0, ret; u32 ver = psci_get_version(); u32 paddr = 0; @@ -246,6 +249,14 @@ static int __init meson_pm_probe(struct platform_device *pdev) suspend_set_ops(&meson_gx_ops); } + p_data = devm_kzalloc(&pdev->dev, sizeof(struct pm_data), GFP_KERNEL); + if (!p_data) + return -ENOMEM; + p_data->dev = dev; + dev_set_drvdata(dev, p_data); + + vad_wakeup_power_init(pdev, p_data); + ret = of_property_read_u32(pdev->dev.of_node, "debug_reg", &paddr); if (!ret) { @@ -280,6 +291,23 @@ uniomap: return -ENXIO; } +int pm_suspend_noirq(struct device *dev) +{ + vad_wakeup_power_suspend(dev); + return 0; +} + +int pm_resume_noirq(struct device *dev) +{ + vad_wakeup_power_resume(dev); + return 0; +} + +static const struct dev_pm_ops meson_pm_noirq_ops = { + .suspend_noirq = pm_suspend_noirq, + .resume_noirq = pm_resume_noirq, +}; + static int meson_pm_remove(struct platform_device *pdev) { return 0; @@ -296,6 +324,7 @@ static struct platform_driver meson_pm_driver = { .name = "pm-meson", .owner = THIS_MODULE, .of_match_table = amlogic_pm_dt_match, + .pm = &meson_pm_noirq_ops, }, .probe = meson_pm_probe, .remove = meson_pm_remove, diff --git a/drivers/amlogic/pm/vad_power.c b/drivers/amlogic/pm/vad_power.c new file mode 100644 index 0000000..77cadc9 --- a/dev/null +++ b/drivers/amlogic/pm/vad_power.c @@ -0,0 +1,217 @@ +/* + * drivers/amlogic/pm/vad_power.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 <linux/pm.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/errno.h> +#include <linux/of_address.h> +#include <linux/amlogic/pm.h> +#include <linux/kobject.h> +#include <linux/gpio.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include "../../gpio/gpiolib.h" +#include "vad_power.h" + +#define IO3V3_EN "vddio3v3_en" + +static int fixed_pll_cnt; + +int vad_wakeup_power_init(struct platform_device *pdev, struct pm_data *p_data) +{ + int ret; + const char *value; + struct gpio_desc *desc; + u32 paddr = 0; + + ret = of_property_read_string(pdev->dev.of_node, "vddio3v3_en", &value); + if (ret) { + pr_info("no vddio3v3_en pin"); + p_data->vddio3v3_en = 0; + } else { + desc = of_get_named_gpiod_flags(pdev->dev.of_node, + "vddio3v3_en", 0, NULL); + p_data->vddio3v3_en = desc_to_gpio(desc); + } + if (p_data->vddio3v3_en > 0) + gpio_request(p_data->vddio3v3_en, IO3V3_EN); + + ret = of_property_read_u32(pdev->dev.of_node, + "vad_wakeup_disable", &paddr); + if (!ret) { + p_data->vad_wakeup_disable = paddr; + pr_info("vad_wakeup_disable: 0x%x\n", paddr); + } else { + p_data->vad_wakeup_disable = 1; + } + + ret = of_property_read_u32(pdev->dev.of_node, + "dmc_asr", &paddr); + if (!ret) { + pr_info("dmc_asr: 0x%x\n", paddr); + p_data->dmc_asr = ioremap(paddr, 0x4); + } else { + p_data->dmc_asr = 0; + } + + ret = of_property_read_u32(pdev->dev.of_node, + "cpu_reg", &paddr); + if (!ret) { + pr_info("cpu_reg: 0x%x\n", paddr); + p_data->cpu_reg = ioremap(paddr, 0x4); + } else { + p_data->cpu_reg = 0; + } + + p_data->switch_clk81 = devm_clk_get(&pdev->dev, "switch_clk81"); + if (IS_ERR(p_data->switch_clk81)) { + dev_err(&pdev->dev, + "Can't get switch_clk81\n"); + return PTR_ERR(p_data->switch_clk81); + } + p_data->clk81 = devm_clk_get(&pdev->dev, "clk81"); + if (IS_ERR(p_data->clk81)) { + dev_err(&pdev->dev, + "Can't get clk81\n"); + return PTR_ERR(p_data->clk81); + } + p_data->xtal = devm_clk_get(&pdev->dev, "xtal"); + if (IS_ERR(p_data->xtal)) { + dev_err(&pdev->dev, + "Can't get xtal\n"); + return PTR_ERR(p_data->xtal); + } + p_data->fixed_pll = devm_clk_get(&pdev->dev, "fixed_pll"); + if (IS_ERR(p_data->fixed_pll)) { + dev_err(&pdev->dev, + "Can't get fixed_pll\n"); + return PTR_ERR(p_data->fixed_pll); + } + + return 0; +} + +void cpu_clk_switch_to_gp1(unsigned int flag, void __iomem *paddr) +{ + u32 control; + u32 dyn_pre_mux = 0; + u32 dyn_post_mux = 1; + u32 dyn_div = 1; + + control = readl(paddr); + /*check cpu busy or not*/ + do { + control = readl(paddr); + } while (control & (1 << 28)); + + if (!flag) { + dyn_pre_mux = 3; + dyn_post_mux = 1; + dyn_div = 1; + /*cpu clk sel channel a*/ + if (control & (1 << 10)) { + control = (control & ~((1 << 10) | (0x3f << 4) + | (1 << 2) | (0x3 << 0))) + | ((0 << 10) + | (dyn_div << 4) + | (dyn_post_mux << 2) + | (dyn_pre_mux << 0)); + } else { + /*cpu clk sel channel b*/ + control = (control & ~((1 << 10) | (0x3f << 20) + | (1 << 18) | (0x3 << 16))) + | ((1 << 10) + | (dyn_div << 20) + | (dyn_post_mux << 18) + | (dyn_pre_mux << 16)); + } + } else { + if (control & (1 << 10)) + control = control & ~(1 << 10); + else + control = control | (1 << 10); + } + writel(control, paddr); +} + +int vad_wakeup_power_suspend(struct device *dev) +{ + struct pm_data *p_data = dev_get_drvdata(dev); + int i; + + if (!is_pm_freeze_mode() || p_data->vad_wakeup_disable) + return 0; + + clk_set_parent(p_data->switch_clk81, p_data->xtal); + pr_info("switch clk81 to 24M.\n"); + + /*cpu clk switch to gp1*/ + cpu_clk_switch_to_gp1(0, p_data->cpu_reg); + pr_info("cpu clk switch to gp1.\n"); + + fixed_pll_cnt = __clk_get_enable_count(p_data->fixed_pll); + if (fixed_pll_cnt > 1) + dev_warn(dev, + "Now fixed pll enable count = %d\n", + fixed_pll_cnt); + + for (i = 0; i < fixed_pll_cnt; i++) + clk_disable_unprepare(p_data->fixed_pll); + + gpio_direction_output(p_data->vddio3v3_en, 0); + pr_info("power off vddio_3v3.\n"); + + if (p_data->dmc_asr) { + writel(0x3fe00, p_data->dmc_asr); + pr_info("enable dmc asr\n"); + } + + return 0; +} + +int vad_wakeup_power_resume(struct device *dev) +{ + struct pm_data *p_data = dev_get_drvdata(dev); + int i; + + if (!is_pm_freeze_mode() || p_data->vad_wakeup_disable) + return 0; + + gpio_direction_output(p_data->vddio3v3_en, 1); + pr_info("power on vddio_3v3.\n"); + + if (p_data->dmc_asr) { + writel(0x0, p_data->dmc_asr); + pr_info("disable dmc asr\n"); + } + + /* enable fixed pll */ + for (i = 0; i < fixed_pll_cnt; i++) { + if (clk_prepare_enable(p_data->fixed_pll)) + dev_err(dev, "failed to enable fixed pll\n"); + } + + /*restore cpu clk*/ + cpu_clk_switch_to_gp1(1, p_data->cpu_reg); + pr_info("cpu clk restore.\n"); + clk_set_parent(p_data->switch_clk81, p_data->clk81); + pr_info("switch clk81 to 166M.\n"); + + return 0; +} diff --git a/drivers/amlogic/pm/vad_power.h b/drivers/amlogic/pm/vad_power.h new file mode 100644 index 0000000..ed651c2 --- a/dev/null +++ b/drivers/amlogic/pm/vad_power.h @@ -0,0 +1,33 @@ +/* + * drivers/amlogic/pm/vad_power.h + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + */ + +struct pm_data { + struct device *dev; + int vddio3v3_en; + bool vad_wakeup_disable; + void __iomem *dmc_asr; + void __iomem *cpu_reg; + struct clk *switch_clk81; + struct clk *clk81; + struct clk *fixed_pll; + struct clk *xtal; +}; + +int vad_wakeup_power_init(struct platform_device *pdev, struct pm_data *p_data); +int vad_wakeup_power_suspend(struct device *dev); +int vad_wakeup_power_resume(struct device *dev); + |