ARM: tegra: clock: Add shared bus clock type
Some clocks may have multiple downstream users that need to request a higher clock rate. Shared bus clocks provide a unique shared_bus_user clock to each user. The frequency of the bus is set to the highest enabled shared_bus_user clock, with a minimum value set by the shared bus. Drivers can use clk_enable and clk_disable to enable or disable their requirement, and clk_set_rate to set the minimum rate. Acked-by: Olof Johansson <olof@lixom.net> Signed-off-by: Colin Cross <ccross@android.com>
This commit is contained in:
parent
89a5fb84da
commit
310992ca4b
|
@ -85,6 +85,7 @@ struct clk {
|
||||||
struct clk_ops *ops;
|
struct clk_ops *ops;
|
||||||
unsigned long rate;
|
unsigned long rate;
|
||||||
unsigned long max_rate;
|
unsigned long max_rate;
|
||||||
|
unsigned long min_rate;
|
||||||
u32 flags;
|
u32 flags;
|
||||||
const char *name;
|
const char *name;
|
||||||
|
|
||||||
|
@ -98,6 +99,8 @@ struct clk {
|
||||||
u32 reg;
|
u32 reg;
|
||||||
u32 reg_shift;
|
u32 reg_shift;
|
||||||
|
|
||||||
|
struct list_head shared_bus_list;
|
||||||
|
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
unsigned int clk_num;
|
unsigned int clk_num;
|
||||||
|
@ -120,6 +123,11 @@ struct clk {
|
||||||
struct clk *main;
|
struct clk *main;
|
||||||
struct clk *backup;
|
struct clk *backup;
|
||||||
} cpu;
|
} cpu;
|
||||||
|
struct {
|
||||||
|
struct list_head node;
|
||||||
|
bool enabled;
|
||||||
|
unsigned long rate;
|
||||||
|
} shared_bus_user;
|
||||||
} u;
|
} u;
|
||||||
|
|
||||||
spinlock_t spinlock;
|
spinlock_t spinlock;
|
||||||
|
|
|
@ -1188,6 +1188,110 @@ static struct clk_ops tegra_cdev_clk_ops = {
|
||||||
.disable = &tegra2_cdev_clk_disable,
|
.disable = &tegra2_cdev_clk_disable,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* shared bus ops */
|
||||||
|
/*
|
||||||
|
* Some clocks may have multiple downstream users that need to request a
|
||||||
|
* higher clock rate. Shared bus clocks provide a unique shared_bus_user
|
||||||
|
* clock to each user. The frequency of the bus is set to the highest
|
||||||
|
* enabled shared_bus_user clock, with a minimum value set by the
|
||||||
|
* shared bus.
|
||||||
|
*/
|
||||||
|
static int tegra_clk_shared_bus_update(struct clk *bus)
|
||||||
|
{
|
||||||
|
struct clk *c;
|
||||||
|
unsigned long rate = bus->min_rate;
|
||||||
|
|
||||||
|
list_for_each_entry(c, &bus->shared_bus_list, u.shared_bus_user.node)
|
||||||
|
if (c->u.shared_bus_user.enabled)
|
||||||
|
rate = max(c->u.shared_bus_user.rate, rate);
|
||||||
|
|
||||||
|
if (rate == clk_get_rate_locked(bus))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return clk_set_rate_locked(bus, rate);
|
||||||
|
};
|
||||||
|
|
||||||
|
static void tegra_clk_shared_bus_init(struct clk *c)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
c->max_rate = c->parent->max_rate;
|
||||||
|
c->u.shared_bus_user.rate = c->parent->max_rate;
|
||||||
|
c->state = OFF;
|
||||||
|
#ifdef CONFIG_DEBUG_FS
|
||||||
|
c->set = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
spin_lock_irqsave(&c->parent->spinlock, flags);
|
||||||
|
|
||||||
|
list_add_tail(&c->u.shared_bus_user.node,
|
||||||
|
&c->parent->shared_bus_list);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&c->parent->spinlock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra_clk_shared_bus_set_rate(struct clk *c, unsigned long rate)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
rate = clk_round_rate(c->parent, rate);
|
||||||
|
if (rate < 0)
|
||||||
|
return rate;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&c->parent->spinlock, flags);
|
||||||
|
|
||||||
|
c->u.shared_bus_user.rate = rate;
|
||||||
|
ret = tegra_clk_shared_bus_update(c->parent);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&c->parent->spinlock, flags);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long tegra_clk_shared_bus_round_rate(struct clk *c, unsigned long rate)
|
||||||
|
{
|
||||||
|
return clk_round_rate(c->parent, rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra_clk_shared_bus_enable(struct clk *c)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&c->parent->spinlock, flags);
|
||||||
|
|
||||||
|
c->u.shared_bus_user.enabled = true;
|
||||||
|
ret = tegra_clk_shared_bus_update(c->parent);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&c->parent->spinlock, flags);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tegra_clk_shared_bus_disable(struct clk *c)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&c->parent->spinlock, flags);
|
||||||
|
|
||||||
|
c->u.shared_bus_user.enabled = false;
|
||||||
|
ret = tegra_clk_shared_bus_update(c->parent);
|
||||||
|
WARN_ON_ONCE(ret);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&c->parent->spinlock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct clk_ops tegra_clk_shared_bus_ops = {
|
||||||
|
.init = tegra_clk_shared_bus_init,
|
||||||
|
.enable = tegra_clk_shared_bus_enable,
|
||||||
|
.disable = tegra_clk_shared_bus_disable,
|
||||||
|
.set_rate = tegra_clk_shared_bus_set_rate,
|
||||||
|
.round_rate = tegra_clk_shared_bus_round_rate,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/* Clock definitions */
|
/* Clock definitions */
|
||||||
static struct clk tegra_clk_32k = {
|
static struct clk tegra_clk_32k = {
|
||||||
.name = "clk_32k",
|
.name = "clk_32k",
|
||||||
|
@ -1863,6 +1967,17 @@ static struct clk_mux_sel mux_pclk[] = {
|
||||||
}, \
|
}, \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define SHARED_CLK(_name, _dev, _con, _parent) \
|
||||||
|
{ \
|
||||||
|
.name = _name, \
|
||||||
|
.lookup = { \
|
||||||
|
.dev_id = _dev, \
|
||||||
|
.con_id = _con, \
|
||||||
|
}, \
|
||||||
|
.ops = &tegra_clk_shared_bus_ops, \
|
||||||
|
.parent = _parent, \
|
||||||
|
}
|
||||||
|
|
||||||
struct clk tegra_list_clks[] = {
|
struct clk tegra_list_clks[] = {
|
||||||
PERIPH_CLK("apbdma", "tegra-dma", NULL, 34, 0, 108000000, mux_pclk, 0),
|
PERIPH_CLK("apbdma", "tegra-dma", NULL, 34, 0, 108000000, mux_pclk, 0),
|
||||||
PERIPH_CLK("rtc", "rtc-tegra", NULL, 4, 0, 32768, mux_clk_32k, PERIPH_NO_RESET),
|
PERIPH_CLK("rtc", "rtc-tegra", NULL, 4, 0, 32768, mux_clk_32k, PERIPH_NO_RESET),
|
||||||
|
@ -2007,6 +2122,7 @@ struct clk *tegra_ptr_clks[] = {
|
||||||
static void tegra2_init_one_clock(struct clk *c)
|
static void tegra2_init_one_clock(struct clk *c)
|
||||||
{
|
{
|
||||||
clk_init(c);
|
clk_init(c);
|
||||||
|
INIT_LIST_HEAD(&c->shared_bus_list);
|
||||||
if (!c->lookup.dev_id && !c->lookup.con_id)
|
if (!c->lookup.dev_id && !c->lookup.con_id)
|
||||||
c->lookup.con_id = c->name;
|
c->lookup.con_id = c->name;
|
||||||
c->lookup.clk = c;
|
c->lookup.clk = c;
|
||||||
|
|
Loading…
Reference in New Issue