arm: tegra14: bbc: prepare for lp1bb state
Vinayak Pane [Fri, 24 May 2013 23:54:16 +0000 (16:54 -0700)]
There is window when BBC driver may not be aware of upcoming
paging event. During LP1BB suspend the EMC rate is used whatever
the system was using before suspend. That EMC rate may not be well
suited for BBC to operate in LP1BB state. Changing the emc clock
rate to set at the maximum floor for BBC. The LP1BB entry part
should reduce it down to the actual frequency floor.

Also for RPC request during LP1BB this new minimum floor gives a
safeguard against the resume time to raise EMC frequency.

Bug 1294872

Change-Id: I3b98cfac174f9dac12307923a1ef54c2c9430bad
Signed-off-by: Vinayak Pane <vpane@nvidia.com>
Reviewed-on: http://git-master/r/238238
(cherry picked from commit 36a3bffaf90209f1a6b607a2a3bf581e9731ae49)
Reviewed-on: http://git-master/r/254494
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Steve Lin <stlin@nvidia.com>

arch/arm/mach-tegra/tegra_bb.c

index 889247c..de58207 100644 (file)
@@ -30,6 +30,7 @@
 #include <asm/io.h>
 #include <linux/regulator/consumer.h>
 #include <linux/clk.h>
+#include <linux/suspend.h>
 #include <linux/pm_runtime.h>
 
 #include <mach/clk.h>
@@ -142,6 +143,7 @@ struct tegra_bb {
        struct work_struct work;
        struct clk *emc_clk;
        struct device *proxy_dev;
+       struct notifier_block pm_notifier;
 };
 
 
@@ -1027,6 +1029,45 @@ EXPORT_SYMBOL(tegra_bb_set_emc_floor);
 
 #endif
 
+static int tegra_bb_pm_notifier_event(struct notifier_block *this,
+                                       unsigned long event, void *ptr)
+{
+       void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
+       struct tegra_bb *bb = container_of(this, struct tegra_bb, pm_notifier);
+       int sts, mem_req_soon;
+
+       if (!bb) {
+               pr_err("tegra_bb not found!\n");
+               return NOTIFY_OK;
+       }
+
+       sts = readl(pmc + APBDEV_PMC_IPC_PMC_IPC_STS_0);
+       mem_req_soon = (sts >>
+                       APBDEV_PMC_IPC_PMC_IPC_STS_0_BB2AP_MEM_REQ_SOON_SHIFT)
+               & 1;
+       sts = sts >> APBDEV_PMC_IPC_PMC_IPC_STS_0_AP2BB_RESET_SHIFT;
+       sts &= APBDEV_PMC_IPC_PMC_IPC_STS_0_AP2BB_RESET_DEFAULT_MASK;
+
+       switch (event) {
+       case PM_SUSPEND_PREPARE:
+               /* prepare for possible LP1BB state */
+               if (sts) {
+                       pr_debug("prepare for lp1bb %lu\n", bb->emc_min_freq);
+                       clk_set_rate(bb->emc_clk, BBC_MC_MIN_FREQ);
+               }
+               return NOTIFY_OK;
+
+       case PM_POST_SUSPEND:
+               if (sts && !mem_req_soon) {
+                       pr_debug("bbc is inactive so remove floor\n");
+                       clk_set_rate(bb->emc_clk, 0);
+               }
+               /* else, wait for IRQs to do the job */
+               return NOTIFY_OK;
+       }
+       return NOTIFY_DONE;
+}
+
 static int tegra_bb_probe(struct platform_device *pdev)
 {
        struct tegra_bb *bb;
@@ -1305,6 +1346,8 @@ static int tegra_bb_probe(struct platform_device *pdev)
                return -EAGAIN;
        }
 
+       bb->pm_notifier.notifier_call = tegra_bb_pm_notifier_event;
+       register_pm_notifier(&bb->pm_notifier);
 #endif
        return 0;
 }