Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
n9: fix for CMT not working
CMT would fail with WAKLINES TEST FAILED error. The underlying cause
was usage of function irq_to_gpio that returned wrong GPIO number
for a given IRQ number (it returned 87 instead of 151 for cawake IRQ).
Subsequently ssi_wakein function would always inspected wrong GPIO pin
and always retuned "high" causing ssi_wake_tasklet to misbehave.
To avoid this ssi_resources array now contains GPIO number (not only
IRQ) and omap_ssi stores that GPIO number in omap_ssi_port struct as
wake_gpio alongside IRQ number (stored in wake_irq). This removes the
need for irq_to_gpio function.

Signed-off-by: Filip Matijević <filip.matijevic.pz@gmail.com>
  • Loading branch information
filippz committed Jun 8, 2014
1 parent eeb2723 commit 1ac56a2
Show file tree
Hide file tree
Showing 4 changed files with 294 additions and 30 deletions.
1 change: 1 addition & 0 deletions arch/arm/mach-omap2/ssi.c
Expand Up @@ -125,6 +125,7 @@ int __init omap_ssi_config(struct omap_ssi_board_config *ssi_config)
}
gpio_direction_input(cawake_gpio);
ssi_resources[offset].start = gpio_to_irq(cawake_gpio);
ssi_resources[offset].end = cawake_gpio; //HACK: store gpio number so we can use it instead of irq_to_gpio function
ssi_resources[offset].flags &= ~IORESOURCE_UNSET;
ssi_resources[offset].flags |= IORESOURCE_IRQ_HIGHEDGE |
IORESOURCE_IRQ_LOWEDGE;
Expand Down
230 changes: 222 additions & 8 deletions arch/arm/plat-omap/clock.c
Expand Up @@ -21,6 +21,7 @@
#include <linux/mutex.h>
#include <linux/cpufreq.h>
#include <linux/io.h>
#include <linux/slab.h>

#include <plat/clock.h>

Expand All @@ -29,6 +30,78 @@ static DEFINE_MUTEX(clocks_mutex);
static DEFINE_SPINLOCK(clockfw_lock);

static struct clk_functions *arch_clock;
static LIST_HEAD(clk_notifier_list);

/*
* _clk_free_notifier_chain - safely remove struct clk_notifier
* @cn: struct clk_notifier *
*
* Removes the struct clk_notifier @cn from the clk_notifier_list and
* frees it.
*/
static void _clk_free_notifier_chain(struct clk_notifier *cn)
{
list_del(&cn->node);
kfree(cn);
}

/*
* 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)
{
struct clk_notifier *cn;
struct clk_notifier_data cnd;

cnd.clk = clk;
cnd.rate = clk->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" in mach/clock.h
*
* 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)
{
struct clk *child;
int ret;

if (!clk->notifier_count)
return NOTIFY_DONE;

omap_clk_notify(clk, msg);

if (list_empty(&clk->children))
return NOTIFY_DONE;

list_for_each_entry_reverse(child, &clk->children, sibling) {
ret = omap_clk_notify_downstream(child, msg);
if (ret)
break;
}
return ret;
}

/*
* Standard clock functions defined in include/linux/clk.h
Expand Down Expand Up @@ -121,19 +194,32 @@ 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;

if (!arch_clock || !arch_clock->clk_set_rate)
return ret;
mutex_lock(&clocks_mutex);

if (clk->notifier_count)
omap_clk_notify_downstream(clk, CLK_PRE_RATE_CHANGE);

spin_lock_irqsave(&clockfw_lock, flags);
ret = arch_clock->clk_set_rate(clk, rate);
if (ret == 0)
if (arch_clock->clk_set_rate)
ret = arch_clock->clk_set_rate(clk, rate);
if (ret == 0) {
if (clk->recalc)
clk->rate = clk->recalc(clk);
propagate_rate(clk);
}
spin_unlock_irqrestore(&clockfw_lock, flags);

msg = (ret) ? CLK_ABORT_RATE_CHANGE : CLK_POST_RATE_CHANGE;

omap_clk_notify_downstream(clk, msg);

mutex_unlock(&clocks_mutex);

return ret;
}
EXPORT_SYMBOL(clk_set_rate);
Expand All @@ -142,22 +228,35 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
{
unsigned long flags;
int ret = -EINVAL;
int msg;

if (clk == NULL || IS_ERR(clk) || parent == NULL || IS_ERR(parent))
return ret;

if (!arch_clock || !arch_clock->clk_set_parent)
return ret;
mutex_lock(&clocks_mutex);

if (clk->notifier_count)
omap_clk_notify_downstream(clk, CLK_PRE_RATE_CHANGE);

spin_lock_irqsave(&clockfw_lock, flags);
if (clk->usecount == 0) {
ret = arch_clock->clk_set_parent(clk, parent);
if (ret == 0)
if (arch_clock->clk_set_parent)
ret = arch_clock->clk_set_parent(clk, parent);
if (ret == 0) {
if (clk->recalc)
clk->rate = clk->recalc(clk);
propagate_rate(clk);
}
} else
ret = -EBUSY;
spin_unlock_irqrestore(&clockfw_lock, flags);

msg = (ret) ? CLK_ABORT_RATE_CHANGE : CLK_POST_RATE_CHANGE;

omap_clk_notify_downstream(clk, msg);

mutex_unlock(&clocks_mutex);

return ret;
}
EXPORT_SYMBOL(clk_set_parent);
Expand Down Expand Up @@ -402,6 +501,121 @@ struct clk dummy_ck = {
*
*/

