These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / clk / at91 / clk-peripheral.c
index df2c1af..d69cd2a 100644 (file)
 #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);