Commit 4729fd7a authored by Colin Cross's avatar Colin Cross

ARM: tegra: clock: Convert global lock to a lock per clock

Give each clock its own lock, and remove all lock traversals from
parent to child clocks to prevent AB-BA deadlocks.

This brings the locking in line with the common struct clk
patches and should make conversion simple.
Acked-by: default avatarOlof Johansson <olof@lixom.net>
Signed-off-by: default avatarColin Cross <ccross@android.com>
parent f1519611
...@@ -18,83 +18,117 @@ ...@@ -18,83 +18,117 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/list.h> #include <linux/clkdev.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/list.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/debugfs.h> #include <linux/sched.h>
#include <linux/slab.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/clkdev.h> #include <linux/slab.h>
#include <mach/clk.h>
#include "board.h" #include "board.h"
#include "clock.h" #include "clock.h"
/*
* Locking:
*
* Each struct clk has a spinlock.
*
* To avoid AB-BA locking problems, locks must always be traversed from child
* clock to parent clock. For example, when enabling a clock, the clock's lock
* is taken, and then clk_enable is called on the parent, which take's the
* parent clock's lock. There is one exceptions to this ordering: When dumping
* the clock tree through debugfs. In this case, clk_lock_all is called,
* which attemps to iterate through the entire list of clocks and take every
* clock lock. If any call to spin_trylock fails, all locked clocks are
* unlocked, and the process is retried. When all the locks are held,
* the only clock operation that can be called is clk_get_rate_all_locked.
*
* Within a single clock, no clock operation can call another clock operation
* on itself, except for clk_get_rate_locked and clk_set_rate_locked. Any
* clock operation can call any other clock operation on any of it's possible
* parents.
*
* An additional mutex, clock_list_lock, is used to protect the list of all
* clocks.
*
* The clock operations must lock internally to protect against
* read-modify-write on registers that are shared by multiple clocks
*/
static DEFINE_MUTEX(clock_list_lock);
static LIST_HEAD(clocks); static LIST_HEAD(clocks);
static DEFINE_SPINLOCK(clock_lock);
struct clk *tegra_get_clock_by_name(const char *name) struct clk *tegra_get_clock_by_name(const char *name)
{ {
struct clk *c; struct clk *c;
struct clk *ret = NULL; struct clk *ret = NULL;
unsigned long flags; mutex_lock(&clock_list_lock);
spin_lock_irqsave(&clock_lock, flags);
list_for_each_entry(c, &clocks, node) { list_for_each_entry(c, &clocks, node) {
if (strcmp(c->name, name) == 0) { if (strcmp(c->name, name) == 0) {
ret = c; ret = c;
break; break;
} }
} }
spin_unlock_irqrestore(&clock_lock, flags); mutex_unlock(&clock_list_lock);
return ret; return ret;
} }
static void clk_recalculate_rate(struct clk *c) /* Must be called with c->spinlock held */
static unsigned long clk_predict_rate_from_parent(struct clk *c, struct clk *p)
{ {
u64 rate; u64 rate;
if (!c->parent) rate = clk_get_rate(p);
return;
rate = c->parent->rate;
if (c->mul != 0 && c->div != 0) { if (c->mul != 0 && c->div != 0) {
rate = rate * c->mul; rate *= c->mul;
do_div(rate, c->div); do_div(rate, c->div);
} }
if (rate > c->max_rate) return rate;
pr_warn("clocks: Set clock %s to rate %llu, max is %lu\n",
c->name, rate, c->max_rate);
c->rate = rate;
} }
int clk_reparent(struct clk *c, struct clk *parent) /* Must be called with c->spinlock held */
unsigned long clk_get_rate_locked(struct clk *c)
{ {
c->parent = parent; unsigned long rate;
list_del(&c->sibling);
list_add_tail(&c->sibling, &parent->children);
return 0;
}
static void propagate_rate(struct clk *c) if (c->parent)
{ rate = clk_predict_rate_from_parent(c, c->parent);
struct clk *clkp; else
rate = c->rate;
list_for_each_entry(clkp, &c->children, sibling) { return rate;
clk_recalculate_rate(clkp);
propagate_rate(clkp);
}
} }
void clk_init(struct clk *c) unsigned long clk_get_rate(struct clk *c)
{ {
unsigned long flags; unsigned long flags;
unsigned long rate;
spin_lock_irqsave(&clock_lock, flags); spin_lock_irqsave(&c->spinlock, flags);
INIT_LIST_HEAD(&c->children); rate = clk_get_rate_locked(c);
INIT_LIST_HEAD(&c->sibling);
spin_unlock_irqrestore(&c->spinlock, flags);
return rate;
}
EXPORT_SYMBOL(clk_get_rate);
int clk_reparent(struct clk *c, struct clk *parent)
{
c->parent = parent;
return 0;
}
void clk_init(struct clk *c)
{
spin_lock_init(&c->spinlock);
if (c->ops && c->ops->init) if (c->ops && c->ops->init)
c->ops->init(c); c->ops->init(c);
...@@ -108,33 +142,31 @@ void clk_init(struct clk *c) ...@@ -108,33 +142,31 @@ void clk_init(struct clk *c)
c->state = ON; c->state = ON;
} }
clk_recalculate_rate(c); mutex_lock(&clock_list_lock);
list_add(&c->node, &clocks); list_add(&c->node, &clocks);
mutex_unlock(&clock_list_lock);
if (c->parent)
list_add_tail(&c->sibling, &c->parent->children);
spin_unlock_irqrestore(&clock_lock, flags);
} }
int clk_enable_locked(struct clk *c) int clk_enable(struct clk *c)
{ {
int ret; int ret = 0;
unsigned long flags;
spin_lock_irqsave(&c->spinlock, flags);
if (c->refcnt == 0) { if (c->refcnt == 0) {
if (c->parent) { if (c->parent) {
ret = clk_enable_locked(c->parent); ret = clk_enable(c->parent);
if (ret) if (ret)
return ret; goto out;
} }
if (c->ops && c->ops->enable) { if (c->ops && c->ops->enable) {
ret = c->ops->enable(c); ret = c->ops->enable(c);
if (ret) { if (ret) {
if (c->parent) if (c->parent)
clk_disable_locked(c->parent); clk_disable(c->parent);
return ret; goto out;
} }
c->state = ON; c->state = ON;
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
...@@ -143,27 +175,21 @@ int clk_enable_locked(struct clk *c) ...@@ -143,27 +175,21 @@ int clk_enable_locked(struct clk *c)
} }
} }
c->refcnt++; c->refcnt++;
out:
return 0; spin_unlock_irqrestore(&c->spinlock, flags);
return ret;
} }
EXPORT_SYMBOL(clk_enable);
int clk_enable(struct clk *c) void clk_disable(struct clk *c)
{ {
int ret;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&clock_lock, flags); spin_lock_irqsave(&c->spinlock, flags);
ret = clk_enable_locked(c);
spin_unlock_irqrestore(&clock_lock, flags);
return ret;
}
EXPORT_SYMBOL(clk_enable);
void clk_disable_locked(struct clk *c)
{
if (c->refcnt == 0) { if (c->refcnt == 0) {
WARN(1, "Attempting to disable clock %s with refcnt 0", c->name); WARN(1, "Attempting to disable clock %s with refcnt 0", c->name);
spin_unlock_irqrestore(&c->spinlock, flags);
return; return;
} }
if (c->refcnt == 1) { if (c->refcnt == 1) {
...@@ -171,49 +197,39 @@ void clk_disable_locked(struct clk *c) ...@@ -171,49 +197,39 @@ void clk_disable_locked(struct clk *c)
c->ops->disable(c); c->ops->disable(c);
if (c->parent) if (c->parent)
clk_disable_locked(c->parent); clk_disable(c->parent);
c->state = OFF; c->state = OFF;
} }
c->refcnt--; c->refcnt--;
}
void clk_disable(struct clk *c) spin_unlock_irqrestore(&c->spinlock, flags);
{
unsigned long flags;
spin_lock_irqsave(&clock_lock, flags);
clk_disable_locked(c);
spin_unlock_irqrestore(&clock_lock, flags);
} }
EXPORT_SYMBOL(clk_disable); EXPORT_SYMBOL(clk_disable);
int clk_set_parent_locked(struct clk *c, struct clk *parent) int clk_set_parent(struct clk *c, struct clk *parent)
{ {
int ret; int ret;
unsigned long flags;
unsigned long new_rate;
unsigned long old_rate;
if (!c->ops || !c->ops->set_parent) spin_lock_irqsave(&c->spinlock, flags);
return -ENOSYS;
ret = c->ops->set_parent(c, parent); if (!c->ops || !c->ops->set_parent) {
ret = -ENOSYS;
if (ret) goto out;
return ret; }
clk_recalculate_rate(c);
propagate_rate(c); new_rate = clk_predict_rate_from_parent(c, parent);
old_rate = clk_get_rate_locked(c);
return 0; ret = c->ops->set_parent(c, parent);
} if (ret)
goto out;
int clk_set_parent(struct clk *c, struct clk *parent) out:
{ spin_unlock_irqrestore(&c->spinlock, flags);
int ret;
unsigned long flags;
spin_lock_irqsave(&clock_lock, flags);
ret = clk_set_parent_locked(c, parent);
spin_unlock_irqrestore(&clock_lock, flags);
return ret; return ret;
} }
EXPORT_SYMBOL(clk_set_parent); EXPORT_SYMBOL(clk_set_parent);
...@@ -226,62 +242,75 @@ EXPORT_SYMBOL(clk_get_parent); ...@@ -226,62 +242,75 @@ EXPORT_SYMBOL(clk_get_parent);
int clk_set_rate_locked(struct clk *c, unsigned long rate) int clk_set_rate_locked(struct clk *c, unsigned long rate)
{ {
int ret;
if (rate > c->max_rate)
rate = c->max_rate;
if (!c->ops || !c->ops->set_rate) if (!c->ops || !c->ops->set_rate)
return -ENOSYS; return -ENOSYS;
ret = c->ops->set_rate(c, rate); if (rate > c->max_rate)
rate = c->max_rate;
if (ret)
return ret;
clk_recalculate_rate(c);
propagate_rate(c);
return 0; return c->ops->set_rate(c, rate);
} }
int clk_set_rate(struct clk *c, unsigned long rate) int clk_set_rate(struct clk *c, unsigned long rate)
{ {
int ret = 0; int ret;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&clock_lock, flags); spin_lock_irqsave(&c->spinlock, flags);
ret = clk_set_rate_locked(c, rate); ret = clk_set_rate_locked(c, rate);
spin_unlock_irqrestore(&clock_lock, flags);
spin_unlock_irqrestore(&c->spinlock, flags);
return ret; return ret;
} }
EXPORT_SYMBOL(clk_set_rate); EXPORT_SYMBOL(clk_set_rate);
unsigned long clk_get_rate(struct clk *c)
{
unsigned long flags;
unsigned long ret;
spin_lock_irqsave(&clock_lock, flags); /* Must be called with clocks lock and all indvidual clock locks held */
unsigned long clk_get_rate_all_locked(struct clk *c)
{
u64 rate;
int mul = 1;
int div = 1;
struct clk *p = c;
while (p) {
c = p;
if (c->mul != 0 && c->div != 0) {
mul *= c->mul;
div *= c->div;
}
p = c->parent;
}
ret = c->rate; rate = c->rate;
rate *= mul;
do_div(rate, div);
spin_unlock_irqrestore(&clock_lock, flags); return rate;
return ret;
} }
EXPORT_SYMBOL(clk_get_rate);
long clk_round_rate(struct clk *c, unsigned long rate) long clk_round_rate(struct clk *c, unsigned long rate)
{ {
if (!c->ops || !c->ops->round_rate) unsigned long flags;
return -ENOSYS; long ret;
spin_lock_irqsave(&c->spinlock, flags);
if (!c->ops || !c->ops->round_rate) {
ret = -ENOSYS;
goto out;
}
if (rate > c->max_rate) if (rate > c->max_rate)
rate = c->max_rate; rate = c->max_rate;
return c->ops->round_rate(c, rate); ret = c->ops->round_rate(c, rate);
out:
spin_unlock_irqrestore(&c->spinlock, flags);
return ret;
} }
EXPORT_SYMBOL(clk_round_rate); EXPORT_SYMBOL(clk_round_rate);
...@@ -364,13 +393,75 @@ void __init tegra_init_clock(void) ...@@ -364,13 +393,75 @@ void __init tegra_init_clock(void)
} }
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
static int __clk_lock_all_spinlocks(void)
{
struct clk *c;
list_for_each_entry(c, &clocks, node)
if (!spin_trylock(&c->spinlock))
goto unlock_spinlocks;
return 0;
unlock_spinlocks:
list_for_each_entry_continue_reverse(c, &clocks, node)
spin_unlock(&c->spinlock);
return -EAGAIN;
}
static void __clk_unlock_all_spinlocks(void)
{
struct clk *c;
list_for_each_entry_reverse(c, &clocks, node)
spin_unlock(&c->spinlock);
}
/*
* This function retries until it can take all locks, and may take
* an arbitrarily long time to complete.
* Must be called with irqs enabled, returns with irqs disabled
* Must be called with clock_list_lock held
*/
static void clk_lock_all(void)
{
int ret;
retry:
local_irq_disable();
ret = __clk_lock_all_spinlocks();
if (ret)
goto failed_spinlocks;
/* All locks taken successfully, return */
return;
failed_spinlocks:
local_irq_enable();
yield();
goto retry;
}
/*
* Unlocks all clocks after a clk_lock_all
* Must be called with irqs disabled, returns with irqs enabled
* Must be called with clock_list_lock held
*/
static void clk_unlock_all(void)
{
__clk_unlock_all_spinlocks();
local_irq_enable();
}
static struct dentry *clk_debugfs_root; static struct dentry *clk_debugfs_root;
static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level) static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level)
{ {
struct clk *child; struct clk *child;
struct clk *safe;
const char *state = "uninit"; const char *state = "uninit";
char div[8] = {0}; char div[8] = {0};
...@@ -401,8 +492,12 @@ static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level) ...@@ -401,8 +492,12 @@ static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level)
c->rate > c->max_rate ? '!' : ' ', c->rate > c->max_rate ? '!' : ' ',
!c->set ? '*' : ' ', !c->set ? '*' : ' ',
30 - level * 3, c->name, 30 - level * 3, c->name,
state, c->refcnt, div, c->rate); state, c->refcnt, div, clk_get_rate_all_locked(c));
list_for_each_entry_safe(child, safe, &c->children, sibling) {
list_for_each_entry(child, &clocks, node) {
if (child->parent != c)
continue;
clock_tree_show_one(s, child, level + 1); clock_tree_show_one(s, child, level + 1);
} }
} }
...@@ -410,14 +505,20 @@ static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level) ...@@ -410,14 +505,20 @@ static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level)
static int clock_tree_show(struct seq_file *s, void *data) static int clock_tree_show(struct seq_file *s, void *data)
{ {
struct clk *c; struct clk *c;
unsigned long flags;
seq_printf(s, " clock state ref div rate\n"); seq_printf(s, " clock state ref div rate\n");
seq_printf(s, "--------------------------------------------------------------\n"); seq_printf(s, "--------------------------------------------------------------\n");
spin_lock_irqsave(&clock_lock, flags);
mutex_lock(&clock_list_lock);
clk_lock_all();
list_for_each_entry(c, &clocks, node) list_for_each_entry(c, &clocks, node)
if (c->parent == NULL) if (c->parent == NULL)
clock_tree_show_one(s, c, 0); clock_tree_show_one(s, c, 0);
spin_unlock_irqrestore(&clock_lock, flags);
clk_unlock_all();
mutex_unlock(&clock_list_lock);
return 0; return 0;
} }
......
...@@ -20,8 +20,9 @@ ...@@ -20,8 +20,9 @@
#ifndef __MACH_TEGRA_CLOCK_H #ifndef __MACH_TEGRA_CLOCK_H
#define __MACH_TEGRA_CLOCK_H #define __MACH_TEGRA_CLOCK_H
#include <linux/list.h>
#include <linux/clkdev.h> #include <linux/clkdev.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#define DIV_BUS (1 << 0) #define DIV_BUS (1 << 0)
#define DIV_U71 (1 << 1) #define DIV_U71 (1 << 1)
...@@ -75,8 +76,6 @@ enum clk_state { ...@@ -75,8 +76,6 @@ enum clk_state {
struct clk { struct clk {
/* node for master clocks list */ /* node for master clocks list */
struct list_head node; /* node for list of all clocks */ struct list_head node; /* node for list of all clocks */
struct list_head children; /* list of children */
struct list_head sibling; /* node for children */
struct clk_lookup lookup; struct clk_lookup lookup;
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
...@@ -122,8 +121,9 @@ struct clk { ...@@ -122,8 +121,9 @@ struct clk {
struct clk *backup; struct clk *backup;
} cpu; } cpu;
} u; } u;
};
spinlock_t spinlock;
};
struct clk_duplicate { struct clk_duplicate {
const char *name; const char *name;
...@@ -143,11 +143,9 @@ void tegra2_periph_reset_assert(struct clk *c); ...@@ -143,11 +143,9 @@ void tegra2_periph_reset_assert(struct clk *c);
void clk_init(struct clk *clk); void clk_init(struct clk *clk);
struct clk *tegra_get_clock_by_name(const char *name); struct clk *tegra_get_clock_by_name(const char *name);
unsigned long clk_measure_input_freq(void); unsigned long clk_measure_input_freq(void);
void clk_disable_locked(struct clk *c);
int clk_enable_locked(struct clk *c);
int clk_set_parent_locked(struct clk *c, struct clk *parent);
int clk_set_rate_locked(struct clk *c, unsigned long rate);
int clk_reparent(struct clk *c, struct clk *parent); int clk_reparent(struct clk *c, struct clk *parent);
void tegra_clk_init_from_table(struct tegra_clk_init_table *table); void tegra_clk_init_from_table(struct tegra_clk_init_table *table);
unsigned long clk_get_rate_locked(struct clk *c);
int clk_set_rate_locked(struct clk *c, unsigned long rate);
#endif #endif
...@@ -25,4 +25,5 @@ struct clk; ...@@ -25,4 +25,5 @@ struct clk;
void tegra_periph_reset_deassert(struct clk *c); void tegra_periph_reset_deassert(struct clk *c);
void tegra_periph_reset_assert(struct clk *c); void tegra_periph_reset_assert(struct clk *c);
unsigned long clk_get_rate_all_locked(struct clk *c);
#endif #endif
...@@ -23,8 +23,8 @@ ...@@ -23,8 +23,8 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/hrtimer.h>
#include <linux/clkdev.h> #include <linux/clkdev.h>
#include <linux/clk.h>
#include <mach/iomap.h> #include <mach/iomap.h>
#include <mach/suspend.h> #include <mach/suspend.h>
...@@ -147,6 +147,13 @@ ...@@ -147,6 +147,13 @@
static void __iomem *reg_clk_base = IO_ADDRESS(TEGRA_CLK_RESET_BASE); static void __iomem *reg_clk_base = IO_ADDRESS(TEGRA_CLK_RESET_BASE);
static void __iomem *reg_pmc_base = IO_ADDRESS(TEGRA_PMC_BASE); static void __iomem *reg_pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
/*
* Some clocks share a register with other clocks. Any clock op that
* non-atomically modifies a register used by another clock must lock
* clock_register_lock first.
*/
static DEFINE_SPINLOCK(clock_register_lock);
#define clk_writel(value, reg) \ #define clk_writel(value, reg) \
__raw_writel(value, (u32)reg_clk_base + (reg)) __raw_writel(value, (u32)reg_clk_base + (reg))
#define clk_readl(reg) \ #define clk_readl(reg) \
...@@ -330,12 +337,12 @@ static int tegra2_super_clk_set_parent(struct clk *c, struct clk *p) ...@@ -330,12 +337,12 @@ static int tegra2_super_clk_set_parent(struct clk *c, struct clk *p)
val |= sel->value << shift; val |= sel->value << shift;
if (c->refcnt) if (c->refcnt)
clk_enable_locked(p); clk_enable(p);
clk_writel(val, c->reg); clk_writel(val, c->reg);
if (c->refcnt && c->parent) if (c->refcnt && c->parent)
clk_disable_locked(c->parent); clk_disable(c->parent);
clk_reparent(c, p); clk_reparent(c, p);
return 0; return 0;
...@@ -378,22 +385,22 @@ static void tegra2_cpu_clk_disable(struct clk *c) ...@@ -378,22 +385,22 @@ static void tegra2_cpu_clk_disable(struct clk *c)
static int tegra2_cpu_clk_set_rate(struct clk *c, unsigned long rate) static int tegra2_cpu_clk_set_rate(struct clk *c, unsigned long rate)
{ {
int ret; int ret;
ret = clk_set_parent_locked(c->parent, c->u.cpu.backup); ret = clk_set_parent(c->parent, c->u.cpu.backup);
if (ret) { if (ret) {
pr_err("Failed to switch cpu to clock %s\n", c->u.cpu.backup->name); pr_err("Failed to switch cpu to clock %s\n", c->u.cpu.backup->name);
return ret; return ret;
} }
if (rate == c->u.cpu.backup->rate) if (rate == clk_get_rate(c->u.cpu.backup))
goto out; goto out;
ret = clk_set_rate_locked(c->u.cpu.main, rate); ret = clk_set_rate(c->u.cpu.main, rate);
if (ret) { if (ret) {
pr_err("Failed to change cpu pll to %lu\n", rate); pr_err("Failed to change cpu pll to %lu\n", rate);
return ret; return ret;
} }
ret = clk_set_parent_locked(c->parent, c->u.cpu.main); ret = clk_set_parent(c->parent, c->u.cpu.main);
if (ret) { if (ret) {
pr_err("Failed to switch cpu to clock %s\n", c->u.cpu.main->name); pr_err("Failed to switch cpu to clock %s\n", c->u.cpu.main->name);
return ret; return ret;
...@@ -421,24 +428,45 @@ static void tegra2_bus_clk_init(struct clk *c) ...@@ -421,24 +428,45 @@ static void tegra2_bus_clk_init(struct clk *c)
static int tegra2_bus_clk_enable(struct clk *c) static int tegra2_bus_clk_enable(struct clk *c)
{ {
u32 val = clk_readl(c->reg); u32 val;
unsigned long flags;
spin_lock_irqsave(&clock_register_lock, flags);
val = clk_readl(c->reg);
val &= ~(BUS_CLK_DISABLE << c->reg_shift); val &= ~(BUS_CLK_DISABLE << c->reg_shift);
clk_writel(val, c->reg); clk_writel(val, c->reg);
spin_unlock_irqrestore(&clock_register_lock, flags);
return 0; return 0;
} }
static void tegra2_bus_clk_disable(struct clk *c) static void tegra2_bus_clk_disable(struct clk *c)
{ {
u32 val = clk_readl(c->reg); u32 val;
unsigned long flags;
spin_lock_irqsave(&clock_register_lock, flags);
val = clk_readl(c->reg);
val |= BUS_CLK_DISABLE << c->reg_shift; val |= BUS_CLK_DISABLE << c->reg_shift;
clk_writel(val, c->reg); clk_writel(val, c->reg);
spin_unlock_irqrestore(&clock_register_lock, flags);
} }
static int tegra2_bus_clk_set_rate(struct clk *c, unsigned long rate) static int tegra2_bus_clk_set_rate(struct clk *c, unsigned long rate)
{ {
u32 val = clk_readl(c->reg); u32 val;
unsigned long parent_rate = c->parent->rate; unsigned long parent_rate = clk_get_rate(c->parent);
unsigned long flags;
int ret = -EINVAL;
int i; int i;
spin_lock_irqsave(&clock_register_lock, flags);
val = clk_readl(c->reg);
for (i = 1; i <= 4; i++) { for (i = 1; i <= 4; i++) {
if (rate == parent_rate / i) { if (rate == parent_rate / i) {
val &= ~(BUS_CLK_DIV_MASK << c->reg_shift); val &= ~(BUS_CLK_DIV_MASK << c->reg_shift);
...@@ -446,10 +474,14 @@ static int tegra2_bus_clk_set_rate(struct clk *c, unsigned long rate) ...@@ -446,10 +474,14 @@ static int tegra2_bus_clk_set_rate(struct clk *c, unsigned long rate)
clk_writel(val, c->reg); clk_writel(val, c->reg);
c->div = i; c->div = i;
c->mul = 1; c->mul = 1;
return 0; ret = 0;
break;
} }
} }
return -EINVAL;
spin_unlock_irqrestore(&clock_register_lock, flags);
return ret;
} }
static struct clk_ops tegra_bus_ops = { static struct clk_ops tegra_bus_ops = {
...@@ -511,14 +543,15 @@ static void tegra2_blink_clk_disable(struct clk *c) ...@@ -511,14 +543,15 @@ static void tegra2_blink_clk_disable(struct clk *c)
static int tegra2_blink_clk_set_rate(struct clk *c, unsigned long rate) static int tegra2_blink_clk_set_rate(struct clk *c, unsigned long rate)
{ {
if (rate >= c->parent->rate) { unsigned long parent_rate = clk_get_rate(c->parent);
if (rate >= parent_rate) {
c->div = 1; c->div = 1;
pmc_writel(0, c->reg); pmc_writel(0, c->reg);
} else { } else {
unsigned int on_off; unsigned int on_off;
u32 val; u32 val;
on_off = DIV_ROUND_UP(c->parent->rate / 8, rate); on_off = DIV_ROUND_UP(parent_rate / 8, rate);
c->div = on_off * 8; c->div = on_off * 8;
val = (on_off & PMC_BLINK_TIMER_DATA_ON_MASK) << val = (on_off & PMC_BLINK_TIMER_DATA_ON_MASK) <<
...@@ -604,7 +637,7 @@ static int tegra2_pll_clk_set_rate(struct clk *c, unsigned long rate) ...@@ -604,7 +637,7 @@ static int tegra2_pll_clk_set_rate(struct clk *c, unsigned long rate)
pr_debug("%s: %s %lu\n", __func__, c->name, rate); pr_debug("%s: %s %lu\n", __func__, c->name, rate);
input_rate = c->parent->rate; input_rate = clk_get_rate(c->parent);
for (sel = c->u.pll.freq_table; sel->input_rate != 0; sel++) { for (sel = c->u.pll.freq_table; sel->input_rate != 0; sel++) {
if (sel->input_rate == input_rate && sel->output_rate == rate) { if (sel->input_rate == input_rate && sel->output_rate == rate) {
c->mul = sel->n; c->mul = sel->n;
...@@ -717,9 +750,11 @@ static int tegra2_pll_div_clk_enable(struct clk *c) ...@@ -717,9 +750,11 @@ static int tegra2_pll_div_clk_enable(struct clk *c)
{ {
u32 val; u32 val;
u32 new_val; u32 new_val;
unsigned long flags;
pr_debug("%s: %s\n", __func__, c->name); pr_debug("%s: %s\n", __func__, c->name);
if (c->flags & DIV_U71) { if (c->flags & DIV_U71) {
spin_lock_irqsave(&clock_register_lock, flags);
val = clk_readl(c->reg); val = clk_readl(c->reg);
new_val = val >> c->reg_shift; new_val = val >> c->reg_shift;
new_val &= 0xFFFF; new_val &= 0xFFFF;
...@@ -729,12 +764,15 @@ static int tegra2_pll_div_clk_enable(struct clk *c) ...@@ -729,12 +764,15 @@ static int tegra2_pll_div_clk_enable(struct clk *c)
val &= ~(0xFFFF << c->reg_shift); val &= ~(0xFFFF << c->reg_shift);
val |= new_val << c->reg_shift; val |= new_val << c->reg_shift;
clk_writel(val, c->reg); clk_writel(val, c->reg);
spin_unlock_irqrestore(&clock_register_lock, flags);
return 0; return 0;
} else if (c->flags & DIV_2) { } else if (c->flags & DIV_2) {
BUG_ON(!(c->flags & PLLD)); BUG_ON(!(c->flags & PLLD));
spin_lock_irqsave(&clock_register_lock, flags);
val = clk_readl(c->reg); val = clk_readl(c->reg);
val &= ~PLLD_MISC_DIV_RST; val &= ~PLLD_MISC_DIV_RST;
clk_writel(val, c->reg); clk_writel(val, c->reg);
spin_unlock_irqrestore(&clock_register_lock, flags);
return 0; return 0;
} }
return -EINVAL; return -EINVAL;
...@@ -744,9 +782,11 @@ static void tegra2_pll_div_clk_disable(struct clk *c) ...@@ -744,9 +782,11 @@ static void tegra2_pll_div_clk_disable(struct clk *c)
{ {
u32 val; u32 val;
u32 new_val; u32 new_val;
unsigned long flags;
pr_debug("%s: %s\n", __func__, c->name); pr_debug("%s: %s\n", __func__, c->name);
if (c->flags & DIV_U71) { if (c->flags & DIV_U71) {
spin_lock_irqsave(&clock_register_lock, flags);
val = clk_readl(c->reg); val = clk_readl(c->reg);
new_val = val >> c->reg_shift; new_val = val >> c->reg_shift;
new_val &= 0xFFFF; new_val &= 0xFFFF;
...@@ -756,11 +796,14 @@ static void tegra2_pll_div_clk_disable(struct clk *c) ...@@ -756,11 +796,14 @@ static void tegra2_pll_div_clk_disable(struct clk *c)
val &= ~(0xFFFF << c->reg_shift); val &= ~(0xFFFF << c->reg_shift);
val |= new_val << c->reg_shift; val |= new_val << c->reg_shift;
clk_writel(val, c->reg); clk_writel(val, c->reg);
spin_unlock_irqrestore(&clock_register_lock, flags);
} else if (c->flags & DIV_2) { } else if (c->flags & DIV_2) {
BUG_ON(!(c->flags & PLLD)); BUG_ON(!(c->flags & PLLD));
spin_lock_irqsave(&clock_register_lock, flags);
val = clk_readl(c->reg); val = clk_readl(c->reg);
val |= PLLD_MISC_DIV_RST; val |= PLLD_MISC_DIV_RST;
clk_writel(val, c->reg); clk_writel(val, c->reg);
spin_unlock_irqrestore(&clock_register_lock, flags);
} }
} }
...@@ -769,10 +812,14 @@ static int tegra2_pll_div_clk_set_rate(struct clk *c, unsigned long rate) ...@@ -769,10 +812,14 @@ static int tegra2_pll_div_clk_set_rate(struct clk *c, unsigned long rate)
u32 val; u32 val;
u32 new_val; u32 new_val;
int divider_u71; int divider_u71;
unsigned long parent_rate = clk_get_rate(c->parent);
unsigned long flags;
pr_debug("%s: %s %lu\n", __func__, c->name, rate); pr_debug("%s: %s %lu\n", __func__, c->name, rate);
if (c->flags & DIV_U71) { if (c->flags & DIV_U71) {
divider_u71 = clk_div71_get_divider(c->parent->rate, rate); divider_u71 = clk_div71_get_divider(parent_rate, rate);
if (divider_u71 >= 0) { if (divider_u71 >= 0) {
spin_lock_irqsave(&clock_register_lock, flags);
val = clk_readl(c->reg); val = clk_readl(c->reg);
new_val = val >> c->reg_shift; new_val = val >> c->reg_shift;
new_val &= 0xFFFF; new_val &= 0xFFFF;
...@@ -786,10 +833,11 @@ static int tegra2_pll_div_clk_set_rate(struct clk *c, unsigned long rate) ...@@ -786,10 +833,11 @@ static int tegra2_pll_div_clk_set_rate(struct clk *c, unsigned long rate)
clk_writel(val, c->reg); clk_writel(val, c->reg);
c->div = divider_u71 + 2; c->div = divider_u71 + 2;
c->mul = 2; c->mul = 2;
spin_unlock_irqrestore(&clock_register_lock, flags);
return 0; return 0;
} }
} else if (c->flags & DIV_2) { } else if (c->flags & DIV_2) {
if (c->parent->rate == rate * 2) if (parent_rate == rate * 2)
return 0; return 0;
} }
return -EINVAL; return -EINVAL;
...@@ -798,15 +846,16 @@ static int tegra2_pll_div_clk_set_rate(struct clk *c, unsigned long rate) ...@@ -798,15 +846,16 @@ static int tegra2_pll_div_clk_set_rate(struct clk *c, unsigned long rate)
static long tegra2_pll_div_clk_round_rate(struct clk *c, unsigned long rate) static long tegra2_pll_div_clk_round_rate(struct clk *c, unsigned long rate)
{ {
int divider; int divider;
unsigned long parent_rate = clk_get_rate(c->parent);
pr_debug("%s: %s %lu\n", __func__, c->name, rate); pr_debug("%s: %s %lu\n", __func__, c->name, rate);
if (c->flags & DIV_U71) { if (c->flags & DIV_U71) {
divider = clk_div71_get_divider(c->parent->rate, rate); divider = clk_div71_get_divider(parent_rate, rate);
if (divider < 0) if (divider < 0)
return divider; return divider;
return c->parent->rate * 2 / (divider + 2); return parent_rate * 2 / (divider + 2);
} else if (c->flags & DIV_2) { } else if (c->flags & DIV_2) {
return c->parent->rate / 2; return parent_rate / 2;
} }
return -EINVAL; return -EINVAL;
} }
...@@ -912,12 +961,12 @@ static int tegra2_periph_clk_set_parent(struct clk *c, struct clk *p) ...@@ -912,12 +961,12 @@ static int tegra2_periph_clk_set_parent(struct clk *c, struct clk *p)
val |= (sel->value) << PERIPH_CLK_SOURCE_SHIFT; val |= (sel->value) << PERIPH_CLK_SOURCE_SHIFT;
if (c->refcnt) if (c->refcnt)
clk_enable_locked(p); clk_enable(p);
clk_writel(val, c->reg); clk_writel(val, c->reg);
if (c->refcnt && c->parent) if (c->refcnt && c->parent)
clk_disable_locked(c->parent); clk_disable(c->parent);
clk_reparent(c, p); clk_reparent(c, p);
return 0; return 0;
...@@ -931,9 +980,10 @@ static int tegra2_periph_clk_set_rate(struct clk *c, unsigned long rate) ...@@ -931,9 +980,10 @@ static int tegra2_periph_clk_set_rate(struct clk *c, unsigned long rate)
{ {
u32 val; u32 val;
int divider; int divider;
pr_debug("%s: %lu\n", __func__, rate); unsigned long parent_rate = clk_get_rate(c->parent);
if (c->flags & DIV_U71) { if (c->flags & DIV_U71) {
divider = clk_div71_get_divider(c->parent->rate, rate); divider = clk_div71_get_divider(parent_rate, rate);
if (divider >= 0) { if (divider >= 0) {
val = clk_readl(c->reg); val = clk_readl(c->reg);
val &= ~PERIPH_CLK_SOURCE_DIVU71_MASK; val &= ~PERIPH_CLK_SOURCE_DIVU71_MASK;
...@@ -944,7 +994,7 @@ static int tegra2_periph_clk_set_rate(struct clk *c, unsigned long rate) ...@@ -944,7 +994,7 @@ static int tegra2_periph_clk_set_rate(struct clk *c, unsigned long rate)
return 0; return 0;
} }
} else if (c->flags & DIV_U16) { } else if (c->flags & DIV_U16) {
divider = clk_div16_get_divider(c->parent->rate, rate); divider = clk_div16_get_divider(parent_rate, rate);
if (divider >= 0) { if (divider >= 0) {
val = clk_readl(c->reg); val = clk_readl(c->reg);
val &= ~PERIPH_CLK_SOURCE_DIVU16_MASK; val &= ~PERIPH_CLK_SOURCE_DIVU16_MASK;
...@@ -954,7 +1004,7 @@ static int tegra2_periph_clk_set_rate(struct clk *c, unsigned long rate) ...@@ -954,7 +1004,7 @@ static int tegra2_periph_clk_set_rate(struct clk *c, unsigned long rate)
c->mul = 1; c->mul = 1;
return 0; return 0;
} }
} else if (c->parent->rate <= rate) { } else if (parent_rate <= rate) {
c->div = 1; c->div = 1;
c->mul = 1; c->mul = 1;
return 0; return 0;
...@@ -966,19 +1016,20 @@ static long tegra2_periph_clk_round_rate(struct clk *c, ...@@ -966,19 +1016,20 @@ static long tegra2_periph_clk_round_rate(struct clk *c,
unsigned long rate) unsigned long rate)
{ {
int divider; int divider;
unsigned long parent_rate = clk_get_rate(c->parent);
pr_debug("%s: %s %lu\n", __func__, c->name, rate); pr_debug("%s: %s %lu\n", __func__, c->name, rate);
if (c->flags & DIV_U71) { if (c->flags & DIV_U71) {
divider = clk_div71_get_divider(c->parent->rate, rate); divider = clk_div71_get_divider(parent_rate, rate);
if (divider < 0) if (divider < 0)
return divider; return divider;
return c->parent->rate * 2 / (divider + 2); return parent_rate * 2 / (divider + 2);
} else if (c->flags & DIV_U16) { } else if (c->flags & DIV_U16) {
divider = clk_div16_get_divider(c->parent->rate, rate); divider = clk_div16_get_divider(parent_rate, rate);
if (divider < 0) if (divider < 0)
return divider; return divider;
return c->parent->rate / (divider + 1); return parent_rate / (divider + 1);
} }
return -EINVAL; return -EINVAL;
} }
...@@ -1006,7 +1057,7 @@ static void tegra2_clk_double_init(struct clk *c) ...@@ -1006,7 +1057,7 @@ static void tegra2_clk_double_init(struct clk *c)
static int tegra2_clk_double_set_rate(struct clk *c, unsigned long rate) static int tegra2_clk_double_set_rate(struct clk *c, unsigned long rate)
{ {
if (rate != 2 * c->parent->rate) if (rate != 2 * clk_get_rate(c->parent))
return -EINVAL; return -EINVAL;
c->mul = 2; c->mul = 2;
c->div = 1; c->div = 1;
...@@ -1057,12 +1108,12 @@ static int tegra2_audio_sync_clk_set_parent(struct clk *c, struct clk *p) ...@@ -1057,12 +1108,12 @@ static int tegra2_audio_sync_clk_set_parent(struct clk *c, struct clk *p)
val |= sel->value; val |= sel->value;
if (c->refcnt) if (c->refcnt)
clk_enable_locked(p); clk_enable(p);
clk_writel(val, c->reg); clk_writel(val, c->reg);
if (c->refcnt && c->parent) if (c->refcnt && c->parent)
clk_disable_locked(c->parent); clk_disable(c->parent);
clk_reparent(c, p); clk_reparent(c, p);
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment