diff options
Diffstat (limited to 'drivers/clk/st/clk-flexgen.c')
-rw-r--r-- | drivers/clk/st/clk-flexgen.c | 63 |
1 files changed, 60 insertions, 3 deletions
diff --git a/drivers/clk/st/clk-flexgen.c b/drivers/clk/st/clk-flexgen.c index 546bd79c8e3a..a485f3b284b9 100644 --- a/drivers/clk/st/clk-flexgen.c +++ b/drivers/clk/st/clk-flexgen.c @@ -15,6 +15,11 @@ #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; |