This allows memory clients to collaboratively control the EMC
performance.
Each client may set its performance demands. EMC driver then tries to
find a DRAM performance level which is able to statisfy all those
demands.

Signed-off-by: Lucas Stach <[email protected]>
---
 arch/arm/mach-tegra/tegra2_emc.c       | 84 ++++++++++++++++++++++++++++++++--
 include/memory/tegra_emc_performance.h | 79 ++++++++++++++++++++++++++++++++
 2 Dateien geändert, 160 Zeilen hinzugefügt(+), 3 Zeilen entfernt(-)
 create mode 100644 include/memory/tegra_emc_performance.h

diff --git a/arch/arm/mach-tegra/tegra2_emc.c b/arch/arm/mach-tegra/tegra2_emc.c
index 837c7b9..d2cf7a1 100644
--- a/arch/arm/mach-tegra/tegra2_emc.c
+++ b/arch/arm/mach-tegra/tegra2_emc.c
@@ -15,6 +15,7 @@
  *
  */
 
+#include <asm/div64.h>
 #include <linux/kernel.h>
 #include <linux/device.h>
 #include <linux/clk.h>
@@ -24,6 +25,8 @@
 #include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/platform_data/tegra_emc.h>
+#include <linux/types.h>
+#include <memory/tegra_emc_performance.h>
 
 #include "tegra2_emc.h"
 #include "fuse.h"
@@ -35,8 +38,16 @@ static bool emc_enable;
 #endif
 module_param(emc_enable, bool, 0644);
 
+struct emc_superclient_constraint {
+       int bandwidth;
+       enum tegra_emc_perf_level perf_level;
+};
+
+static struct emc_superclient_constraint constraints[TEGRA_EMC_SC_NUM];
+
 static struct platform_device *emc_pdev;
 static void __iomem *emc_regbase;
+static struct clk *emc_clk;
 
 static inline void emc_writel(u32 val, unsigned long addr)
 {
@@ -176,6 +187,64 @@ int tegra_emc_set_rate(unsigned long rate)
        return 0;
 }
 