/* Clk notifier implementation */

/*
* clk_notifier_register - add a clock parameter change notifier
* @clk: struct clk * to watch
* @nb: struct notifier_block * with callback info
*
* Request notification for changes to the clock 'clk'. This uses a
* blocking notifier. Callback code must not call into the clock
* framework, as clocks_mutex is held. Pre-notifier callbacks will be
* passed the previous and new rate of the clock.
*
* clk_notifier_register() must be called from process
* context. Returns -EINVAL if called with null arguments, -ENOMEM
* upon allocation failure; otherwise, passes along the return value
* of blocking_notifier_chain_register().
*/
int clk_notifier_register(struct clk *clk, struct notifier_block *nb)
{
struct clk_notifier *cn = NULL, *cn_new = NULL;
int r;
struct clk *clkp;

if (!clk || !nb)
return -EINVAL;

mutex_lock(&clocks_mutex);

list_for_each_entry(cn, &clk_notifier_list, node)
if (cn->clk == clk)
break;

if (cn->clk != clk) {
cn_new = kzalloc(sizeof(struct clk_notifier), GFP_KERNEL);
if (!cn_new) {
r = -ENOMEM;
goto cnr_out;
};

cn_new->clk = clk;
BLOCKING_INIT_NOTIFIER_HEAD(&cn_new->notifier_head);

list_add(&cn_new->node, &clk_notifier_list);
cn = cn_new;
}

r = blocking_notifier_chain_register(&cn->notifier_head, nb);
if (!IS_ERR_VALUE(r)) {
clkp = clk;
do {
clkp->notifier_count++;
} while ((clkp = clkp->parent));
} else {
if (cn_new)
_clk_free_notifier_chain(cn);
}

cnr_out:
mutex_unlock(&clocks_mutex);

return r;
}
EXPORT_SYMBOL(clk_notifier_register);

/*
* clk_notifier_unregister - remove a clock change notifier
* @clk: struct clk *
* @nb: struct notifier_block * with callback info
*
* Request no further notification for changes to clock 'clk'.
* Returns -EINVAL if called with null arguments; otherwise, passes
* along the return value of blocking_notifier_chain_unregister().
*/
int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb)
{
struct clk_notifier *cn = NULL;
struct clk *clkp;
int r = -EINVAL;

if (!clk || !nb)
return -EINVAL;

mutex_lock(&clocks_mutex);

list_for_each_entry(cn, &clk_notifier_list, node)
if (cn->clk == clk)
break;

if (cn->clk != clk) {
r = -ENOENT;
goto cnu_out;
};

r = blocking_notifier_chain_unregister(&cn->notifier_head, nb);
if (!IS_ERR_VALUE(r)) {
clkp = clk;
do {
clkp->notifier_count--;
} while ((clkp = clkp->parent));
}

/*
* XXX ugh, layering violation. There should be some
* support in the notifier code for this.
*/
if (!cn->notifier_head.head)
_clk_free_notifier_chain(cn);

cnu_out:
mutex_unlock(&clocks_mutex);

return r;
}
EXPORT_SYMBOL(clk_notifier_unregister);

