power: optimize the power consumption of vad wakeup [1/1]
authorzhiqiang liang <zhiqiang.liang@amlogic.com>
Fri, 10 May 2019 05:34:49 +0000 (13:34 +0800)
committerTao Zeng <tao.zeng@amlogic.com>
Tue, 17 Sep 2019 02:41:42 +0000 (19:41 -0700)
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>
15 files changed:
MAINTAINERS
arch/arm/boot/dts/amlogic/mesontl1.dtsi
arch/arm/boot/dts/amlogic/tl1_t962x2_t309.dts
arch/arm/boot/dts/amlogic/tl1_t962x2_x301_1g.dts
arch/arm/boot/dts/amlogic/tl1_t962x2_x301_2g.dts
arch/arm64/boot/dts/amlogic/mesontl1.dtsi
arch/arm64/boot/dts/amlogic/tl1_t962x2_t309.dts
arch/arm64/boot/dts/amlogic/tl1_t962x2_x301_1g.dts
arch/arm64/boot/dts/amlogic/tl1_t962x2_x301_2g.dts
drivers/amlogic/clk/tl1/tl1.c
drivers/amlogic/clk/tl1/tl1_clk-pll.c
drivers/amlogic/pm/Makefile
drivers/amlogic/pm/gx_pm.c
drivers/amlogic/pm/vad_power.c [new file with mode: 0644]
drivers/amlogic/pm/vad_power.h [new file with mode: 0644]

index 9fc6c2a..2898117 100644 (file)
@@ -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
index 87efd0a..b91c297 100644 (file)
                 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 {
index 5d03e01..3d62f88 100644 (file)
                };
        };
 
+       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";
index 525b488..4cbe11c 100644 (file)
                };
        };
 
+       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";
index fe94a3f..3c595dc 100644 (file)
                };
        };
 
+       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";
index 2d02cdd..605f609 100644 (file)
                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 {
index 8d62ed6..5aae92f 100644 (file)
                };
        };
 
+       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";
index c9dbfc8..6f5ad3c 100644 (file)
                         */
                };
        };
+
+       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";
index 085f7ec..5e7ef33 100644 (file)
                         */
                };
        };
+
+       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";
index ee90068..5d63053 100644 (file)
@@ -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) {
index b08c29f..637f4bc 100644 (file)
@@ -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),
index 49582c2..87b8709 100644 (file)
@@ -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
index 951a3dc..0ea41c5 100644 (file)
@@ -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 (file)
index 0000000..77cadc9
--- /dev/null
@@ -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 (file)
index 0000000..ed651c2
--- /dev/null
@@ -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);
+