clk: at91: remove IRQ handling and use polling
The AT91 clock drivers make use of IRQs to avoid polling when waiting for some clocks to be enabled. Unfortunately, this leads to a crash when those IRQs are threaded (which happens when using preempt-rt) because they are registered before thread creation is possible. Use polling on those clocks instead to avoid the problem. Acked-by: Stephen Boyd <sboyd@codeaurora.org> Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
This commit is contained in:
parent
1bdf02326b
commit
99a8170652
@ -13,15 +13,8 @@
|
|||||||
#include <linux/clk/at91_pmc.h>
|
#include <linux/clk/at91_pmc.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_address.h>
|
|
||||||
#include <linux/of_irq.h>
|
|
||||||
#include <linux/io.h>
|
|
||||||
#include <linux/interrupt.h>
|
|
||||||
#include <linux/irq.h>
|
|
||||||
#include <linux/mfd/syscon.h>
|
#include <linux/mfd/syscon.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/wait.h>
|
|
||||||
|
|
||||||
#include "pmc.h"
|
#include "pmc.h"
|
||||||
|
|
||||||
@ -37,8 +30,6 @@
|
|||||||
struct clk_main_osc {
|
struct clk_main_osc {
|
||||||
struct clk_hw hw;
|
struct clk_hw hw;
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
unsigned int irq;
|
|
||||||
wait_queue_head_t wait;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define to_clk_main_osc(hw) container_of(hw, struct clk_main_osc, hw)
|
#define to_clk_main_osc(hw) container_of(hw, struct clk_main_osc, hw)
|
||||||
@ -46,8 +37,6 @@ struct clk_main_osc {
|
|||||||
struct clk_main_rc_osc {
|
struct clk_main_rc_osc {
|
||||||
struct clk_hw hw;
|
struct clk_hw hw;
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
unsigned int irq;
|
|
||||||
wait_queue_head_t wait;
|
|
||||||
unsigned long frequency;
|
unsigned long frequency;
|
||||||
unsigned long accuracy;
|
unsigned long accuracy;
|
||||||
};
|
};
|
||||||
@ -64,23 +53,11 @@ struct clk_rm9200_main {
|
|||||||
struct clk_sam9x5_main {
|
struct clk_sam9x5_main {
|
||||||
struct clk_hw hw;
|
struct clk_hw hw;
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
unsigned int irq;
|
|
||||||
wait_queue_head_t wait;
|
|
||||||
u8 parent;
|
u8 parent;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define to_clk_sam9x5_main(hw) container_of(hw, struct clk_sam9x5_main, hw)
|
#define to_clk_sam9x5_main(hw) container_of(hw, struct clk_sam9x5_main, hw)
|
||||||
|
|
||||||
static irqreturn_t clk_main_osc_irq_handler(int irq, void *dev_id)
|
|
||||||
{
|
|
||||||
struct clk_main_osc *osc = dev_id;
|
|
||||||
|
|
||||||
wake_up(&osc->wait);
|
|
||||||
disable_irq_nosync(osc->irq);
|
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool clk_main_osc_ready(struct regmap *regmap)
|
static inline bool clk_main_osc_ready(struct regmap *regmap)
|
||||||
{
|
{
|
||||||
unsigned int status;
|
unsigned int status;
|
||||||
@ -107,11 +84,8 @@ static int clk_main_osc_prepare(struct clk_hw *hw)
|
|||||||
regmap_write(regmap, AT91_CKGR_MOR, tmp);
|
regmap_write(regmap, AT91_CKGR_MOR, tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!clk_main_osc_ready(regmap)) {
|
while (!clk_main_osc_ready(regmap))
|
||||||
enable_irq(osc->irq);
|
cpu_relax();
|
||||||
wait_event(osc->wait,
|
|
||||||
clk_main_osc_ready(regmap));
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -156,17 +130,15 @@ static const struct clk_ops main_osc_ops = {
|
|||||||
|
|
||||||
static struct clk * __init
|
static struct clk * __init
|
||||||
at91_clk_register_main_osc(struct regmap *regmap,
|
at91_clk_register_main_osc(struct regmap *regmap,
|
||||||
unsigned int irq,
|
|
||||||
const char *name,
|
const char *name,
|
||||||
const char *parent_name,
|
const char *parent_name,
|
||||||
bool bypass)
|
bool bypass)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
struct clk_main_osc *osc;
|
struct clk_main_osc *osc;
|
||||||
struct clk *clk = NULL;
|
struct clk *clk = NULL;
|
||||||
struct clk_init_data init;
|
struct clk_init_data init;
|
||||||
|
|
||||||
if (!irq || !name || !parent_name)
|
if (!name || !parent_name)
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
|
|
||||||
osc = kzalloc(sizeof(*osc), GFP_KERNEL);
|
osc = kzalloc(sizeof(*osc), GFP_KERNEL);
|
||||||
@ -181,16 +153,6 @@ at91_clk_register_main_osc(struct regmap *regmap,
|
|||||||
|
|
||||||
osc->hw.init = &init;
|
osc->hw.init = &init;
|
||||||
osc->regmap = regmap;
|
osc->regmap = regmap;
|
||||||
osc->irq = irq;
|
|
||||||
|
|
||||||
init_waitqueue_head(&osc->wait);
|
|
||||||
irq_set_status_flags(osc->irq, IRQ_NOAUTOEN);
|
|
||||||
ret = request_irq(osc->irq, clk_main_osc_irq_handler,
|
|
||||||
IRQF_TRIGGER_HIGH, name, osc);
|
|
||||||
if (ret) {
|
|
||||||
kfree(osc);
|
|
||||||
return ERR_PTR(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bypass)
|
if (bypass)
|
||||||
regmap_update_bits(regmap,
|
regmap_update_bits(regmap,
|
||||||
@ -199,10 +161,8 @@ at91_clk_register_main_osc(struct regmap *regmap,
|
|||||||
AT91_PMC_OSCBYPASS | AT91_PMC_KEY);
|
AT91_PMC_OSCBYPASS | AT91_PMC_KEY);
|
||||||
|
|
||||||
clk = clk_register(NULL, &osc->hw);
|
clk = clk_register(NULL, &osc->hw);
|
||||||
if (IS_ERR(clk)) {
|
if (IS_ERR(clk))
|
||||||
free_irq(irq, osc);
|
|
||||||
kfree(osc);
|
kfree(osc);
|
||||||
}
|
|
||||||
|
|
||||||
return clk;
|
return clk;
|
||||||
}
|
}
|
||||||
@ -210,7 +170,6 @@ at91_clk_register_main_osc(struct regmap *regmap,
|
|||||||
static void __init of_at91rm9200_clk_main_osc_setup(struct device_node *np)
|
static void __init of_at91rm9200_clk_main_osc_setup(struct device_node *np)
|
||||||
{
|
{
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
unsigned int irq;
|
|
||||||
const char *name = np->name;
|
const char *name = np->name;
|
||||||
const char *parent_name;
|
const char *parent_name;
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
@ -224,11 +183,7 @@ static void __init of_at91rm9200_clk_main_osc_setup(struct device_node *np)
|
|||||||
if (IS_ERR(regmap))
|
if (IS_ERR(regmap))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
irq = irq_of_parse_and_map(np, 0);
|
clk = at91_clk_register_main_osc(regmap, name, parent_name, bypass);
|
||||||
if (!irq)
|
|
||||||
return;
|
|
||||||
|
|
||||||
clk = at91_clk_register_main_osc(regmap, irq, name, parent_name, bypass);
|
|
||||||
if (IS_ERR(clk))
|
if (IS_ERR(clk))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -237,16 +192,6 @@ static void __init of_at91rm9200_clk_main_osc_setup(struct device_node *np)
|
|||||||
CLK_OF_DECLARE(at91rm9200_clk_main_osc, "atmel,at91rm9200-clk-main-osc",
|
CLK_OF_DECLARE(at91rm9200_clk_main_osc, "atmel,at91rm9200-clk-main-osc",
|
||||||
of_at91rm9200_clk_main_osc_setup);
|
of_at91rm9200_clk_main_osc_setup);
|
||||||
|
|
||||||
static irqreturn_t clk_main_rc_osc_irq_handler(int irq, void *dev_id)
|
|
||||||
{
|
|
||||||
struct clk_main_rc_osc *osc = dev_id;
|
|
||||||
|
|
||||||
wake_up(&osc->wait);
|
|
||||||
disable_irq_nosync(osc->irq);
|
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool clk_main_rc_osc_ready(struct regmap *regmap)
|
static bool clk_main_rc_osc_ready(struct regmap *regmap)
|
||||||
{
|
{
|
||||||
unsigned int status;
|
unsigned int status;
|
||||||
@ -269,11 +214,8 @@ static int clk_main_rc_osc_prepare(struct clk_hw *hw)
|
|||||||
MOR_KEY_MASK | AT91_PMC_MOSCRCEN,
|
MOR_KEY_MASK | AT91_PMC_MOSCRCEN,
|
||||||
AT91_PMC_MOSCRCEN | AT91_PMC_KEY);
|
AT91_PMC_MOSCRCEN | AT91_PMC_KEY);
|
||||||
|
|
||||||
while (!clk_main_rc_osc_ready(regmap)) {
|
while (!clk_main_rc_osc_ready(regmap))
|
||||||
enable_irq(osc->irq);
|
cpu_relax();
|
||||||
wait_event(osc->wait,
|
|
||||||
clk_main_rc_osc_ready(regmap));
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -331,11 +273,9 @@ static const struct clk_ops main_rc_osc_ops = {
|
|||||||
|
|
||||||
static struct clk * __init
|
static struct clk * __init
|
||||||
at91_clk_register_main_rc_osc(struct regmap *regmap,
|
at91_clk_register_main_rc_osc(struct regmap *regmap,
|
||||||
unsigned int irq,
|
|
||||||
const char *name,
|
const char *name,
|
||||||
u32 frequency, u32 accuracy)
|
u32 frequency, u32 accuracy)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
struct clk_main_rc_osc *osc;
|
struct clk_main_rc_osc *osc;
|
||||||
struct clk *clk = NULL;
|
struct clk *clk = NULL;
|
||||||
struct clk_init_data init;
|
struct clk_init_data init;
|
||||||
@ -355,22 +295,12 @@ at91_clk_register_main_rc_osc(struct regmap *regmap,
|
|||||||
|
|
||||||
osc->hw.init = &init;
|
osc->hw.init = &init;
|
||||||
osc->regmap = regmap;
|
osc->regmap = regmap;
|
||||||
osc->irq = irq;
|
|
||||||
osc->frequency = frequency;
|
osc->frequency = frequency;
|
||||||
osc->accuracy = accuracy;
|
osc->accuracy = accuracy;
|
||||||
|
|
||||||
init_waitqueue_head(&osc->wait);
|
|
||||||
irq_set_status_flags(osc->irq, IRQ_NOAUTOEN);
|
|
||||||
ret = request_irq(osc->irq, clk_main_rc_osc_irq_handler,
|
|
||||||
IRQF_TRIGGER_HIGH, name, osc);
|
|
||||||
if (ret)
|
|
||||||
return ERR_PTR(ret);
|
|
||||||
|
|
||||||
clk = clk_register(NULL, &osc->hw);
|
clk = clk_register(NULL, &osc->hw);
|
||||||
if (IS_ERR(clk)) {
|
if (IS_ERR(clk))
|
||||||
free_irq(irq, osc);
|
|
||||||
kfree(osc);
|
kfree(osc);
|
||||||
}
|
|
||||||
|
|
||||||
return clk;
|
return clk;
|
||||||
}
|
}
|
||||||
@ -378,7 +308,6 @@ at91_clk_register_main_rc_osc(struct regmap *regmap,
|
|||||||
static void __init of_at91sam9x5_clk_main_rc_osc_setup(struct device_node *np)
|
static void __init of_at91sam9x5_clk_main_rc_osc_setup(struct device_node *np)
|
||||||
{
|
{
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
unsigned int irq;
|
|
||||||
u32 frequency = 0;
|
u32 frequency = 0;
|
||||||
u32 accuracy = 0;
|
u32 accuracy = 0;
|
||||||
const char *name = np->name;
|
const char *name = np->name;
|
||||||
@ -388,16 +317,11 @@ static void __init of_at91sam9x5_clk_main_rc_osc_setup(struct device_node *np)
|
|||||||
of_property_read_u32(np, "clock-frequency", &frequency);
|
of_property_read_u32(np, "clock-frequency", &frequency);
|
||||||
of_property_read_u32(np, "clock-accuracy", &accuracy);
|
of_property_read_u32(np, "clock-accuracy", &accuracy);
|
||||||
|
|
||||||
irq = irq_of_parse_and_map(np, 0);
|
|
||||||
if (!irq)
|
|
||||||
return;
|
|
||||||
|
|
||||||
regmap = syscon_node_to_regmap(of_get_parent(np));
|
regmap = syscon_node_to_regmap(of_get_parent(np));
|
||||||
if (IS_ERR(regmap))
|
if (IS_ERR(regmap))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
clk = at91_clk_register_main_rc_osc(regmap, irq, name, frequency,
|
clk = at91_clk_register_main_rc_osc(regmap, name, frequency, accuracy);
|
||||||
accuracy);
|
|
||||||
if (IS_ERR(clk))
|
if (IS_ERR(clk))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -529,16 +453,6 @@ static void __init of_at91rm9200_clk_main_setup(struct device_node *np)
|
|||||||
CLK_OF_DECLARE(at91rm9200_clk_main, "atmel,at91rm9200-clk-main",
|
CLK_OF_DECLARE(at91rm9200_clk_main, "atmel,at91rm9200-clk-main",
|
||||||
of_at91rm9200_clk_main_setup);
|
of_at91rm9200_clk_main_setup);
|
||||||
|
|
||||||
static irqreturn_t clk_sam9x5_main_irq_handler(int irq, void *dev_id)
|
|
||||||
{
|
|
||||||
struct clk_sam9x5_main *clkmain = dev_id;
|
|
||||||
|
|
||||||
wake_up(&clkmain->wait);
|
|
||||||
disable_irq_nosync(clkmain->irq);
|
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool clk_sam9x5_main_ready(struct regmap *regmap)
|
static inline bool clk_sam9x5_main_ready(struct regmap *regmap)
|
||||||
{
|
{
|
||||||
unsigned int status;
|
unsigned int status;
|
||||||
@ -553,11 +467,8 @@ static int clk_sam9x5_main_prepare(struct clk_hw *hw)
|
|||||||
struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw);
|
struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw);
|
||||||
struct regmap *regmap = clkmain->regmap;
|
struct regmap *regmap = clkmain->regmap;
|
||||||
|
|
||||||
while (!clk_sam9x5_main_ready(regmap)) {
|
while (!clk_sam9x5_main_ready(regmap))
|
||||||
enable_irq(clkmain->irq);
|
cpu_relax();
|
||||||
wait_event(clkmain->wait,
|
|
||||||
clk_sam9x5_main_ready(regmap));
|
|
||||||
}
|
|
||||||
|
|
||||||
return clk_main_probe_frequency(regmap);
|
return clk_main_probe_frequency(regmap);
|
||||||
}
|
}
|
||||||
@ -594,11 +505,8 @@ static int clk_sam9x5_main_set_parent(struct clk_hw *hw, u8 index)
|
|||||||
else if (!index && (tmp & AT91_PMC_MOSCSEL))
|
else if (!index && (tmp & AT91_PMC_MOSCSEL))
|
||||||
regmap_write(regmap, AT91_CKGR_MOR, tmp & ~AT91_PMC_MOSCSEL);
|
regmap_write(regmap, AT91_CKGR_MOR, tmp & ~AT91_PMC_MOSCSEL);
|
||||||
|
|
||||||
while (!clk_sam9x5_main_ready(regmap)) {
|
while (!clk_sam9x5_main_ready(regmap))
|
||||||
enable_irq(clkmain->irq);
|
cpu_relax();
|
||||||
wait_event(clkmain->wait,
|
|
||||||
clk_sam9x5_main_ready(regmap));
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -623,12 +531,10 @@ static const struct clk_ops sam9x5_main_ops = {
|
|||||||
|
|
||||||
static struct clk * __init
|
static struct clk * __init
|
||||||
at91_clk_register_sam9x5_main(struct regmap *regmap,
|
at91_clk_register_sam9x5_main(struct regmap *regmap,
|
||||||
unsigned int irq,
|
|
||||||
const char *name,
|
const char *name,
|
||||||
const char **parent_names,
|
const char **parent_names,
|
||||||
int num_parents)
|
int num_parents)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
struct clk_sam9x5_main *clkmain;
|
struct clk_sam9x5_main *clkmain;
|
||||||
struct clk *clk = NULL;
|
struct clk *clk = NULL;
|
||||||
struct clk_init_data init;
|
struct clk_init_data init;
|
||||||
@ -652,21 +558,12 @@ at91_clk_register_sam9x5_main(struct regmap *regmap,
|
|||||||
|
|
||||||
clkmain->hw.init = &init;
|
clkmain->hw.init = &init;
|
||||||
clkmain->regmap = regmap;
|
clkmain->regmap = regmap;
|
||||||
clkmain->irq = irq;
|
|
||||||
regmap_read(clkmain->regmap, AT91_CKGR_MOR, &status);
|
regmap_read(clkmain->regmap, AT91_CKGR_MOR, &status);
|
||||||
clkmain->parent = status & AT91_PMC_MOSCEN ? 1 : 0;
|
clkmain->parent = status & AT91_PMC_MOSCEN ? 1 : 0;
|
||||||
init_waitqueue_head(&clkmain->wait);
|
|
||||||
irq_set_status_flags(clkmain->irq, IRQ_NOAUTOEN);
|
|
||||||
ret = request_irq(clkmain->irq, clk_sam9x5_main_irq_handler,
|
|
||||||
IRQF_TRIGGER_HIGH, name, clkmain);
|
|
||||||
if (ret)
|
|
||||||
return ERR_PTR(ret);
|
|
||||||
|
|
||||||
clk = clk_register(NULL, &clkmain->hw);
|
clk = clk_register(NULL, &clkmain->hw);
|
||||||
if (IS_ERR(clk)) {
|
if (IS_ERR(clk))
|
||||||
free_irq(clkmain->irq, clkmain);
|
|
||||||
kfree(clkmain);
|
kfree(clkmain);
|
||||||
}
|
|
||||||
|
|
||||||
return clk;
|
return clk;
|
||||||
}
|
}
|
||||||
@ -676,7 +573,6 @@ static void __init of_at91sam9x5_clk_main_setup(struct device_node *np)
|
|||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
const char *parent_names[2];
|
const char *parent_names[2];
|
||||||
int num_parents;
|
int num_parents;
|
||||||
unsigned int irq;
|
|
||||||
const char *name = np->name;
|
const char *name = np->name;
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
|
|
||||||
@ -691,11 +587,7 @@ static void __init of_at91sam9x5_clk_main_setup(struct device_node *np)
|
|||||||
|
|
||||||
of_property_read_string(np, "clock-output-names", &name);
|
of_property_read_string(np, "clock-output-names", &name);
|
||||||
|
|
||||||
irq = irq_of_parse_and_map(np, 0);
|
clk = at91_clk_register_sam9x5_main(regmap, name, parent_names,
|
||||||
if (!irq)
|
|
||||||
return;
|
|
||||||
|
|
||||||
clk = at91_clk_register_sam9x5_main(regmap, irq, name, parent_names,
|
|
||||||
num_parents);
|
num_parents);
|
||||||
if (IS_ERR(clk))
|
if (IS_ERR(clk))
|
||||||
return;
|
return;
|
||||||
|
@ -12,13 +12,6 @@
|
|||||||
#include <linux/clkdev.h>
|
#include <linux/clkdev.h>
|
||||||
#include <linux/clk/at91_pmc.h>
|
#include <linux/clk/at91_pmc.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_address.h>
|
|
||||||
#include <linux/of_irq.h>
|
|
||||||
#include <linux/io.h>
|
|
||||||
#include <linux/wait.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/interrupt.h>
|
|
||||||
#include <linux/irq.h>
|
|
||||||
#include <linux/mfd/syscon.h>
|
#include <linux/mfd/syscon.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
@ -47,22 +40,10 @@ struct clk_master_layout {
|
|||||||
struct clk_master {
|
struct clk_master {
|
||||||
struct clk_hw hw;
|
struct clk_hw hw;
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
unsigned int irq;
|
|
||||||
wait_queue_head_t wait;
|
|
||||||
const struct clk_master_layout *layout;
|
const struct clk_master_layout *layout;
|
||||||
const struct clk_master_characteristics *characteristics;
|
const struct clk_master_characteristics *characteristics;
|
||||||
};
|
};
|
||||||
|
|
||||||
static irqreturn_t clk_master_irq_handler(int irq, void *dev_id)
|
|
||||||
{
|
|
||||||
struct clk_master *master = (struct clk_master *)dev_id;
|
|
||||||
|
|
||||||
wake_up(&master->wait);
|
|
||||||
disable_irq_nosync(master->irq);
|
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool clk_master_ready(struct regmap *regmap)
|
static inline bool clk_master_ready(struct regmap *regmap)
|
||||||
{
|
{
|
||||||
unsigned int status;
|
unsigned int status;
|
||||||
@ -76,11 +57,8 @@ static int clk_master_prepare(struct clk_hw *hw)
|
|||||||
{
|
{
|
||||||
struct clk_master *master = to_clk_master(hw);
|
struct clk_master *master = to_clk_master(hw);
|
||||||
|
|
||||||
while (!clk_master_ready(master->regmap)) {
|
while (!clk_master_ready(master->regmap))
|
||||||
enable_irq(master->irq);
|
cpu_relax();
|
||||||
wait_event(master->wait,
|
|
||||||
clk_master_ready(master->regmap));
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -143,13 +121,12 @@ static const struct clk_ops master_ops = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static struct clk * __init
|
static struct clk * __init
|
||||||
at91_clk_register_master(struct regmap *regmap, unsigned int irq,
|
at91_clk_register_master(struct regmap *regmap,
|
||||||
const char *name, int num_parents,
|
const char *name, int num_parents,
|
||||||
const char **parent_names,
|
const char **parent_names,
|
||||||
const struct clk_master_layout *layout,
|
const struct clk_master_layout *layout,
|
||||||
const struct clk_master_characteristics *characteristics)
|
const struct clk_master_characteristics *characteristics)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
struct clk_master *master;
|
struct clk_master *master;
|
||||||
struct clk *clk = NULL;
|
struct clk *clk = NULL;
|
||||||
struct clk_init_data init;
|
struct clk_init_data init;
|
||||||
@ -171,19 +148,9 @@ at91_clk_register_master(struct regmap *regmap, unsigned int irq,
|
|||||||
master->layout = layout;
|
master->layout = layout;
|
||||||
master->characteristics = characteristics;
|
master->characteristics = characteristics;
|
||||||
master->regmap = regmap;
|
master->regmap = regmap;
|
||||||
master->irq = irq;
|
|
||||||
init_waitqueue_head(&master->wait);
|
|
||||||
irq_set_status_flags(master->irq, IRQ_NOAUTOEN);
|
|
||||||
ret = request_irq(master->irq, clk_master_irq_handler,
|
|
||||||
IRQF_TRIGGER_HIGH, "clk-master", master);
|
|
||||||
if (ret) {
|
|
||||||
kfree(master);
|
|
||||||
return ERR_PTR(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
clk = clk_register(NULL, &master->hw);
|
clk = clk_register(NULL, &master->hw);
|
||||||
if (IS_ERR(clk)) {
|
if (IS_ERR(clk)) {
|
||||||
free_irq(master->irq, master);
|
|
||||||
kfree(master);
|
kfree(master);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,7 +200,6 @@ of_at91_clk_master_setup(struct device_node *np,
|
|||||||
{
|
{
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
int num_parents;
|
int num_parents;
|
||||||
unsigned int irq;
|
|
||||||
const char *parent_names[MASTER_SOURCE_MAX];
|
const char *parent_names[MASTER_SOURCE_MAX];
|
||||||
const char *name = np->name;
|
const char *name = np->name;
|
||||||
struct clk_master_characteristics *characteristics;
|
struct clk_master_characteristics *characteristics;
|
||||||
@ -255,11 +221,7 @@ of_at91_clk_master_setup(struct device_node *np,
|
|||||||
if (IS_ERR(regmap))
|
if (IS_ERR(regmap))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
irq = irq_of_parse_and_map(np, 0);
|
clk = at91_clk_register_master(regmap, name, num_parents,
|
||||||
if (!irq)
|
|
||||||
goto out_free_characteristics;
|
|
||||||
|
|
||||||
clk = at91_clk_register_master(regmap, irq, name, num_parents,
|
|
||||||
parent_names, layout,
|
parent_names, layout,
|
||||||
characteristics);
|
characteristics);
|
||||||
if (IS_ERR(clk))
|
if (IS_ERR(clk))
|
||||||
|
@ -12,14 +12,6 @@
|
|||||||
#include <linux/clkdev.h>
|
#include <linux/clkdev.h>
|
||||||
#include <linux/clk/at91_pmc.h>
|
#include <linux/clk/at91_pmc.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_address.h>
|
|
||||||
#include <linux/of_irq.h>
|
|
||||||
#include <linux/io.h>
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/wait.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/interrupt.h>
|
|
||||||
#include <linux/irq.h>
|
|
||||||
#include <linux/mfd/syscon.h>
|
#include <linux/mfd/syscon.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
@ -61,8 +53,6 @@ struct clk_pll_layout {
|
|||||||
struct clk_pll {
|
struct clk_pll {
|
||||||
struct clk_hw hw;
|
struct clk_hw hw;
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
unsigned int irq;
|
|
||||||
wait_queue_head_t wait;
|
|
||||||
u8 id;
|
u8 id;
|
||||||
u8 div;
|
u8 div;
|
||||||
u8 range;
|
u8 range;
|
||||||
@ -71,16 +61,6 @@ struct clk_pll {
|
|||||||
const struct clk_pll_characteristics *characteristics;
|
const struct clk_pll_characteristics *characteristics;
|
||||||
};
|
};
|
||||||
|
|
||||||
static irqreturn_t clk_pll_irq_handler(int irq, void *dev_id)
|
|
||||||
{
|
|
||||||
struct clk_pll *pll = (struct clk_pll *)dev_id;
|
|
||||||
|
|
||||||
wake_up(&pll->wait);
|
|
||||||
disable_irq_nosync(pll->irq);
|
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool clk_pll_ready(struct regmap *regmap, int id)
|
static inline bool clk_pll_ready(struct regmap *regmap, int id)
|
||||||
{
|
{
|
||||||
unsigned int status;
|
unsigned int status;
|
||||||
@ -127,11 +107,8 @@ static int clk_pll_prepare(struct clk_hw *hw)
|
|||||||
(out << PLL_OUT_SHIFT) |
|
(out << PLL_OUT_SHIFT) |
|
||||||
((pll->mul & layout->mul_mask) << layout->mul_shift));
|
((pll->mul & layout->mul_mask) << layout->mul_shift));
|
||||||
|
|
||||||
while (!clk_pll_ready(regmap, pll->id)) {
|
while (!clk_pll_ready(regmap, pll->id))
|
||||||
enable_irq(pll->irq);
|
cpu_relax();
|
||||||
wait_event(pll->wait,
|
|
||||||
clk_pll_ready(regmap, pll->id));
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -320,7 +297,7 @@ static const struct clk_ops pll_ops = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static struct clk * __init
|
static struct clk * __init
|
||||||
at91_clk_register_pll(struct regmap *regmap, unsigned int irq, const char *name,
|
at91_clk_register_pll(struct regmap *regmap, const char *name,
|
||||||
const char *parent_name, u8 id,
|
const char *parent_name, u8 id,
|
||||||
const struct clk_pll_layout *layout,
|
const struct clk_pll_layout *layout,
|
||||||
const struct clk_pll_characteristics *characteristics)
|
const struct clk_pll_characteristics *characteristics)
|
||||||
@ -328,7 +305,6 @@ at91_clk_register_pll(struct regmap *regmap, unsigned int irq, const char *name,
|
|||||||
struct clk_pll *pll;
|
struct clk_pll *pll;
|
||||||
struct clk *clk = NULL;
|
struct clk *clk = NULL;
|
||||||
struct clk_init_data init;
|
struct clk_init_data init;
|
||||||
int ret;
|
|
||||||
int offset = PLL_REG(id);
|
int offset = PLL_REG(id);
|
||||||
unsigned int pllr;
|
unsigned int pllr;
|
||||||
|
|
||||||
@ -350,22 +326,12 @@ at91_clk_register_pll(struct regmap *regmap, unsigned int irq, const char *name,
|
|||||||
pll->layout = layout;
|
pll->layout = layout;
|
||||||
pll->characteristics = characteristics;
|
pll->characteristics = characteristics;
|
||||||
pll->regmap = regmap;
|
pll->regmap = regmap;
|
||||||
pll->irq = irq;
|
|
||||||
regmap_read(regmap, offset, &pllr);
|
regmap_read(regmap, offset, &pllr);
|
||||||
pll->div = PLL_DIV(pllr);
|
pll->div = PLL_DIV(pllr);
|
||||||
pll->mul = PLL_MUL(pllr, layout);
|
pll->mul = PLL_MUL(pllr, layout);
|
||||||
init_waitqueue_head(&pll->wait);
|
|
||||||
irq_set_status_flags(pll->irq, IRQ_NOAUTOEN);
|
|
||||||
ret = request_irq(pll->irq, clk_pll_irq_handler, IRQF_TRIGGER_HIGH,
|
|
||||||
id ? "clk-pllb" : "clk-plla", pll);
|
|
||||||
if (ret) {
|
|
||||||
kfree(pll);
|
|
||||||
return ERR_PTR(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
clk = clk_register(NULL, &pll->hw);
|
clk = clk_register(NULL, &pll->hw);
|
||||||
if (IS_ERR(clk)) {
|
if (IS_ERR(clk)) {
|
||||||
free_irq(pll->irq, pll);
|
|
||||||
kfree(pll);
|
kfree(pll);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -499,7 +465,6 @@ of_at91_clk_pll_setup(struct device_node *np,
|
|||||||
const struct clk_pll_layout *layout)
|
const struct clk_pll_layout *layout)
|
||||||
{
|
{
|
||||||
u32 id;
|
u32 id;
|
||||||
unsigned int irq;
|
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
const char *parent_name;
|
const char *parent_name;
|
||||||
@ -521,11 +486,7 @@ of_at91_clk_pll_setup(struct device_node *np,
|
|||||||
if (!characteristics)
|
if (!characteristics)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
irq = irq_of_parse_and_map(np, 0);
|
clk = at91_clk_register_pll(regmap, name, parent_name, id, layout,
|
||||||
if (!irq)
|
|
||||||
return;
|
|
||||||
|
|
||||||
clk = at91_clk_register_pll(regmap, irq, name, parent_name, id, layout,
|
|
||||||
characteristics);
|
characteristics);
|
||||||
if (IS_ERR(clk))
|
if (IS_ERR(clk))
|
||||||
goto out_free_characteristics;
|
goto out_free_characteristics;
|
||||||
|
@ -12,13 +12,6 @@
|
|||||||
#include <linux/clkdev.h>
|
#include <linux/clkdev.h>
|
||||||
#include <linux/clk/at91_pmc.h>
|
#include <linux/clk/at91_pmc.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_address.h>
|
|
||||||
#include <linux/io.h>
|
|
||||||
#include <linux/irq.h>
|
|
||||||
#include <linux/of_irq.h>
|
|
||||||
#include <linux/interrupt.h>
|
|
||||||
#include <linux/wait.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/mfd/syscon.h>
|
#include <linux/mfd/syscon.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
@ -32,8 +25,6 @@
|
|||||||
struct clk_system {
|
struct clk_system {
|
||||||
struct clk_hw hw;
|
struct clk_hw hw;
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
unsigned int irq;
|
|
||||||
wait_queue_head_t wait;
|
|
||||||
u8 id;
|
u8 id;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -41,15 +32,6 @@ static inline int is_pck(int id)
|
|||||||
{
|
{
|
||||||
return (id >= 8) && (id <= 15);
|
return (id >= 8) && (id <= 15);
|
||||||
}
|
}
|
||||||
static irqreturn_t clk_system_irq_handler(int irq, void *dev_id)
|
|
||||||
{
|
|
||||||
struct clk_system *sys = (struct clk_system *)dev_id;
|
|
||||||
|
|
||||||
wake_up(&sys->wait);
|
|
||||||
disable_irq_nosync(sys->irq);
|
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool clk_system_ready(struct regmap *regmap, int id)
|
static inline bool clk_system_ready(struct regmap *regmap, int id)
|
||||||
{
|
{
|
||||||
@ -69,15 +51,9 @@ static int clk_system_prepare(struct clk_hw *hw)
|
|||||||
if (!is_pck(sys->id))
|
if (!is_pck(sys->id))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
while (!clk_system_ready(sys->regmap, sys->id)) {
|
while (!clk_system_ready(sys->regmap, sys->id))
|
||||||
if (sys->irq) {
|
|
||||||
enable_irq(sys->irq);
|
|
||||||
wait_event(sys->wait,
|
|
||||||
clk_system_ready(sys->regmap, sys->id));
|
|
||||||
} else {
|
|
||||||
cpu_relax();
|
cpu_relax();
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,12 +90,11 @@ static const struct clk_ops system_ops = {
|
|||||||
|
|
||||||
static struct clk * __init
|
static struct clk * __init
|
||||||
at91_clk_register_system(struct regmap *regmap, const char *name,
|
at91_clk_register_system(struct regmap *regmap, const char *name,
|
||||||
const char *parent_name, u8 id, int irq)
|
const char *parent_name, u8 id)
|
||||||
{
|
{
|
||||||
struct clk_system *sys;
|
struct clk_system *sys;
|
||||||
struct clk *clk = NULL;
|
struct clk *clk = NULL;
|
||||||
struct clk_init_data init;
|
struct clk_init_data init;
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!parent_name || id > SYSTEM_MAX_ID)
|
if (!parent_name || id > SYSTEM_MAX_ID)
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
@ -137,24 +112,10 @@ at91_clk_register_system(struct regmap *regmap, const char *name,
|
|||||||
sys->id = id;
|
sys->id = id;
|
||||||
sys->hw.init = &init;
|
sys->hw.init = &init;
|
||||||
sys->regmap = regmap;
|
sys->regmap = regmap;
|
||||||
sys->irq = irq;
|
|
||||||
if (irq) {
|
|
||||||
init_waitqueue_head(&sys->wait);
|
|
||||||
irq_set_status_flags(sys->irq, IRQ_NOAUTOEN);
|
|
||||||
ret = request_irq(sys->irq, clk_system_irq_handler,
|
|
||||||
IRQF_TRIGGER_HIGH, name, sys);
|
|
||||||
if (ret) {
|
|
||||||
kfree(sys);
|
|
||||||
return ERR_PTR(ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clk = clk_register(NULL, &sys->hw);
|
clk = clk_register(NULL, &sys->hw);
|
||||||
if (IS_ERR(clk)) {
|
if (IS_ERR(clk))
|
||||||
if (irq)
|
|
||||||
free_irq(sys->irq, sys);
|
|
||||||
kfree(sys);
|
kfree(sys);
|
||||||
}
|
|
||||||
|
|
||||||
return clk;
|
return clk;
|
||||||
}
|
}
|
||||||
@ -162,7 +123,6 @@ at91_clk_register_system(struct regmap *regmap, const char *name,
|
|||||||
static void __init of_at91rm9200_clk_sys_setup(struct device_node *np)
|
static void __init of_at91rm9200_clk_sys_setup(struct device_node *np)
|
||||||
{
|
{
|
||||||
int num;
|
int num;
|
||||||
int irq = 0;
|
|
||||||
u32 id;
|
u32 id;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
const char *name;
|
const char *name;
|
||||||
@ -185,13 +145,9 @@ static void __init of_at91rm9200_clk_sys_setup(struct device_node *np)
|
|||||||
if (of_property_read_string(np, "clock-output-names", &name))
|
if (of_property_read_string(np, "clock-output-names", &name))
|
||||||
name = sysclknp->name;
|
name = sysclknp->name;
|
||||||
|
|
||||||
if (is_pck(id))
|
|
||||||
irq = irq_of_parse_and_map(sysclknp, 0);
|
|
||||||
|
|
||||||
parent_name = of_clk_get_parent_name(sysclknp, 0);
|
parent_name = of_clk_get_parent_name(sysclknp, 0);
|
||||||
|
|
||||||
clk = at91_clk_register_system(regmap, name, parent_name, id,
|
clk = at91_clk_register_system(regmap, name, parent_name, id);
|
||||||
irq);
|
|
||||||
if (IS_ERR(clk))
|
if (IS_ERR(clk))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -11,14 +11,7 @@
|
|||||||
#include <linux/clk-provider.h>
|
#include <linux/clk-provider.h>
|
||||||
#include <linux/clkdev.h>
|
#include <linux/clkdev.h>
|
||||||
#include <linux/clk/at91_pmc.h>
|
#include <linux/clk/at91_pmc.h>
|
||||||
#include <linux/interrupt.h>
|
|
||||||
#include <linux/irq.h>
|
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_address.h>
|
|
||||||
#include <linux/of_irq.h>
|
|
||||||
#include <linux/io.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/wait.h>
|
|
||||||
#include <linux/mfd/syscon.h>
|
#include <linux/mfd/syscon.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
@ -29,22 +22,10 @@
|
|||||||
struct clk_utmi {
|
struct clk_utmi {
|
||||||
struct clk_hw hw;
|
struct clk_hw hw;
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
unsigned int irq;
|
|
||||||
wait_queue_head_t wait;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define to_clk_utmi(hw) container_of(hw, struct clk_utmi, hw)
|
#define to_clk_utmi(hw) container_of(hw, struct clk_utmi, hw)
|
||||||
|
|
||||||
static irqreturn_t clk_utmi_irq_handler(int irq, void *dev_id)
|
|
||||||
{
|
|
||||||
struct clk_utmi *utmi = (struct clk_utmi *)dev_id;
|
|
||||||
|
|
||||||
wake_up(&utmi->wait);
|
|
||||||
disable_irq_nosync(utmi->irq);
|
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool clk_utmi_ready(struct regmap *regmap)
|
static inline bool clk_utmi_ready(struct regmap *regmap)
|
||||||
{
|
{
|
||||||
unsigned int status;
|
unsigned int status;
|
||||||
@ -62,11 +43,8 @@ static int clk_utmi_prepare(struct clk_hw *hw)
|
|||||||
|
|
||||||
regmap_update_bits(utmi->regmap, AT91_CKGR_UCKR, uckr, uckr);
|
regmap_update_bits(utmi->regmap, AT91_CKGR_UCKR, uckr, uckr);
|
||||||
|
|
||||||
while (!clk_utmi_ready(utmi->regmap)) {
|
while (!clk_utmi_ready(utmi->regmap))
|
||||||
enable_irq(utmi->irq);
|
cpu_relax();
|
||||||
wait_event(utmi->wait,
|
|
||||||
clk_utmi_ready(utmi->regmap));
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -100,10 +78,9 @@ static const struct clk_ops utmi_ops = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static struct clk * __init
|
static struct clk * __init
|
||||||
at91_clk_register_utmi(struct regmap *regmap, unsigned int irq,
|
at91_clk_register_utmi(struct regmap *regmap,
|
||||||
const char *name, const char *parent_name)
|
const char *name, const char *parent_name)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
struct clk_utmi *utmi;
|
struct clk_utmi *utmi;
|
||||||
struct clk *clk = NULL;
|
struct clk *clk = NULL;
|
||||||
struct clk_init_data init;
|
struct clk_init_data init;
|
||||||
@ -120,28 +97,16 @@ at91_clk_register_utmi(struct regmap *regmap, unsigned int irq,
|
|||||||
|
|
||||||
utmi->hw.init = &init;
|
utmi->hw.init = &init;
|
||||||
utmi->regmap = regmap;
|
utmi->regmap = regmap;
|
||||||
utmi->irq = irq;
|
|
||||||
init_waitqueue_head(&utmi->wait);
|
|
||||||
irq_set_status_flags(utmi->irq, IRQ_NOAUTOEN);
|
|
||||||
ret = request_irq(utmi->irq, clk_utmi_irq_handler,
|
|
||||||
IRQF_TRIGGER_HIGH, "clk-utmi", utmi);
|
|
||||||
if (ret) {
|
|
||||||
kfree(utmi);
|
|
||||||
return ERR_PTR(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
clk = clk_register(NULL, &utmi->hw);
|
clk = clk_register(NULL, &utmi->hw);
|
||||||
if (IS_ERR(clk)) {
|
if (IS_ERR(clk))
|
||||||
free_irq(utmi->irq, utmi);
|
|
||||||
kfree(utmi);
|
kfree(utmi);
|
||||||
}
|
|
||||||
|
|
||||||
return clk;
|
return clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np)
|
static void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np)
|
||||||
{
|
{
|
||||||
unsigned int irq;
|
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
const char *parent_name;
|
const char *parent_name;
|
||||||
const char *name = np->name;
|
const char *name = np->name;
|
||||||
@ -151,15 +116,11 @@ static void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np)
|
|||||||
|
|
||||||
of_property_read_string(np, "clock-output-names", &name);
|
of_property_read_string(np, "clock-output-names", &name);
|
||||||
|
|
||||||
irq = irq_of_parse_and_map(np, 0);
|
|
||||||
if (!irq)
|
|
||||||
return;
|
|
||||||
|
|
||||||
regmap = syscon_node_to_regmap(of_get_parent(np));
|
regmap = syscon_node_to_regmap(of_get_parent(np));
|
||||||
if (IS_ERR(regmap))
|
if (IS_ERR(regmap))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
clk = at91_clk_register_utmi(regmap, irq, name, parent_name);
|
clk = at91_clk_register_utmi(regmap, name, parent_name);
|
||||||
if (IS_ERR(clk))
|
if (IS_ERR(clk))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -13,12 +13,6 @@
|
|||||||
#include <linux/clk/at91_pmc.h>
|
#include <linux/clk/at91_pmc.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
#include <linux/io.h>
|
|
||||||
#include <linux/interrupt.h>
|
|
||||||
#include <linux/irq.h>
|
|
||||||
#include <linux/irqchip/chained_irq.h>
|
|
||||||
#include <linux/irqdomain.h>
|
|
||||||
#include <linux/of_irq.h>
|
|
||||||
#include <linux/mfd/syscon.h>
|
#include <linux/mfd/syscon.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
@ -67,118 +61,6 @@ int of_at91_get_clk_range(struct device_node *np, const char *propname,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(of_at91_get_clk_range);
|
EXPORT_SYMBOL_GPL(of_at91_get_clk_range);
|
||||||
|
|
||||||
static void pmc_irq_mask(struct irq_data *d)
|
|
||||||
{
|
|
||||||
struct at91_pmc *pmc = irq_data_get_irq_chip_data(d);
|
|
||||||
|
|
||||||
regmap_write(pmc->regmap, AT91_PMC_IDR, 1 << d->hwirq);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pmc_irq_unmask(struct irq_data *d)
|
|
||||||
{
|
|
||||||
struct at91_pmc *pmc = irq_data_get_irq_chip_data(d);
|
|
||||||
|
|
||||||
regmap_write(pmc->regmap, AT91_PMC_IER, 1 << d->hwirq);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pmc_irq_set_type(struct irq_data *d, unsigned type)
|
|
||||||
{
|
|
||||||
if (type != IRQ_TYPE_LEVEL_HIGH) {
|
|
||||||
pr_warn("PMC: type not supported (support only IRQ_TYPE_LEVEL_HIGH type)\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pmc_irq_suspend(struct irq_data *d)
|
|
||||||
{
|
|
||||||
struct at91_pmc *pmc = irq_data_get_irq_chip_data(d);
|
|
||||||
|
|
||||||
regmap_read(pmc->regmap, AT91_PMC_IMR, &pmc->imr);
|
|
||||||
regmap_write(pmc->regmap, AT91_PMC_IDR, pmc->imr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pmc_irq_resume(struct irq_data *d)
|
|
||||||
{
|
|
||||||
struct at91_pmc *pmc = irq_data_get_irq_chip_data(d);
|
|
||||||
|
|
||||||
regmap_write(pmc->regmap, AT91_PMC_IER, pmc->imr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct irq_chip pmc_irq = {
|
|
||||||
.name = "PMC",
|
|
||||||
.irq_disable = pmc_irq_mask,
|
|
||||||
.irq_mask = pmc_irq_mask,
|
|
||||||
.irq_unmask = pmc_irq_unmask,
|
|
||||||
.irq_set_type = pmc_irq_set_type,
|
|
||||||
.irq_suspend = pmc_irq_suspend,
|
|
||||||
.irq_resume = pmc_irq_resume,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct lock_class_key pmc_lock_class;
|
|
||||||
|
|
||||||
static int pmc_irq_map(struct irq_domain *h, unsigned int virq,
|
|
||||||
irq_hw_number_t hw)
|
|
||||||
{
|
|
||||||
struct at91_pmc *pmc = h->host_data;
|
|
||||||
|
|
||||||
irq_set_lockdep_class(virq, &pmc_lock_class);
|
|
||||||
|
|
||||||
irq_set_chip_and_handler(virq, &pmc_irq,
|
|
||||||
handle_level_irq);
|
|
||||||
irq_set_chip_data(virq, pmc);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pmc_irq_domain_xlate(struct irq_domain *d,
|
|
||||||
struct device_node *ctrlr,
|
|
||||||
const u32 *intspec, unsigned int intsize,
|
|
||||||
irq_hw_number_t *out_hwirq,
|
|
||||||
unsigned int *out_type)
|
|
||||||
{
|
|
||||||
struct at91_pmc *pmc = d->host_data;
|
|
||||||
const struct at91_pmc_caps *caps = pmc->caps;
|
|
||||||
|
|
||||||
if (WARN_ON(intsize < 1))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
*out_hwirq = intspec[0];
|
|
||||||
|
|
||||||
if (!(caps->available_irqs & (1 << *out_hwirq)))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
*out_type = IRQ_TYPE_LEVEL_HIGH;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct irq_domain_ops pmc_irq_ops = {
|
|
||||||
.map = pmc_irq_map,
|
|
||||||
.xlate = pmc_irq_domain_xlate,
|
|
||||||
};
|
|
||||||
|
|
||||||
static irqreturn_t pmc_irq_handler(int irq, void *data)
|
|
||||||
{
|
|
||||||
struct at91_pmc *pmc = (struct at91_pmc *)data;
|
|
||||||
unsigned int tmpsr, imr;
|
|
||||||
unsigned long sr;
|
|
||||||
int n;
|
|
||||||
|
|
||||||
regmap_read(pmc->regmap, AT91_PMC_SR, &tmpsr);
|
|
||||||
regmap_read(pmc->regmap, AT91_PMC_IMR, &imr);
|
|
||||||
|
|
||||||
sr = tmpsr & imr;
|
|
||||||
if (!sr)
|
|
||||||
return IRQ_NONE;
|
|
||||||
|
|
||||||
for_each_set_bit(n, &sr, BITS_PER_LONG)
|
|
||||||
generic_handle_irq(irq_find_mapping(pmc->irqdomain, n));
|
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct at91_pmc_caps at91rm9200_caps = {
|
static const struct at91_pmc_caps at91rm9200_caps = {
|
||||||
.available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_LOCKB |
|
.available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_LOCKB |
|
||||||
AT91_PMC_MCKRDY | AT91_PMC_PCK0RDY |
|
AT91_PMC_MCKRDY | AT91_PMC_PCK0RDY |
|
||||||
@ -230,12 +112,12 @@ static const struct at91_pmc_caps sama5d3_caps = {
|
|||||||
|
|
||||||
static struct at91_pmc *__init at91_pmc_init(struct device_node *np,
|
static struct at91_pmc *__init at91_pmc_init(struct device_node *np,
|
||||||
struct regmap *regmap,
|
struct regmap *regmap,
|
||||||
void __iomem *regbase, int virq,
|
void __iomem *regbase,
|
||||||
const struct at91_pmc_caps *caps)
|
const struct at91_pmc_caps *caps)
|
||||||
{
|
{
|
||||||
struct at91_pmc *pmc;
|
struct at91_pmc *pmc;
|
||||||
|
|
||||||
if (!regbase || !virq || !caps)
|
if (!regbase || !caps)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
at91_pmc_base = regbase;
|
at91_pmc_base = regbase;
|
||||||
@ -245,26 +127,11 @@ static struct at91_pmc *__init at91_pmc_init(struct device_node *np,
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
pmc->regmap = regmap;
|
pmc->regmap = regmap;
|
||||||
pmc->virq = virq;
|
|
||||||
pmc->caps = caps;
|
pmc->caps = caps;
|
||||||
|
|
||||||
pmc->irqdomain = irq_domain_add_linear(np, 32, &pmc_irq_ops, pmc);
|
|
||||||
if (!pmc->irqdomain)
|
|
||||||
goto out_free_pmc;
|
|
||||||
|
|
||||||
regmap_write(pmc->regmap, AT91_PMC_IDR, 0xffffffff);
|
regmap_write(pmc->regmap, AT91_PMC_IDR, 0xffffffff);
|
||||||
if (request_irq(pmc->virq, pmc_irq_handler,
|
|
||||||
IRQF_SHARED | IRQF_COND_SUSPEND, "pmc", pmc))
|
|
||||||
goto out_remove_irqdomain;
|
|
||||||
|
|
||||||
return pmc;
|
return pmc;
|
||||||
|
|
||||||
out_remove_irqdomain:
|
|
||||||
irq_domain_remove(pmc->irqdomain);
|
|
||||||
out_free_pmc:
|
|
||||||
kfree(pmc);
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __init of_at91_pmc_setup(struct device_node *np,
|
static void __init of_at91_pmc_setup(struct device_node *np,
|
||||||
@ -273,17 +140,12 @@ static void __init of_at91_pmc_setup(struct device_node *np,
|
|||||||
struct at91_pmc *pmc;
|
struct at91_pmc *pmc;
|
||||||
void __iomem *regbase = of_iomap(np, 0);
|
void __iomem *regbase = of_iomap(np, 0);
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
int virq;
|
|
||||||
|
|
||||||
regmap = syscon_node_to_regmap(np);
|
regmap = syscon_node_to_regmap(np);
|
||||||
if (IS_ERR(regmap))
|
if (IS_ERR(regmap))
|
||||||
panic("Could not retrieve syscon regmap");
|
panic("Could not retrieve syscon regmap");
|
||||||
|
|
||||||
virq = irq_of_parse_and_map(np, 0);
|
pmc = at91_pmc_init(np, regmap, regbase, caps);
|
||||||
if (!virq)
|
|
||||||
return;
|
|
||||||
|
|
||||||
pmc = at91_pmc_init(np, regmap, regbase, virq, caps);
|
|
||||||
if (!pmc)
|
if (!pmc)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -32,10 +32,7 @@ struct at91_pmc_caps {
|
|||||||
|
|
||||||
struct at91_pmc {
|
struct at91_pmc {
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
int virq;
|
|
||||||
const struct at91_pmc_caps *caps;
|
const struct at91_pmc_caps *caps;
|
||||||
struct irq_domain *irqdomain;
|
|
||||||
u32 imr;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
int of_at91_get_clk_range(struct device_node *np, const char *propname,
|
int of_at91_get_clk_range(struct device_node *np, const char *propname,
|
||||||
|
Loading…
Reference in New Issue
Block a user