summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/tegra11_emc.c
diff options
context:
space:
mode:
authorAlex Frid <afrid@nvidia.com>2012-08-02 17:33:39 -0700
committerVarun Colbert <vcolbert@nvidia.com>2012-08-09 21:03:35 -0700
commit55713389a3a7762dbb493cb429684c20f64a8bea (patch)
tree6acb8163b49071c897c1405bba17354eb76a981f /arch/arm/mach-tegra/tegra11_emc.c
parent21ed6da84d867f001540728118a1e10c3e61bd64 (diff)
ARM: tegra11: clock: Implement EMC bus update policy
Implemented EMC bus update policy as follows. Request for EMC rate is rounded up to the available discrete step listed in EMC scaling table. The clock source is selected as specified in the respective table entry (it can be either scaled PLLC or one of fixed rate plls: PLLP, or PLLM). If clock source rate does not match table entry (possible only for scaled PLLC), and - the scaled clock source is not used currently by EMC, then re-scale PLLC, and complete source switch/divider change in one shot - the scaled clock source is used currently by EMC, then switch emc to backup pll, re-scale PLLC, and then switch emc to PLLC with appropriate divider. The backup rate is determined as minimum rate above the target that can be achieved from any fixed rate plls. Change-Id: I1c9e3403c9600f925413fc310d7c8df2e2d3dff4 Signed-off-by: Alex Frid <afrid@nvidia.com> Reviewed-on: http://git-master/r/121436 Reviewed-by: Varun Colbert <vcolbert@nvidia.com> Tested-by: Varun Colbert <vcolbert@nvidia.com>
Diffstat (limited to 'arch/arm/mach-tegra/tegra11_emc.c')
-rw-r--r--arch/arm/mach-tegra/tegra11_emc.c111
1 files changed, 109 insertions, 2 deletions
diff --git a/arch/arm/mach-tegra/tegra11_emc.c b/arch/arm/mach-tegra/tegra11_emc.c
index 88ac17c5089c..dbfb803b63fb 100644
--- a/arch/arm/mach-tegra/tegra11_emc.c
+++ b/arch/arm/mach-tegra/tegra11_emc.c
@@ -127,16 +127,123 @@ int tegra_emc_set_rate(unsigned long rate)
long tegra_emc_round_rate(unsigned long rate)
{
- /* FIXME: This is just a stub */
+ int i;
+
+ if (!tegra_emc_table)
+ return clk_get_rate_locked(emc); /* no table - no rate change */
+
+ if (!emc_enable)
+ return -EINVAL;
+
+ pr_debug("%s: %lu\n", __func__, rate);
+
+ /* Table entries specify rate in kHz */
+ rate = rate / 1000;
+
+ for (i = 0; i < tegra_emc_table_size; i++) {
+ if (tegra_emc_clk_sel[i].input == NULL)
+ continue; /* invalid entry */
+
+ if (tegra_emc_table[i].rate >= rate) {
+ pr_debug("%s: using %lu\n",
+ __func__, tegra_emc_table[i].rate);
+ return tegra_emc_table[i].rate * 1000;
+ }
+ }
+
return -EINVAL;
}
struct clk *tegra_emc_predict_parent(unsigned long rate, u32 *div_value)
{
- /* FIXME: This is just a stub */
+ int i;
+
+ if (!tegra_emc_table) {
+ if (rate == clk_get_rate_locked(emc)) {
+ *div_value = emc->div - 2;
+ return emc->parent;
+ }
+ return NULL;
+ }
+
+ pr_debug("%s: %lu\n", __func__, rate);
+
+ /* Table entries specify rate in kHz */
+ rate = rate / 1000;
+
+ for (i = 0; i < tegra_emc_table_size; i++) {
+ if (tegra_emc_table[i].rate == rate) {
+ struct clk *p = tegra_emc_clk_sel[i].input;
+
+ if (p && (tegra_emc_clk_sel[i].input_rate ==
+ clk_get_rate(p))) {
+ *div_value = (tegra_emc_clk_sel[i].value &
+ EMC_CLK_DIV_MASK) >> EMC_CLK_DIV_SHIFT;
+ return p;
+ }
+ }
+ }
return NULL;
}
+bool tegra_emc_is_parent_ready(unsigned long rate, struct clk **parent,
+ unsigned long *parent_rate, unsigned long *backup_rate)
+{
+
+ int i;
+ struct clk *p = NULL;
+ unsigned long p_rate;
+
+ if (!tegra_emc_table || !emc_enable)
+ return true;
+
+ pr_debug("%s: %lu\n", __func__, rate);
+
+ /* Table entries specify rate in kHz */
+ rate = rate / 1000;
+
+ for (i = 0; i < tegra_emc_table_size; i++) {
+ if (tegra_emc_table[i].rate == rate) {
+ p = tegra_emc_clk_sel[i].input;
+ if (!p)
+ continue; /* invalid entry */
+
+ p_rate = tegra_emc_clk_sel[i].input_rate;
+ if (p_rate == clk_get_rate(p))
+ return true;
+ break;
+ }
+ }
+
+ /* Table match not found - "non existing parent" is ready */
+ if (!p)
+ return true;
+
+ /*
+ * Table match found, but parent is not ready - continue search
+ * for backup rate: min rate above requested that has different
+ * parent source (since only pll_c is scaled and may not be ready,
+ * any other parent can provide backup)
+ */
+ *parent = p;
+ *parent_rate = p_rate;
+
+ for (i++; i < tegra_emc_table_size; i++) {
+ p = tegra_emc_clk_sel[i].input;
+ if (!p)
+ continue; /* invalid entry */
+
+ if (p != (*parent)) {
+ *backup_rate = tegra_emc_table[i].rate * 1000;
+ return false;
+ }
+ }
+
+ /* Parent is not ready, and no backup found */
+ *backup_rate = -EINVAL;
+ return false;
+}
+
static int find_matching_input(const struct tegra_emc_table *table,
struct clk *pll_c, struct emc_sel *emc_clk_sel)
{