+static void tegra_emc_scale_clock(void)
+{
+       u64 bandwidth_floor = 0;
+       enum tegra_emc_perf_level highest_level = TEGRA_EMC_PL_LOW;
+       unsigned long clock_rate;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(constraints); i++) {
+               bandwidth_floor += constraints[i].bandwidth;
+               if (constraints[i].perf_level > highest_level)
+                       highest_level = constraints[i].perf_level;
+       }
+
+       /*
+        * Add some headroom to the bandwidth, we use a conservative 60%
+        * DRAM bandwidth efficiency factor
+        */
+       bandwidth_floor *= 5;
+       do_div(bandwidth_floor, 3);
+
+       clock_rate = bandwidth_floor >> 2; /* 4byte/clock */
+
+       /* boost clock according to perf level */
+       switch (highest_level) {
+       case TEGRA_EMC_PL_MID:
+               clock_rate += 300000000;
+               break;
+       case TEGRA_EMC_PL_HIGH:
+               clock_rate += 600000000;
+               break;
+       default:
+               break;
+       }
+
+       /* cap clock_rate at 600MHz, enough to select highest rate table */
+       clock_rate = min(600000000UL, clock_rate);
+
+       clk_set_rate(emc_clk, clock_rate);
+}
+
+void tegra_emc_request_bandwidth(enum tegra_emc_superclient client,
+                                int bandwidth)
+{
+       constraints[client].bandwidth = bandwidth;
+
+       tegra_emc_scale_clock();
+}
+EXPORT_SYMBOL_GPL(tegra_emc_request_bandwidth);
+
+void tegra_emc_request_perf_level(enum tegra_emc_superclient client,
+                                 enum tegra_emc_perf_level level)
+{
+       constraints[client].perf_level = level;
+
+       tegra_emc_scale_clock();
+}
+EXPORT_SYMBOL_GPL(tegra_emc_request_perf_level);
+
 #ifdef CONFIG_OF
 static struct device_node *tegra_emc_ramcode_devnode(struct device_node *np)
 {
@@ -270,19 +339,17 @@ static struct tegra_emc_pdata *tegra_emc_dt_parse_pdata(
 
 static struct tegra_emc_pdata __devinit *tegra_emc_fill_pdata(struct 
platform_device *pdev)
 {
-       struct clk *c = clk_get_sys(NULL, "emc");
        struct tegra_emc_pdata *pdata;
        unsigned long khz;
        int i;
 
        WARN_ON(pdev->dev.platform_data);
-       BUG_ON(IS_ERR_OR_NULL(c));
 
        pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
        pdata->tables = devm_kzalloc(&pdev->dev, sizeof(*pdata->tables),
                                     GFP_KERNEL);
 
-       pdata->tables[0].rate = clk_get_rate(c) / 2 / 1000;
+       pdata->tables[0].rate = clk_get_rate(emc_clk) / 2 / 1000;
 
        for (i = 0; i < TEGRA_EMC_NUM_REGS; i++)
                pdata->tables[0].regs[i] = emc_readl(emc_reg_addr[i]);
@@ -306,6 +373,9 @@ static int __devinit tegra_emc_probe(struct platform_device 
*pdev)
                return -ENODEV;
        }
 
+       emc_clk = clk_get_sys(NULL, "emc");
+       BUG_ON(IS_ERR_OR_NULL(emc_clk));
+
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res) {
                dev_err(&pdev->dev, "missing register base\n");
@@ -333,6 +403,13 @@ static int __devinit tegra_emc_probe(struct 
platform_device *pdev)
        return 0;
 }
 
+static int tegra_emc_remove(struct platform_device *pdev)
+{
+       clk_put(emc_clk);
+
+       return 0;
+}
+
 static struct of_device_id tegra_emc_of_match[] __devinitdata = {
        { .compatible = "nvidia,tegra20-emc", },
        { },
@@ -345,6 +422,7 @@ static struct platform_driver tegra_emc_driver = {
                .of_match_table = tegra_emc_of_match,
        },
        .probe          = tegra_emc_probe,
+       .remove         = tegra_emc_remove,
 };
 
 static int __init tegra_emc_init(void)
diff --git a/include/memory/tegra_emc_performance.h 
b/include/memory/tegra_emc_performance.h
new file mode 100644
index 0000000..6c23099
--- /dev/null
+++ b/include/memory/tegra_emc_performance.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2012 Lucas Stach
+ *
+ * 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.
+ */
+
+#ifndef __TEGRA_EMC_PERFORMANCE_H
+#define __TEGRA_EMC_PERFORMANCE_H
+
+enum tegra_emc_superclient {
+       TEGRA_EMC_SC_AFI = 0,   /* Tegra30 only */
+       TEGRA_EMC_SC_AVP,
+       TEGRA_EMC_SC_DC,
+       TEGRA_EMC_SC_DCB,
+       TEGRA_EMC_SC_EPP,
+       TEGRA_EMC_SC_G2,
+       TEGRA_EMC_SC_HC,
+       TEGRA_EMC_SC_HDA,       /* Tegra30 only */
+       TEGRA_EMC_SC_ISP,
+       TEGRA_EMC_SC_MPCORE,
+       TEGRA_EMC_SC_MPCORELP,  /* Tegra30 only */
+       TEGRA_EMC_SC_MPE,
+       TEGRA_EMC_SC_NV,
+       TEGRA_EMC_SC_NV2,       /* Tegra30 only */
+       TEGRA_EMC_SC_PPCS,
+       TEGRA_EMC_SC_SATA,      /* Tegra30 only */
+       TEGRA_EMC_SC_VDE,
+       TEGRA_EMC_SC_VI,
+
+       TEGRA_EMC_SC_NUM
+};
+
+enum tegra_emc_perf_level {
+       TEGRA_EMC_PL_LOW = 0,   /* no boost */
+       TEGRA_EMC_PL_MID,       /* half frequency boost */
+       TEGRA_EMC_PL_HIGH       /* max frequency boost */
+};
+
+/**
+ * tegra_emc_request_bandwidth - request bandwidth for isochronous clients
+ * @client: the EMC superclient which requests bandwidth
+ * @bandwidth: needed bandwidth in bytes/s
+ *
+ * Use this function to request bandwidth for isochronous clients, which
+ * need a specific bandwidth to operate properly. EMC will make sure to not
+ * drop below this limit while scaling DRAM frequency.
+ * Superclients have to add up the required bandwidth for their subunits
+ * themselves.
+ */
+void tegra_emc_request_bandwidth(enum tegra_emc_superclient client,
+                                int bandwidth);
+
+/**
+ * tegra_emc_request_perf_level - request DRAM frequency boost
+ * @client: the EMC superclient which requests boost
+ * @level: the requested boost level
+ *
+ * Use this function to boost DRAM frequency for clients, which don't need a
+ * specific bandwidth but want to scale up DRAM bandwidth to implement
+ * race-to-idle.
+ */
+void tegra_emc_request_perf_level(enum tegra_emc_superclient client,
+                                 enum tegra_emc_perf_level level);
+
+/* FIXME: Tegra 30 has no EMC driver yet, stub out performance functions */
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+void tegra_emc_request_bandwidth(enum tegra_emc_superclient client,
+                                int bandwidth)
+{
+}
+void tegra_emc_request_perf_level(enum tegra_emc_superclient client,
+                                 enum tegra_emc_perf_level level)
+{
+}
+#endif /* !CONFIG_ARCH_TEGRA_2x_SOC */
+
+#endif /* __TEGRA_EMC_PERFORMANCE_H */
-- 
1.7.11.7

--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to