@@ -826,6 +826,26 @@ static u32 _omap2_clksel_get_src_field(struct clk *src_clk, struct clk *clk,
return clkr->div;
}
+long omap2_clk_round_rate_parent(struct clk *clk, struct clk *new_parent)
+{
+ u32 field_val, parent_div;
+ long rate;
+
+ if (!clk->clksel || !new_parent)
+ return -EINVAL;
+
+ parent_div = _omap2_clksel_get_src_field(new_parent, clk, &field_val);
+ if (!parent_div)
+ return -EINVAL;
+
+ /* CLKSEL clocks follow their parents' rates, divided by a divisor */
+ rate = new_parent->rate;
+ if (parent_div > 0)
+ rate /= parent_div;
+
+ return rate;
+}
+
int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent)
{
u32 field_val, v, parent_div;
@@ -41,6 +41,7 @@ int omap2_clk_register(struct clk *clk);
int omap2_clk_enable(struct clk *clk);
void omap2_clk_disable(struct clk *clk);
long omap2_clk_round_rate(struct clk *clk, unsigned long rate);
+long omap2_clk_round_rate_parent(struct clk *clk, struct clk *parent);
int omap2_clk_set_rate(struct clk *clk, unsigned long rate);
int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent);
int omap2_dpll_set_rate_tolerance(struct clk *clk, unsigned int tolerance);
@@ -448,6 +448,7 @@ static struct clk_functions omap2_clk_functions = {
.clk_enable = omap2_clk_enable,
.clk_disable = omap2_clk_disable,
.clk_round_rate = omap2_clk_round_rate,
+ .clk_round_rate_parent = omap2_clk_round_rate_parent,
.clk_set_rate = omap2_clk_set_rate,
.clk_set_parent = omap2_clk_set_parent,
.clk_get_parent = omap2_clk_get_parent,
@@ -725,6 +725,7 @@ static struct clk_functions omap2_clk_functions = {
.clk_enable = omap2_clk_enable,
.clk_disable = omap2_clk_disable,
.clk_round_rate = omap2_clk_round_rate,
+ .clk_round_rate_parent = omap2_clk_round_rate_parent,
.clk_set_rate = omap2_clk_set_rate,
.clk_set_parent = omap2_clk_set_parent,
.clk_get_parent = omap2_clk_get_parent,
@@ -173,6 +173,61 @@ void omap_clk_del_child(struct clk *clk, struct clk *clk2)
}
}
+/**
+ * omap_clk_notify - call clk notifier chain
+ * @clk: struct clk * that is changing rate
+ * @msg: clk notifier type (i.e., CLK_POST_RATE_CHANGE; see mach/clock.h)
+ * @old_rate: old rate
+ * @new_rate: new rate
+ *
+ * Triggers a notifier call chain on the post-clk-rate-change notifier
+ * for clock 'clk'. Passes a pointer to the struct clk and the
+ * previous and current rates to the notifier callback. Intended to be
+ * called by internal clock code only. No return value.
+ */
+static void omap_clk_notify(struct clk *clk, unsigned long msg,
+ unsigned long old_rate, unsigned long new_rate)
+{
+ struct clk_notifier *cn;
+ struct clk_notifier_data cnd;
+
+ cnd.clk = clk;
+ cnd.old_rate = old_rate;
+ cnd.new_rate = new_rate;
+
+ list_for_each_entry(cn, &clk_notifier_list, node) {
+ if (cn->clk == clk) {
+ blocking_notifier_call_chain(&cn->notifier_head, msg,
+ &cnd);
+ break;
+ }
+ }
+}
+
+/**
+ * omap_clk_notify_downstream - trigger clock change notifications
+ * @clk: struct clk * to start the notifications with
+ * @msg: notifier msg - see "Clk notifier callback types"
+ * @param2: (not used - any u8 will do)
+ *
+ * Call clock change notifiers on clocks starting with @clk and including
+ * all of @clk's downstream children clocks. Returns NOTIFY_DONE.
+ */
+static int omap_clk_notify_downstream(struct clk *clk, unsigned long msg,
+ u8 param2)
+{
+ if (!clk->notifier_count)
+ return NOTIFY_DONE;
+
+ omap_clk_notify(clk, msg, clk->rate, clk->temp_rate);
+
+ if (!omap_clk_has_children(clk))
+ return NOTIFY_DONE;
+
+ return omap_clk_for_each_child(clk, msg, 0, omap_clk_notify_downstream);
+}
+
+
/*-------------------------------------------------------------------------
* Standard clock functions defined in include/linux/clk.h
*-------------------------------------------------------------------------*/
@@ -309,10 +364,20 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
{
unsigned long flags;
int ret = -EINVAL;
+ int msg;
if (clk == NULL || IS_ERR(clk))
return ret;
+ mutex_lock(&clocks_mutex);
+
+ if (clk->notifier_count) {
+ clk->temp_rate = rate;
+ propagate_rate(clk, TEMP_RATE);
+
+ omap_clk_notify_downstream(clk, CLK_PRE_RATE_CHANGE, 0);
+ }
+
spin_lock_irqsave(&clockfw_lock, flags);
if (arch_clock->clk_set_rate) {
@@ -324,6 +389,12 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
spin_unlock_irqrestore(&clockfw_lock, flags);
+ msg = (ret) ? CLK_ABORT_RATE_CHANGE : CLK_POST_RATE_CHANGE;
+
+ omap_clk_notify_downstream(clk, msg, 0);
+
+ mutex_unlock(&clocks_mutex);
+
return ret;
}
EXPORT_SYMBOL(clk_set_rate);
@@ -333,10 +404,20 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
unsigned long flags;
struct clk *prev_parent;
int ret = -EINVAL;
+ int msg;
if (clk == NULL || IS_ERR(clk) || parent == NULL || IS_ERR(parent))
return ret;
+ mutex_lock(&clocks_mutex);
+
+ if (clk->notifier_count && arch_clock->clk_round_rate_parent) {
+ clk->temp_rate = arch_clock->clk_round_rate_parent(clk, parent);
+ propagate_rate(clk, TEMP_RATE);
+
+ omap_clk_notify_downstream(clk, CLK_PRE_RATE_CHANGE, 0);
+ }
+
spin_lock_irqsave(&clockfw_lock, flags);
if (arch_clock->clk_set_parent) {
@@ -352,6 +433,12 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
spin_unlock_irqrestore(&clockfw_lock, flags);
+ msg = (ret) ? CLK_ABORT_RATE_CHANGE : CLK_POST_RATE_CHANGE;
+
+ omap_clk_notify_downstream(clk, msg, 0);
+
+ mutex_unlock(&clocks_mutex);
+
return ret;
}
EXPORT_SYMBOL(clk_set_parent);
@@ -538,6 +625,8 @@ void clk_init_cpufreq_table(struct cpufreq_frequency_table **table)
EXPORT_SYMBOL(clk_init_cpufreq_table);
#endif
+/* Clk notifier implementations */
+
/**
* clk_notifier_register - add a clock parameter change notifier
* @clk: struct clk * to watch
@@ -158,6 +158,8 @@ struct clk_functions {
int (*clk_enable)(struct clk *clk);
void (*clk_disable)(struct clk *clk);
long (*clk_round_rate)(struct clk *clk, unsigned long rate);
+ long (*clk_round_rate_parent)(struct clk *clk,
+ struct clk *parent);
int (*clk_set_rate)(struct clk *clk, unsigned long rate);
int (*clk_set_parent)(struct clk *clk, struct clk *parent);
struct clk * (*clk_get_parent)(struct clk *clk);