diff options
Diffstat (limited to 'kernel/drivers/clk/at91/clk-peripheral.c')
-rw-r--r-- | kernel/drivers/clk/at91/clk-peripheral.c | 134 |
1 files changed, 81 insertions, 53 deletions
diff --git a/kernel/drivers/clk/at91/clk-peripheral.c b/kernel/drivers/clk/at91/clk-peripheral.c index df2c1afa5..d69cd2a12 100644 --- a/kernel/drivers/clk/at91/clk-peripheral.c +++ b/kernel/drivers/clk/at91/clk-peripheral.c @@ -12,11 +12,13 @@ #include <linux/clkdev.h> #include <linux/clk/at91_pmc.h> #include <linux/of.h> -#include <linux/of_address.h> -#include <linux/io.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> #include "pmc.h" +DEFINE_SPINLOCK(pmc_pcr_lock); + #define PERIPHERAL_MAX 64 #define PERIPHERAL_AT91RM9200 0 @@ -33,7 +35,7 @@ struct clk_peripheral { struct clk_hw hw; - struct at91_pmc *pmc; + struct regmap *regmap; u32 id; }; @@ -41,8 +43,9 @@ struct clk_peripheral { struct clk_sam9x5_peripheral { struct clk_hw hw; - struct at91_pmc *pmc; + struct regmap *regmap; struct clk_range range; + spinlock_t *lock; u32 id; u32 div; bool auto_div; @@ -54,7 +57,6 @@ struct clk_sam9x5_peripheral { static int clk_peripheral_enable(struct clk_hw *hw) { struct clk_peripheral *periph = to_clk_peripheral(hw); - struct at91_pmc *pmc = periph->pmc; int offset = AT91_PMC_PCER; u32 id = periph->id; @@ -62,14 +64,14 @@ static int clk_peripheral_enable(struct clk_hw *hw) return 0; if (id > PERIPHERAL_ID_MAX) offset = AT91_PMC_PCER1; - pmc_write(pmc, offset, PERIPHERAL_MASK(id)); + regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id)); + return 0; } static void clk_peripheral_disable(struct clk_hw *hw) { struct clk_peripheral *periph = to_clk_peripheral(hw); - struct at91_pmc *pmc = periph->pmc; int offset = AT91_PMC_PCDR; u32 id = periph->id; @@ -77,21 +79,23 @@ static void clk_peripheral_disable(struct clk_hw *hw) return; if (id > PERIPHERAL_ID_MAX) offset = AT91_PMC_PCDR1; - pmc_write(pmc, offset, PERIPHERAL_MASK(id)); + regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id)); } static int clk_peripheral_is_enabled(struct clk_hw *hw) { struct clk_peripheral *periph = to_clk_peripheral(hw); - struct at91_pmc *pmc = periph->pmc; int offset = AT91_PMC_PCSR; + unsigned int status; u32 id = periph->id; if (id < PERIPHERAL_ID_MIN) return 1; if (id > PERIPHERAL_ID_MAX) offset = AT91_PMC_PCSR1; - return !!(pmc_read(pmc, offset) & PERIPHERAL_MASK(id)); + regmap_read(periph->regmap, offset, &status); + + return status & PERIPHERAL_MASK(id) ? 1 : 0; } static const struct clk_ops peripheral_ops = { @@ -101,14 +105,14 @@ static const struct clk_ops peripheral_ops = { }; static struct clk * __init -at91_clk_register_peripheral(struct at91_pmc *pmc, const char *name, +at91_clk_register_peripheral(struct regmap *regmap, const char *name, const char *parent_name, u32 id) { struct clk_peripheral *periph; struct clk *clk = NULL; struct clk_init_data init; - if (!pmc || !name || !parent_name || id > PERIPHERAL_ID_MAX) + if (!name || !parent_name || id > PERIPHERAL_ID_MAX) return ERR_PTR(-EINVAL); periph = kzalloc(sizeof(*periph), GFP_KERNEL); @@ -123,7 +127,7 @@ at91_clk_register_peripheral(struct at91_pmc *pmc, const char *name, periph->id = id; periph->hw.init = &init; - periph->pmc = pmc; + periph->regmap = regmap; clk = clk_register(NULL, &periph->hw); if (IS_ERR(clk)) @@ -134,7 +138,7 @@ at91_clk_register_peripheral(struct at91_pmc *pmc, const char *name, static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph) { - struct clk *parent; + struct clk_hw *parent; unsigned long parent_rate; int shift = 0; @@ -142,8 +146,8 @@ static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph) return; if (periph->range.max) { - parent = clk_get_parent_by_index(periph->hw.clk, 0); - parent_rate = __clk_get_rate(parent); + parent = clk_hw_get_parent_by_index(&periph->hw, 0); + parent_rate = clk_hw_get_rate(parent); if (!parent_rate) return; @@ -160,45 +164,58 @@ static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph) static int clk_sam9x5_peripheral_enable(struct clk_hw *hw) { struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); - struct at91_pmc *pmc = periph->pmc; + unsigned long flags; if (periph->id < PERIPHERAL_ID_MIN) return 0; - pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID) | - AT91_PMC_PCR_CMD | - AT91_PMC_PCR_DIV(periph->div) | - AT91_PMC_PCR_EN); + spin_lock_irqsave(periph->lock, flags); + regmap_write(periph->regmap, AT91_PMC_PCR, + (periph->id & AT91_PMC_PCR_PID_MASK)); + regmap_update_bits(periph->regmap, AT91_PMC_PCR, + AT91_PMC_PCR_DIV_MASK | AT91_PMC_PCR_CMD | + AT91_PMC_PCR_EN, + AT91_PMC_PCR_DIV(periph->div) | + AT91_PMC_PCR_CMD | + AT91_PMC_PCR_EN); + spin_unlock_irqrestore(periph->lock, flags); + return 0; } static void clk_sam9x5_peripheral_disable(struct clk_hw *hw) { struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); - struct at91_pmc *pmc = periph->pmc; + unsigned long flags; if (periph->id < PERIPHERAL_ID_MIN) return; - pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID) | - AT91_PMC_PCR_CMD); + spin_lock_irqsave(periph->lock, flags); + regmap_write(periph->regmap, AT91_PMC_PCR, + (periph->id & AT91_PMC_PCR_PID_MASK)); + regmap_update_bits(periph->regmap, AT91_PMC_PCR, + AT91_PMC_PCR_EN | AT91_PMC_PCR_CMD, + AT91_PMC_PCR_CMD); + spin_unlock_irqrestore(periph->lock, flags); } static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw) { struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); - struct at91_pmc *pmc = periph->pmc; - int ret; + unsigned long flags; + unsigned int status; if (periph->id < PERIPHERAL_ID_MIN) return 1; - pmc_lock(pmc); - pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID)); - ret = !!(pmc_read(pmc, AT91_PMC_PCR) & AT91_PMC_PCR_EN); - pmc_unlock(pmc); + spin_lock_irqsave(periph->lock, flags); + regmap_write(periph->regmap, AT91_PMC_PCR, + (periph->id & AT91_PMC_PCR_PID_MASK)); + regmap_read(periph->regmap, AT91_PMC_PCR, &status); + spin_unlock_irqrestore(periph->lock, flags); - return ret; + return status & AT91_PMC_PCR_EN ? 1 : 0; } static unsigned long @@ -206,19 +223,20 @@ clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); - struct at91_pmc *pmc = periph->pmc; - u32 tmp; + unsigned long flags; + unsigned int status; if (periph->id < PERIPHERAL_ID_MIN) return parent_rate; - pmc_lock(pmc); - pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID)); - tmp = pmc_read(pmc, AT91_PMC_PCR); - pmc_unlock(pmc); + spin_lock_irqsave(periph->lock, flags); + regmap_write(periph->regmap, AT91_PMC_PCR, + (periph->id & AT91_PMC_PCR_PID_MASK)); + regmap_read(periph->regmap, AT91_PMC_PCR, &status); + spin_unlock_irqrestore(periph->lock, flags); - if (tmp & AT91_PMC_PCR_EN) { - periph->div = PERIPHERAL_RSHIFT(tmp); + if (status & AT91_PMC_PCR_EN) { + periph->div = PERIPHERAL_RSHIFT(status); periph->auto_div = false; } else { clk_sam9x5_peripheral_autodiv(periph); @@ -310,15 +328,15 @@ static const struct clk_ops sam9x5_peripheral_ops = { }; static struct clk * __init -at91_clk_register_sam9x5_peripheral(struct at91_pmc *pmc, const char *name, - const char *parent_name, u32 id, - const struct clk_range *range) +at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock, + const char *name, const char *parent_name, + u32 id, const struct clk_range *range) { struct clk_sam9x5_peripheral *periph; struct clk *clk = NULL; struct clk_init_data init; - if (!pmc || !name || !parent_name) + if (!name || !parent_name) return ERR_PTR(-EINVAL); periph = kzalloc(sizeof(*periph), GFP_KERNEL); @@ -334,7 +352,8 @@ at91_clk_register_sam9x5_peripheral(struct at91_pmc *pmc, const char *name, periph->id = id; periph->hw.init = &init; periph->div = 0; - periph->pmc = pmc; + periph->regmap = regmap; + periph->lock = lock; periph->auto_div = true; periph->range = *range; @@ -348,7 +367,7 @@ at91_clk_register_sam9x5_peripheral(struct at91_pmc *pmc, const char *name, } static void __init -of_at91_clk_periph_setup(struct device_node *np, struct at91_pmc *pmc, u8 type) +of_at91_clk_periph_setup(struct device_node *np, u8 type) { int num; u32 id; @@ -356,6 +375,7 @@ of_at91_clk_periph_setup(struct device_node *np, struct at91_pmc *pmc, u8 type) const char *parent_name; const char *name; struct device_node *periphclknp; + struct regmap *regmap; parent_name = of_clk_get_parent_name(np, 0); if (!parent_name) @@ -365,6 +385,10 @@ of_at91_clk_periph_setup(struct device_node *np, struct at91_pmc *pmc, u8 type) if (!num || num > PERIPHERAL_MAX) return; + regmap = syscon_node_to_regmap(of_get_parent(np)); + if (IS_ERR(regmap)) + return; + for_each_child_of_node(np, periphclknp) { if (of_property_read_u32(periphclknp, "reg", &id)) continue; @@ -376,7 +400,7 @@ of_at91_clk_periph_setup(struct device_node *np, struct at91_pmc *pmc, u8 type) name = periphclknp->name; if (type == PERIPHERAL_AT91RM9200) { - clk = at91_clk_register_peripheral(pmc, name, + clk = at91_clk_register_peripheral(regmap, name, parent_name, id); } else { struct clk_range range = CLK_RANGE(0, 0); @@ -385,7 +409,9 @@ of_at91_clk_periph_setup(struct device_node *np, struct at91_pmc *pmc, u8 type) "atmel,clk-output-range", &range); - clk = at91_clk_register_sam9x5_peripheral(pmc, name, + clk = at91_clk_register_sam9x5_peripheral(regmap, + &pmc_pcr_lock, + name, parent_name, id, &range); } @@ -397,14 +423,16 @@ of_at91_clk_periph_setup(struct device_node *np, struct at91_pmc *pmc, u8 type) } } -void __init of_at91rm9200_clk_periph_setup(struct device_node *np, - struct at91_pmc *pmc) +static void __init of_at91rm9200_clk_periph_setup(struct device_node *np) { - of_at91_clk_periph_setup(np, pmc, PERIPHERAL_AT91RM9200); + of_at91_clk_periph_setup(np, PERIPHERAL_AT91RM9200); } +CLK_OF_DECLARE(at91rm9200_clk_periph, "atmel,at91rm9200-clk-peripheral", + of_at91rm9200_clk_periph_setup); -void __init of_at91sam9x5_clk_periph_setup(struct device_node *np, - struct at91_pmc *pmc) +static void __init of_at91sam9x5_clk_periph_setup(struct device_node *np) { - of_at91_clk_periph_setup(np, pmc, PERIPHERAL_AT91SAM9X5); + of_at91_clk_periph_setup(np, PERIPHERAL_AT91SAM9X5); } +CLK_OF_DECLARE(at91sam9x5_clk_periph, "atmel,at91sam9x5-clk-peripheral", + of_at91sam9x5_clk_periph_setup); |