]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/clk/st/clk-flexgen.c
Merge tag 'armsoc-late' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
[karo-tx-linux.git] / drivers / clk / st / clk-flexgen.c
index 546bd79c8e3a68eb86ccd7b51f50bf5225b15f62..a485f3b284b9f5825fb206966197489df8594746 100644 (file)
 #include <linux/of.h>
 #include <linux/of_address.h>
 
+struct clkgen_data {
+       unsigned long flags;
+       bool mode;
+};
+
 struct flexgen {
        struct clk_hw hw;
 
@@ -28,9 +33,14 @@ struct flexgen {
        struct clk_gate fgate;
        /* Final divisor */
        struct clk_divider fdiv;
+       /* Asynchronous mode control */
+       struct clk_gate sync;
+       /* hw control flags */
+       bool control_mode;
 };
 
 #define to_flexgen(_hw) container_of(_hw, struct flexgen, hw)
+#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw)
 
 static int flexgen_enable(struct clk_hw *hw)
 {
@@ -139,12 +149,21 @@ static int flexgen_set_rate(struct clk_hw *hw, unsigned long rate,
        struct flexgen *flexgen = to_flexgen(hw);
        struct clk_hw *pdiv_hw = &flexgen->pdiv.hw;
        struct clk_hw *fdiv_hw = &flexgen->fdiv.hw;
+       struct clk_hw *sync_hw = &flexgen->sync.hw;
+       struct clk_gate *config = to_clk_gate(sync_hw);
        unsigned long div = 0;
        int ret = 0;
+       u32 reg;
 
        __clk_hw_set_clk(pdiv_hw, hw);
        __clk_hw_set_clk(fdiv_hw, hw);
 
+       if (flexgen->control_mode) {
+               reg = readl(config->reg);
+               reg &= ~BIT(config->bit_idx);
+               writel(reg, config->reg);
+       }
+
        div = clk_best_div(parent_rate, rate);
 
        /*
@@ -178,7 +197,7 @@ static const struct clk_ops flexgen_ops = {
 static struct clk *clk_register_flexgen(const char *name,
                                const char **parent_names, u8 num_parents,
                                void __iomem *reg, spinlock_t *lock, u32 idx,
-                               unsigned long flexgen_flags) {
+                               unsigned long flexgen_flags, bool mode) {
        struct flexgen *fgxbar;
        struct clk *clk;
        struct clk_init_data init;
@@ -227,6 +246,13 @@ static struct clk *clk_register_flexgen(const char *name,
        fgxbar->fdiv.reg = fdiv_reg;
        fgxbar->fdiv.width = 6;
 
+       /* Final divider sync config */
+       fgxbar->sync.lock = lock;
+       fgxbar->sync.reg = fdiv_reg;
+       fgxbar->sync.bit_idx = 7;
+
+       fgxbar->control_mode = mode;
+
        fgxbar->hw.init = &init;
 
        clk = clk_register(NULL, &fgxbar->hw);
@@ -259,6 +285,27 @@ static const char ** __init flexgen_get_parents(struct device_node *np,
        return parents;
 }
 
+static const struct clkgen_data clkgen_audio = {
+       .flags = CLK_SET_RATE_PARENT,
+};
+
+static const struct clkgen_data clkgen_video = {
+       .flags = CLK_SET_RATE_PARENT,
+       .mode = 1,
+};
+
+static const struct of_device_id flexgen_of_match[] = {
+       {
+               .compatible = "st,flexgen-audio",
+               .data = &clkgen_audio,
+       },
+       {
+               .compatible = "st,flexgen-video",
+               .data = &clkgen_video,
+       },
+       {}
+};
+
 static void __init st_of_flexgen_setup(struct device_node *np)
 {
        struct device_node *pnode;
@@ -267,7 +314,11 @@ static void __init st_of_flexgen_setup(struct device_node *np)
        const char **parents;
        int num_parents, i;
        spinlock_t *rlock = NULL;
+       const struct of_device_id *match;
+       struct clkgen_data *data = NULL;
+       unsigned long flex_flags = 0;
        int ret;
+       bool clk_mode = 0;
 
        pnode = of_get_parent(np);
        if (!pnode)
@@ -281,6 +332,13 @@ static void __init st_of_flexgen_setup(struct device_node *np)
        if (!parents)
                return;
 
+       match = of_match_node(flexgen_of_match, np);
+       if (match) {
+               data = (struct clkgen_data *)match->data;
+               flex_flags = data->flags;
+               clk_mode = data->mode;
+       }
+
        clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
        if (!clk_data)
                goto err;
@@ -307,7 +365,6 @@ static void __init st_of_flexgen_setup(struct device_node *np)
        for (i = 0; i < clk_data->clk_num; i++) {
                struct clk *clk;
                const char *clk_name;
-               unsigned long flex_flags = 0;
 
                if (of_property_read_string_index(np, "clock-output-names",
                                                  i, &clk_name)) {
@@ -323,7 +380,7 @@ static void __init st_of_flexgen_setup(struct device_node *np)
                        continue;
 
                clk = clk_register_flexgen(clk_name, parents, num_parents,
-                                          reg, rlock, i, flex_flags);
+                                          reg, rlock, i, flex_flags, clk_mode);
 
                if (IS_ERR(clk))
                        goto err;