forked from Minki/linux
Merge branch 'for-3.7/common-clk' into for-3.7/cleanup2
This commit is contained in:
commit
f49540d1d7
@ -686,6 +686,7 @@ config ARCH_TEGRA
|
||||
select NEED_MACH_IO_H if PCI
|
||||
select ARCH_HAS_CPUFREQ
|
||||
select USE_OF
|
||||
select COMMON_CLK
|
||||
help
|
||||
This enables support for NVIDIA Tegra based systems (Tegra APX,
|
||||
Tegra 6xx and Tegra 2 series).
|
||||
|
@ -12,9 +12,11 @@ obj-y += powergate.o
|
||||
obj-y += apbio.o
|
||||
obj-$(CONFIG_CPU_IDLE) += cpuidle.o
|
||||
obj-$(CONFIG_CPU_IDLE) += sleep.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_clocks.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20_clocks.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20_clocks_data.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_emc.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30_clocks.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30_clocks_data.o
|
||||
obj-$(CONFIG_SMP) += platsmp.o headsmp.o
|
||||
obj-$(CONFIG_SMP) += reset.o
|
||||
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
|
||||
|
@ -70,6 +70,7 @@ struct of_dev_auxdata tegra20_auxdata_lookup[] __initdata = {
|
||||
|
||||
static __initdata struct tegra_clk_init_table tegra_dt_clk_init_table[] = {
|
||||
/* name parent rate enabled */
|
||||
{ "uarta", "pll_p", 216000000, true },
|
||||
{ "uartd", "pll_p", 216000000, true },
|
||||
{ "usbd", "clk_m", 12000000, false },
|
||||
{ "usb2", "clk_m", 12000000, false },
|
||||
|
@ -1,6 +1,7 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
* Copyright (c) 2012 NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Author:
|
||||
* Colin Cross <ccross@google.com>
|
||||
@ -19,8 +20,6 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
@ -36,40 +35,30 @@
|
||||
/*
|
||||
* 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);
|
||||
|
||||
void tegra_clk_add(struct clk *clk)
|
||||
{
|
||||
struct clk_tegra *c = to_clk_tegra(__clk_get_hw(clk));
|
||||
|
||||
mutex_lock(&clock_list_lock);
|
||||
list_add(&c->node, &clocks);
|
||||
mutex_unlock(&clock_list_lock);
|
||||
}
|
||||
|
||||
struct clk *tegra_get_clock_by_name(const char *name)
|
||||
{
|
||||
struct clk *c;
|
||||
struct clk_tegra *c;
|
||||
struct clk *ret = NULL;
|
||||
mutex_lock(&clock_list_lock);
|
||||
list_for_each_entry(c, &clocks, node) {
|
||||
if (strcmp(c->name, name) == 0) {
|
||||
ret = c;
|
||||
if (strcmp(__clk_get_name(c->hw.clk), name) == 0) {
|
||||
ret = c->hw.clk;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -77,280 +66,36 @@ struct clk *tegra_get_clock_by_name(const char *name)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Must be called with c->spinlock held */
|
||||
static unsigned long clk_predict_rate_from_parent(struct clk *c, struct clk *p)
|
||||
{
|
||||
u64 rate;
|
||||
|
||||
rate = clk_get_rate(p);
|
||||
|
||||
if (c->mul != 0 && c->div != 0) {
|
||||
rate *= c->mul;
|
||||
rate += c->div - 1; /* round up */
|
||||
do_div(rate, c->div);
|
||||
}
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
/* Must be called with c->spinlock held */
|
||||
unsigned long clk_get_rate_locked(struct clk *c)
|
||||
{
|
||||
unsigned long rate;
|
||||
|
||||
if (c->parent)
|
||||
rate = clk_predict_rate_from_parent(c, c->parent);
|
||||
else
|
||||
rate = c->rate;
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
unsigned long clk_get_rate(struct clk *c)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long rate;
|
||||
|
||||
spin_lock_irqsave(&c->spinlock, flags);
|
||||
|
||||
rate = clk_get_rate_locked(c);
|
||||
|
||||
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)
|
||||
c->ops->init(c);
|
||||
|
||||
if (!c->ops || !c->ops->enable) {
|
||||
c->refcnt++;
|
||||
c->set = true;
|
||||
if (c->parent)
|
||||
c->state = c->parent->state;
|
||||
else
|
||||
c->state = ON;
|
||||
}
|
||||
|
||||
mutex_lock(&clock_list_lock);
|
||||
list_add(&c->node, &clocks);
|
||||
mutex_unlock(&clock_list_lock);
|
||||
}
|
||||
|
||||
int clk_enable(struct clk *c)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&c->spinlock, flags);
|
||||
|
||||
if (c->refcnt == 0) {
|
||||
if (c->parent) {
|
||||
ret = clk_enable(c->parent);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (c->ops && c->ops->enable) {
|
||||
ret = c->ops->enable(c);
|
||||
if (ret) {
|
||||
if (c->parent)
|
||||
clk_disable(c->parent);
|
||||
goto out;
|
||||
}
|
||||
c->state = ON;
|
||||
c->set = true;
|
||||
}
|
||||
}
|
||||
c->refcnt++;
|
||||
out:
|
||||
spin_unlock_irqrestore(&c->spinlock, flags);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(clk_enable);
|
||||
|
||||
void clk_disable(struct clk *c)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&c->spinlock, flags);
|
||||
|
||||
if (c->refcnt == 0) {
|
||||
WARN(1, "Attempting to disable clock %s with refcnt 0", c->name);
|
||||
spin_unlock_irqrestore(&c->spinlock, flags);
|
||||
return;
|
||||
}
|
||||
if (c->refcnt == 1) {
|
||||
if (c->ops && c->ops->disable)
|
||||
c->ops->disable(c);
|
||||
|
||||
if (c->parent)
|
||||
clk_disable(c->parent);
|
||||
|
||||
c->state = OFF;
|
||||
}
|
||||
c->refcnt--;
|
||||
|
||||
spin_unlock_irqrestore(&c->spinlock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(clk_disable);
|
||||
|
||||
int clk_set_parent(struct clk *c, struct clk *parent)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
unsigned long new_rate;
|
||||
unsigned long old_rate;
|
||||
|
||||
spin_lock_irqsave(&c->spinlock, flags);
|
||||
|
||||
if (!c->ops || !c->ops->set_parent) {
|
||||
ret = -ENOSYS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
new_rate = clk_predict_rate_from_parent(c, parent);
|
||||
old_rate = clk_get_rate_locked(c);
|
||||
|
||||
ret = c->ops->set_parent(c, parent);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&c->spinlock, flags);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(clk_set_parent);
|
||||
|
||||
struct clk *clk_get_parent(struct clk *c)
|
||||
{
|
||||
return c->parent;
|
||||
}
|
||||
EXPORT_SYMBOL(clk_get_parent);
|
||||
|
||||
int clk_set_rate_locked(struct clk *c, unsigned long rate)
|
||||
{
|
||||
long new_rate;
|
||||
|
||||
if (!c->ops || !c->ops->set_rate)
|
||||
return -ENOSYS;
|
||||
|
||||
if (rate > c->max_rate)
|
||||
rate = c->max_rate;
|
||||
|
||||
if (c->ops && c->ops->round_rate) {
|
||||
new_rate = c->ops->round_rate(c, rate);
|
||||
|
||||
if (new_rate < 0)
|
||||
return new_rate;
|
||||
|
||||
rate = new_rate;
|
||||
}
|
||||
|
||||
return c->ops->set_rate(c, rate);
|
||||
}
|
||||
|
||||
int clk_set_rate(struct clk *c, unsigned long rate)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&c->spinlock, flags);
|
||||
|
||||
ret = clk_set_rate_locked(c, rate);
|
||||
|
||||
spin_unlock_irqrestore(&c->spinlock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(clk_set_rate);
|
||||
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
rate = c->rate;
|
||||
rate *= mul;
|
||||
do_div(rate, div);
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
long clk_round_rate(struct clk *c, unsigned long rate)
|
||||
{
|
||||
unsigned long flags;
|
||||
long ret;
|
||||
|
||||
spin_lock_irqsave(&c->spinlock, flags);
|
||||
|
||||
if (!c->ops || !c->ops->round_rate) {
|
||||
ret = -ENOSYS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (rate > c->max_rate)
|
||||
rate = c->max_rate;
|
||||
|
||||
ret = c->ops->round_rate(c, rate);
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&c->spinlock, flags);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(clk_round_rate);
|
||||
|
||||
static int tegra_clk_init_one_from_table(struct tegra_clk_init_table *table)
|
||||
{
|
||||
struct clk *c;
|
||||
struct clk *p;
|
||||
struct clk *parent;
|
||||
|
||||
int ret = 0;
|
||||
|
||||
c = tegra_get_clock_by_name(table->name);
|
||||
|
||||
if (!c) {
|
||||
pr_warning("Unable to initialize clock %s\n",
|
||||
pr_warn("Unable to initialize clock %s\n",
|
||||
table->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
parent = clk_get_parent(c);
|
||||
|
||||
if (table->parent) {
|
||||
p = tegra_get_clock_by_name(table->parent);
|
||||
if (!p) {
|
||||
pr_warning("Unable to find parent %s of clock %s\n",
|
||||
pr_warn("Unable to find parent %s of clock %s\n",
|
||||
table->parent, table->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (c->parent != p) {
|
||||
if (parent != p) {
|
||||
ret = clk_set_parent(c, p);
|
||||
if (ret) {
|
||||
pr_warning("Unable to set parent %s of clock %s: %d\n",
|
||||
pr_warn("Unable to set parent %s of clock %s: %d\n",
|
||||
table->parent, table->name, ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -360,16 +105,16 @@ static int tegra_clk_init_one_from_table(struct tegra_clk_init_table *table)
|
||||
if (table->rate && table->rate != clk_get_rate(c)) {
|
||||
ret = clk_set_rate(c, table->rate);
|
||||
if (ret) {
|
||||
pr_warning("Unable to set clock %s to rate %lu: %d\n",
|
||||
pr_warn("Unable to set clock %s to rate %lu: %d\n",
|
||||
table->name, table->rate, ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (table->enabled) {
|
||||
ret = clk_enable(c);
|
||||
ret = clk_prepare_enable(c);
|
||||
if (ret) {
|
||||
pr_warning("Unable to enable clock %s: %d\n",
|
||||
pr_warn("Unable to enable clock %s: %d\n",
|
||||
table->name, ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -383,19 +128,20 @@ void tegra_clk_init_from_table(struct tegra_clk_init_table *table)
|
||||
for (; table->name; table++)
|
||||
tegra_clk_init_one_from_table(table);
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_clk_init_from_table);
|
||||
|
||||
void tegra_periph_reset_deassert(struct clk *c)
|
||||
{
|
||||
BUG_ON(!c->ops->reset);
|
||||
c->ops->reset(c, false);
|
||||
struct clk_tegra *clk = to_clk_tegra(__clk_get_hw(c));
|
||||
BUG_ON(!clk->reset);
|
||||
clk->reset(__clk_get_hw(c), false);
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_periph_reset_deassert);
|
||||
|
||||
void tegra_periph_reset_assert(struct clk *c)
|
||||
{
|
||||
BUG_ON(!c->ops->reset);
|
||||
c->ops->reset(c, true);
|
||||
struct clk_tegra *clk = to_clk_tegra(__clk_get_hw(c));
|
||||
BUG_ON(!clk->reset);
|
||||
clk->reset(__clk_get_hw(c), true);
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_periph_reset_assert);
|
||||
|
||||
@ -405,268 +151,14 @@ EXPORT_SYMBOL(tegra_periph_reset_assert);
|
||||
int tegra_clk_cfg_ex(struct clk *c, enum tegra_clk_ex_param p, u32 setting)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
struct clk_tegra *clk = to_clk_tegra(__clk_get_hw(c));
|
||||
|
||||
spin_lock_irqsave(&c->spinlock, flags);
|
||||
|
||||
if (!c->ops || !c->ops->clk_cfg_ex) {
|
||||
if (!clk->clk_cfg_ex) {
|
||||
ret = -ENOSYS;
|
||||
goto out;
|
||||
}
|
||||
ret = c->ops->clk_cfg_ex(c, p, setting);
|
||||
ret = clk->clk_cfg_ex(__clk_get_hw(c), p, setting);
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&c->spinlock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#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 void clock_tree_show_one(struct seq_file *s, struct clk *c, int level)
|
||||
{
|
||||
struct clk *child;
|
||||
const char *state = "uninit";
|
||||
char div[8] = {0};
|
||||
|
||||
if (c->state == ON)
|
||||
state = "on";
|
||||
else if (c->state == OFF)
|
||||
state = "off";
|
||||
|
||||
if (c->mul != 0 && c->div != 0) {
|
||||
if (c->mul > c->div) {
|
||||
int mul = c->mul / c->div;
|
||||
int mul2 = (c->mul * 10 / c->div) % 10;
|
||||
int mul3 = (c->mul * 10) % c->div;
|
||||
if (mul2 == 0 && mul3 == 0)
|
||||
snprintf(div, sizeof(div), "x%d", mul);
|
||||
else if (mul3 == 0)
|
||||
snprintf(div, sizeof(div), "x%d.%d", mul, mul2);
|
||||
else
|
||||
snprintf(div, sizeof(div), "x%d.%d..", mul, mul2);
|
||||
} else {
|
||||
snprintf(div, sizeof(div), "%d%s", c->div / c->mul,
|
||||
(c->div % c->mul) ? ".5" : "");
|
||||
}
|
||||
}
|
||||
|
||||
seq_printf(s, "%*s%c%c%-*s %-6s %-3d %-8s %-10lu\n",
|
||||
level * 3 + 1, "",
|
||||
c->rate > c->max_rate ? '!' : ' ',
|
||||
!c->set ? '*' : ' ',
|
||||
30 - level * 3, c->name,
|
||||
state, c->refcnt, div, clk_get_rate_all_locked(c));
|
||||
|
||||
list_for_each_entry(child, &clocks, node) {
|
||||
if (child->parent != c)
|
||||
continue;
|
||||
|
||||
clock_tree_show_one(s, child, level + 1);
|
||||
}
|
||||
}
|
||||
|
||||
static int clock_tree_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct clk *c;
|
||||
seq_printf(s, " clock state ref div rate\n");
|
||||
seq_printf(s, "--------------------------------------------------------------\n");
|
||||
|
||||
mutex_lock(&clock_list_lock);
|
||||
|
||||
clk_lock_all();
|
||||
|
||||
list_for_each_entry(c, &clocks, node)
|
||||
if (c->parent == NULL)
|
||||
clock_tree_show_one(s, c, 0);
|
||||
|
||||
clk_unlock_all();
|
||||
|
||||
mutex_unlock(&clock_list_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clock_tree_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, clock_tree_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations clock_tree_fops = {
|
||||
.open = clock_tree_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int possible_parents_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct clk *c = s->private;
|
||||
int i;
|
||||
|
||||
for (i = 0; c->inputs[i].input; i++) {
|
||||
char *first = (i == 0) ? "" : " ";
|
||||
seq_printf(s, "%s%s", first, c->inputs[i].input->name);
|
||||
}
|
||||
seq_printf(s, "\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int possible_parents_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, possible_parents_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations possible_parents_fops = {
|
||||
.open = possible_parents_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int clk_debugfs_register_one(struct clk *c)
|
||||
{
|
||||
struct dentry *d;
|
||||
|
||||
d = debugfs_create_dir(c->name, clk_debugfs_root);
|
||||
if (!d)
|
||||
return -ENOMEM;
|
||||
c->dent = d;
|
||||
|
||||
d = debugfs_create_u8("refcnt", S_IRUGO, c->dent, (u8 *)&c->refcnt);
|
||||
if (!d)
|
||||
goto err_out;
|
||||
|
||||
d = debugfs_create_u32("rate", S_IRUGO, c->dent, (u32 *)&c->rate);
|
||||
if (!d)
|
||||
goto err_out;
|
||||
|
||||
d = debugfs_create_x32("flags", S_IRUGO, c->dent, (u32 *)&c->flags);
|
||||
if (!d)
|
||||
goto err_out;
|
||||
|
||||
if (c->inputs) {
|
||||
d = debugfs_create_file("possible_parents", S_IRUGO, c->dent,
|
||||
c, &possible_parents_fops);
|
||||
if (!d)
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
debugfs_remove_recursive(c->dent);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int clk_debugfs_register(struct clk *c)
|
||||
{
|
||||
int err;
|
||||
struct clk *pa = c->parent;
|
||||
|
||||
if (pa && !pa->dent) {
|
||||
err = clk_debugfs_register(pa);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!c->dent) {
|
||||
err = clk_debugfs_register_one(c);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __init tegra_clk_debugfs_init(void)
|
||||
{
|
||||
struct clk *c;
|
||||
struct dentry *d;
|
||||
int err = -ENOMEM;
|
||||
|
||||
d = debugfs_create_dir("clock", NULL);
|
||||
if (!d)
|
||||
return -ENOMEM;
|
||||
clk_debugfs_root = d;
|
||||
|
||||
d = debugfs_create_file("clock_tree", S_IRUGO, clk_debugfs_root, NULL,
|
||||
&clock_tree_fops);
|
||||
if (!d)
|
||||
goto err_out;
|
||||
|
||||
list_for_each_entry(c, &clocks, node) {
|
||||
err = clk_debugfs_register(c);
|
||||
if (err)
|
||||
goto err_out;
|
||||
}
|
||||
return 0;
|
||||
err_out:
|
||||
debugfs_remove_recursive(clk_debugfs_root);
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -2,6 +2,7 @@
|
||||
* arch/arm/mach-tegra/include/mach/clock.h
|
||||
*
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
* Copyright (c) 2012 NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Author:
|
||||
* Colin Cross <ccross@google.com>
|
||||
@ -20,9 +21,9 @@
|
||||
#ifndef __MACH_TEGRA_CLOCK_H
|
||||
#define __MACH_TEGRA_CLOCK_H
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include <mach/clk.h>
|
||||
|
||||
@ -52,7 +53,8 @@
|
||||
#define ENABLE_ON_INIT (1 << 28)
|
||||
#define PERIPH_ON_APB (1 << 29)
|
||||
|
||||
struct clk;
|
||||
struct clk_tegra;
|
||||
#define to_clk_tegra(_hw) container_of(_hw, struct clk_tegra, hw)
|
||||
|
||||
struct clk_mux_sel {
|
||||
struct clk *input;
|
||||
@ -68,47 +70,29 @@ struct clk_pll_freq_table {
|
||||
u8 cpcon;
|
||||
};
|
||||
|
||||
struct clk_ops {
|
||||
void (*init)(struct clk *);
|
||||
int (*enable)(struct clk *);
|
||||
void (*disable)(struct clk *);
|
||||
int (*set_parent)(struct clk *, struct clk *);
|
||||
int (*set_rate)(struct clk *, unsigned long);
|
||||
long (*round_rate)(struct clk *, unsigned long);
|
||||
void (*reset)(struct clk *, bool);
|
||||
int (*clk_cfg_ex)(struct clk *,
|
||||
enum tegra_clk_ex_param, u32);
|
||||
};
|
||||
|
||||
enum clk_state {
|
||||
UNINITIALIZED = 0,
|
||||
ON,
|
||||
OFF,
|
||||
};
|
||||
|
||||
struct clk {
|
||||
struct clk_tegra {
|
||||
/* 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 clk_lookup lookup;
|
||||
struct clk_hw hw;
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *dent;
|
||||
#endif
|
||||
bool set;
|
||||
struct clk_ops *ops;
|
||||
unsigned long rate;
|
||||
unsigned long fixed_rate;
|
||||
unsigned long max_rate;
|
||||
unsigned long min_rate;
|
||||
u32 flags;
|
||||
const char *name;
|
||||
|
||||
u32 refcnt;
|
||||
enum clk_state state;
|
||||
struct clk *parent;
|
||||
u32 div;
|
||||
u32 mul;
|
||||
|
||||
const struct clk_mux_sel *inputs;
|
||||
u32 reg;
|
||||
u32 reg_shift;
|
||||
|
||||
@ -144,7 +128,8 @@ struct clk {
|
||||
} shared_bus_user;
|
||||
} u;
|
||||
|
||||
spinlock_t spinlock;
|
||||
void (*reset)(struct clk_hw *, bool);
|
||||
int (*clk_cfg_ex)(struct clk_hw *, enum tegra_clk_ex_param, u32);
|
||||
};
|
||||
|
||||
struct clk_duplicate {
|
||||
@ -159,13 +144,10 @@ struct tegra_clk_init_table {
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
void tegra_clk_add(struct clk *c);
|
||||
void tegra2_init_clocks(void);
|
||||
void tegra30_init_clocks(void);
|
||||
void clk_init(struct clk *clk);
|
||||
struct clk *tegra_get_clock_by_name(const char *name);
|
||||
int clk_reparent(struct clk *c, struct clk *parent);
|
||||
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
|
||||
|
@ -152,6 +152,5 @@ void __init tegra30_init_early(void)
|
||||
|
||||
void __init tegra_init_late(void)
|
||||
{
|
||||
tegra_clk_debugfs_init();
|
||||
tegra_powergate_debugfs_init();
|
||||
}
|
||||
|
@ -49,6 +49,8 @@ static struct cpufreq_frequency_table freq_table[] = {
|
||||
#define NUM_CPUS 2
|
||||
|
||||
static struct clk *cpu_clk;
|
||||
static struct clk *pll_x_clk;
|
||||
static struct clk *pll_p_clk;
|
||||
static struct clk *emc_clk;
|
||||
|
||||
static unsigned long target_cpu_speed[NUM_CPUS];
|
||||
@ -71,6 +73,42 @@ static unsigned int tegra_getspeed(unsigned int cpu)
|
||||
return rate;
|
||||
}
|
||||
|
||||
static int tegra_cpu_clk_set_rate(unsigned long rate)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Take an extra reference to the main pll so it doesn't turn
|
||||
* off when we move the cpu off of it
|
||||
*/
|
||||
clk_prepare_enable(pll_x_clk);
|
||||
|
||||
ret = clk_set_parent(cpu_clk, pll_p_clk);
|
||||
if (ret) {
|
||||
pr_err("Failed to switch cpu to clock pll_p\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (rate == clk_get_rate(pll_p_clk))
|
||||
goto out;
|
||||
|
||||
ret = clk_set_rate(pll_x_clk, rate);
|
||||
if (ret) {
|
||||
pr_err("Failed to change pll_x to %lu\n", rate);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = clk_set_parent(cpu_clk, pll_x_clk);
|
||||
if (ret) {
|
||||
pr_err("Failed to switch cpu to clock pll_x\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
clk_disable_unprepare(pll_x_clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tegra_update_cpu_speed(unsigned long rate)
|
||||
{
|
||||
int ret = 0;
|
||||
@ -101,7 +139,7 @@ static int tegra_update_cpu_speed(unsigned long rate)
|
||||
freqs.old, freqs.new);
|
||||
#endif
|
||||
|
||||
ret = clk_set_rate(cpu_clk, freqs.new * 1000);
|
||||
ret = tegra_cpu_clk_set_rate(freqs.new * 1000);
|
||||
if (ret) {
|
||||
pr_err("cpu-tegra: Failed to set cpu frequency to %d kHz\n",
|
||||
freqs.new);
|
||||
@ -183,6 +221,14 @@ static int tegra_cpu_init(struct cpufreq_policy *policy)
|
||||
if (IS_ERR(cpu_clk))
|
||||
return PTR_ERR(cpu_clk);
|
||||
|
||||
pll_x_clk = clk_get_sys(NULL, "pll_x");
|
||||
if (IS_ERR(pll_x_clk))
|
||||
return PTR_ERR(pll_x_clk);
|
||||
|
||||
pll_p_clk = clk_get_sys(NULL, "pll_p");
|
||||
if (IS_ERR(pll_p_clk))
|
||||
return PTR_ERR(pll_p_clk);
|
||||
|
||||
emc_clk = clk_get_sys("cpu", "emc");
|
||||
if (IS_ERR(emc_clk)) {
|
||||
clk_put(cpu_clk);
|
||||
|
@ -34,7 +34,10 @@ enum tegra_clk_ex_param {
|
||||
void tegra_periph_reset_deassert(struct clk *c);
|
||||
void tegra_periph_reset_assert(struct clk *c);
|
||||
|
||||
#ifndef CONFIG_COMMON_CLK
|
||||
unsigned long clk_get_rate_all_locked(struct clk *c);
|
||||
#endif
|
||||
|
||||
void tegra2_sdmmc_tap_delay(struct clk *c, int delay);
|
||||
int tegra_clk_cfg_ex(struct clk *c, enum tegra_clk_ex_param p, u32 setting);
|
||||
|
||||
|
1555
arch/arm/mach-tegra/tegra20_clocks.c
Normal file
1555
arch/arm/mach-tegra/tegra20_clocks.c
Normal file
File diff suppressed because it is too large
Load Diff
42
arch/arm/mach-tegra/tegra20_clocks.h
Normal file
42
arch/arm/mach-tegra/tegra20_clocks.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __MACH_TEGRA20_CLOCK_H
|
||||
#define __MACH_TEGRA20_CLOCK_H
|
||||
|
||||
extern struct clk_ops tegra_clk_32k_ops;
|
||||
extern struct clk_ops tegra_pll_ops;
|
||||
extern struct clk_ops tegra_clk_m_ops;
|
||||
extern struct clk_ops tegra_pll_div_ops;
|
||||
extern struct clk_ops tegra_pllx_ops;
|
||||
extern struct clk_ops tegra_plle_ops;
|
||||
extern struct clk_ops tegra_clk_double_ops;
|
||||
extern struct clk_ops tegra_cdev_clk_ops;
|
||||
extern struct clk_ops tegra_audio_sync_clk_ops;
|
||||
extern struct clk_ops tegra_super_ops;
|
||||
extern struct clk_ops tegra_cpu_ops;
|
||||
extern struct clk_ops tegra_twd_ops;
|
||||
extern struct clk_ops tegra_cop_ops;
|
||||
extern struct clk_ops tegra_bus_ops;
|
||||
extern struct clk_ops tegra_blink_clk_ops;
|
||||
extern struct clk_ops tegra_emc_clk_ops;
|
||||
extern struct clk_ops tegra_periph_clk_ops;
|
||||
extern struct clk_ops tegra_clk_shared_bus_ops;
|
||||
|
||||
void tegra2_periph_clk_reset(struct clk_hw *hw, bool assert);
|
||||
void tegra2_cop_clk_reset(struct clk_hw *hw, bool assert);
|
||||
|
||||
#endif
|
1142
arch/arm/mach-tegra/tegra20_clocks_data.c
Normal file
1142
arch/arm/mach-tegra/tegra20_clocks_data.c
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
53
arch/arm/mach-tegra/tegra30_clocks.h
Normal file
53
arch/arm/mach-tegra/tegra30_clocks.h
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __MACH_TEGRA30_CLOCK_H
|
||||
#define __MACH_TEGRA30_CLOCK_H
|
||||
|
||||
extern struct clk_ops tegra30_clk_32k_ops;
|
||||
extern struct clk_ops tegra30_clk_m_ops;
|
||||
extern struct clk_ops tegra_clk_m_div_ops;
|
||||
extern struct clk_ops tegra_pll_ref_ops;
|
||||
extern struct clk_ops tegra30_pll_ops;
|
||||
extern struct clk_ops tegra30_pll_div_ops;
|
||||
extern struct clk_ops tegra_plld_ops;
|
||||
extern struct clk_ops tegra30_plle_ops;
|
||||
extern struct clk_ops tegra_cml_clk_ops;
|
||||
extern struct clk_ops tegra_pciex_clk_ops;
|
||||
extern struct clk_ops tegra_sync_source_ops;
|
||||
extern struct clk_ops tegra30_audio_sync_clk_ops;
|
||||
extern struct clk_ops tegra30_clk_double_ops;
|
||||
extern struct clk_ops tegra_clk_out_ops;
|
||||
extern struct clk_ops tegra30_super_ops;
|
||||
extern struct clk_ops tegra30_blink_clk_ops;
|
||||
extern struct clk_ops tegra30_twd_ops;
|
||||
extern struct clk_ops tegra30_periph_clk_ops;
|
||||
extern struct clk_ops tegra30_dsib_clk_ops;
|
||||
extern struct clk_ops tegra_nand_clk_ops;
|
||||
extern struct clk_ops tegra_vi_clk_ops;
|
||||
extern struct clk_ops tegra_dtv_clk_ops;
|
||||
extern struct clk_ops tegra_clk_shared_bus_ops;
|
||||
|
||||
int tegra30_plld_clk_cfg_ex(struct clk_hw *hw,
|
||||
enum tegra_clk_ex_param p, u32 setting);
|
||||
void tegra30_periph_clk_reset(struct clk_hw *hw, bool assert);
|
||||
int tegra30_vi_clk_cfg_ex(struct clk_hw *hw,
|
||||
enum tegra_clk_ex_param p, u32 setting);
|
||||
int tegra30_nand_clk_cfg_ex(struct clk_hw *hw,
|
||||
enum tegra_clk_ex_param p, u32 setting);
|
||||
int tegra30_dtv_clk_cfg_ex(struct clk_hw *hw,
|
||||
enum tegra_clk_ex_param p, u32 setting);
|
||||
#endif
|
1369
arch/arm/mach-tegra/tegra30_clocks_data.c
Normal file
1369
arch/arm/mach-tegra/tegra30_clocks_data.c
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user