#ifdef CONFIG_OMAP_RESET_CLOCKS
/*
* Disable any unused clocks left on by the bootloader
Expand Down
62 changes: 62 additions & 0 deletions arch/arm/plat-omap/include/plat/clock.h
Expand Up @@ -14,6 +14,7 @@
#define __ARCH_ARM_OMAP_CLOCK_H

#include <linux/list.h>
#include <linux/notifier.h>

struct module;
struct clk;
Expand Down Expand Up @@ -192,6 +193,37 @@ struct dpll_data {
#define INVERT_ENABLE (1 << 4) /* 0 enables, 1 disables */
#define CLOCK_CLKOUTX2 (1 << 5)

/*
* struct clk_notifier - associate a clk with a notifier
* @clk: struct clk * to associate the notifier with
* @notifier_head: a blocking_notifier_head for this clk
* @node: linked list pointers
*
* A list of struct clk_notifier is maintained by the notifier code.
* An entry is created whenever code registers the first notifier on a
* particular @clk. Future notifiers on that @clk are added to the
* @notifier_head.
*/
struct clk_notifier {
struct clk *clk;
struct blocking_notifier_head notifier_head;
struct list_head node;
};

/*
* struct clk_notifier_data - rate data to pass to the notifier callback
* @clk: struct clk * being changed
* @rate: current rate of this clock
*
* This struct is passed as parameter to the clock notifier callbacks when
* a clock is changed. Current rate of the clock is passed along with the
* call in pre-notifier, and the new rate in post-notifier.
*/
struct clk_notifier_data {
struct clk *clk;
unsigned long rate;
};

/**
* struct clk - OMAP struct clk
* @node: list_head connecting this clock into the full clock list
Expand Down Expand Up @@ -252,6 +284,7 @@ struct clk {
int (*set_rate)(struct clk *, unsigned long);
long (*round_rate)(struct clk *, unsigned long);
void (*init)(struct clk *);
u16 notifier_count;
u8 enable_bit;
s8 usecount;
u8 fixed_div;
Expand Down Expand Up @@ -294,6 +327,8 @@ extern void propagate_rate(struct clk *clk);
extern void recalculate_root_clocks(void);
extern unsigned long followparent_recalc(struct clk *clk);
extern void clk_enable_init_clocks(void);
extern int clk_notifier_register(struct clk *clk, struct notifier_block *nb);
extern int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb);
unsigned long omap_fixed_divisor_recalc(struct clk *clk);
extern struct clk *omap_clk_get_by_name(const char *name);
extern int omap_clk_enable_autoidle_all(void);
Expand All @@ -303,4 +338,31 @@ extern const struct clkops clkops_null;

extern struct clk dummy_ck;

/*
* Clk notifier callback types
*
* Since the notifier is called with interrupts disabled, any actions
* taken by callbacks must be extremely fast and lightweight.
*
* CLK_PRE_RATE_CHANGE - called immediately before the clock rate is
* changed. Drivers must immediately terminate any operations that
* will be affected by the rate change. Callbacks must always
* return NOTIFY_DONE.
*
* CLK_ABORT_RATE_CHANGE: called if the rate change failed for some
* reason after CLK_PRE_RATE_CHANGE. In this case, all registered
* notifiers on the clock will be called with
* CLK_ABORT_RATE_CHANGE. Callbacks must always return
* NOTIFY_DONE.
*
* CLK_POST_RATE_CHANGE - called after the clock rate change has
* successfully completed. Callbacks must always return
* NOTIFY_DONE.
*
*/
#define CLK_PRE_RATE_CHANGE 1
#define CLK_ABORT_RATE_CHANGE 2
#define CLK_POST_RATE_CHANGE 3


#endif

0 comments on commit 1ac56a2

Please sign in to comment.