From f64111ebaf6776558f0e60d0ea8c7a9c579b9436 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Mon, 3 Feb 2014 09:51:37 +0800 Subject: clk: sunxi: add clock-output-names dt property support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sunxi clock drivers use dt node name as clock name, but clock nodes should be named clk@X, so the names would be the same. Let the drivers read clock names from dt clock-output-names property. Signed-off-by: Chen-Yu Tsai Acked-by: Maxime Ripard Acked-by: Mike Turquette Signed-off-by: Emilio López --- drivers/clk/sunxi/clk-sunxi.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index abb6c5ac8a10..0ed97946c457 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -51,6 +51,8 @@ static void __init sun4i_osc_clk_setup(struct device_node *node) if (!gate) goto err_free_fixed; + of_property_read_string(node, "clock-output-names", &clk_name); + /* set up gate and fixed rate properties */ gate->reg = of_iomap(node, 0); gate->bit_idx = SUNXI_OSC24M_GATE; @@ -601,6 +603,8 @@ static void __init sunxi_mux_clk_setup(struct device_node *node, (parents[i] = of_clk_get_parent_name(node, i)) != NULL) i++; + of_property_read_string(node, "clock-output-names", &clk_name); + clk = clk_register_mux(NULL, clk_name, parents, i, CLK_SET_RATE_NO_REPARENT, reg, data->shift, SUNXI_MUX_GATE_WIDTH, @@ -660,6 +664,8 @@ static void __init sunxi_divider_clk_setup(struct device_node *node, clk_parent = of_clk_get_parent_name(node, 0); + of_property_read_string(node, "clock-output-names", &clk_name); + clk = clk_register_divider(NULL, clk_name, clk_parent, 0, reg, data->shift, data->width, data->pow ? CLK_DIVIDER_POWER_OF_TWO : 0, -- cgit v1.2.3 From 667f542db542fddc62d1299b17451d7cae84f6e1 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Mon, 3 Feb 2014 09:51:39 +0800 Subject: clk: sunxi: add names for pll5, pll6 parent clocks to factors_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some factor clocks, such as the parent clock of pll5 and pll6, have multiple output names. Add the corresponding names to factors_data tied to compatible string. Signed-off-by: Chen-Yu Tsai Acked-by: Maxime Ripard Acked-by: Mike Turquette Signed-off-by: Emilio López --- drivers/clk/sunxi/clk-sunxi.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 0ed97946c457..7a2ed98c353e 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -389,6 +389,7 @@ struct factors_data { int mux; struct clk_factors_config *table; void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p); + const char *name; }; static struct clk_factors_config sun4i_pll1_config = { @@ -457,6 +458,14 @@ static const struct factors_data sun4i_pll5_data __initconst = { .enable = 31, .table = &sun4i_pll5_config, .getter = sun4i_get_pll5_factors, + .name = "pll5", +}; + +static const struct factors_data sun4i_pll6_data __initconst = { + .enable = 31, + .table = &sun4i_pll5_config, + .getter = sun4i_get_pll5_factors, + .name = "pll6", }; static const struct factors_data sun4i_apb1_data __initconst = { @@ -499,14 +508,14 @@ static struct clk * __init sunxi_factors_clk_setup(struct device_node *node, (parents[i] = of_clk_get_parent_name(node, i)) != NULL) i++; - /* Nodes should be providing the name via clock-output-names - * but originally our dts didn't, and so we used node->name. - * The new, better nodes look like clk@deadbeef, so we pull the - * name just in this case */ - if (!strcmp("clk", clk_name)) { - of_property_read_string_index(node, "clock-output-names", - 0, &clk_name); - } + /* + * some factor clocks, such as pll5 and pll6, may have multiple + * outputs, and have their name designated in factors_data + */ + if (data->name) + clk_name = data->name; + else + of_property_read_string(node, "clock-output-names", &clk_name); factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL); if (!factors) @@ -838,7 +847,7 @@ static const struct divs_data pll5_divs_data __initconst = { }; static const struct divs_data pll6_divs_data __initconst = { - .factors = &sun4i_pll5_data, + .factors = &sun4i_pll6_data, .div = { { .shift = 0, .table = pll6_sata_tbl, .gate = 14 }, /* M, SATA */ { .fixed = 2 }, /* P, other */ -- cgit v1.2.3 From 97e36b3ce3106988b82e1ca53b1d1c872bde855a Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Mon, 3 Feb 2014 09:51:40 +0800 Subject: clk: sunxi: get divs parent clock name from parent factor clock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Divs clocks consist of a parent factor clock with multiple outputs, and seperate clocks for each output. Get the name of the parent clock from the parent factor clock, instead of the DT node name. Signed-off-by: Chen-Yu Tsai Acked-by: Maxime Ripard Acked-by: Mike Turquette Signed-off-by: Emilio López --- drivers/clk/sunxi/clk-sunxi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 7a2ed98c353e..736fb604bfbc 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -869,7 +869,7 @@ static void __init sunxi_divs_clk_setup(struct device_node *node, struct divs_data *data) { struct clk_onecell_data *clk_data; - const char *parent = node->name; + const char *parent; const char *clk_name; struct clk **clks, *pclk; struct clk_hw *gate_hw, *rate_hw; @@ -883,6 +883,7 @@ static void __init sunxi_divs_clk_setup(struct device_node *node, /* Set up factor clock that we will be dividing */ pclk = sunxi_factors_clk_setup(node, data->factors); + parent = __clk_get_name(pclk); reg = of_iomap(node, 0); -- cgit v1.2.3 From 5d836c58f2e690517ee6fbea4b19f1ad3677c1a5 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Fri, 14 Feb 2014 17:15:00 -0300 Subject: clk: mvebu: do not copy the contents of clk_corediv_desc Signed-off-by: Thomas Petazzoni Signed-off-by: Ezequiel Garcia Signed-off-by: Jason Cooper --- drivers/clk/mvebu/clk-corediv.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/mvebu/clk-corediv.c b/drivers/clk/mvebu/clk-corediv.c index 7162615bcdcd..fb79375a535e 100644 --- a/drivers/clk/mvebu/clk-corediv.c +++ b/drivers/clk/mvebu/clk-corediv.c @@ -31,13 +31,13 @@ struct clk_corediv_desc { struct clk_corediv { struct clk_hw hw; void __iomem *reg; - struct clk_corediv_desc desc; + const struct clk_corediv_desc *desc; spinlock_t lock; }; static struct clk_onecell_data clk_data; -static const struct clk_corediv_desc mvebu_corediv_desc[] __initconst = { +static const struct clk_corediv_desc mvebu_corediv_desc[] = { { .mask = 0x3f, .offset = 8, .fieldbit = 1 }, /* NAND clock */ }; @@ -46,7 +46,7 @@ static const struct clk_corediv_desc mvebu_corediv_desc[] __initconst = { static int clk_corediv_is_enabled(struct clk_hw *hwclk) { struct clk_corediv *corediv = to_corediv_clk(hwclk); - struct clk_corediv_desc *desc = &corediv->desc; + const struct clk_corediv_desc *desc = corediv->desc; u32 enable_mask = BIT(desc->fieldbit) << CORE_CLK_DIV_ENABLE_OFFSET; return !!(readl(corediv->reg) & enable_mask); @@ -55,7 +55,7 @@ static int clk_corediv_is_enabled(struct clk_hw *hwclk) static int clk_corediv_enable(struct clk_hw *hwclk) { struct clk_corediv *corediv = to_corediv_clk(hwclk); - struct clk_corediv_desc *desc = &corediv->desc; + const struct clk_corediv_desc *desc = corediv->desc; unsigned long flags = 0; u32 reg; @@ -73,7 +73,7 @@ static int clk_corediv_enable(struct clk_hw *hwclk) static void clk_corediv_disable(struct clk_hw *hwclk) { struct clk_corediv *corediv = to_corediv_clk(hwclk); - struct clk_corediv_desc *desc = &corediv->desc; + const struct clk_corediv_desc *desc = corediv->desc; unsigned long flags = 0; u32 reg; @@ -90,7 +90,7 @@ static unsigned long clk_corediv_recalc_rate(struct clk_hw *hwclk, unsigned long parent_rate) { struct clk_corediv *corediv = to_corediv_clk(hwclk); - struct clk_corediv_desc *desc = &corediv->desc; + const struct clk_corediv_desc *desc = corediv->desc; u32 reg, div; reg = readl(corediv->reg + CORE_CLK_DIV_RATIO_OFFSET); @@ -117,7 +117,7 @@ static int clk_corediv_set_rate(struct clk_hw *hwclk, unsigned long rate, unsigned long parent_rate) { struct clk_corediv *corediv = to_corediv_clk(hwclk); - struct clk_corediv_desc *desc = &corediv->desc; + const struct clk_corediv_desc *desc = corediv->desc; unsigned long flags = 0; u32 reg, div; @@ -202,7 +202,7 @@ static void __init mvebu_corediv_clk_init(struct device_node *node) init.ops = &corediv_ops; init.flags = 0; - corediv[i].desc = mvebu_corediv_desc[i]; + corediv[i].desc = mvebu_corediv_desc + i; corediv[i].reg = base; corediv[i].hw.init = &init; -- cgit v1.2.3 From 846f33e6c31ca0a9665a6754af8432dd558a0fc3 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Fri, 14 Feb 2014 17:15:01 -0300 Subject: clk: mvebu: add a little bit of documentation about data structures Signed-off-by: Thomas Petazzoni Signed-off-by: Ezequiel Garcia Signed-off-by: Jason Cooper --- drivers/clk/mvebu/clk-corediv.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/mvebu/clk-corediv.c b/drivers/clk/mvebu/clk-corediv.c index fb79375a535e..59db71df86a8 100644 --- a/drivers/clk/mvebu/clk-corediv.c +++ b/drivers/clk/mvebu/clk-corediv.c @@ -22,12 +22,24 @@ #define CORE_CLK_DIV_ENABLE_OFFSET 24 #define CORE_CLK_DIV_RATIO_OFFSET 0x8 +/* + * This structure describes the hardware details (bit offset and mask) + * to configure one particular core divider clock. Those hardware + * details may differ from one SoC to another. This structure is + * therefore typically instantiated statically to describe the + * hardware details. + */ struct clk_corediv_desc { unsigned int mask; unsigned int offset; unsigned int fieldbit; }; +/* + * This structure represents one core divider clock for the clock + * framework, and is dynamically allocated for each core divider clock + * existing in the current SoC. + */ struct clk_corediv { struct clk_hw hw; void __iomem *reg; @@ -37,6 +49,11 @@ struct clk_corediv { static struct clk_onecell_data clk_data; +/* + * Description of the core divider clocks available. For now, we + * support only NAND, and it is available at the same register + * locations regardless of the SoC. + */ static const struct clk_corediv_desc mvebu_corediv_desc[] = { { .mask = 0x3f, .offset = 8, .fieldbit = 1 }, /* NAND clock */ }; -- cgit v1.2.3 From c642e6a95bbb0bee488078b740ee5b3bac57f7fb Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Fri, 14 Feb 2014 17:15:02 -0300 Subject: clk: mvebu: refactor corediv driver to support more SoC This commit refactors the corediv clock driver so that it is capable of handling various SOCs that have slightly different corediv clock registers and capabilities. It introduces a clk_corediv_soc_desc structure that encapsulates all the SoC specific details. Signed-off-by: Thomas Petazzoni Signed-off-by: Ezequiel Garcia Signed-off-by: Jason Cooper --- drivers/clk/mvebu/clk-corediv.c | 81 +++++++++++++++++++++++++++++------------ 1 file changed, 57 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/mvebu/clk-corediv.c b/drivers/clk/mvebu/clk-corediv.c index 59db71df86a8..f9e8a120f22b 100644 --- a/drivers/clk/mvebu/clk-corediv.c +++ b/drivers/clk/mvebu/clk-corediv.c @@ -18,9 +18,6 @@ #include "common.h" #define CORE_CLK_DIV_RATIO_MASK 0xff -#define CORE_CLK_DIV_RATIO_RELOAD BIT(8) -#define CORE_CLK_DIV_ENABLE_OFFSET 24 -#define CORE_CLK_DIV_RATIO_OFFSET 0x8 /* * This structure describes the hardware details (bit offset and mask) @@ -35,6 +32,21 @@ struct clk_corediv_desc { unsigned int fieldbit; }; +/* + * This structure describes the hardware details to configure the core + * divider clocks on a given SoC. Amongst others, it points to the + * array of core divider clock descriptors for this SoC, as well as + * the corresponding operations to manipulate them. + */ +struct clk_corediv_soc_desc { + const struct clk_corediv_desc *descs; + unsigned int ndescs; + const struct clk_ops ops; + u32 ratio_reload; + u32 enable_bit_offset; + u32 ratio_offset; +}; + /* * This structure represents one core divider clock for the clock * framework, and is dynamically allocated for each core divider clock @@ -44,6 +56,7 @@ struct clk_corediv { struct clk_hw hw; void __iomem *reg; const struct clk_corediv_desc *desc; + const struct clk_corediv_soc_desc *soc_desc; spinlock_t lock; }; @@ -63,8 +76,9 @@ static const struct clk_corediv_desc mvebu_corediv_desc[] = { static int clk_corediv_is_enabled(struct clk_hw *hwclk) { struct clk_corediv *corediv = to_corediv_clk(hwclk); + const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc; const struct clk_corediv_desc *desc = corediv->desc; - u32 enable_mask = BIT(desc->fieldbit) << CORE_CLK_DIV_ENABLE_OFFSET; + u32 enable_mask = BIT(desc->fieldbit) << soc_desc->enable_bit_offset; return !!(readl(corediv->reg) & enable_mask); } @@ -72,6 +86,7 @@ static int clk_corediv_is_enabled(struct clk_hw *hwclk) static int clk_corediv_enable(struct clk_hw *hwclk) { struct clk_corediv *corediv = to_corediv_clk(hwclk); + const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc; const struct clk_corediv_desc *desc = corediv->desc; unsigned long flags = 0; u32 reg; @@ -79,7 +94,7 @@ static int clk_corediv_enable(struct clk_hw *hwclk) spin_lock_irqsave(&corediv->lock, flags); reg = readl(corediv->reg); - reg |= (BIT(desc->fieldbit) << CORE_CLK_DIV_ENABLE_OFFSET); + reg |= (BIT(desc->fieldbit) << soc_desc->enable_bit_offset); writel(reg, corediv->reg); spin_unlock_irqrestore(&corediv->lock, flags); @@ -90,6 +105,7 @@ static int clk_corediv_enable(struct clk_hw *hwclk) static void clk_corediv_disable(struct clk_hw *hwclk) { struct clk_corediv *corediv = to_corediv_clk(hwclk); + const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc; const struct clk_corediv_desc *desc = corediv->desc; unsigned long flags = 0; u32 reg; @@ -97,7 +113,7 @@ static void clk_corediv_disable(struct clk_hw *hwclk) spin_lock_irqsave(&corediv->lock, flags); reg = readl(corediv->reg); - reg &= ~(BIT(desc->fieldbit) << CORE_CLK_DIV_ENABLE_OFFSET); + reg &= ~(BIT(desc->fieldbit) << soc_desc->enable_bit_offset); writel(reg, corediv->reg); spin_unlock_irqrestore(&corediv->lock, flags); @@ -107,10 +123,11 @@ static unsigned long clk_corediv_recalc_rate(struct clk_hw *hwclk, unsigned long parent_rate) { struct clk_corediv *corediv = to_corediv_clk(hwclk); + const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc; const struct clk_corediv_desc *desc = corediv->desc; u32 reg, div; - reg = readl(corediv->reg + CORE_CLK_DIV_RATIO_OFFSET); + reg = readl(corediv->reg + soc_desc->ratio_offset); div = (reg >> desc->offset) & desc->mask; return parent_rate / div; } @@ -134,6 +151,7 @@ static int clk_corediv_set_rate(struct clk_hw *hwclk, unsigned long rate, unsigned long parent_rate) { struct clk_corediv *corediv = to_corediv_clk(hwclk); + const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc; const struct clk_corediv_desc *desc = corediv->desc; unsigned long flags = 0; u32 reg, div; @@ -143,17 +161,17 @@ static int clk_corediv_set_rate(struct clk_hw *hwclk, unsigned long rate, spin_lock_irqsave(&corediv->lock, flags); /* Write new divider to the divider ratio register */ - reg = readl(corediv->reg + CORE_CLK_DIV_RATIO_OFFSET); + reg = readl(corediv->reg + soc_desc->ratio_offset); reg &= ~(desc->mask << desc->offset); reg |= (div & desc->mask) << desc->offset; - writel(reg, corediv->reg + CORE_CLK_DIV_RATIO_OFFSET); + writel(reg, corediv->reg + soc_desc->ratio_offset); /* Set reload-force for this clock */ reg = readl(corediv->reg) | BIT(desc->fieldbit); writel(reg, corediv->reg); /* Now trigger the clock update */ - reg = readl(corediv->reg) | CORE_CLK_DIV_RATIO_RELOAD; + reg = readl(corediv->reg) | soc_desc->ratio_reload; writel(reg, corediv->reg); /* @@ -161,7 +179,7 @@ static int clk_corediv_set_rate(struct clk_hw *hwclk, unsigned long rate, * ratios request and the reload request. */ udelay(1000); - reg &= ~(CORE_CLK_DIV_RATIO_MASK | CORE_CLK_DIV_RATIO_RELOAD); + reg &= ~(CORE_CLK_DIV_RATIO_MASK | soc_desc->ratio_reload); writel(reg, corediv->reg); udelay(1000); @@ -170,16 +188,25 @@ static int clk_corediv_set_rate(struct clk_hw *hwclk, unsigned long rate, return 0; } -static const struct clk_ops corediv_ops = { - .enable = clk_corediv_enable, - .disable = clk_corediv_disable, - .is_enabled = clk_corediv_is_enabled, - .recalc_rate = clk_corediv_recalc_rate, - .round_rate = clk_corediv_round_rate, - .set_rate = clk_corediv_set_rate, +static const struct clk_corediv_soc_desc armada370_corediv_soc = { + .descs = mvebu_corediv_desc, + .ndescs = ARRAY_SIZE(mvebu_corediv_desc), + .ops = { + .enable = clk_corediv_enable, + .disable = clk_corediv_disable, + .is_enabled = clk_corediv_is_enabled, + .recalc_rate = clk_corediv_recalc_rate, + .round_rate = clk_corediv_round_rate, + .set_rate = clk_corediv_set_rate, + }, + .ratio_reload = BIT(8), + .enable_bit_offset = 24, + .ratio_offset = 0x8, }; -static void __init mvebu_corediv_clk_init(struct device_node *node) +static void __init +mvebu_corediv_clk_init(struct device_node *node, + const struct clk_corediv_soc_desc *soc_desc) { struct clk_init_data init; struct clk_corediv *corediv; @@ -195,7 +222,7 @@ static void __init mvebu_corediv_clk_init(struct device_node *node) parent_name = of_clk_get_parent_name(node, 0); - clk_data.clk_num = ARRAY_SIZE(mvebu_corediv_desc); + clk_data.clk_num = soc_desc->ndescs; /* clks holds the clock array */ clks = kcalloc(clk_data.clk_num, sizeof(struct clk *), @@ -216,10 +243,11 @@ static void __init mvebu_corediv_clk_init(struct device_node *node) init.num_parents = 1; init.parent_names = &parent_name; init.name = clk_name; - init.ops = &corediv_ops; + init.ops = &soc_desc->ops; init.flags = 0; - corediv[i].desc = mvebu_corediv_desc + i; + corediv[i].soc_desc = soc_desc; + corediv[i].desc = soc_desc->descs + i; corediv[i].reg = base; corediv[i].hw.init = &init; @@ -236,5 +264,10 @@ err_free_clks: err_unmap: iounmap(base); } -CLK_OF_DECLARE(mvebu_corediv_clk, "marvell,armada-370-corediv-clock", - mvebu_corediv_clk_init); + +static void __init armada370_corediv_clk_init(struct device_node *node) +{ + return mvebu_corediv_clk_init(node, &armada370_corediv_soc); +} +CLK_OF_DECLARE(armada370_corediv_clk, "marvell,armada-370-corediv-clock", + armada370_corediv_clk_init); -- cgit v1.2.3 From e4aec65c865d200196efd42f26ff1630b44997a5 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Fri, 14 Feb 2014 17:15:03 -0300 Subject: clk: mvebu: add Armada 375 support to the corediv clock driver This commit adds support for the Core Divider clocks of the Armada 375. Compared to Armada 370 and XP the Core Divider clocks of the 375 cannot be gated: only their ratio can be changed. This is reflected by the fact that the enable, disable and is_enabled clock operations are not defined, and that the enable_bit_offset field is also undefined. Signed-off-by: Thomas Petazzoni Signed-off-by: Ezequiel Garcia Signed-off-by: Jason Cooper --- drivers/clk/mvebu/clk-corediv.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/mvebu/clk-corediv.c b/drivers/clk/mvebu/clk-corediv.c index f9e8a120f22b..4da60760be10 100644 --- a/drivers/clk/mvebu/clk-corediv.c +++ b/drivers/clk/mvebu/clk-corediv.c @@ -204,6 +204,18 @@ static const struct clk_corediv_soc_desc armada370_corediv_soc = { .ratio_offset = 0x8, }; +static const struct clk_corediv_soc_desc armada375_corediv_soc = { + .descs = mvebu_corediv_desc, + .ndescs = ARRAY_SIZE(mvebu_corediv_desc), + .ops = { + .recalc_rate = clk_corediv_recalc_rate, + .round_rate = clk_corediv_round_rate, + .set_rate = clk_corediv_set_rate, + }, + .ratio_reload = BIT(8), + .ratio_offset = 0x8, +}; + static void __init mvebu_corediv_clk_init(struct device_node *node, const struct clk_corediv_soc_desc *soc_desc) @@ -271,3 +283,10 @@ static void __init armada370_corediv_clk_init(struct device_node *node) } CLK_OF_DECLARE(armada370_corediv_clk, "marvell,armada-370-corediv-clock", armada370_corediv_clk_init); + +static void __init armada375_corediv_clk_init(struct device_node *node) +{ + return mvebu_corediv_clk_init(node, &armada375_corediv_soc); +} +CLK_OF_DECLARE(armada375_corediv_clk, "marvell,armada-375-corediv-clock", + armada375_corediv_clk_init); -- cgit v1.2.3 From 41d3c64f8cddb51f7caf45b7591d27024cae38a0 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Mon, 10 Feb 2014 18:32:44 +0100 Subject: clk: mvebu: add clock support for Armada 375 Add the clock support for the new SoC Armada 375: core clocks and gating clocks. Signed-off-by: Gregory CLEMENT Reviewed-by: Thomas Petazzoni Signed-off-by: Jason Cooper --- drivers/clk/mvebu/Kconfig | 4 + drivers/clk/mvebu/Makefile | 1 + drivers/clk/mvebu/armada-375.c | 184 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100644 drivers/clk/mvebu/armada-375.c (limited to 'drivers') diff --git a/drivers/clk/mvebu/Kconfig b/drivers/clk/mvebu/Kconfig index c339b829d3e3..a54ba170634b 100644 --- a/drivers/clk/mvebu/Kconfig +++ b/drivers/clk/mvebu/Kconfig @@ -13,6 +13,10 @@ config ARMADA_370_CLK select MVEBU_CLK_CPU select MVEBU_CLK_COREDIV +config ARMADA_375_CLK + bool + select MVEBU_CLK_COMMON + config ARMADA_XP_CLK bool select MVEBU_CLK_COMMON diff --git a/drivers/clk/mvebu/Makefile b/drivers/clk/mvebu/Makefile index 21bbfb4a9f42..0b13811b9f62 100644 --- a/drivers/clk/mvebu/Makefile +++ b/drivers/clk/mvebu/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_MVEBU_CLK_CPU) += clk-cpu.o obj-$(CONFIG_MVEBU_CLK_COREDIV) += clk-corediv.o obj-$(CONFIG_ARMADA_370_CLK) += armada-370.o +obj-$(CONFIG_ARMADA_375_CLK) += armada-375.o obj-$(CONFIG_ARMADA_XP_CLK) += armada-xp.o obj-$(CONFIG_DOVE_CLK) += dove.o obj-$(CONFIG_KIRKWOOD_CLK) += kirkwood.o diff --git a/drivers/clk/mvebu/armada-375.c b/drivers/clk/mvebu/armada-375.c new file mode 100644 index 000000000000..c991a4d95e10 --- /dev/null +++ b/drivers/clk/mvebu/armada-375.c @@ -0,0 +1,184 @@ +/* + * Marvell Armada 375 SoC clocks + * + * Copyright (C) 2014 Marvell + * + * Gregory CLEMENT + * Sebastian Hesselbarth + * Andrew Lunn + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include "common.h" + +/* + * Core Clocks + */ + +/* + * For the Armada 375 SoCs, the CPU, DDR and L2 clocks frequencies are + * all modified at the same time, and not separately as for the Armada + * 370 or the Armada XP SoCs. + * + * SAR0[21:17] : CPU frequency DDR frequency L2 frequency + * 6 = 400 MHz 400 MHz 200 MHz + * 15 = 600 MHz 600 MHz 300 MHz + * 21 = 800 MHz 534 MHz 400 MHz + * 25 = 1000 MHz 500 MHz 500 MHz + * others reserved. + * + * SAR0[22] : TCLK frequency + * 0 = 166 MHz + * 1 = 200 MHz + */ + +#define SAR1_A375_TCLK_FREQ_OPT 22 +#define SAR1_A375_TCLK_FREQ_OPT_MASK 0x1 +#define SAR1_A375_CPU_DDR_L2_FREQ_OPT 17 +#define SAR1_A375_CPU_DDR_L2_FREQ_OPT_MASK 0x1F + +static const u32 armada_375_tclk_frequencies[] __initconst = { + 166000000, + 200000000, +}; + +static u32 __init armada_375_get_tclk_freq(void __iomem *sar) +{ + u8 tclk_freq_select; + + tclk_freq_select = ((readl(sar) >> SAR1_A375_TCLK_FREQ_OPT) & + SAR1_A375_TCLK_FREQ_OPT_MASK); + return armada_375_tclk_frequencies[tclk_freq_select]; +} + + +static const u32 armada_375_cpu_frequencies[] __initconst = { + 0, 0, 0, 0, 0, 0, + 400000000, + 0, 0, 0, 0, 0, 0, 0, 0, + 600000000, + 0, 0, 0, 0, 0, + 800000000, + 0, 0, 0, + 1000000000, +}; + +static u32 __init armada_375_get_cpu_freq(void __iomem *sar) +{ + u8 cpu_freq_select; + + cpu_freq_select = ((readl(sar) >> SAR1_A375_CPU_DDR_L2_FREQ_OPT) & + SAR1_A375_CPU_DDR_L2_FREQ_OPT_MASK); + if (cpu_freq_select >= ARRAY_SIZE(armada_375_cpu_frequencies)) { + pr_err("Selected CPU frequency (%d) unsupported\n", + cpu_freq_select); + return 0; + } else + return armada_375_cpu_frequencies[cpu_freq_select]; +} + +enum { A375_CPU_TO_DDR, A375_CPU_TO_L2 }; + +static const struct coreclk_ratio armada_375_coreclk_ratios[] __initconst = { + { .id = A375_CPU_TO_L2, .name = "l2clk" }, + { .id = A375_CPU_TO_DDR, .name = "ddrclk" }, +}; + +static const int armada_375_cpu_l2_ratios[32][2] __initconst = { + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {1, 2}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {1, 2}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {1, 2}, {0, 1}, {0, 1}, + {0, 1}, {1, 2}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, +}; + +static const int armada_375_cpu_ddr_ratios[32][2] __initconst = { + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {1, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {2, 3}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {2, 3}, {0, 1}, {0, 1}, + {0, 1}, {1, 2}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, +}; + +static void __init armada_375_get_clk_ratio( + void __iomem *sar, int id, int *mult, int *div) +{ + u32 opt = ((readl(sar) >> SAR1_A375_CPU_DDR_L2_FREQ_OPT) & + SAR1_A375_CPU_DDR_L2_FREQ_OPT_MASK); + + switch (id) { + case A375_CPU_TO_L2: + *mult = armada_375_cpu_l2_ratios[opt][0]; + *div = armada_375_cpu_l2_ratios[opt][1]; + break; + case A375_CPU_TO_DDR: + *mult = armada_375_cpu_ddr_ratios[opt][0]; + *div = armada_375_cpu_ddr_ratios[opt][1]; + break; + } +} + +static const struct coreclk_soc_desc armada_375_coreclks = { + .get_tclk_freq = armada_375_get_tclk_freq, + .get_cpu_freq = armada_375_get_cpu_freq, + .get_clk_ratio = armada_375_get_clk_ratio, + .ratios = armada_375_coreclk_ratios, + .num_ratios = ARRAY_SIZE(armada_375_coreclk_ratios), +}; + +static void __init armada_375_coreclk_init(struct device_node *np) +{ + mvebu_coreclk_setup(np, &armada_375_coreclks); +} +CLK_OF_DECLARE(armada_375_core_clk, "marvell,armada-375-core-clock", + armada_375_coreclk_init); + +/* + * Clock Gating Control + */ +static const struct clk_gating_soc_desc armada_375_gating_desc[] __initconst = { + { "mu", NULL, 2 }, + { "pp", NULL, 3 }, + { "ptp", NULL, 4 }, + { "pex0", NULL, 5 }, + { "pex1", NULL, 6 }, + { "audio", NULL, 8 }, + { "nd_clk", "nand", 11 }, + { "sata0_link", "sata0_core", 14 }, + { "sata0_core", NULL, 15 }, + { "usb3", NULL, 16 }, + { "sdio", NULL, 17 }, + { "usb", NULL, 18 }, + { "gop", NULL, 19 }, + { "sata1_link", "sata1_core", 20 }, + { "sata1_core", NULL, 21 }, + { "xor0", NULL, 22 }, + { "xor1", NULL, 23 }, + { "copro", NULL, 24 }, + { "tdm", NULL, 25 }, + { "crypto0_enc", NULL, 28 }, + { "crypto0_core", NULL, 29 }, + { "crypto1_enc", NULL, 30 }, + { "crypto1_core", NULL, 31 }, + { } +}; + +static void __init armada_375_clk_gating_init(struct device_node *np) +{ + mvebu_clk_gating_setup(np, armada_375_gating_desc); +} +CLK_OF_DECLARE(armada_375_clk_gating, "marvell,armada-375-gating-clock", + armada_375_clk_gating_init); -- cgit v1.2.3 From 0e85aeced4d6cd2f4f2755eff453da3e0a2286fc Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Mon, 10 Feb 2014 18:32:47 +0100 Subject: clk: mvebu: add clock support for Armada 380/385 Add the clock support for the new SoCs Armada 380 and Armada 385: core clocks and gating clocks. Signed-off-by: Gregory CLEMENT Reviewed-by: Thomas Petazzoni Signed-off-by: Jason Cooper --- drivers/clk/mvebu/Kconfig | 4 + drivers/clk/mvebu/Makefile | 1 + drivers/clk/mvebu/armada-38x.c | 167 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 172 insertions(+) create mode 100644 drivers/clk/mvebu/armada-38x.c (limited to 'drivers') diff --git a/drivers/clk/mvebu/Kconfig b/drivers/clk/mvebu/Kconfig index a54ba170634b..693f7be129f1 100644 --- a/drivers/clk/mvebu/Kconfig +++ b/drivers/clk/mvebu/Kconfig @@ -17,6 +17,10 @@ config ARMADA_375_CLK bool select MVEBU_CLK_COMMON +config ARMADA_38X_CLK + bool + select MVEBU_CLK_COMMON + config ARMADA_XP_CLK bool select MVEBU_CLK_COMMON diff --git a/drivers/clk/mvebu/Makefile b/drivers/clk/mvebu/Makefile index 0b13811b9f62..4c66162fb0b4 100644 --- a/drivers/clk/mvebu/Makefile +++ b/drivers/clk/mvebu/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_MVEBU_CLK_COREDIV) += clk-corediv.o obj-$(CONFIG_ARMADA_370_CLK) += armada-370.o obj-$(CONFIG_ARMADA_375_CLK) += armada-375.o +obj-$(CONFIG_ARMADA_38X_CLK) += armada-38x.o obj-$(CONFIG_ARMADA_XP_CLK) += armada-xp.o obj-$(CONFIG_DOVE_CLK) += dove.o obj-$(CONFIG_KIRKWOOD_CLK) += kirkwood.o diff --git a/drivers/clk/mvebu/armada-38x.c b/drivers/clk/mvebu/armada-38x.c new file mode 100644 index 000000000000..8bccf4ecdab6 --- /dev/null +++ b/drivers/clk/mvebu/armada-38x.c @@ -0,0 +1,167 @@ +/* + * Marvell Armada 380/385 SoC clocks + * + * Copyright (C) 2014 Marvell + * + * Gregory CLEMENT + * Sebastian Hesselbarth + * Andrew Lunn + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include "common.h" + +/* + * SAR[14:10] : Ratios between PCLK0, NBCLK, HCLK and DRAM clocks + * + * SAR[15] : TCLK frequency + * 0 = 250 MHz + * 1 = 200 MHz + */ + +#define SAR_A380_TCLK_FREQ_OPT 15 +#define SAR_A380_TCLK_FREQ_OPT_MASK 0x1 +#define SAR_A380_CPU_DDR_L2_FREQ_OPT 10 +#define SAR_A380_CPU_DDR_L2_FREQ_OPT_MASK 0x1F + +static const u32 armada_38x_tclk_frequencies[] __initconst = { + 250000000, + 200000000, +}; + +static u32 __init armada_38x_get_tclk_freq(void __iomem *sar) +{ + u8 tclk_freq_select; + + tclk_freq_select = ((readl(sar) >> SAR_A380_TCLK_FREQ_OPT) & + SAR_A380_TCLK_FREQ_OPT_MASK); + return armada_38x_tclk_frequencies[tclk_freq_select]; +} + +static const u32 armada_38x_cpu_frequencies[] __initconst = { + 0, 0, 0, 0, + 1066 * 1000 * 1000, 0, 0, 0, + 1332 * 1000 * 1000, 0, 0, 0, + 1600 * 1000 * 1000, +}; + +static u32 __init armada_38x_get_cpu_freq(void __iomem *sar) +{ + u8 cpu_freq_select; + + cpu_freq_select = ((readl(sar) >> SAR_A380_CPU_DDR_L2_FREQ_OPT) & + SAR_A380_CPU_DDR_L2_FREQ_OPT_MASK); + if (cpu_freq_select >= ARRAY_SIZE(armada_38x_cpu_frequencies)) { + pr_err("Selected CPU frequency (%d) unsupported\n", + cpu_freq_select); + return 0; + } + + return armada_38x_cpu_frequencies[cpu_freq_select]; +} + +enum { A380_CPU_TO_DDR, A380_CPU_TO_L2 }; + +static const struct coreclk_ratio armada_38x_coreclk_ratios[] __initconst = { + { .id = A380_CPU_TO_L2, .name = "l2clk" }, + { .id = A380_CPU_TO_DDR, .name = "ddrclk" }, +}; + +static const int armada_38x_cpu_l2_ratios[32][2] __initconst = { + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {1, 2}, {0, 1}, {0, 1}, {0, 1}, + {1, 2}, {0, 1}, {0, 1}, {0, 1}, + {1, 2}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, +}; + +static const int armada_38x_cpu_ddr_ratios[32][2] __initconst = { + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {1, 2}, {0, 1}, {0, 1}, {0, 1}, + {1, 2}, {0, 1}, {0, 1}, {0, 1}, + {1, 2}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, +}; + +static void __init armada_38x_get_clk_ratio( + void __iomem *sar, int id, int *mult, int *div) +{ + u32 opt = ((readl(sar) >> SAR_A380_CPU_DDR_L2_FREQ_OPT) & + SAR_A380_CPU_DDR_L2_FREQ_OPT_MASK); + + switch (id) { + case A380_CPU_TO_L2: + *mult = armada_38x_cpu_l2_ratios[opt][0]; + *div = armada_38x_cpu_l2_ratios[opt][1]; + break; + case A380_CPU_TO_DDR: + *mult = armada_38x_cpu_ddr_ratios[opt][0]; + *div = armada_38x_cpu_ddr_ratios[opt][1]; + break; + } +} + +static const struct coreclk_soc_desc armada_38x_coreclks = { + .get_tclk_freq = armada_38x_get_tclk_freq, + .get_cpu_freq = armada_38x_get_cpu_freq, + .get_clk_ratio = armada_38x_get_clk_ratio, + .ratios = armada_38x_coreclk_ratios, + .num_ratios = ARRAY_SIZE(armada_38x_coreclk_ratios), +}; + +static void __init armada_38x_coreclk_init(struct device_node *np) +{ + mvebu_coreclk_setup(np, &armada_38x_coreclks); +} +CLK_OF_DECLARE(armada_38x_core_clk, "marvell,armada-380-core-clock", + armada_38x_coreclk_init); + +/* + * Clock Gating Control + */ +static const struct clk_gating_soc_desc armada_38x_gating_desc[] __initconst = { + { "audio", NULL, 0 }, + { "ge2", NULL, 2 }, + { "ge1", NULL, 3 }, + { "ge0", NULL, 4 }, + { "pex1", NULL, 5 }, + { "pex2", NULL, 6 }, + { "pex3", NULL, 7 }, + { "pex0", NULL, 8 }, + { "usb3h0", NULL, 9 }, + { "usb3h1", NULL, 10 }, + { "usb3d", NULL, 11 }, + { "bm", NULL, 13 }, + { "crypto0z", NULL, 14 }, + { "sata0", NULL, 15 }, + { "crypto1z", NULL, 16 }, + { "sdio", NULL, 17 }, + { "usb2", NULL, 18 }, + { "crypto1", NULL, 21 }, + { "xor0", NULL, 22 }, + { "crypto0", NULL, 23 }, + { "tdm", NULL, 25 }, + { "xor1", NULL, 28 }, + { "sata1", NULL, 30 }, + { } +}; + +static void __init armada_38x_clk_gating_init(struct device_node *np) +{ + mvebu_clk_gating_setup(np, armada_38x_gating_desc); +} +CLK_OF_DECLARE(armada_38x_clk_gating, "marvell,armada-380-gating-clock", + armada_38x_clk_gating_init); -- cgit v1.2.3 From cfb0086dca781ac7ab9b63e6c8088064c29a4118 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 7 Feb 2014 16:21:49 +0100 Subject: clk: sunxi: Add support for USB clock-register reset bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The usb-clk register is special in that it not only contains clk gate bits, but also has a few reset bits. This commit adds support for this by allowing gates type sunxi clks to also register a reset controller. Signed-off-by: Hans de Goede Acked-by: Philipp Zabel Acked-by: Maxime Ripard Signed-off-by: Emilio López --- drivers/clk/sunxi/clk-sunxi.c | 71 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 736fb604bfbc..23beb6e89558 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "clk-factors.h" @@ -687,6 +688,59 @@ static void __init sunxi_divider_clk_setup(struct device_node *node, +/** + * sunxi_gates_reset... - reset bits in leaf gate clk registers handling + */ + +struct gates_reset_data { + void __iomem *reg; + spinlock_t *lock; + struct reset_controller_dev rcdev; +}; + +static int sunxi_gates_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct gates_reset_data *data = container_of(rcdev, + struct gates_reset_data, + rcdev); + unsigned long flags; + u32 reg; + + spin_lock_irqsave(data->lock, flags); + + reg = readl(data->reg); + writel(reg & ~BIT(id), data->reg); + + spin_unlock_irqrestore(data->lock, flags); + + return 0; +} + +static int sunxi_gates_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct gates_reset_data *data = container_of(rcdev, + struct gates_reset_data, + rcdev); + unsigned long flags; + u32 reg; + + spin_lock_irqsave(data->lock, flags); + + reg = readl(data->reg); + writel(reg | BIT(id), data->reg); + + spin_unlock_irqrestore(data->lock, flags); + + return 0; +} + +static struct reset_control_ops sunxi_gates_reset_ops = { + .assert = sunxi_gates_reset_assert, + .deassert = sunxi_gates_reset_deassert, +}; + /** * sunxi_gates_clk_setup() - Setup function for leaf gates on clocks */ @@ -695,6 +749,7 @@ static void __init sunxi_divider_clk_setup(struct device_node *node, struct gates_data { DECLARE_BITMAP(mask, SUNXI_GATES_MAX_SIZE); + u32 reset_mask; }; static const struct gates_data sun4i_axi_gates_data __initconst = { @@ -765,6 +820,7 @@ static void __init sunxi_gates_clk_setup(struct device_node *node, struct gates_data *data) { struct clk_onecell_data *clk_data; + struct gates_reset_data *reset_data; const char *clk_parent; const char *clk_name; void *reg; @@ -808,6 +864,21 @@ static void __init sunxi_gates_clk_setup(struct device_node *node, clk_data->clk_num = i; of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + /* Register a reset controler for gates with reset bits */ + if (data->reset_mask == 0) + return; + + reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL); + if (!reset_data) + return; + + reset_data->reg = reg; + reset_data->lock = &clk_lock; + reset_data->rcdev.nr_resets = __fls(data->reset_mask) + 1; + reset_data->rcdev.ops = &sunxi_gates_reset_ops; + reset_data->rcdev.of_node = node; + reset_controller_register(&reset_data->rcdev); } -- cgit v1.2.3 From 5abdbf2f497c1769aa9df284ad125d40641207c7 Mon Sep 17 00:00:00 2001 From: Roman Byshko Date: Fri, 7 Feb 2014 16:21:50 +0100 Subject: clk: sunxi: Add USB clock register defintions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add register definitions for the usb-clk register found on sun4i, sun5i and sun7i SoCs. Signed-off-by: Roman Byshko Signed-off-by: Hans de Goede Acked-by: Maxime Ripard Signed-off-by: Emilio López --- Documentation/devicetree/bindings/clock/sunxi.txt | 5 +++++ drivers/clk/sunxi/clk-sunxi.c | 12 ++++++++++++ 2 files changed, 17 insertions(+) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index 0cf679b8384f..ca2b6920fca4 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -37,6 +37,8 @@ Required properties: "allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31 "allwinner,sun4i-mod0-clk" - for the module 0 family of clocks "allwinner,sun7i-a20-out-clk" - for the external output clocks + "allwinner,sun4i-a10-usb-clk" - for usb gates + resets on A10 / A20 + "allwinner,sun5i-a13-usb-clk" - for usb gates + resets on A13 Required properties for all clocks: - reg : shall be the control register address for the clock. @@ -50,6 +52,9 @@ Required properties for all clocks: If the clock module only has one output, the name shall be the module name. +And "allwinner,*-usb-clk" clocks also require: +- reset-cells : shall be set to 1 + Clock consumers should specify the desired clocks they use with a "clocks" phandle cell. Consumers that are using a gated clock should provide an additional ID in their clock property. This ID is the diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 23beb6e89558..a779c31b0de9 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -816,6 +816,16 @@ static const struct gates_data sun7i_a20_apb1_gates_data __initconst = { .mask = { 0xff80ff }, }; +static const struct gates_data sun4i_a10_usb_gates_data __initconst = { + .mask = {0x1C0}, + .reset_mask = 0x07, +}; + +static const struct gates_data sun5i_a13_usb_gates_data __initconst = { + .mask = {0x140}, + .reset_mask = 0x03, +}; + static void __init sunxi_gates_clk_setup(struct device_node *node, struct gates_data *data) { @@ -1107,6 +1117,8 @@ static const struct of_device_id clk_gates_match[] __initconst = { {.compatible = "allwinner,sun6i-a31-apb1-gates-clk", .data = &sun6i_a31_apb1_gates_data,}, {.compatible = "allwinner,sun7i-a20-apb1-gates-clk", .data = &sun7i_a20_apb1_gates_data,}, {.compatible = "allwinner,sun6i-a31-apb2-gates-clk", .data = &sun6i_a31_apb2_gates_data,}, + {.compatible = "allwinner,sun4i-a10-usb-clk", .data = &sun4i_a10_usb_gates_data,}, + {.compatible = "allwinner,sun5i-a13-usb-clk", .data = &sun5i_a13_usb_gates_data,}, {} }; -- cgit v1.2.3 From 92ef67c53ad92487c3c8de75e7940384c2edd793 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 5 Feb 2014 14:05:03 +0100 Subject: clk: sunxi: Add support for PLL6 on the A31 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The A31 has a slightly different PLL6 clock. Add support for this new clock in our driver. Signed-off-by: Maxime Ripard Signed-off-by: Emilio López --- Documentation/devicetree/bindings/clock/sunxi.txt | 1 + drivers/clk/sunxi/clk-sunxi.c | 45 +++++++++++++++++++++++ 2 files changed, 46 insertions(+) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index ca2b6920fca4..c37c764cbbc5 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -11,6 +11,7 @@ Required properties: "allwinner,sun6i-a31-pll1-clk" - for the main PLL clock on A31 "allwinner,sun4i-pll5-clk" - for the PLL5 clock "allwinner,sun4i-pll6-clk" - for the PLL6 clock + "allwinner,sun6i-a31-pll6-clk" - for the PLL6 clock on A31 "allwinner,sun4i-cpu-clk" - for the CPU multiplexer clock "allwinner,sun4i-axi-clk" - for the AXI clock "allwinner,sun4i-axi-gates-clk" - for the AXI gates diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index a779c31b0de9..d4cf297e8fac 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -252,7 +252,38 @@ static void sun4i_get_pll5_factors(u32 *freq, u32 parent_rate, *n = DIV_ROUND_UP(div, (*k+1)); } +/** + * sun6i_a31_get_pll6_factors() - calculates n, k factors for A31 PLL6 + * PLL6 rate is calculated as follows + * rate = parent_rate * n * (k + 1) / 2 + * parent_rate is always 24Mhz + */ + +static void sun6i_a31_get_pll6_factors(u32 *freq, u32 parent_rate, + u8 *n, u8 *k, u8 *m, u8 *p) +{ + u8 div; + + /* + * We always have 24MHz / 2, so we can just say that our + * parent clock is 12MHz. + */ + parent_rate = parent_rate / 2; + + /* Normalize value to a parent_rate multiple (24M / 2) */ + div = *freq / parent_rate; + *freq = parent_rate * div; + + /* we were called to round the frequency, we can now return */ + if (n == NULL) + return; + + *k = div / 32; + if (*k > 3) + *k = 3; + *n = DIV_ROUND_UP(div, (*k+1)); +} /** * sun4i_get_apb1_factors() - calculates m, p factors for APB1 @@ -420,6 +451,13 @@ static struct clk_factors_config sun4i_pll5_config = { .kwidth = 2, }; +static struct clk_factors_config sun6i_a31_pll6_config = { + .nshift = 8, + .nwidth = 5, + .kshift = 4, + .kwidth = 2, +}; + static struct clk_factors_config sun4i_apb1_config = { .mshift = 0, .mwidth = 5, @@ -469,6 +507,12 @@ static const struct factors_data sun4i_pll6_data __initconst = { .name = "pll6", }; +static const struct factors_data sun6i_a31_pll6_data __initconst = { + .enable = 31, + .table = &sun6i_a31_pll6_config, + .getter = sun6i_a31_get_pll6_factors, +}; + static const struct factors_data sun4i_apb1_data __initconst = { .table = &sun4i_apb1_config, .getter = sun4i_get_apb1_factors, @@ -1069,6 +1113,7 @@ free_clkdata: static const struct of_device_id clk_factors_match[] __initconst = { {.compatible = "allwinner,sun4i-pll1-clk", .data = &sun4i_pll1_data,}, {.compatible = "allwinner,sun6i-a31-pll1-clk", .data = &sun6i_a31_pll1_data,}, + {.compatible = "allwinner,sun6i-a31-pll6-clk", .data = &sun6i_a31_pll6_data,}, {.compatible = "allwinner,sun4i-apb1-clk", .data = &sun4i_apb1_data,}, {.compatible = "allwinner,sun4i-mod0-clk", .data = &sun4i_mod0_data,}, {.compatible = "allwinner,sun7i-a20-out-clk", .data = &sun7i_a20_out_data,}, -- cgit v1.2.3 From e4c6d6c11bee5ff11feb837a0a76103b3eba252f Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Mon, 10 Feb 2014 18:35:47 +0800 Subject: clk: sunxi: Add Allwinner A20/A31 GMAC clock unit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Allwinner A20/A31 clock module controls the transmit clock source and interface type of the GMAC ethernet controller. Model this as a single clock for GMAC drivers to use. Signed-off-by: Chen-Yu Tsai Acked-by: Maxime Ripard Signed-off-by: Emilio López --- Documentation/devicetree/bindings/clock/sunxi.txt | 30 +++++++ drivers/clk/sunxi/clk-sunxi.c | 96 +++++++++++++++++++++++ 2 files changed, 126 insertions(+) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index c37c764cbbc5..256a9089f677 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -38,6 +38,7 @@ Required properties: "allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31 "allwinner,sun4i-mod0-clk" - for the module 0 family of clocks "allwinner,sun7i-a20-out-clk" - for the external output clocks + "allwinner,sun7i-a20-gmac-clk" - for the GMAC clock module on A20/A31 "allwinner,sun4i-a10-usb-clk" - for usb gates + resets on A10 / A20 "allwinner,sun5i-a13-usb-clk" - for usb gates + resets on A13 @@ -56,6 +57,9 @@ Required properties for all clocks: And "allwinner,*-usb-clk" clocks also require: - reset-cells : shall be set to 1 +For "allwinner,sun7i-a20-gmac-clk", the parent clocks shall be fixed rate +dummy clocks at 25 MHz and 125 MHz, respectively. See example. + Clock consumers should specify the desired clocks they use with a "clocks" phandle cell. Consumers that are using a gated clock should provide an additional ID in their clock property. This ID is the @@ -102,3 +106,29 @@ mmc0_clk: clk@01c20088 { clocks = <&osc24M>, <&pll6 1>, <&pll5 1>; clock-output-names = "mmc0"; }; + +mii_phy_tx_clk: clk@2 { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <25000000>; + clock-output-names = "mii_phy_tx"; +}; + +gmac_int_tx_clk: clk@3 { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <125000000>; + clock-output-names = "gmac_int_tx"; +}; + +gmac_clk: clk@01c20164 { + #clock-cells = <0>; + compatible = "allwinner,sun7i-a20-gmac-clk"; + reg = <0x01c20164 0x4>; + /* + * The first clock must be fixed at 25MHz; + * the second clock must be fixed at 125MHz + */ + clocks = <&mii_phy_tx_clk>, <&gmac_int_tx_clk>; + clock-output-names = "gmac"; +}; diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index d4cf297e8fac..335c98721218 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -410,6 +410,102 @@ static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate, +/** + * sun7i_a20_gmac_clk_setup - Setup function for A20/A31 GMAC clock module + * + * This clock looks something like this + * ________________________ + * MII TX clock from PHY >-----|___________ _________|----> to GMAC core + * GMAC Int. RGMII TX clk >----|___________\__/__gate---|----> to PHY + * Ext. 125MHz RGMII TX clk >--|__divider__/ | + * |________________________| + * + * The external 125 MHz reference is optional, i.e. GMAC can use its + * internal TX clock just fine. The A31 GMAC clock module does not have + * the divider controls for the external reference. + * + * To keep it simple, let the GMAC use either the MII TX clock for MII mode, + * and its internal TX clock for GMII and RGMII modes. The GMAC driver should + * select the appropriate source and gate/ungate the output to the PHY. + * + * Only the GMAC should use this clock. Altering the clock so that it doesn't + * match the GMAC's operation parameters will result in the GMAC not being + * able to send traffic out. The GMAC driver should set the clock rate and + * enable/disable this clock to configure the required state. The clock + * driver then responds by auto-reparenting the clock. + */ + +#define SUN7I_A20_GMAC_GPIT 2 +#define SUN7I_A20_GMAC_MASK 0x3 +#define SUN7I_A20_GMAC_PARENTS 2 + +static void __init sun7i_a20_gmac_clk_setup(struct device_node *node) +{ + struct clk *clk; + struct clk_mux *mux; + struct clk_gate *gate; + const char *clk_name = node->name; + const char *parents[SUN7I_A20_GMAC_PARENTS]; + void *reg; + + if (of_property_read_string(node, "clock-output-names", &clk_name)) + return; + + /* allocate mux and gate clock structs */ + mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL); + if (!mux) + return; + + gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL); + if (!gate) + goto free_mux; + + /* gmac clock requires exactly 2 parents */ + parents[0] = of_clk_get_parent_name(node, 0); + parents[1] = of_clk_get_parent_name(node, 1); + if (!parents[0] || !parents[1]) + goto free_gate; + + reg = of_iomap(node, 0); + if (!reg) + goto free_gate; + + /* set up gate and fixed rate properties */ + gate->reg = reg; + gate->bit_idx = SUN7I_A20_GMAC_GPIT; + gate->lock = &clk_lock; + mux->reg = reg; + mux->mask = SUN7I_A20_GMAC_MASK; + mux->flags = CLK_MUX_INDEX_BIT; + mux->lock = &clk_lock; + + clk = clk_register_composite(NULL, clk_name, + parents, SUN7I_A20_GMAC_PARENTS, + &mux->hw, &clk_mux_ops, + NULL, NULL, + &gate->hw, &clk_gate_ops, + 0); + + if (IS_ERR(clk)) + goto iounmap_reg; + + of_clk_add_provider(node, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + + return; + +iounmap_reg: + iounmap(reg); +free_gate: + kfree(gate); +free_mux: + kfree(mux); +} +CLK_OF_DECLARE(sun7i_a20_gmac, "allwinner,sun7i-a20-gmac-clk", + sun7i_a20_gmac_clk_setup); + + + /** * sunxi_factors_clk_setup() - Setup function for factor clocks */ -- cgit v1.2.3 From fd1b22f6fb3b31980b80505ac9d86521569ed2ee Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 6 Feb 2014 09:55:57 +0100 Subject: clk: sunxi: Add new clock compatibles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Allwinner A10 compatibles were following a slightly different compatible patterns than the rest of the SoCs for historical reasons. Add compatibles matching the other pattern to the clock driver for consistency, and keep the older one for backward compatibility. Signed-off-by: Maxime Ripard Signed-off-by: Emilio López --- Documentation/devicetree/bindings/clock/sunxi.txt | 36 +++++++++++------------ drivers/clk/sunxi/clk-sunxi.c | 30 +++++++++---------- 2 files changed, 33 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index 256a9089f677..a5160d8cbb5f 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -6,37 +6,37 @@ This binding uses the common clock binding[1]. Required properties: - compatible : shall be one of the following: - "allwinner,sun4i-osc-clk" - for a gatable oscillator - "allwinner,sun4i-pll1-clk" - for the main PLL clock and PLL4 + "allwinner,sun4i-a10-osc-clk" - for a gatable oscillator + "allwinner,sun4i-a10-pll1-clk" - for the main PLL clock and PLL4 "allwinner,sun6i-a31-pll1-clk" - for the main PLL clock on A31 - "allwinner,sun4i-pll5-clk" - for the PLL5 clock - "allwinner,sun4i-pll6-clk" - for the PLL6 clock + "allwinner,sun4i-a10-pll5-clk" - for the PLL5 clock + "allwinner,sun4i-a10-pll6-clk" - for the PLL6 clock "allwinner,sun6i-a31-pll6-clk" - for the PLL6 clock on A31 - "allwinner,sun4i-cpu-clk" - for the CPU multiplexer clock - "allwinner,sun4i-axi-clk" - for the AXI clock - "allwinner,sun4i-axi-gates-clk" - for the AXI gates - "allwinner,sun4i-ahb-clk" - for the AHB clock - "allwinner,sun4i-ahb-gates-clk" - for the AHB gates on A10 + "allwinner,sun4i-a10-cpu-clk" - for the CPU multiplexer clock + "allwinner,sun4i-a10-axi-clk" - for the AXI clock + "allwinner,sun4i-a10-axi-gates-clk" - for the AXI gates + "allwinner,sun4i-a10-ahb-clk" - for the AHB clock + "allwinner,sun4i-a10-ahb-gates-clk" - for the AHB gates on A10 "allwinner,sun5i-a13-ahb-gates-clk" - for the AHB gates on A13 "allwinner,sun5i-a10s-ahb-gates-clk" - for the AHB gates on A10s "allwinner,sun7i-a20-ahb-gates-clk" - for the AHB gates on A20 "allwinner,sun6i-a31-ahb1-mux-clk" - for the AHB1 multiplexer on A31 "allwinner,sun6i-a31-ahb1-gates-clk" - for the AHB1 gates on A31 - "allwinner,sun4i-apb0-clk" - for the APB0 clock - "allwinner,sun4i-apb0-gates-clk" - for the APB0 gates on A10 + "allwinner,sun4i-a10-apb0-clk" - for the APB0 clock + "allwinner,sun4i-a10-apb0-gates-clk" - for the APB0 gates on A10 "allwinner,sun5i-a13-apb0-gates-clk" - for the APB0 gates on A13 "allwinner,sun5i-a10s-apb0-gates-clk" - for the APB0 gates on A10s "allwinner,sun7i-a20-apb0-gates-clk" - for the APB0 gates on A20 - "allwinner,sun4i-apb1-clk" - for the APB1 clock - "allwinner,sun4i-apb1-mux-clk" - for the APB1 clock muxing - "allwinner,sun4i-apb1-gates-clk" - for the APB1 gates on A10 + "allwinner,sun4i-a10-apb1-clk" - for the APB1 clock + "allwinner,sun4i-a10-apb1-mux-clk" - for the APB1 clock muxing + "allwinner,sun4i-a10-apb1-gates-clk" - for the APB1 gates on A10 "allwinner,sun5i-a13-apb1-gates-clk" - for the APB1 gates on A13 "allwinner,sun5i-a10s-apb1-gates-clk" - for the APB1 gates on A10s "allwinner,sun6i-a31-apb1-gates-clk" - for the APB1 gates on A31 "allwinner,sun7i-a20-apb1-gates-clk" - for the APB1 gates on A20 "allwinner,sun6i-a31-apb2-div-clk" - for the APB2 gates on A31 "allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31 - "allwinner,sun4i-mod0-clk" - for the module 0 family of clocks + "allwinner,sun4i-a10-mod0-clk" - for the module 0 family of clocks "allwinner,sun7i-a20-out-clk" - for the external output clocks "allwinner,sun7i-a20-gmac-clk" - for the GMAC clock module on A20/A31 "allwinner,sun4i-a10-usb-clk" - for usb gates + resets on A10 / A20 @@ -69,7 +69,7 @@ For example: osc24M: clk@01c20050 { #clock-cells = <0>; - compatible = "allwinner,sun4i-osc-clk"; + compatible = "allwinner,sun4i-a10-osc-clk"; reg = <0x01c20050 0x4>; clocks = <&osc24M_fixed>; clock-output-names = "osc24M"; @@ -77,7 +77,7 @@ osc24M: clk@01c20050 { pll1: clk@01c20000 { #clock-cells = <0>; - compatible = "allwinner,sun4i-pll1-clk"; + compatible = "allwinner,sun4i-a10-pll1-clk"; reg = <0x01c20000 0x4>; clocks = <&osc24M>; clock-output-names = "pll1"; @@ -93,7 +93,7 @@ pll5: clk@01c20020 { cpu: cpu@01c20054 { #clock-cells = <0>; - compatible = "allwinner,sun4i-cpu-clk"; + compatible = "allwinner,sun4i-a10-cpu-clk"; reg = <0x01c20054 0x4>; clocks = <&osc32k>, <&osc24M>, <&pll1>; clock-output-names = "cpu"; diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 335c98721218..23baad9d934a 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -80,7 +80,7 @@ err_free_gate: err_free_fixed: kfree(fixed); } -CLK_OF_DECLARE(sun4i_osc, "allwinner,sun4i-osc-clk", sun4i_osc_clk_setup); +CLK_OF_DECLARE(sun4i_osc, "allwinner,sun4i-a10-osc-clk", sun4i_osc_clk_setup); @@ -1207,52 +1207,52 @@ free_clkdata: /* Matches for factors clocks */ static const struct of_device_id clk_factors_match[] __initconst = { - {.compatible = "allwinner,sun4i-pll1-clk", .data = &sun4i_pll1_data,}, + {.compatible = "allwinner,sun4i-a10-pll1-clk", .data = &sun4i_pll1_data,}, {.compatible = "allwinner,sun6i-a31-pll1-clk", .data = &sun6i_a31_pll1_data,}, {.compatible = "allwinner,sun6i-a31-pll6-clk", .data = &sun6i_a31_pll6_data,}, - {.compatible = "allwinner,sun4i-apb1-clk", .data = &sun4i_apb1_data,}, - {.compatible = "allwinner,sun4i-mod0-clk", .data = &sun4i_mod0_data,}, + {.compatible = "allwinner,sun4i-a10-apb1-clk", .data = &sun4i_apb1_data,}, + {.compatible = "allwinner,sun4i-a10-mod0-clk", .data = &sun4i_mod0_data,}, {.compatible = "allwinner,sun7i-a20-out-clk", .data = &sun7i_a20_out_data,}, {} }; /* Matches for divider clocks */ static const struct of_device_id clk_div_match[] __initconst = { - {.compatible = "allwinner,sun4i-axi-clk", .data = &sun4i_axi_data,}, - {.compatible = "allwinner,sun4i-ahb-clk", .data = &sun4i_ahb_data,}, - {.compatible = "allwinner,sun4i-apb0-clk", .data = &sun4i_apb0_data,}, + {.compatible = "allwinner,sun4i-a10-axi-clk", .data = &sun4i_axi_data,}, + {.compatible = "allwinner,sun4i-a10-ahb-clk", .data = &sun4i_ahb_data,}, + {.compatible = "allwinner,sun4i-a10-apb0-clk", .data = &sun4i_apb0_data,}, {.compatible = "allwinner,sun6i-a31-apb2-div-clk", .data = &sun6i_a31_apb2_div_data,}, {} }; /* Matches for divided outputs */ static const struct of_device_id clk_divs_match[] __initconst = { - {.compatible = "allwinner,sun4i-pll5-clk", .data = &pll5_divs_data,}, - {.compatible = "allwinner,sun4i-pll6-clk", .data = &pll6_divs_data,}, + {.compatible = "allwinner,sun4i-a10-pll5-clk", .data = &pll5_divs_data,}, + {.compatible = "allwinner,sun4i-a10-pll6-clk", .data = &pll6_divs_data,}, {} }; /* Matches for mux clocks */ static const struct of_device_id clk_mux_match[] __initconst = { - {.compatible = "allwinner,sun4i-cpu-clk", .data = &sun4i_cpu_mux_data,}, - {.compatible = "allwinner,sun4i-apb1-mux-clk", .data = &sun4i_apb1_mux_data,}, + {.compatible = "allwinner,sun4i-a10-cpu-clk", .data = &sun4i_cpu_mux_data,}, + {.compatible = "allwinner,sun4i-a10-apb1-mux-clk", .data = &sun4i_apb1_mux_data,}, {.compatible = "allwinner,sun6i-a31-ahb1-mux-clk", .data = &sun6i_a31_ahb1_mux_data,}, {} }; /* Matches for gate clocks */ static const struct of_device_id clk_gates_match[] __initconst = { - {.compatible = "allwinner,sun4i-axi-gates-clk", .data = &sun4i_axi_gates_data,}, - {.compatible = "allwinner,sun4i-ahb-gates-clk", .data = &sun4i_ahb_gates_data,}, + {.compatible = "allwinner,sun4i-a10-axi-gates-clk", .data = &sun4i_axi_gates_data,}, + {.compatible = "allwinner,sun4i-a10-ahb-gates-clk", .data = &sun4i_ahb_gates_data,}, {.compatible = "allwinner,sun5i-a10s-ahb-gates-clk", .data = &sun5i_a10s_ahb_gates_data,}, {.compatible = "allwinner,sun5i-a13-ahb-gates-clk", .data = &sun5i_a13_ahb_gates_data,}, {.compatible = "allwinner,sun6i-a31-ahb1-gates-clk", .data = &sun6i_a31_ahb1_gates_data,}, {.compatible = "allwinner,sun7i-a20-ahb-gates-clk", .data = &sun7i_a20_ahb_gates_data,}, - {.compatible = "allwinner,sun4i-apb0-gates-clk", .data = &sun4i_apb0_gates_data,}, + {.compatible = "allwinner,sun4i-a10-apb0-gates-clk", .data = &sun4i_apb0_gates_data,}, {.compatible = "allwinner,sun5i-a10s-apb0-gates-clk", .data = &sun5i_a10s_apb0_gates_data,}, {.compatible = "allwinner,sun5i-a13-apb0-gates-clk", .data = &sun5i_a13_apb0_gates_data,}, {.compatible = "allwinner,sun7i-a20-apb0-gates-clk", .data = &sun7i_a20_apb0_gates_data,}, - {.compatible = "allwinner,sun4i-apb1-gates-clk", .data = &sun4i_apb1_gates_data,}, + {.compatible = "allwinner,sun4i-a10-apb1-gates-clk", .data = &sun4i_apb1_gates_data,}, {.compatible = "allwinner,sun5i-a10s-apb1-gates-clk", .data = &sun5i_a10s_apb1_gates_data,}, {.compatible = "allwinner,sun5i-a13-apb1-gates-clk", .data = &sun5i_a13_apb1_gates_data,}, {.compatible = "allwinner,sun6i-a31-apb1-gates-clk", .data = &sun6i_a31_apb1_gates_data,}, -- cgit v1.2.3 From 5324fda79e28d1d4db0631369b39ff263d0142b4 Mon Sep 17 00:00:00 2001 From: Soren Brinkmann Date: Wed, 22 Jan 2014 11:48:37 -0800 Subject: clk: Fix notifier documentation Contradicting to documenation, the notifier callbacks do receive the original clock rate in struct clk_notifier_data.old_rate and the new frequency struct clk_notifier_data.new_rate, independent of the notification reason. This behavior also seems to make more sense, since callbacks can use the same code to deterimine whether clocks are scaled up or down. Something which would not even possible in the post-rate-change case if the behavior was as documented. Signed-off-by: Soren Brinkmann Signed-off-by: Mike Turquette --- drivers/clk/clk.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 5517944495d8..ea2ca9ff2677 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2259,20 +2259,11 @@ void __clk_put(struct clk *clk) * re-enter into the clk framework by calling any top-level clk APIs; * this will cause a nested prepare_lock mutex. * - * Pre-change notifier callbacks will be passed the current, pre-change - * rate of the clk via struct clk_notifier_data.old_rate. The new, - * post-change rate of the clk is passed via struct + * In all notification cases cases (pre, post and abort rate change) the + * original clock rate is passed to the callback via struct + * clk_notifier_data.old_rate and the new frequency is passed via struct * clk_notifier_data.new_rate. * - * Post-change notifiers will pass the now-current, post-change rate of - * the clk in both struct clk_notifier_data.old_rate and struct - * clk_notifier_data.new_rate. - * - * Abort-change notifiers are effectively the opposite of pre-change - * notifiers: the original pre-change clk rate is passed in via struct - * clk_notifier_data.new_rate and the failed post-change rate is passed - * in via struct clk_notifier_data.old_rate. - * * clk_notifier_register() must be called from non-atomic context. * Returns -EINVAL if called with null arguments, -ENOMEM upon * allocation failure; otherwise, passes along the return value of -- cgit v1.2.3 From 6a7e71221d4e6cd185a51e2659f279da67f2e22d Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Mon, 9 Dec 2013 17:16:38 -0600 Subject: clk: socfpga: Map the clk manager base address in the clock driver The clk manager's base address was being mapped in SOCFPGA's arch code and being extern'ed out to the clock driver. This method is not correct, and the arch code was not really doing anything with that clk manager anyways. This patch moves the mapping of the clk manager's base address in the clock driver itself. Cleans up CLK_OF_DECLARE() into a single registration of all the clocks. Suggested-by: Arnd Bergmann Signed-off-by: Dinh Nguyen Acked-by: Arnd Bergmann --- v2: Use a static declaration for the clk_mgr_base_addr. Clean up the CLK_OF_DECLARE() as suggested by Arnd. --- arch/arm/mach-socfpga/socfpga.c | 4 ---- drivers/clk/socfpga/clk.c | 20 ++++++++++++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/arch/arm/mach-socfpga/socfpga.c b/arch/arm/mach-socfpga/socfpga.c index dd0d49cdbe09..c43c28118092 100644 --- a/arch/arm/mach-socfpga/socfpga.c +++ b/arch/arm/mach-socfpga/socfpga.c @@ -29,7 +29,6 @@ void __iomem *socfpga_scu_base_addr = ((void __iomem *)(SOCFPGA_SCU_VIRT_BASE)); void __iomem *sys_manager_base_addr; void __iomem *rst_manager_base_addr; -void __iomem *clk_mgr_base_addr; unsigned long cpu1start_addr; static struct map_desc scu_io_desc __initdata = { @@ -78,9 +77,6 @@ void __init socfpga_sysmgr_init(void) np = of_find_compatible_node(NULL, NULL, "altr,rst-mgr"); rst_manager_base_addr = of_iomap(np, 0); - - np = of_find_compatible_node(NULL, NULL, "altr,clk-mgr"); - clk_mgr_base_addr = of_iomap(np, 0); } static void __init socfpga_init_irq(void) diff --git a/drivers/clk/socfpga/clk.c b/drivers/clk/socfpga/clk.c index 5983a26a8c5f..4fb52e1fc848 100644 --- a/drivers/clk/socfpga/clk.c +++ b/drivers/clk/socfpga/clk.c @@ -22,6 +22,7 @@ #include #include #include +#include /* Clock Manager offsets */ #define CLKMGR_CTRL 0x0 @@ -55,7 +56,7 @@ #define div_mask(width) ((1 << (width)) - 1) #define streq(a, b) (strcmp((a), (b)) == 0) -extern void __iomem *clk_mgr_base_addr; +static void __iomem *clk_mgr_base_addr; struct socfpga_clk { struct clk_gate hw; @@ -320,19 +321,30 @@ static void __init socfpga_pll_init(struct device_node *node) { socfpga_clk_init(node, &clk_pll_ops); } -CLK_OF_DECLARE(socfpga_pll, "altr,socfpga-pll-clock", socfpga_pll_init); static void __init socfpga_periph_init(struct device_node *node) { socfpga_clk_init(node, &periclk_ops); } -CLK_OF_DECLARE(socfpga_periph, "altr,socfpga-perip-clk", socfpga_periph_init); static void __init socfpga_gate_init(struct device_node *node) { socfpga_gate_clk_init(node, &gateclk_ops); } -CLK_OF_DECLARE(socfpga_gate, "altr,socfpga-gate-clk", socfpga_gate_init); + +static struct of_device_id socfpga_child_clocks[] = { + { .compatible = "altr,socfpga-pll-clock", socfpga_pll_init, }, + { .compatible = "altr,socfpga-perip-clk", socfpga_periph_init, }, + { .compatible = "altr,socfpga-gate-clk", socfpga_gate_init, }, + {}, +}; + +static void __init socfpga_clkmgr_init(struct device_node *node) +{ + clk_mgr_base_addr = of_iomap(node, 0); + of_clk_init(socfpga_child_clocks); +} +CLK_OF_DECLARE(socfpga_mgr, "altr,clk-mgr", socfpga_clkmgr_init); void __init socfpga_init_clocks(void) { -- cgit v1.2.3 From b7cec13f082fcc6e690559657d3f5493ea6eecb7 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Fri, 13 Dec 2013 16:38:28 -0600 Subject: clk: socfpga: Look for the GPIO_DB_CLK by its offset After the patch: "clk: socfpga: Map the clk manager base address in the clock driver" The clk->name field in socfpga_clk_recalc_rate() was getting cleared. Replace looking for the GPIO_DB_CLK by its divider offset instead. Also rename the define SOCFPGA_DB_CLK_OFFSET -> SOCFPGA_GPIO_DB_CLK_OFFSET, as this represents the GPIO_DB_CLK. Signed-off-by: Dinh Nguyen --- drivers/clk/socfpga/clk.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/socfpga/clk.c b/drivers/clk/socfpga/clk.c index 4fb52e1fc848..cba21a0823b6 100644 --- a/drivers/clk/socfpga/clk.c +++ b/drivers/clk/socfpga/clk.c @@ -51,7 +51,7 @@ #define SOCFPGA_NAND_CLK "nand_clk" #define SOCFPGA_NAND_X_CLK "nand_x_clk" #define SOCFPGA_MMC_CLK "sdmmc_clk" -#define SOCFPGA_DB_CLK "gpio_db_clk" +#define SOCFPGA_GPIO_DB_CLK_OFFSET 0xA8 #define div_mask(width) ((1 << (width)) - 1) #define streq(a, b) (strcmp((a), (b)) == 0) @@ -234,7 +234,8 @@ static unsigned long socfpga_clk_recalc_rate(struct clk_hw *hwclk, else if (socfpgaclk->div_reg) { val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift; val &= div_mask(socfpgaclk->width); - if (streq(hwclk->init->name, SOCFPGA_DB_CLK)) + /* Check for GPIO_DB_CLK by its offset */ + if ((int)socfpgaclk->div_reg & SOCFPGA_GPIO_DB_CLK_OFFSET) div = val + 1; else div = (1 << val); -- cgit v1.2.3 From 77f1057777818c575f01d1e27d76bc73310eec6e Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Thu, 5 Dec 2013 11:34:48 -0600 Subject: clk: socfpga: Remove socfpga_init_clocks The only thing that socfpga_init_clocks was doing is setting up the smp_twd clk. Now that twd-timer's clock phandle is populated in the DTS, we can remove this function. Signed-off-by: Dinh Nguyen Acked-by: Arnd Bergmann --- arch/arm/mach-socfpga/socfpga.c | 1 - drivers/clk/socfpga/clk.c | 10 ---------- 2 files changed, 11 deletions(-) (limited to 'drivers') diff --git a/arch/arm/mach-socfpga/socfpga.c b/arch/arm/mach-socfpga/socfpga.c index c43c28118092..d86231e11b34 100644 --- a/arch/arm/mach-socfpga/socfpga.c +++ b/arch/arm/mach-socfpga/socfpga.c @@ -102,7 +102,6 @@ static void __init socfpga_cyclone5_init(void) { l2x0_of_init(0, ~0UL); of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); - socfpga_init_clocks(); } static const char *altera_dt_match[] = { diff --git a/drivers/clk/socfpga/clk.c b/drivers/clk/socfpga/clk.c index cba21a0823b6..75b6257f61b9 100644 --- a/drivers/clk/socfpga/clk.c +++ b/drivers/clk/socfpga/clk.c @@ -347,13 +347,3 @@ static void __init socfpga_clkmgr_init(struct device_node *node) } CLK_OF_DECLARE(socfpga_mgr, "altr,clk-mgr", socfpga_clkmgr_init); -void __init socfpga_init_clocks(void) -{ - struct clk *clk; - int ret; - - clk = clk_register_fixed_factor(NULL, "smp_twd", "mpuclk", 0, 1, 4); - ret = clk_register_clkdev(clk, NULL, "smp_twd"); - if (ret) - pr_err("smp_twd alias not registered\n"); -} -- cgit v1.2.3 From ef5043c2d91eb5476cf2a810caf3aee37a8b4709 Mon Sep 17 00:00:00 2001 From: Steffen Trumtrar Date: Thu, 2 Jan 2014 12:31:17 -0600 Subject: clk: socfpga: remove unused field The clk_name field from the socfpga_clk struct is unused. Remove it. Signed-off-by: Steffen Trumtrar Signed-off-by: Dinh Nguyen --- drivers/clk/socfpga/clk.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/socfpga/clk.c b/drivers/clk/socfpga/clk.c index 75b6257f61b9..eb98c1afa15c 100644 --- a/drivers/clk/socfpga/clk.c +++ b/drivers/clk/socfpga/clk.c @@ -61,7 +61,6 @@ static void __iomem *clk_mgr_base_addr; struct socfpga_clk { struct clk_gate hw; char *parent_name; - char *clk_name; u32 fixed_div; void __iomem *div_reg; u32 width; /* only valid if div_reg != 0 */ -- cgit v1.2.3 From 0c5a1872ba04dbcf8430d805a8c34e0ee22f1f75 Mon Sep 17 00:00:00 2001 From: Steffen Trumtrar Date: Thu, 2 Jan 2014 12:34:14 -0600 Subject: clk: socfpga: fix define typo It should be SOCFPGA instead of SOCFGPA. Signed-off-by: Steffen Trumtrar Signed-off-by: Dinh Nguyen --- drivers/clk/socfpga/clk.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/socfpga/clk.c b/drivers/clk/socfpga/clk.c index eb98c1afa15c..61aa29debdc8 100644 --- a/drivers/clk/socfpga/clk.c +++ b/drivers/clk/socfpga/clk.c @@ -44,7 +44,7 @@ #define SOCFPGA_PLL_DIVF_SHIFT 3 #define SOCFPGA_PLL_DIVQ_MASK 0x003F0000 #define SOCFPGA_PLL_DIVQ_SHIFT 16 -#define SOCFGPA_MAX_PARENTS 3 +#define SOCFPGA_MAX_PARENTS 3 #define SOCFPGA_L4_MP_CLK "l4_mp_clk" #define SOCFPGA_L4_SP_CLK "l4_sp_clk" @@ -258,7 +258,7 @@ static void __init socfpga_gate_clk_init(struct device_node *node, struct clk *clk; struct socfpga_clk *socfpga_clk; const char *clk_name = node->name; - const char *parent_name[SOCFGPA_MAX_PARENTS]; + const char *parent_name[SOCFPGA_MAX_PARENTS]; struct clk_init_data init; int rc; int i = 0; @@ -299,7 +299,7 @@ static void __init socfpga_gate_clk_init(struct device_node *node, init.name = clk_name; init.ops = ops; init.flags = 0; - while (i < SOCFGPA_MAX_PARENTS && (parent_name[i] = + while (i < SOCFPGA_MAX_PARENTS && (parent_name[i] = of_clk_get_parent_name(node, i)) != NULL) i++; -- cgit v1.2.3 From 97259e99bdc9144d071815536f1dbc2e41c6b5a8 Mon Sep 17 00:00:00 2001 From: Steffen Trumtrar Date: Mon, 6 Jan 2014 10:27:37 -0600 Subject: clk: socfpga: split clk code Move the different kinds of clocks into their own files. The reason is to aid readability of the code. This also goes along with the other SoC-specific clock drivers. The split introduces new structs for the three types of clocks and uses them. Other changes are not done to the code. Signed-off-by: Steffen Trumtrar Signed-off-by: Dinh Nguyen --- drivers/clk/socfpga/Makefile | 3 + drivers/clk/socfpga/clk-gate.c | 195 +++++++++++++++++++++++++ drivers/clk/socfpga/clk-periph.c | 94 ++++++++++++ drivers/clk/socfpga/clk-pll.c | 111 ++++++++++++++ drivers/clk/socfpga/clk.c | 308 +-------------------------------------- drivers/clk/socfpga/clk.h | 57 ++++++++ 6 files changed, 462 insertions(+), 306 deletions(-) create mode 100644 drivers/clk/socfpga/clk-gate.c create mode 100644 drivers/clk/socfpga/clk-periph.c create mode 100644 drivers/clk/socfpga/clk-pll.c create mode 100644 drivers/clk/socfpga/clk.h (limited to 'drivers') diff --git a/drivers/clk/socfpga/Makefile b/drivers/clk/socfpga/Makefile index 0303c0b99cd0..7e2d15a0c7b8 100644 --- a/drivers/clk/socfpga/Makefile +++ b/drivers/clk/socfpga/Makefile @@ -1 +1,4 @@ obj-y += clk.o +obj-y += clk-gate.o +obj-y += clk-pll.o +obj-y += clk-periph.o diff --git a/drivers/clk/socfpga/clk-gate.c b/drivers/clk/socfpga/clk-gate.c new file mode 100644 index 000000000000..4efcf4e33a82 --- /dev/null +++ b/drivers/clk/socfpga/clk-gate.c @@ -0,0 +1,195 @@ +/* + * Copyright 2011-2012 Calxeda, Inc. + * Copyright (C) 2012-2013 Altera Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Based from clk-highbank.c + * + */ +#include +#include +#include +#include +#include + +#include "clk.h" + +#define SOCFPGA_L4_MP_CLK "l4_mp_clk" +#define SOCFPGA_L4_SP_CLK "l4_sp_clk" +#define SOCFPGA_NAND_CLK "nand_clk" +#define SOCFPGA_NAND_X_CLK "nand_x_clk" +#define SOCFPGA_MMC_CLK "sdmmc_clk" +#define SOCFPGA_GPIO_DB_CLK_OFFSET 0xA8 + +#define div_mask(width) ((1 << (width)) - 1) +#define streq(a, b) (strcmp((a), (b)) == 0) + +#define to_socfpga_gate_clk(p) container_of(p, struct socfpga_gate_clk, hw.hw) + +static u8 socfpga_clk_get_parent(struct clk_hw *hwclk) +{ + u32 l4_src; + u32 perpll_src; + + if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) { + l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC); + return l4_src &= 0x1; + } + if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) { + l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC); + return !!(l4_src & 2); + } + + perpll_src = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC); + if (streq(hwclk->init->name, SOCFPGA_MMC_CLK)) + return perpll_src &= 0x3; + if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) || + streq(hwclk->init->name, SOCFPGA_NAND_X_CLK)) + return (perpll_src >> 2) & 3; + + /* QSPI clock */ + return (perpll_src >> 4) & 3; + +} + +static int socfpga_clk_set_parent(struct clk_hw *hwclk, u8 parent) +{ + u32 src_reg; + + if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) { + src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC); + src_reg &= ~0x1; + src_reg |= parent; + writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC); + } else if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) { + src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC); + src_reg &= ~0x2; + src_reg |= (parent << 1); + writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC); + } else { + src_reg = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC); + if (streq(hwclk->init->name, SOCFPGA_MMC_CLK)) { + src_reg &= ~0x3; + src_reg |= parent; + } else if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) || + streq(hwclk->init->name, SOCFPGA_NAND_X_CLK)) { + src_reg &= ~0xC; + src_reg |= (parent << 2); + } else {/* QSPI clock */ + src_reg &= ~0x30; + src_reg |= (parent << 4); + } + writel(src_reg, clk_mgr_base_addr + CLKMGR_PERPLL_SRC); + } + + return 0; +} + +static unsigned long socfpga_clk_recalc_rate(struct clk_hw *hwclk, + unsigned long parent_rate) +{ + struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk); + u32 div = 1, val; + + if (socfpgaclk->fixed_div) + div = socfpgaclk->fixed_div; + else if (socfpgaclk->div_reg) { + val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift; + val &= div_mask(socfpgaclk->width); + /* Check for GPIO_DB_CLK by its offset */ + if ((int) socfpgaclk->div_reg & SOCFPGA_GPIO_DB_CLK_OFFSET) + div = val + 1; + else + div = (1 << val); + } + + return parent_rate / div; +} + +static struct clk_ops gateclk_ops = { + .recalc_rate = socfpga_clk_recalc_rate, + .get_parent = socfpga_clk_get_parent, + .set_parent = socfpga_clk_set_parent, +}; + +static void __init __socfpga_gate_init(struct device_node *node, + const struct clk_ops *ops) +{ + u32 clk_gate[2]; + u32 div_reg[3]; + u32 fixed_div; + struct clk *clk; + struct socfpga_gate_clk *socfpga_clk; + const char *clk_name = node->name; + const char *parent_name[SOCFPGA_MAX_PARENTS]; + struct clk_init_data init; + int rc; + int i = 0; + + socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL); + if (WARN_ON(!socfpga_clk)) + return; + + rc = of_property_read_u32_array(node, "clk-gate", clk_gate, 2); + if (rc) + clk_gate[0] = 0; + + if (clk_gate[0]) { + socfpga_clk->hw.reg = clk_mgr_base_addr + clk_gate[0]; + socfpga_clk->hw.bit_idx = clk_gate[1]; + + gateclk_ops.enable = clk_gate_ops.enable; + gateclk_ops.disable = clk_gate_ops.disable; + } + + rc = of_property_read_u32(node, "fixed-divider", &fixed_div); + if (rc) + socfpga_clk->fixed_div = 0; + else + socfpga_clk->fixed_div = fixed_div; + + rc = of_property_read_u32_array(node, "div-reg", div_reg, 3); + if (!rc) { + socfpga_clk->div_reg = clk_mgr_base_addr + div_reg[0]; + socfpga_clk->shift = div_reg[1]; + socfpga_clk->width = div_reg[2]; + } else { + socfpga_clk->div_reg = 0; + } + + of_property_read_string(node, "clock-output-names", &clk_name); + + init.name = clk_name; + init.ops = ops; + init.flags = 0; + while (i < SOCFPGA_MAX_PARENTS && (parent_name[i] = + of_clk_get_parent_name(node, i)) != NULL) + i++; + + init.parent_names = parent_name; + init.num_parents = i; + socfpga_clk->hw.hw.init = &init; + + clk = clk_register(NULL, &socfpga_clk->hw.hw); + if (WARN_ON(IS_ERR(clk))) { + kfree(socfpga_clk); + return; + } + rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); + if (WARN_ON(rc)) + return; +} + +void __init socfpga_gate_init(struct device_node *node) +{ + __socfpga_gate_init(node, &gateclk_ops); +} diff --git a/drivers/clk/socfpga/clk-periph.c b/drivers/clk/socfpga/clk-periph.c new file mode 100644 index 000000000000..81623a3736f9 --- /dev/null +++ b/drivers/clk/socfpga/clk-periph.c @@ -0,0 +1,94 @@ +/* + * Copyright 2011-2012 Calxeda, Inc. + * Copyright (C) 2012-2013 Altera Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Based from clk-highbank.c + * + */ +#include +#include +#include +#include +#include + +#include "clk.h" + +#define to_socfpga_periph_clk(p) container_of(p, struct socfpga_periph_clk, hw.hw) + +static unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk, + unsigned long parent_rate) +{ + struct socfpga_periph_clk *socfpgaclk = to_socfpga_periph_clk(hwclk); + u32 div; + + if (socfpgaclk->fixed_div) + div = socfpgaclk->fixed_div; + else + div = ((readl(socfpgaclk->hw.reg) & 0x1ff) + 1); + + return parent_rate / div; +} + +static const struct clk_ops periclk_ops = { + .recalc_rate = clk_periclk_recalc_rate, +}; + +static __init void __socfpga_periph_init(struct device_node *node, + const struct clk_ops *ops) +{ + u32 reg; + struct clk *clk; + struct socfpga_periph_clk *periph_clk; + const char *clk_name = node->name; + const char *parent_name; + struct clk_init_data init; + int rc; + u32 fixed_div; + + of_property_read_u32(node, "reg", ®); + + periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL); + if (WARN_ON(!periph_clk)) + return; + + periph_clk->hw.reg = clk_mgr_base_addr + reg; + + rc = of_property_read_u32(node, "fixed-divider", &fixed_div); + if (rc) + periph_clk->fixed_div = 0; + else + periph_clk->fixed_div = fixed_div; + + of_property_read_string(node, "clock-output-names", &clk_name); + + init.name = clk_name; + init.ops = ops; + init.flags = 0; + parent_name = of_clk_get_parent_name(node, 0); + init.parent_names = &parent_name; + init.num_parents = 1; + + periph_clk->hw.hw.init = &init; + + clk = clk_register(NULL, &periph_clk->hw.hw); + if (WARN_ON(IS_ERR(clk))) { + kfree(periph_clk); + return; + } + rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); +} + +void __init socfpga_periph_init(struct device_node *node) +{ + __socfpga_periph_init(node, &periclk_ops); +} diff --git a/drivers/clk/socfpga/clk-pll.c b/drivers/clk/socfpga/clk-pll.c new file mode 100644 index 000000000000..362004e1e6fe --- /dev/null +++ b/drivers/clk/socfpga/clk-pll.c @@ -0,0 +1,111 @@ +/* + * Copyright 2011-2012 Calxeda, Inc. + * Copyright (C) 2012-2013 Altera Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Based from clk-highbank.c + * + */ +#include +#include +#include +#include +#include + +#include "clk.h" + +/* Clock bypass bits */ +#define MAINPLL_BYPASS (1<<0) +#define SDRAMPLL_BYPASS (1<<1) +#define SDRAMPLL_SRC_BYPASS (1<<2) +#define PERPLL_BYPASS (1<<3) +#define PERPLL_SRC_BYPASS (1<<4) + +#define SOCFPGA_PLL_BG_PWRDWN 0 +#define SOCFPGA_PLL_EXT_ENA 1 +#define SOCFPGA_PLL_PWR_DOWN 2 +#define SOCFPGA_PLL_DIVF_MASK 0x0000FFF8 +#define SOCFPGA_PLL_DIVF_SHIFT 3 +#define SOCFPGA_PLL_DIVQ_MASK 0x003F0000 +#define SOCFPGA_PLL_DIVQ_SHIFT 16 + +#define to_socfpga_clk(p) container_of(p, struct socfpga_pll, hw.hw) + +static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk, + unsigned long parent_rate) +{ + struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk); + unsigned long divf, divq, vco_freq, reg; + unsigned long bypass; + + reg = readl(socfpgaclk->hw.reg); + bypass = readl(clk_mgr_base_addr + CLKMGR_BYPASS); + if (bypass & MAINPLL_BYPASS) + return parent_rate; + + divf = (reg & SOCFPGA_PLL_DIVF_MASK) >> SOCFPGA_PLL_DIVF_SHIFT; + divq = (reg & SOCFPGA_PLL_DIVQ_MASK) >> SOCFPGA_PLL_DIVQ_SHIFT; + vco_freq = parent_rate * (divf + 1); + return vco_freq / (1 + divq); +} + +static struct clk_ops clk_pll_ops = { + .recalc_rate = clk_pll_recalc_rate, +}; + +static __init struct clk *__socfpga_pll_init(struct device_node *node, + const struct clk_ops *ops) +{ + u32 reg; + struct clk *clk; + struct socfpga_pll *pll_clk; + const char *clk_name = node->name; + const char *parent_name; + struct clk_init_data init; + int rc; + + of_property_read_u32(node, "reg", ®); + + pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL); + if (WARN_ON(!pll_clk)) + return NULL; + + pll_clk->hw.reg = clk_mgr_base_addr + reg; + + of_property_read_string(node, "clock-output-names", &clk_name); + + init.name = clk_name; + init.ops = ops; + init.flags = 0; + parent_name = of_clk_get_parent_name(node, 0); + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + + pll_clk->hw.hw.init = &init; + + pll_clk->hw.bit_idx = SOCFPGA_PLL_EXT_ENA; + clk_pll_ops.enable = clk_gate_ops.enable; + clk_pll_ops.disable = clk_gate_ops.disable; + + clk = clk_register(NULL, &pll_clk->hw.hw); + if (WARN_ON(IS_ERR(clk))) { + kfree(pll_clk); + return NULL; + } + rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); + return clk; +} + +void __init socfpga_pll_init(struct device_node *node) +{ + __socfpga_pll_init(node, &clk_pll_ops); +} diff --git a/drivers/clk/socfpga/clk.c b/drivers/clk/socfpga/clk.c index 61aa29debdc8..6217d5dc6644 100644 --- a/drivers/clk/socfpga/clk.c +++ b/drivers/clk/socfpga/clk.c @@ -24,313 +24,9 @@ #include #include -/* Clock Manager offsets */ -#define CLKMGR_CTRL 0x0 -#define CLKMGR_BYPASS 0x4 -#define CLKMGR_L4SRC 0x70 -#define CLKMGR_PERPLL_SRC 0xAC +#include "clk.h" -/* Clock bypass bits */ -#define MAINPLL_BYPASS (1<<0) -#define SDRAMPLL_BYPASS (1<<1) -#define SDRAMPLL_SRC_BYPASS (1<<2) -#define PERPLL_BYPASS (1<<3) -#define PERPLL_SRC_BYPASS (1<<4) - -#define SOCFPGA_PLL_BG_PWRDWN 0 -#define SOCFPGA_PLL_EXT_ENA 1 -#define SOCFPGA_PLL_PWR_DOWN 2 -#define SOCFPGA_PLL_DIVF_MASK 0x0000FFF8 -#define SOCFPGA_PLL_DIVF_SHIFT 3 -#define SOCFPGA_PLL_DIVQ_MASK 0x003F0000 -#define SOCFPGA_PLL_DIVQ_SHIFT 16 -#define SOCFPGA_MAX_PARENTS 3 - -#define SOCFPGA_L4_MP_CLK "l4_mp_clk" -#define SOCFPGA_L4_SP_CLK "l4_sp_clk" -#define SOCFPGA_NAND_CLK "nand_clk" -#define SOCFPGA_NAND_X_CLK "nand_x_clk" -#define SOCFPGA_MMC_CLK "sdmmc_clk" -#define SOCFPGA_GPIO_DB_CLK_OFFSET 0xA8 - -#define div_mask(width) ((1 << (width)) - 1) -#define streq(a, b) (strcmp((a), (b)) == 0) - -static void __iomem *clk_mgr_base_addr; - -struct socfpga_clk { - struct clk_gate hw; - char *parent_name; - u32 fixed_div; - void __iomem *div_reg; - u32 width; /* only valid if div_reg != 0 */ - u32 shift; /* only valid if div_reg != 0 */ -}; -#define to_socfpga_clk(p) container_of(p, struct socfpga_clk, hw.hw) - -static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk, - unsigned long parent_rate) -{ - struct socfpga_clk *socfpgaclk = to_socfpga_clk(hwclk); - unsigned long divf, divq, vco_freq, reg; - unsigned long bypass; - - reg = readl(socfpgaclk->hw.reg); - bypass = readl(clk_mgr_base_addr + CLKMGR_BYPASS); - if (bypass & MAINPLL_BYPASS) - return parent_rate; - - divf = (reg & SOCFPGA_PLL_DIVF_MASK) >> SOCFPGA_PLL_DIVF_SHIFT; - divq = (reg & SOCFPGA_PLL_DIVQ_MASK) >> SOCFPGA_PLL_DIVQ_SHIFT; - vco_freq = parent_rate * (divf + 1); - return vco_freq / (1 + divq); -} - - -static struct clk_ops clk_pll_ops = { - .recalc_rate = clk_pll_recalc_rate, -}; - -static unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk, - unsigned long parent_rate) -{ - struct socfpga_clk *socfpgaclk = to_socfpga_clk(hwclk); - u32 div; - - if (socfpgaclk->fixed_div) - div = socfpgaclk->fixed_div; - else - div = ((readl(socfpgaclk->hw.reg) & 0x1ff) + 1); - - return parent_rate / div; -} - -static const struct clk_ops periclk_ops = { - .recalc_rate = clk_periclk_recalc_rate, -}; - -static __init struct clk *socfpga_clk_init(struct device_node *node, - const struct clk_ops *ops) -{ - u32 reg; - struct clk *clk; - struct socfpga_clk *socfpga_clk; - const char *clk_name = node->name; - const char *parent_name; - struct clk_init_data init; - int rc; - u32 fixed_div; - - of_property_read_u32(node, "reg", ®); - - socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL); - if (WARN_ON(!socfpga_clk)) - return NULL; - - socfpga_clk->hw.reg = clk_mgr_base_addr + reg; - - rc = of_property_read_u32(node, "fixed-divider", &fixed_div); - if (rc) - socfpga_clk->fixed_div = 0; - else - socfpga_clk->fixed_div = fixed_div; - - of_property_read_string(node, "clock-output-names", &clk_name); - - init.name = clk_name; - init.ops = ops; - init.flags = 0; - parent_name = of_clk_get_parent_name(node, 0); - init.parent_names = &parent_name; - init.num_parents = 1; - - socfpga_clk->hw.hw.init = &init; - - if (streq(clk_name, "main_pll") || - streq(clk_name, "periph_pll") || - streq(clk_name, "sdram_pll")) { - socfpga_clk->hw.bit_idx = SOCFPGA_PLL_EXT_ENA; - clk_pll_ops.enable = clk_gate_ops.enable; - clk_pll_ops.disable = clk_gate_ops.disable; - } - - clk = clk_register(NULL, &socfpga_clk->hw.hw); - if (WARN_ON(IS_ERR(clk))) { - kfree(socfpga_clk); - return NULL; - } - rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); - return clk; -} - -static u8 socfpga_clk_get_parent(struct clk_hw *hwclk) -{ - u32 l4_src; - u32 perpll_src; - - if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) { - l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC); - return l4_src &= 0x1; - } - if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) { - l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC); - return !!(l4_src & 2); - } - - perpll_src = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC); - if (streq(hwclk->init->name, SOCFPGA_MMC_CLK)) - return perpll_src &= 0x3; - if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) || - streq(hwclk->init->name, SOCFPGA_NAND_X_CLK)) - return (perpll_src >> 2) & 3; - - /* QSPI clock */ - return (perpll_src >> 4) & 3; - -} - -static int socfpga_clk_set_parent(struct clk_hw *hwclk, u8 parent) -{ - u32 src_reg; - - if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) { - src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC); - src_reg &= ~0x1; - src_reg |= parent; - writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC); - } else if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) { - src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC); - src_reg &= ~0x2; - src_reg |= (parent << 1); - writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC); - } else { - src_reg = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC); - if (streq(hwclk->init->name, SOCFPGA_MMC_CLK)) { - src_reg &= ~0x3; - src_reg |= parent; - } else if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) || - streq(hwclk->init->name, SOCFPGA_NAND_X_CLK)) { - src_reg &= ~0xC; - src_reg |= (parent << 2); - } else {/* QSPI clock */ - src_reg &= ~0x30; - src_reg |= (parent << 4); - } - writel(src_reg, clk_mgr_base_addr + CLKMGR_PERPLL_SRC); - } - - return 0; -} - -static unsigned long socfpga_clk_recalc_rate(struct clk_hw *hwclk, - unsigned long parent_rate) -{ - struct socfpga_clk *socfpgaclk = to_socfpga_clk(hwclk); - u32 div = 1, val; - - if (socfpgaclk->fixed_div) - div = socfpgaclk->fixed_div; - else if (socfpgaclk->div_reg) { - val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift; - val &= div_mask(socfpgaclk->width); - /* Check for GPIO_DB_CLK by its offset */ - if ((int)socfpgaclk->div_reg & SOCFPGA_GPIO_DB_CLK_OFFSET) - div = val + 1; - else - div = (1 << val); - } - - return parent_rate / div; -} - -static struct clk_ops gateclk_ops = { - .recalc_rate = socfpga_clk_recalc_rate, - .get_parent = socfpga_clk_get_parent, - .set_parent = socfpga_clk_set_parent, -}; - -static void __init socfpga_gate_clk_init(struct device_node *node, - const struct clk_ops *ops) -{ - u32 clk_gate[2]; - u32 div_reg[3]; - u32 fixed_div; - struct clk *clk; - struct socfpga_clk *socfpga_clk; - const char *clk_name = node->name; - const char *parent_name[SOCFPGA_MAX_PARENTS]; - struct clk_init_data init; - int rc; - int i = 0; - - socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL); - if (WARN_ON(!socfpga_clk)) - return; - - rc = of_property_read_u32_array(node, "clk-gate", clk_gate, 2); - if (rc) - clk_gate[0] = 0; - - if (clk_gate[0]) { - socfpga_clk->hw.reg = clk_mgr_base_addr + clk_gate[0]; - socfpga_clk->hw.bit_idx = clk_gate[1]; - - gateclk_ops.enable = clk_gate_ops.enable; - gateclk_ops.disable = clk_gate_ops.disable; - } - - rc = of_property_read_u32(node, "fixed-divider", &fixed_div); - if (rc) - socfpga_clk->fixed_div = 0; - else - socfpga_clk->fixed_div = fixed_div; - - rc = of_property_read_u32_array(node, "div-reg", div_reg, 3); - if (!rc) { - socfpga_clk->div_reg = clk_mgr_base_addr + div_reg[0]; - socfpga_clk->shift = div_reg[1]; - socfpga_clk->width = div_reg[2]; - } else { - socfpga_clk->div_reg = NULL; - } - - of_property_read_string(node, "clock-output-names", &clk_name); - - init.name = clk_name; - init.ops = ops; - init.flags = 0; - while (i < SOCFPGA_MAX_PARENTS && (parent_name[i] = - of_clk_get_parent_name(node, i)) != NULL) - i++; - - init.parent_names = parent_name; - init.num_parents = i; - socfpga_clk->hw.hw.init = &init; - - clk = clk_register(NULL, &socfpga_clk->hw.hw); - if (WARN_ON(IS_ERR(clk))) { - kfree(socfpga_clk); - return; - } - rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); - if (WARN_ON(rc)) - return; -} - -static void __init socfpga_pll_init(struct device_node *node) -{ - socfpga_clk_init(node, &clk_pll_ops); -} - -static void __init socfpga_periph_init(struct device_node *node) -{ - socfpga_clk_init(node, &periclk_ops); -} - -static void __init socfpga_gate_init(struct device_node *node) -{ - socfpga_gate_clk_init(node, &gateclk_ops); -} +void __iomem *clk_mgr_base_addr; static struct of_device_id socfpga_child_clocks[] = { { .compatible = "altr,socfpga-pll-clock", socfpga_pll_init, }, diff --git a/drivers/clk/socfpga/clk.h b/drivers/clk/socfpga/clk.h new file mode 100644 index 000000000000..d2e54019c94f --- /dev/null +++ b/drivers/clk/socfpga/clk.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2013, Steffen Trumtrar + * + * based on drivers/clk/tegra/clk.h + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#ifndef __SOCFPGA_CLK_H +#define __SOCFPGA_CLK_H + +#include +#include + +/* Clock Manager offsets */ +#define CLKMGR_CTRL 0x0 +#define CLKMGR_BYPASS 0x4 +#define CLKMGR_L4SRC 0x70 +#define CLKMGR_PERPLL_SRC 0xAC + +#define SOCFPGA_MAX_PARENTS 3 + +extern void __iomem *clk_mgr_base_addr; + +void __init socfpga_pll_init(struct device_node *node); +void __init socfpga_periph_init(struct device_node *node); +void __init socfpga_gate_init(struct device_node *node); + +struct socfpga_pll { + struct clk_gate hw; +}; + +struct socfpga_gate_clk { + struct clk_gate hw; + char *parent_name; + u32 fixed_div; + void __iomem *div_reg; + u32 width; /* only valid if div_reg != 0 */ + u32 shift; /* only valid if div_reg != 0 */ + u32 clk_phase[2]; +}; + +struct socfpga_periph_clk { + struct clk_gate hw; + char *parent_name; + u32 fixed_div; +}; + +#endif /* SOCFPGA_CLK_H */ -- cgit v1.2.3 From 044abbde7bef2726489b5e11ec3fcdc012a4de4a Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Mon, 6 Jan 2014 12:17:24 -0600 Subject: clk: socfpga: Add a clk-phase property to the "altr,socfpga-gate-clk" The clk-phase property is used to represent the 2 clock phase values that is needed for the SD/MMC driver. Add a prepare function to the clk_ops, that will use the syscon driver to set sdmmc_clk's phase shift that is located in the system manager. Signed-off-by: Dinh Nguyen Acked-by: Zhangfei Gao Acked-by: Jaehoon Chung --- v9: none v8: Use degrees in the clk-phase binding property v7: Add dts property to represent the clk phase of the sdmmc_clk. Add a prepare function to the gate clk that will toggle clock phase setting. Remove the "altr,socfpga-sdmmc-sdr-clk" clock type. v6: Add a new clock type "altr,socfpga-sdmmc-sdr-clk" that will be used to set the phase shift settings. v5: Use the "snps,dw-mshc" binding v4: Use the sdmmc_clk prepare function to set the phase shift settings v3: Not use the syscon driver because as of 3.13-rc1, the syscon driver is loaded after the clock driver. v2: Use the syscon driver --- .../devicetree/bindings/clock/altr_socfpga.txt | 5 ++ arch/arm/boot/dts/socfpga.dtsi | 1 + drivers/clk/socfpga/clk-gate.c | 68 ++++++++++++++++++++++ 3 files changed, 74 insertions(+) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/clock/altr_socfpga.txt b/Documentation/devicetree/bindings/clock/altr_socfpga.txt index 0045433eae1f..5dfd145d3ccf 100644 --- a/Documentation/devicetree/bindings/clock/altr_socfpga.txt +++ b/Documentation/devicetree/bindings/clock/altr_socfpga.txt @@ -23,3 +23,8 @@ Optional properties: and the bit index. - div-reg : For "socfpga-gate-clk", div-reg contains the divider register, bit shift, and width. +- clk-phase : For the sdmmc_clk, contains the value of the clock phase that controls + the SDMMC CIU clock. The first value is the clk_sample(smpsel), and the second + value is the cclk_in_drv(drvsel). The clk-phase is used to enable the correct + hold/delay times that is needed for the SD/MMC CIU clock. The values of both + can be 0-315 degrees, in 45 degree increments. diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi index 537f1a5c07f5..3d62f47bead2 100644 --- a/arch/arm/boot/dts/socfpga.dtsi +++ b/arch/arm/boot/dts/socfpga.dtsi @@ -415,6 +415,7 @@ compatible = "altr,socfpga-gate-clk"; clocks = <&f2s_periph_ref_clk>, <&main_nand_sdmmc_clk>, <&per_nand_mmc_clk>; clk-gate = <0xa0 8>; + clk-phase = <0 135>; }; nand_x_clk: nand_x_clk { diff --git a/drivers/clk/socfpga/clk-gate.c b/drivers/clk/socfpga/clk-gate.c index 4efcf4e33a82..501d513bf890 100644 --- a/drivers/clk/socfpga/clk-gate.c +++ b/drivers/clk/socfpga/clk-gate.c @@ -19,7 +19,9 @@ #include #include #include +#include #include +#include #include "clk.h" @@ -35,6 +37,11 @@ #define to_socfpga_gate_clk(p) container_of(p, struct socfpga_gate_clk, hw.hw) +/* SDMMC Group for System Manager defines */ +#define SYSMGR_SDMMCGRP_CTRL_OFFSET 0x108 +#define SYSMGR_SDMMC_CTRL_SET(smplsel, drvsel) \ + ((((smplsel) & 0x7) << 3) | (((drvsel) & 0x7) << 0)) + static u8 socfpga_clk_get_parent(struct clk_hw *hwclk) { u32 l4_src; @@ -115,7 +122,61 @@ static unsigned long socfpga_clk_recalc_rate(struct clk_hw *hwclk, return parent_rate / div; } +static int socfpga_clk_prepare(struct clk_hw *hwclk) +{ + struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk); + struct regmap *sys_mgr_base_addr; + int i; + u32 hs_timing; + u32 clk_phase[2]; + + if (socfpgaclk->clk_phase[0] || socfpgaclk->clk_phase[1]) { + sys_mgr_base_addr = syscon_regmap_lookup_by_compatible("altr,sys-mgr"); + if (IS_ERR(sys_mgr_base_addr)) { + pr_err("%s: failed to find altr,sys-mgr regmap!\n", __func__); + return -EINVAL; + } + + for (i = 0; i < 2; i++) { + switch (socfpgaclk->clk_phase[i]) { + case 0: + clk_phase[i] = 0; + break; + case 45: + clk_phase[i] = 1; + break; + case 90: + clk_phase[i] = 2; + break; + case 135: + clk_phase[i] = 3; + break; + case 180: + clk_phase[i] = 4; + break; + case 225: + clk_phase[i] = 5; + break; + case 270: + clk_phase[i] = 6; + break; + case 315: + clk_phase[i] = 7; + break; + default: + clk_phase[i] = 0; + break; + } + } + hs_timing = SYSMGR_SDMMC_CTRL_SET(clk_phase[0], clk_phase[1]); + regmap_write(sys_mgr_base_addr, SYSMGR_SDMMCGRP_CTRL_OFFSET, + hs_timing); + } + return 0; +} + static struct clk_ops gateclk_ops = { + .prepare = socfpga_clk_prepare, .recalc_rate = socfpga_clk_recalc_rate, .get_parent = socfpga_clk_get_parent, .set_parent = socfpga_clk_set_parent, @@ -126,6 +187,7 @@ static void __init __socfpga_gate_init(struct device_node *node, { u32 clk_gate[2]; u32 div_reg[3]; + u32 clk_phase[2]; u32 fixed_div; struct clk *clk; struct socfpga_gate_clk *socfpga_clk; @@ -166,6 +228,12 @@ static void __init __socfpga_gate_init(struct device_node *node, socfpga_clk->div_reg = 0; } + rc = of_property_read_u32_array(node, "clk-phase", clk_phase, 2); + if (!rc) { + socfpga_clk->clk_phase[0] = clk_phase[0]; + socfpga_clk->clk_phase[1] = clk_phase[1]; + } + of_property_read_string(node, "clock-output-names", &clk_name); init.name = clk_name; -- cgit v1.2.3 From 7a0fc1a3df82d29e00b4c9f88a6b37450d6711f1 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Thu, 13 Feb 2014 18:02:49 +0000 Subject: clk: add clock-indices support Add a property called clock-indices to allow clock-output-names to be used where the index used to lookup a clock is not a 1:1 mapping to the array position in the clock-output-names Signed-off-by: Ben Dooks Signed-off-by: Mike Turquette --- .../devicetree/bindings/clock/clock-bindings.txt | 17 +++++++++++++++++ drivers/clk/clk.c | 20 +++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/clock/clock-bindings.txt b/Documentation/devicetree/bindings/clock/clock-bindings.txt index 7c52c29d99fa..700e7aac3717 100644 --- a/Documentation/devicetree/bindings/clock/clock-bindings.txt +++ b/Documentation/devicetree/bindings/clock/clock-bindings.txt @@ -44,6 +44,23 @@ For example: clocks by index. The names should reflect the clock output signal names for the device. +clock-indices: If the identifyng number for the clocks in the node + is not linear from zero, then the this mapping allows + the mapping of identifiers into the clock-output-names + array. + +For example, if we have two clocks <&oscillator 1> and <&oscillator 3>: + + oscillator { + compatible = "myclocktype"; + #clock-cells = <1>; + clock-indices = <1>, <3>; + clock-output-names = "clka", "clkb"; + } + + This ensures we do not have any empty nodes in clock-output-names + + ==Clock consumers== Required properties: diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index ea2ca9ff2677..a6f079d23eaa 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2496,8 +2496,12 @@ EXPORT_SYMBOL_GPL(of_clk_get_parent_count); const char *of_clk_get_parent_name(struct device_node *np, int index) { struct of_phandle_args clkspec; + struct property *prop; const char *clk_name; + const __be32 *vp; + u32 pv; int rc; + int count; if (index < 0) return NULL; @@ -2507,8 +2511,22 @@ const char *of_clk_get_parent_name(struct device_node *np, int index) if (rc) return NULL; + index = clkspec.args_count ? clkspec.args[0] : 0; + count = 0; + + /* if there is an indices property, use it to transfer the index + * specified into an array offset for the clock-output-names property. + */ + of_property_for_each_u32(clkspec.np, "clock-indices", prop, vp, pv) { + if (index == pv) { + index = count; + break; + } + count++; + } + if (of_property_read_string_index(clkspec.np, "clock-output-names", - clkspec.args_count ? clkspec.args[0] : 0, + index, &clk_name) < 0) clk_name = clkspec.np->name; -- cgit v1.2.3 From 22e5de816b49f1c75c1f1480a99d1c06d46fbe21 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 15 Jan 2014 11:13:25 +0530 Subject: clk: tegra: Staticize tegra_clk_periph_no_gate_ops tegra_clk_periph_no_gate_ops is a local symbol. Signed-off-by: Sachin Kamat Signed-off-by: Mike Turquette --- drivers/clk/tegra/clk-periph.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c index 356e9b804421..9e899c18af86 100644 --- a/drivers/clk/tegra/clk-periph.c +++ b/drivers/clk/tegra/clk-periph.c @@ -130,7 +130,7 @@ static const struct clk_ops tegra_clk_periph_nodiv_ops = { .disable = clk_periph_disable, }; -const struct clk_ops tegra_clk_periph_no_gate_ops = { +static const struct clk_ops tegra_clk_periph_no_gate_ops = { .get_parent = clk_periph_get_parent, .set_parent = clk_periph_set_parent, .recalc_rate = clk_periph_recalc_rate, -- cgit v1.2.3 From c112c1d8ef2997773368cf5aaf64e89ce6bf75b2 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 15 Jan 2014 11:13:26 +0530 Subject: clk: ux500: Staticize ux500_twocell_get ux500_twocell_get is a local symbol. Signed-off-by: Sachin Kamat Acked-by: Lee Jones Acked-by: Ulf Hansson Signed-off-by: Mike Turquette --- drivers/clk/ux500/u8500_of_clk.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/ux500/u8500_of_clk.c b/drivers/clk/ux500/u8500_of_clk.c index cdeff299de26..7b55ef89baa5 100644 --- a/drivers/clk/ux500/u8500_of_clk.c +++ b/drivers/clk/ux500/u8500_of_clk.c @@ -29,7 +29,8 @@ static struct clk *prcc_kclk[(PRCC_NUM_PERIPH_CLUSTERS + 1) * PRCC_PERIPHS_PER_C #define PRCC_KCLK_STORE(clk, base, bit) \ prcc_kclk[(base * PRCC_PERIPHS_PER_CLUSTER) + bit] = clk -struct clk *ux500_twocell_get(struct of_phandle_args *clkspec, void *data) +static struct clk *ux500_twocell_get(struct of_phandle_args *clkspec, + void *data) { struct clk **clk_data = data; unsigned int base, bit; -- cgit v1.2.3 From 86bcfa2e87c42b8af77188e7a939e952199d4da1 Mon Sep 17 00:00:00 2001 From: Mike Turquette Date: Mon, 24 Feb 2014 16:08:41 -0800 Subject: clk: add pr_debug & kerneldoc around clk notifiers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both the pr_err and the additional kerneldoc aim to help when debugging errors thrown from within a clock rate-change notifier callback. Reported-by: Sören Brinkmann Acked-by: Sören Brinkmann Signed-off-by: Mike Turquette --- drivers/clk/clk.c | 5 ++++- include/linux/clk.h | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index a6f079d23eaa..f30e6af90332 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1339,8 +1339,11 @@ static int __clk_speculate_rates(struct clk *clk, unsigned long parent_rate) if (clk->notifier_count) ret = __clk_notify(clk, PRE_RATE_CHANGE, clk->rate, new_rate); - if (ret & NOTIFY_STOP_MASK) + if (ret & NOTIFY_STOP_MASK) { + pr_debug("%s: clk notifier callback for clock %s aborted with error %d\n", + __func__, clk->name, ret); goto out; + } hlist_for_each_entry(child, &clk->children, child_node) { ret = __clk_speculate_rates(child, new_rate); diff --git a/include/linux/clk.h b/include/linux/clk.h index 0dd91148165e..fb5e097d8f72 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -78,8 +78,22 @@ struct clk_notifier_data { unsigned long new_rate; }; +/** + * clk_notifier_register: register a clock rate-change notifier callback + * @clk: clock whose rate we are interested in + * @nb: notifier block with callback function pointer + * + * ProTip: debugging across notifier chains can be frustrating. Make sure that + * your notifier callback function prints a nice big warning in case of + * failure. + */ int clk_notifier_register(struct clk *clk, struct notifier_block *nb); +/** + * clk_notifier_unregister: unregister a clock rate-change notifier callback + * @clk: clock whose rate we are no longer interested in + * @nb: notifier block which will be unregistered + */ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb); /** -- cgit v1.2.3 From a34cd4666f3da84228a82f70c94b8d9b692034ea Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 25 Nov 2013 19:47:04 +0100 Subject: clk: return probe defer when DT clock not yet ready At probe time, a clock device may not be ready when some other device wants to use it. This patch lets the functions clk_get/devm_clk_get return a probe defer when the clock is defined in the DT but not yet available. Signed-off-by: Jean-Francois Moine Reviewed-by: Sylwester Nawrocki Tested-by: Sylwester Nawrocki Signed-off-by: Mike Turquette --- drivers/clk/clk.c | 2 +- drivers/clk/clkdev.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index f30e6af90332..cc2ca9aebe02 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2466,7 +2466,7 @@ EXPORT_SYMBOL_GPL(of_clk_del_provider); struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec) { struct of_clk_provider *provider; - struct clk *clk = ERR_PTR(-ENOENT); + struct clk *clk = ERR_PTR(-EPROBE_DEFER); /* Check if we have such a provider in our array */ list_for_each_entry(provider, &of_clk_providers, link) { diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c index 48f67218247c..a360b2eca5cb 100644 --- a/drivers/clk/clkdev.c +++ b/drivers/clk/clkdev.c @@ -167,6 +167,8 @@ struct clk *clk_get(struct device *dev, const char *con_id) clk = of_clk_get_by_name(dev->of_node, con_id); if (!IS_ERR(clk)) return clk; + if (PTR_ERR(clk) == -EPROBE_DEFER) + return clk; } return clk_get_sys(dev_id, con_id); -- cgit v1.2.3 From c05ab9a0cc8e2b958bb2db2f8f1d200e568a75d4 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Wed, 29 Jan 2014 12:19:16 -0600 Subject: clk: ti: am335x: remove unecessary cpu0 clk node cpu0 clock node has no functionality, since cpufreq-cpu0 is already capable of picking up the clock from dts. Signed-off-by: Nishanth Menon Acked-by: Tero Kristo Signed-off-by: Mike Turquette --- drivers/clk/ti/clk-33xx.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/ti/clk-33xx.c b/drivers/clk/ti/clk-33xx.c index 776ee4594bd4..028b33783d38 100644 --- a/drivers/clk/ti/clk-33xx.c +++ b/drivers/clk/ti/clk-33xx.c @@ -34,7 +34,6 @@ static struct ti_dt_clk am33xx_clks[] = { DT_CLK(NULL, "dpll_core_m5_ck", "dpll_core_m5_ck"), DT_CLK(NULL, "dpll_core_m6_ck", "dpll_core_m6_ck"), DT_CLK(NULL, "dpll_mpu_ck", "dpll_mpu_ck"), - DT_CLK("cpu0", NULL, "dpll_mpu_ck"), DT_CLK(NULL, "dpll_mpu_m2_ck", "dpll_mpu_m2_ck"), DT_CLK(NULL, "dpll_ddr_ck", "dpll_ddr_ck"), DT_CLK(NULL, "dpll_ddr_m2_ck", "dpll_ddr_m2_ck"), -- cgit v1.2.3 From 95aa4f9b5fe577de902aa780e91140c6e89c73a2 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 24 Feb 2014 20:57:11 +0100 Subject: clk: shmobile: div6: use proper description in kernel doc These variable clocks have nothing to do with MSTP gating, probably a copy&paste leftover. Signed-off-by: Wolfram Sang Acked-by: Laurent Pinchart Signed-off-by: Mike Turquette --- drivers/clk/shmobile/clk-div6.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/shmobile/clk-div6.c b/drivers/clk/shmobile/clk-div6.c index aac4756ec52e..f065f694cb65 100644 --- a/drivers/clk/shmobile/clk-div6.c +++ b/drivers/clk/shmobile/clk-div6.c @@ -23,7 +23,7 @@ #define CPG_DIV6_DIV_MASK 0x3f /** - * struct div6_clock - MSTP gating clock + * struct div6_clock - CPG 6 bit divider clock * @hw: handle between common and hardware-specific interfaces * @reg: IO-remapped register * @div: divisor value (1-64) -- cgit v1.2.3 From 2c97ec58420d852f3f0ede905b92fbab1df5961c Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 20 Feb 2014 09:55:46 +0100 Subject: clk: zynq: Use clk_readl/clk_writel helper function Do not use readl/writel directly because the whole clk subsystem is using clk_readl/clk_writel functions. Signed-off-by: Michal Simek Acked-by: Soren Brinkmann Signed-off-by: Mike Turquette --- drivers/clk/zynq/clkc.c | 4 ++-- drivers/clk/zynq/pll.c | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/zynq/clkc.c b/drivers/clk/zynq/clkc.c index 09dd0173ea0a..e726c1b11218 100644 --- a/drivers/clk/zynq/clkc.c +++ b/drivers/clk/zynq/clkc.c @@ -148,7 +148,7 @@ static void __init zynq_clk_register_fclk(enum zynq_clk fclk, clks[fclk] = clk_register_gate(NULL, clk_name, div1_name, CLK_SET_RATE_PARENT, fclk_gate_reg, 0, CLK_GATE_SET_TO_DISABLE, fclk_gate_lock); - enable_reg = readl(fclk_gate_reg) & 1; + enable_reg = clk_readl(fclk_gate_reg) & 1; if (enable && !enable_reg) { if (clk_prepare_enable(clks[fclk])) pr_warn("%s: FCLK%u enable failed\n", __func__, @@ -277,7 +277,7 @@ static void __init zynq_clk_setup(struct device_node *np) SLCR_IOPLL_CTRL, 4, 1, 0, &iopll_lock); /* CPU clocks */ - tmp = readl(SLCR_621_TRUE) & 1; + tmp = clk_readl(SLCR_621_TRUE) & 1; clk = clk_register_mux(NULL, "cpu_mux", cpu_parents, 4, CLK_SET_RATE_NO_REPARENT, SLCR_ARM_CLK_CTRL, 4, 2, 0, &armclk_lock); diff --git a/drivers/clk/zynq/pll.c b/drivers/clk/zynq/pll.c index 3226f54fa595..cec97596fe65 100644 --- a/drivers/clk/zynq/pll.c +++ b/drivers/clk/zynq/pll.c @@ -90,7 +90,7 @@ static unsigned long zynq_pll_recalc_rate(struct clk_hw *hw, * makes probably sense to redundantly save fbdiv in the struct * zynq_pll to save the IO access. */ - fbdiv = (readl(clk->pll_ctrl) & PLLCTRL_FBDIV_MASK) >> + fbdiv = (clk_readl(clk->pll_ctrl) & PLLCTRL_FBDIV_MASK) >> PLLCTRL_FBDIV_SHIFT; return parent_rate * fbdiv; @@ -112,7 +112,7 @@ static int zynq_pll_is_enabled(struct clk_hw *hw) spin_lock_irqsave(clk->lock, flags); - reg = readl(clk->pll_ctrl); + reg = clk_readl(clk->pll_ctrl); spin_unlock_irqrestore(clk->lock, flags); @@ -138,10 +138,10 @@ static int zynq_pll_enable(struct clk_hw *hw) /* Power up PLL and wait for lock */ spin_lock_irqsave(clk->lock, flags); - reg = readl(clk->pll_ctrl); + reg = clk_readl(clk->pll_ctrl); reg &= ~(PLLCTRL_RESET_MASK | PLLCTRL_PWRDWN_MASK); - writel(reg, clk->pll_ctrl); - while (!(readl(clk->pll_status) & (1 << clk->lockbit))) + clk_writel(reg, clk->pll_ctrl); + while (!(clk_readl(clk->pll_status) & (1 << clk->lockbit))) ; spin_unlock_irqrestore(clk->lock, flags); @@ -168,9 +168,9 @@ static void zynq_pll_disable(struct clk_hw *hw) /* shut down PLL */ spin_lock_irqsave(clk->lock, flags); - reg = readl(clk->pll_ctrl); + reg = clk_readl(clk->pll_ctrl); reg |= PLLCTRL_RESET_MASK | PLLCTRL_PWRDWN_MASK; - writel(reg, clk->pll_ctrl); + clk_writel(reg, clk->pll_ctrl); spin_unlock_irqrestore(clk->lock, flags); } @@ -225,9 +225,9 @@ struct clk *clk_register_zynq_pll(const char *name, const char *parent, spin_lock_irqsave(pll->lock, flags); - reg = readl(pll->pll_ctrl); + reg = clk_readl(pll->pll_ctrl); reg &= ~PLLCTRL_BPQUAL_MASK; - writel(reg, pll->pll_ctrl); + clk_writel(reg, pll->pll_ctrl); spin_unlock_irqrestore(pll->lock, flags); -- cgit v1.2.3 From 5585f7317573873f95c1bb7748322c62a6c3a919 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Wed, 19 Feb 2014 15:11:10 -0600 Subject: clk: socfpga: Fix integer overflow in clock calculation Use 64-bit integer for calculating clock rate. Also use do_div for the 64-bit division. Signed-off-by: Graham Moore Signed-off-by: Dinh Nguyen Cc: Steffen Trumtrar Signed-off-by: Mike Turquette --- drivers/clk/socfpga/clk-pll.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/socfpga/clk-pll.c b/drivers/clk/socfpga/clk-pll.c index 362004e1e6fe..834b6e961971 100644 --- a/drivers/clk/socfpga/clk-pll.c +++ b/drivers/clk/socfpga/clk-pll.c @@ -44,7 +44,8 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk, unsigned long parent_rate) { struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk); - unsigned long divf, divq, vco_freq, reg; + unsigned long divf, divq, reg; + unsigned long long vco_freq; unsigned long bypass; reg = readl(socfpgaclk->hw.reg); @@ -54,8 +55,9 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk, divf = (reg & SOCFPGA_PLL_DIVF_MASK) >> SOCFPGA_PLL_DIVF_SHIFT; divq = (reg & SOCFPGA_PLL_DIVQ_MASK) >> SOCFPGA_PLL_DIVQ_SHIFT; - vco_freq = parent_rate * (divf + 1); - return vco_freq / (1 + divq); + vco_freq = (unsigned long long)parent_rate * (divf + 1); + do_div(vco_freq, (1 + divq)); + return (unsigned long)vco_freq; } static struct clk_ops clk_pll_ops = { -- cgit v1.2.3 From b89cd950cbcd6a6aca37e82ecf369ab9909fdf24 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Wed, 19 Feb 2014 15:11:11 -0600 Subject: clk: socfpga: Support multiple parents for the pll clocks The PLLs can be from 3 different sources: osc1, osc2, or the f2s_ref_clk. Update the clock driver to be able to get the correct parent. Signed-off-by: Dinh Nguyen Cc: Steffen Trumtrar Signed-off-by: Mike Turquette --- drivers/clk/socfpga/clk-pll.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/socfpga/clk-pll.c b/drivers/clk/socfpga/clk-pll.c index 834b6e961971..88dafb5e9627 100644 --- a/drivers/clk/socfpga/clk-pll.c +++ b/drivers/clk/socfpga/clk-pll.c @@ -38,6 +38,9 @@ #define SOCFPGA_PLL_DIVQ_MASK 0x003F0000 #define SOCFPGA_PLL_DIVQ_SHIFT 16 +#define CLK_MGR_PLL_CLK_SRC_SHIFT 22 +#define CLK_MGR_PLL_CLK_SRC_MASK 0x3 + #define to_socfpga_clk(p) container_of(p, struct socfpga_pll, hw.hw) static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk, @@ -60,8 +63,19 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk, return (unsigned long)vco_freq; } +static u8 clk_pll_get_parent(struct clk_hw *hwclk) +{ + u32 pll_src; + struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk); + + pll_src = readl(socfpgaclk->hw.reg); + return (pll_src >> CLK_MGR_PLL_CLK_SRC_SHIFT) & + CLK_MGR_PLL_CLK_SRC_MASK; +} + static struct clk_ops clk_pll_ops = { .recalc_rate = clk_pll_recalc_rate, + .get_parent = clk_pll_get_parent, }; static __init struct clk *__socfpga_pll_init(struct device_node *node, @@ -71,9 +85,10 @@ static __init struct clk *__socfpga_pll_init(struct device_node *node, struct clk *clk; struct socfpga_pll *pll_clk; const char *clk_name = node->name; - const char *parent_name; + const char *parent_name[SOCFPGA_MAX_PARENTS]; struct clk_init_data init; int rc; + int i = 0; of_property_read_u32(node, "reg", ®); @@ -88,10 +103,13 @@ static __init struct clk *__socfpga_pll_init(struct device_node *node, init.name = clk_name; init.ops = ops; init.flags = 0; - parent_name = of_clk_get_parent_name(node, 0); - init.parent_names = parent_name ? &parent_name : NULL; - init.num_parents = parent_name ? 1 : 0; + while (i < SOCFPGA_MAX_PARENTS && (parent_name[i] = + of_clk_get_parent_name(node, i)) != NULL) + i++; + + init.num_parents = i; + init.parent_names = parent_name; pll_clk->hw.hw.init = &init; pll_clk->hw.bit_idx = SOCFPGA_PLL_EXT_ENA; -- cgit v1.2.3 From 62ac983b614149db5a98f333dbb13848cb6b7524 Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Mon, 13 Jan 2014 17:37:32 +0800 Subject: clk: hisilicon: add hi3620_mmc_clks Suggest by Arnd: abstract mmc tuning as clock behavior, also because different soc have different tuning method and registers. hi3620_mmc_clks is added to handle mmc clock specifically on hi3620. Signed-off-by: Zhangfei Gao Acked-by: Arnd Bergmann Acked-by: Jaehoon Chung Signed-off-by: Mike Turquette --- .../bindings/arm/hisilicon/hisilicon.txt | 14 ++ .../devicetree/bindings/clock/hi3620-clock.txt | 1 + drivers/clk/hisilicon/clk-hi3620.c | 274 +++++++++++++++++++++ include/dt-bindings/clock/hi3620-clock.h | 5 + 4 files changed, 294 insertions(+) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt index 8c7a4653508d..df0a452b8526 100644 --- a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt +++ b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt @@ -30,3 +30,17 @@ Example: resume-offset = <0x308>; reboot-offset = <0x4>; }; + +PCTRL: Peripheral misc control register + +Required Properties: +- compatible: "hisilicon,pctrl" +- reg: Address and size of pctrl. + +Example: + + /* for Hi3620 */ + pctrl: pctrl@fca09000 { + compatible = "hisilicon,pctrl"; + reg = <0xfca09000 0x1000>; + }; diff --git a/Documentation/devicetree/bindings/clock/hi3620-clock.txt b/Documentation/devicetree/bindings/clock/hi3620-clock.txt index 4b71ab41be53..dad6269f52c5 100644 --- a/Documentation/devicetree/bindings/clock/hi3620-clock.txt +++ b/Documentation/devicetree/bindings/clock/hi3620-clock.txt @@ -7,6 +7,7 @@ Required Properties: - compatible: should be one of the following. - "hisilicon,hi3620-clock" - controller compatible with Hi3620 SoC. + - "hisilicon,hi3620-mmc-clock" - controller specific for Hi3620 mmc. - reg: physical base address of the controller and length of memory mapped region. diff --git a/drivers/clk/hisilicon/clk-hi3620.c b/drivers/clk/hisilicon/clk-hi3620.c index f24ad6a3a797..38faa469d288 100644 --- a/drivers/clk/hisilicon/clk-hi3620.c +++ b/drivers/clk/hisilicon/clk-hi3620.c @@ -240,3 +240,277 @@ static void __init hi3620_clk_init(struct device_node *np) base); } CLK_OF_DECLARE(hi3620_clk, "hisilicon,hi3620-clock", hi3620_clk_init); + +struct hisi_mmc_clock { + unsigned int id; + const char *name; + const char *parent_name; + unsigned long flags; + u32 clken_reg; + u32 clken_bit; + u32 div_reg; + u32 div_off; + u32 div_bits; + u32 drv_reg; + u32 drv_off; + u32 drv_bits; + u32 sam_reg; + u32 sam_off; + u32 sam_bits; +}; + +struct clk_mmc { + struct clk_hw hw; + u32 id; + void __iomem *clken_reg; + u32 clken_bit; + void __iomem *div_reg; + u32 div_off; + u32 div_bits; + void __iomem *drv_reg; + u32 drv_off; + u32 drv_bits; + void __iomem *sam_reg; + u32 sam_off; + u32 sam_bits; +}; + +#define to_mmc(_hw) container_of(_hw, struct clk_mmc, hw) + +static struct hisi_mmc_clock hi3620_mmc_clks[] __initdata = { + { HI3620_SD_CIUCLK, "sd_bclk1", "sd_clk", CLK_SET_RATE_PARENT, 0x1f8, 0, 0x1f8, 1, 3, 0x1f8, 4, 4, 0x1f8, 8, 4}, + { HI3620_MMC_CIUCLK1, "mmc_bclk1", "mmc_clk1", CLK_SET_RATE_PARENT, 0x1f8, 12, 0x1f8, 13, 3, 0x1f8, 16, 4, 0x1f8, 20, 4}, + { HI3620_MMC_CIUCLK2, "mmc_bclk2", "mmc_clk2", CLK_SET_RATE_PARENT, 0x1f8, 24, 0x1f8, 25, 3, 0x1f8, 28, 4, 0x1fc, 0, 4}, + { HI3620_MMC_CIUCLK3, "mmc_bclk3", "mmc_clk3", CLK_SET_RATE_PARENT, 0x1fc, 4, 0x1fc, 5, 3, 0x1fc, 8, 4, 0x1fc, 12, 4}, +}; + +static unsigned long mmc_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + switch (parent_rate) { + case 26000000: + return 13000000; + case 180000000: + return 25000000; + case 360000000: + return 50000000; + case 720000000: + return 100000000; + case 1440000000: + return 180000000; + default: + return parent_rate; + } +} + +static long mmc_clk_determine_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *best_parent_rate, + struct clk **best_parent_p) +{ + struct clk_mmc *mclk = to_mmc(hw); + unsigned long best = 0; + + if ((rate <= 13000000) && (mclk->id == HI3620_MMC_CIUCLK1)) { + rate = 13000000; + best = 26000000; + } else if (rate <= 26000000) { + rate = 25000000; + best = 180000000; + } else if (rate <= 52000000) { + rate = 50000000; + best = 360000000; + } else if (rate <= 100000000) { + rate = 100000000; + best = 720000000; + } else { + /* max is 180M */ + rate = 180000000; + best = 1440000000; + } + *best_parent_rate = best; + return rate; +} + +static u32 mmc_clk_delay(u32 val, u32 para, u32 off, u32 len) +{ + u32 i; + + if (para >= 0) { + for (i = 0; i < len; i++) { + if (para % 2) + val |= 1 << (off + i); + else + val &= ~(1 << (off + i)); + para = para >> 1; + } + } + return val; +} + +static int mmc_clk_set_timing(struct clk_hw *hw, unsigned long rate) +{ + struct clk_mmc *mclk = to_mmc(hw); + unsigned long flags; + u32 sam, drv, div, val; + static DEFINE_SPINLOCK(mmc_clk_lock); + + switch (rate) { + case 13000000: + sam = 3; + drv = 1; + div = 1; + break; + case 25000000: + sam = 13; + drv = 6; + div = 6; + break; + case 50000000: + sam = 3; + drv = 6; + div = 6; + break; + case 100000000: + sam = 6; + drv = 4; + div = 6; + break; + case 180000000: + sam = 6; + drv = 4; + div = 7; + break; + default: + return -EINVAL; + } + + spin_lock_irqsave(&mmc_clk_lock, flags); + + val = readl_relaxed(mclk->clken_reg); + val &= ~(1 << mclk->clken_bit); + writel_relaxed(val, mclk->clken_reg); + + val = readl_relaxed(mclk->sam_reg); + val = mmc_clk_delay(val, sam, mclk->sam_off, mclk->sam_bits); + writel_relaxed(val, mclk->sam_reg); + + val = readl_relaxed(mclk->drv_reg); + val = mmc_clk_delay(val, drv, mclk->drv_off, mclk->drv_bits); + writel_relaxed(val, mclk->drv_reg); + + val = readl_relaxed(mclk->div_reg); + val = mmc_clk_delay(val, div, mclk->div_off, mclk->div_bits); + writel_relaxed(val, mclk->div_reg); + + val = readl_relaxed(mclk->clken_reg); + val |= 1 << mclk->clken_bit; + writel_relaxed(val, mclk->clken_reg); + + spin_unlock_irqrestore(&mmc_clk_lock, flags); + + return 0; +} + +static int mmc_clk_prepare(struct clk_hw *hw) +{ + struct clk_mmc *mclk = to_mmc(hw); + unsigned long rate; + + if (mclk->id == HI3620_MMC_CIUCLK1) + rate = 13000000; + else + rate = 25000000; + + return mmc_clk_set_timing(hw, rate); +} + +static int mmc_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + return mmc_clk_set_timing(hw, rate); +} + +static struct clk_ops clk_mmc_ops = { + .prepare = mmc_clk_prepare, + .determine_rate = mmc_clk_determine_rate, + .set_rate = mmc_clk_set_rate, + .recalc_rate = mmc_clk_recalc_rate, +}; + +static struct clk *hisi_register_clk_mmc(struct hisi_mmc_clock *mmc_clk, + void __iomem *base, struct device_node *np) +{ + struct clk_mmc *mclk; + struct clk *clk; + struct clk_init_data init; + + mclk = kzalloc(sizeof(*mclk), GFP_KERNEL); + if (!mclk) { + pr_err("%s: fail to allocate mmc clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + init.name = mmc_clk->name; + init.ops = &clk_mmc_ops; + init.flags = mmc_clk->flags | CLK_IS_BASIC; + init.parent_names = (mmc_clk->parent_name ? &mmc_clk->parent_name : NULL); + init.num_parents = (mmc_clk->parent_name ? 1 : 0); + mclk->hw.init = &init; + + mclk->id = mmc_clk->id; + mclk->clken_reg = base + mmc_clk->clken_reg; + mclk->clken_bit = mmc_clk->clken_bit; + mclk->div_reg = base + mmc_clk->div_reg; + mclk->div_off = mmc_clk->div_off; + mclk->div_bits = mmc_clk->div_bits; + mclk->drv_reg = base + mmc_clk->drv_reg; + mclk->drv_off = mmc_clk->drv_off; + mclk->drv_bits = mmc_clk->drv_bits; + mclk->sam_reg = base + mmc_clk->sam_reg; + mclk->sam_off = mmc_clk->sam_off; + mclk->sam_bits = mmc_clk->sam_bits; + + clk = clk_register(NULL, &mclk->hw); + if (WARN_ON(IS_ERR(clk))) + kfree(mclk); + return clk; +} + +static void __init hi3620_mmc_clk_init(struct device_node *node) +{ + void __iomem *base; + int i, num = ARRAY_SIZE(hi3620_mmc_clks); + struct clk_onecell_data *clk_data; + + if (!node) { + pr_err("failed to find pctrl node in DTS\n"); + return; + } + + base = of_iomap(node, 0); + if (!base) { + pr_err("failed to map pctrl\n"); + return; + } + + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); + if (WARN_ON(!clk_data)) + return; + + clk_data->clks = kzalloc(sizeof(struct clk *) * num, GFP_KERNEL); + if (!clk_data->clks) { + pr_err("%s: fail to allocate mmc clk\n", __func__); + return; + } + + for (i = 0; i < num; i++) { + struct hisi_mmc_clock *mmc_clk = &hi3620_mmc_clks[i]; + clk_data->clks[mmc_clk->id] = + hisi_register_clk_mmc(mmc_clk, base, node); + } + + clk_data->clk_num = num; + of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); +} + +CLK_OF_DECLARE(hi3620_mmc_clk, "hisilicon,hi3620-mmc-clock", hi3620_mmc_clk_init); diff --git a/include/dt-bindings/clock/hi3620-clock.h b/include/dt-bindings/clock/hi3620-clock.h index 6eaa6a45e110..21b9d0e2eb0c 100644 --- a/include/dt-bindings/clock/hi3620-clock.h +++ b/include/dt-bindings/clock/hi3620-clock.h @@ -147,6 +147,11 @@ #define HI3620_MMC_CLK3 217 #define HI3620_MCU_CLK 218 +#define HI3620_SD_CIUCLK 0 +#define HI3620_MMC_CIUCLK1 1 +#define HI3620_MMC_CIUCLK2 2 +#define HI3620_MMC_CIUCLK3 3 + #define HI3620_NR_CLKS 219 #endif /* __DTS_HI3620_CLOCK_H */ -- cgit v1.2.3 From 1887c3a64fab8300a5be3fb5fd8d6474a63b50a0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 17 Feb 2014 10:31:53 +0100 Subject: clk: axi-clkgen: Add support for v2 This patch adds support for the new v2 version of the axi-clkgen core. Unfortunately the method of accessing the registers is quite different on v2, while the content still stays largely the same. So the patch adds a small abstraction layer which implements the specific read and write functions for v1 and v2 in callback functions. Signed-off-by: Lars-Peter Clausen --- .../devicetree/bindings/clock/axi-clkgen.txt | 2 +- drivers/clk/clk-axi-clkgen.c | 312 ++++++++++++++++++--- 2 files changed, 270 insertions(+), 44 deletions(-) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/clock/axi-clkgen.txt b/Documentation/devicetree/bindings/clock/axi-clkgen.txt index 028b493e97ff..20e1704e7df2 100644 --- a/Documentation/devicetree/bindings/clock/axi-clkgen.txt +++ b/Documentation/devicetree/bindings/clock/axi-clkgen.txt @@ -5,7 +5,7 @@ This binding uses the common clock binding[1]. [1] Documentation/devicetree/bindings/clock/clock-bindings.txt Required properties: -- compatible : shall be "adi,axi-clkgen". +- compatible : shall be "adi,axi-clkgen-1.00.a" or "adi,axi-clkgen-2.00.a". - #clock-cells : from common clock binding; Should always be set to 0. - reg : Address and length of the axi-clkgen register set. - clocks : Phandle and clock specifier for the parent clock. diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c index 8137327847c3..1127ee46b802 100644 --- a/drivers/clk/clk-axi-clkgen.c +++ b/drivers/clk/clk-axi-clkgen.c @@ -17,23 +17,75 @@ #include #include -#define AXI_CLKGEN_REG_UPDATE_ENABLE 0x04 -#define AXI_CLKGEN_REG_CLK_OUT1 0x08 -#define AXI_CLKGEN_REG_CLK_OUT2 0x0c -#define AXI_CLKGEN_REG_CLK_DIV 0x10 -#define AXI_CLKGEN_REG_CLK_FB1 0x14 -#define AXI_CLKGEN_REG_CLK_FB2 0x18 -#define AXI_CLKGEN_REG_LOCK1 0x1c -#define AXI_CLKGEN_REG_LOCK2 0x20 -#define AXI_CLKGEN_REG_LOCK3 0x24 -#define AXI_CLKGEN_REG_FILTER1 0x28 -#define AXI_CLKGEN_REG_FILTER2 0x2c +#define AXI_CLKGEN_V1_REG_UPDATE_ENABLE 0x04 +#define AXI_CLKGEN_V1_REG_CLK_OUT1 0x08 +#define AXI_CLKGEN_V1_REG_CLK_OUT2 0x0c +#define AXI_CLKGEN_V1_REG_CLK_DIV 0x10 +#define AXI_CLKGEN_V1_REG_CLK_FB1 0x14 +#define AXI_CLKGEN_V1_REG_CLK_FB2 0x18 +#define AXI_CLKGEN_V1_REG_LOCK1 0x1c +#define AXI_CLKGEN_V1_REG_LOCK2 0x20 +#define AXI_CLKGEN_V1_REG_LOCK3 0x24 +#define AXI_CLKGEN_V1_REG_FILTER1 0x28 +#define AXI_CLKGEN_V1_REG_FILTER2 0x2c + +#define AXI_CLKGEN_V2_REG_RESET 0x40 +#define AXI_CLKGEN_V2_REG_DRP_CNTRL 0x70 +#define AXI_CLKGEN_V2_REG_DRP_STATUS 0x74 + +#define AXI_CLKGEN_V2_RESET_MMCM_ENABLE BIT(1) +#define AXI_CLKGEN_V2_RESET_ENABLE BIT(0) + +#define AXI_CLKGEN_V2_DRP_CNTRL_SEL BIT(29) +#define AXI_CLKGEN_V2_DRP_CNTRL_READ BIT(28) + +#define AXI_CLKGEN_V2_DRP_STATUS_BUSY BIT(16) + +#define MMCM_REG_CLKOUT0_1 0x08 +#define MMCM_REG_CLKOUT0_2 0x09 +#define MMCM_REG_CLK_FB1 0x14 +#define MMCM_REG_CLK_FB2 0x15 +#define MMCM_REG_CLK_DIV 0x16 +#define MMCM_REG_LOCK1 0x18 +#define MMCM_REG_LOCK2 0x19 +#define MMCM_REG_LOCK3 0x1a +#define MMCM_REG_FILTER1 0x4e +#define MMCM_REG_FILTER2 0x4f + +struct axi_clkgen; + +struct axi_clkgen_mmcm_ops { + void (*enable)(struct axi_clkgen *axi_clkgen, bool enable); + int (*write)(struct axi_clkgen *axi_clkgen, unsigned int reg, + unsigned int val, unsigned int mask); + int (*read)(struct axi_clkgen *axi_clkgen, unsigned int reg, + unsigned int *val); +}; struct axi_clkgen { void __iomem *base; + const struct axi_clkgen_mmcm_ops *mmcm_ops; struct clk_hw clk_hw; }; +static void axi_clkgen_mmcm_enable(struct axi_clkgen *axi_clkgen, + bool enable) +{ + axi_clkgen->mmcm_ops->enable(axi_clkgen, enable); +} + +static int axi_clkgen_mmcm_write(struct axi_clkgen *axi_clkgen, + unsigned int reg, unsigned int val, unsigned int mask) +{ + return axi_clkgen->mmcm_ops->write(axi_clkgen, reg, val, mask); +} + +static int axi_clkgen_mmcm_read(struct axi_clkgen *axi_clkgen, + unsigned int reg, unsigned int *val) +{ + return axi_clkgen->mmcm_ops->read(axi_clkgen, reg, val); +} + static uint32_t axi_clkgen_lookup_filter(unsigned int m) { switch (m) { @@ -156,6 +208,148 @@ static void axi_clkgen_read(struct axi_clkgen *axi_clkgen, *val = readl(axi_clkgen->base + reg); } +static unsigned int axi_clkgen_v1_map_mmcm_reg(unsigned int reg) +{ + switch (reg) { + case MMCM_REG_CLKOUT0_1: + return AXI_CLKGEN_V1_REG_CLK_OUT1; + case MMCM_REG_CLKOUT0_2: + return AXI_CLKGEN_V1_REG_CLK_OUT2; + case MMCM_REG_CLK_FB1: + return AXI_CLKGEN_V1_REG_CLK_FB1; + case MMCM_REG_CLK_FB2: + return AXI_CLKGEN_V1_REG_CLK_FB2; + case MMCM_REG_CLK_DIV: + return AXI_CLKGEN_V1_REG_CLK_DIV; + case MMCM_REG_LOCK1: + return AXI_CLKGEN_V1_REG_LOCK1; + case MMCM_REG_LOCK2: + return AXI_CLKGEN_V1_REG_LOCK2; + case MMCM_REG_LOCK3: + return AXI_CLKGEN_V1_REG_LOCK3; + case MMCM_REG_FILTER1: + return AXI_CLKGEN_V1_REG_FILTER1; + case MMCM_REG_FILTER2: + return AXI_CLKGEN_V1_REG_FILTER2; + default: + return 0; + } +} + +static int axi_clkgen_v1_mmcm_write(struct axi_clkgen *axi_clkgen, + unsigned int reg, unsigned int val, unsigned int mask) +{ + reg = axi_clkgen_v1_map_mmcm_reg(reg); + if (reg == 0) + return -EINVAL; + + axi_clkgen_write(axi_clkgen, reg, val); + + return 0; +} + +static int axi_clkgen_v1_mmcm_read(struct axi_clkgen *axi_clkgen, + unsigned int reg, unsigned int *val) +{ + reg = axi_clkgen_v1_map_mmcm_reg(reg); + if (reg == 0) + return -EINVAL; + + axi_clkgen_read(axi_clkgen, reg, val); + + return 0; +} + +static void axi_clkgen_v1_mmcm_enable(struct axi_clkgen *axi_clkgen, + bool enable) +{ + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V1_REG_UPDATE_ENABLE, enable); +} + +static const struct axi_clkgen_mmcm_ops axi_clkgen_v1_mmcm_ops = { + .write = axi_clkgen_v1_mmcm_write, + .read = axi_clkgen_v1_mmcm_read, + .enable = axi_clkgen_v1_mmcm_enable, +}; + +static int axi_clkgen_wait_non_busy(struct axi_clkgen *axi_clkgen) +{ + unsigned int timeout = 10000; + unsigned int val; + + do { + axi_clkgen_read(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_STATUS, &val); + } while ((val & AXI_CLKGEN_V2_DRP_STATUS_BUSY) && --timeout); + + if (val & AXI_CLKGEN_V2_DRP_STATUS_BUSY) + return -EIO; + + return val & 0xffff; +} + +static int axi_clkgen_v2_mmcm_read(struct axi_clkgen *axi_clkgen, + unsigned int reg, unsigned int *val) +{ + unsigned int reg_val; + int ret; + + ret = axi_clkgen_wait_non_busy(axi_clkgen); + if (ret < 0) + return ret; + + reg_val = AXI_CLKGEN_V2_DRP_CNTRL_SEL | AXI_CLKGEN_V2_DRP_CNTRL_READ; + reg_val |= (reg << 16); + + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_CNTRL, reg_val); + + ret = axi_clkgen_wait_non_busy(axi_clkgen); + if (ret < 0) + return ret; + + *val = ret; + + return 0; +} + +static int axi_clkgen_v2_mmcm_write(struct axi_clkgen *axi_clkgen, + unsigned int reg, unsigned int val, unsigned int mask) +{ + unsigned int reg_val = 0; + int ret; + + ret = axi_clkgen_wait_non_busy(axi_clkgen); + if (ret < 0) + return ret; + + if (mask != 0xffff) { + axi_clkgen_v2_mmcm_read(axi_clkgen, reg, ®_val); + reg_val &= ~mask; + } + + reg_val |= AXI_CLKGEN_V2_DRP_CNTRL_SEL | (reg << 16) | (val & mask); + + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_CNTRL, reg_val); + + return 0; +} + +static void axi_clkgen_v2_mmcm_enable(struct axi_clkgen *axi_clkgen, + bool enable) +{ + unsigned int val = AXI_CLKGEN_V2_RESET_ENABLE; + + if (enable) + val |= AXI_CLKGEN_V2_RESET_MMCM_ENABLE; + + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_RESET, val); +} + +static const struct axi_clkgen_mmcm_ops axi_clkgen_v2_mmcm_ops = { + .write = axi_clkgen_v2_mmcm_write, + .read = axi_clkgen_v2_mmcm_read, + .enable = axi_clkgen_v2_mmcm_enable, +}; + static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw) { return container_of(clk_hw, struct axi_clkgen, clk_hw); @@ -184,33 +378,29 @@ static int axi_clkgen_set_rate(struct clk_hw *clk_hw, filter = axi_clkgen_lookup_filter(m - 1); lock = axi_clkgen_lookup_lock(m - 1); - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 0); - axi_clkgen_calc_clk_params(dout, &low, &high, &edge, &nocount); - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, - (high << 6) | low); - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT2, - (edge << 7) | (nocount << 6)); + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_1, + (high << 6) | low, 0xefff); + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_2, + (edge << 7) | (nocount << 6), 0x03ff); axi_clkgen_calc_clk_params(d, &low, &high, &edge, &nocount); - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, - (edge << 13) | (nocount << 12) | (high << 6) | low); + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_DIV, + (edge << 13) | (nocount << 12) | (high << 6) | low, 0x3fff); axi_clkgen_calc_clk_params(m, &low, &high, &edge, &nocount); - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, - (high << 6) | low); - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB2, - (edge << 7) | (nocount << 6)); - - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK1, lock & 0x3ff); - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK2, - (((lock >> 16) & 0x1f) << 10) | 0x1); - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK3, - (((lock >> 24) & 0x1f) << 10) | 0x3e9); - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER1, filter >> 16); - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER2, filter); - - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 1); + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB1, + (high << 6) | low, 0xefff); + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB2, + (edge << 7) | (nocount << 6), 0x03ff); + + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK1, lock & 0x3ff, 0x3ff); + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK2, + (((lock >> 16) & 0x1f) << 10) | 0x1, 0x7fff); + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK3, + (((lock >> 24) & 0x1f) << 10) | 0x3e9, 0x7fff); + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER1, filter >> 16, 0x9900); + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER2, filter, 0x9900); return 0; } @@ -236,11 +426,11 @@ static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw, unsigned int reg; unsigned long long tmp; - axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, ®); + axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_1, ®); dout = (reg & 0x3f) + ((reg >> 6) & 0x3f); - axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, ®); + axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_DIV, ®); d = (reg & 0x3f) + ((reg >> 6) & 0x3f); - axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, ®); + axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB1, ®); m = (reg & 0x3f) + ((reg >> 6) & 0x3f); if (d == 0 || dout == 0) @@ -255,14 +445,45 @@ static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw, return tmp; } +static int axi_clkgen_enable(struct clk_hw *clk_hw) +{ + struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw); + + axi_clkgen_mmcm_enable(axi_clkgen, true); + + return 0; +} + +static void axi_clkgen_disable(struct clk_hw *clk_hw) +{ + struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw); + + axi_clkgen_mmcm_enable(axi_clkgen, false); +} + static const struct clk_ops axi_clkgen_ops = { .recalc_rate = axi_clkgen_recalc_rate, .round_rate = axi_clkgen_round_rate, .set_rate = axi_clkgen_set_rate, + .enable = axi_clkgen_enable, + .disable = axi_clkgen_disable, }; +static const struct of_device_id axi_clkgen_ids[] = { + { + .compatible = "adi,axi-clkgen-1.00.a", + .data = &axi_clkgen_v1_mmcm_ops + }, { + .compatible = "adi,axi-clkgen-2.00.a", + .data = &axi_clkgen_v2_mmcm_ops, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, axi_clkgen_ids); + static int axi_clkgen_probe(struct platform_device *pdev) { + const struct of_device_id *id; struct axi_clkgen *axi_clkgen; struct clk_init_data init; const char *parent_name; @@ -270,10 +491,19 @@ static int axi_clkgen_probe(struct platform_device *pdev) struct resource *mem; struct clk *clk; + if (!pdev->dev.of_node) + return -ENODEV; + + id = of_match_node(axi_clkgen_ids, pdev->dev.of_node); + if (!id) + return -ENODEV; + axi_clkgen = devm_kzalloc(&pdev->dev, sizeof(*axi_clkgen), GFP_KERNEL); if (!axi_clkgen) return -ENOMEM; + axi_clkgen->mmcm_ops = id->data; + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); axi_clkgen->base = devm_ioremap_resource(&pdev->dev, mem); if (IS_ERR(axi_clkgen->base)) @@ -289,10 +519,12 @@ static int axi_clkgen_probe(struct platform_device *pdev) init.name = clk_name; init.ops = &axi_clkgen_ops; - init.flags = 0; + init.flags = CLK_SET_RATE_GATE; init.parent_names = &parent_name; init.num_parents = 1; + axi_clkgen_mmcm_enable(axi_clkgen, false); + axi_clkgen->clk_hw.init = &init; clk = devm_clk_register(&pdev->dev, &axi_clkgen->clk_hw); if (IS_ERR(clk)) @@ -309,12 +541,6 @@ static int axi_clkgen_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id axi_clkgen_ids[] = { - { .compatible = "adi,axi-clkgen-1.00.a" }, - { }, -}; -MODULE_DEVICE_TABLE(of, axi_clkgen_ids); - static struct platform_driver axi_clkgen_driver = { .driver = { .name = "adi-axi-clkgen", -- cgit v1.2.3 From b11d282dbea27db1788893115dfca8a7856bf205 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Thu, 13 Feb 2014 12:03:59 +0200 Subject: clk: divider: fix rate calculation for fractional rates clk-divider.c does not calculate the rates consistently at the moment. As an example, on OMAP3 we have a clock divider with a source clock of 864000000 Hz. With dividers 6, 7 and 8 the theoretical rates are: 6: 144000000 7: 123428571.428571... 8: 108000000 Calling clk_round_rate() with the rate in the first column will give the rate in the second column: 144000000 -> 144000000 143999999 -> 123428571 123428572 -> 123428571 123428571 -> 108000000 Note how clk_round_rate() returns 123428571 for rates from 123428572 to 143999999, which is mathematically correct, but when clk_round_rate() is called with 123428571, the returned value is surprisingly 108000000. This means that the following code works a bit oddly: rate = clk_round_rate(clk, 123428572); clk_set_rate(clk, rate); As clk_set_rate() also does clock rate rounding, the result is that the clock is set to the rate of 108000000, not 123428571 returned by the clk_round_rate. This patch changes the clk-divider.c to use DIV_ROUND_UP when calculating the rate. This gives the following behavior which fixes the inconsistency: 144000000 -> 144000000 143999999 -> 123428572 123428572 -> 123428572 123428571 -> 108000000 Signed-off-by: Tomi Valkeinen Signed-off-by: Mike Turquette --- drivers/clk/clk-divider.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 5543b7df8e16..ec22112e569f 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -24,7 +24,7 @@ * Traits of this clock: * prepare - clk_prepare only ensures that parents are prepared * enable - clk_enable only ensures that parents are enabled - * rate - rate is adjustable. clk->rate = parent->rate / divisor + * rate - rate is adjustable. clk->rate = DIV_ROUND_UP(parent->rate / divisor) * parent - fixed parent. No clk_set_parent support */ @@ -115,7 +115,7 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw, return parent_rate; } - return parent_rate / div; + return DIV_ROUND_UP(parent_rate, div); } /* @@ -185,7 +185,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, } parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), MULT_ROUND_UP(rate, i)); - now = parent_rate / i; + now = DIV_ROUND_UP(parent_rate, i); if (now <= rate && now > best) { bestdiv = i; best = now; @@ -207,7 +207,7 @@ static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate, int div; div = clk_divider_bestdiv(hw, rate, prate); - return *prate / div; + return DIV_ROUND_UP(*prate, div); } static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, @@ -218,7 +218,7 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long flags = 0; u32 val; - div = parent_rate / rate; + div = DIV_ROUND_UP(parent_rate, rate); value = _get_val(divider, div); if (value > div_mask(divider)) -- cgit v1.2.3 From 7e50e7e176634e8cc0335778915be75df41043dc Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Thu, 13 Feb 2014 12:04:00 +0200 Subject: clk: ti/divider: fix rate calculation for fractional rates ti/clk-divider.c does not calculate the rates consistently at the moment. As an example, on OMAP3 we have a clock divider with a source clock of 864000000 Hz. With dividers 6, 7 and 8 the theoretical rates are: 6: 144000000 7: 123428571.428571... 8: 108000000 Calling clk_round_rate() with the rate in the first column will give the rate in the second column: 144000000 -> 144000000 143999999 -> 123428571 123428572 -> 123428571 123428571 -> 108000000 Note how clk_round_rate() returns 123428571 for rates from 123428572 to 143999999, which is mathematically correct, but when clk_round_rate() is called with 123428571, the returned value is surprisingly 108000000. This means that the following code works a bit oddly: rate = clk_round_rate(clk, 123428572); clk_set_rate(clk, rate); As clk_set_rate() also does clock rate rounding, the result is that the clock is set to the rate of 108000000, not 123428571 returned by the clk_round_rate. This patch changes the ti/clk-divider.c to use DIV_ROUND_UP when calculating the rate. This gives the following behavior which fixes the inconsistency: 144000000 -> 144000000 143999999 -> 123428572 123428572 -> 123428572 123428571 -> 108000000 Signed-off-by: Tomi Valkeinen Signed-off-by: Mike Turquette --- drivers/clk/ti/divider.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/ti/divider.c b/drivers/clk/ti/divider.c index a15e445570b2..e6aa10db7bba 100644 --- a/drivers/clk/ti/divider.c +++ b/drivers/clk/ti/divider.c @@ -112,7 +112,7 @@ static unsigned long ti_clk_divider_recalc_rate(struct clk_hw *hw, return parent_rate; } - return parent_rate / div; + return DIV_ROUND_UP(parent_rate, div); } /* @@ -182,7 +182,7 @@ static int ti_clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, } parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), MULT_ROUND_UP(rate, i)); - now = parent_rate / i; + now = DIV_ROUND_UP(parent_rate, i); if (now <= rate && now > best) { bestdiv = i; best = now; @@ -205,7 +205,7 @@ static long ti_clk_divider_round_rate(struct clk_hw *hw, unsigned long rate, int div; div = ti_clk_divider_bestdiv(hw, rate, prate); - return *prate / div; + return DIV_ROUND_UP(*prate, div); } static int ti_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, @@ -216,7 +216,7 @@ static int ti_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long flags = 0; u32 val; - div = parent_rate / rate; + div = DIV_ROUND_UP(parent_rate, rate); value = _get_val(divider, div); if (value > div_mask(divider)) -- cgit v1.2.3 From 8230a5ab435c6a0f395ff8fb190e53b563f06179 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 12 Mar 2014 12:41:41 -0300 Subject: clk: mvebu: Fix ratio register offset on A375 SoC This commit fixes the ratio register offset which is 0x4, as per the Armada 375 SoC specification. Signed-off-by: Ezequiel Garcia Link: https://lkml.kernel.org/r/1394638901-13368-2-git-send-email-ezequiel.garcia@free-electrons.com Signed-off-by: Jason Cooper --- drivers/clk/mvebu/clk-corediv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/mvebu/clk-corediv.c b/drivers/clk/mvebu/clk-corediv.c index 4da60760be10..4af33ba54a1e 100644 --- a/drivers/clk/mvebu/clk-corediv.c +++ b/drivers/clk/mvebu/clk-corediv.c @@ -213,7 +213,7 @@ static const struct clk_corediv_soc_desc armada375_corediv_soc = { .set_rate = clk_corediv_set_rate, }, .ratio_reload = BIT(8), - .ratio_offset = 0x8, + .ratio_offset = 0x4, }; static void __init -- cgit v1.2.3 From 0737c15ff51857657aa5e2769d033f930334a1f6 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Thu, 13 Mar 2014 17:24:28 -0300 Subject: clk: mvebu: Support Armada 380 SoC on the core divider clock This commit adds support for the Core Divider clocks of the Armada 380 SoCs. Similarly to Armada 370 and XP, the Core Divider clocks of the 380 have gate capabilities. The only difference is the register layout. Signed-off-by: Ezequiel Garcia Link: https://lkml.kernel.org/r/1394742273-5113-2-git-send-email-ezequiel.garcia@free-electrons.com Signed-off-by: Jason Cooper --- drivers/clk/mvebu/clk-corediv.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/mvebu/clk-corediv.c b/drivers/clk/mvebu/clk-corediv.c index 4af33ba54a1e..d1e5863d3375 100644 --- a/drivers/clk/mvebu/clk-corediv.c +++ b/drivers/clk/mvebu/clk-corediv.c @@ -204,6 +204,22 @@ static const struct clk_corediv_soc_desc armada370_corediv_soc = { .ratio_offset = 0x8, }; +static const struct clk_corediv_soc_desc armada380_corediv_soc = { + .descs = mvebu_corediv_desc, + .ndescs = ARRAY_SIZE(mvebu_corediv_desc), + .ops = { + .enable = clk_corediv_enable, + .disable = clk_corediv_disable, + .is_enabled = clk_corediv_is_enabled, + .recalc_rate = clk_corediv_recalc_rate, + .round_rate = clk_corediv_round_rate, + .set_rate = clk_corediv_set_rate, + }, + .ratio_reload = BIT(8), + .enable_bit_offset = 16, + .ratio_offset = 0x4, +}; + static const struct clk_corediv_soc_desc armada375_corediv_soc = { .descs = mvebu_corediv_desc, .ndescs = ARRAY_SIZE(mvebu_corediv_desc), @@ -290,3 +306,10 @@ static void __init armada375_corediv_clk_init(struct device_node *node) } CLK_OF_DECLARE(armada375_corediv_clk, "marvell,armada-375-corediv-clock", armada375_corediv_clk_init); + +static void __init armada380_corediv_clk_init(struct device_node *node) +{ + return mvebu_corediv_clk_init(node, &armada380_corediv_soc); +} +CLK_OF_DECLARE(armada380_corediv_clk, "marvell,armada-380-corediv-clock", + armada380_corediv_clk_init); -- cgit v1.2.3 From c7bb4fc16ead2883a9e4eece2781fa78a0837735 Mon Sep 17 00:00:00 2001 From: Jonas Jensen Date: Tue, 28 Jan 2014 12:09:11 +0100 Subject: clk: add MOXA ART SoCs clock driver MOXA ART SoCs allow to determine PLL output and APB frequencies by reading registers holding multiplier and divisor information. Add a clock driver for this SoC. Signed-off-by: Jonas Jensen Signed-off-by: Mike Turquette --- .../bindings/clock/moxa,moxart-clock.txt | 48 +++++++++++ drivers/clk/Makefile | 1 + drivers/clk/clk-moxart.c | 97 ++++++++++++++++++++++ 3 files changed, 146 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/moxa,moxart-clock.txt create mode 100644 drivers/clk/clk-moxart.c (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/clock/moxa,moxart-clock.txt b/Documentation/devicetree/bindings/clock/moxa,moxart-clock.txt new file mode 100644 index 000000000000..fedea84314a1 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/moxa,moxart-clock.txt @@ -0,0 +1,48 @@ +Device Tree Clock bindings for arch-moxart + +This binding uses the common clock binding[1]. + +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt + +MOXA ART SoCs allow to determine PLL output and APB frequencies +by reading registers holding multiplier and divisor information. + + +PLL: + +Required properties: +- compatible : Must be "moxa,moxart-pll-clock" +- #clock-cells : Should be 0 +- reg : Should contain registers location and length +- clocks : Should contain phandle + clock-specifier for the parent clock + +Optional properties: +- clock-output-names : Should contain clock name + + +APB: + +Required properties: +- compatible : Must be "moxa,moxart-apb-clock" +- #clock-cells : Should be 0 +- reg : Should contain registers location and length +- clocks : Should contain phandle + clock-specifier for the parent clock + +Optional properties: +- clock-output-names : Should contain clock name + + +For example: + + clk_pll: clk_pll@98100000 { + compatible = "moxa,moxart-pll-clock"; + #clock-cells = <0>; + reg = <0x98100000 0x34>; + }; + + clk_apb: clk_apb@98100000 { + compatible = "moxa,moxart-apb-clock"; + #clock-cells = <0>; + reg = <0x98100000 0x34>; + clocks = <&clk_pll>; + }; diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index a367a9831717..48a7ef3d05f6 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o +obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o obj-$(CONFIG_CLK_PPC_CORENET) += clk-ppc-corenet.o diff --git a/drivers/clk/clk-moxart.c b/drivers/clk/clk-moxart.c new file mode 100644 index 000000000000..30a3b6999e10 --- /dev/null +++ b/drivers/clk/clk-moxart.c @@ -0,0 +1,97 @@ +/* + * MOXA ART SoCs clock driver. + * + * Copyright (C) 2013 Jonas Jensen + * + * Jonas Jensen + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include + +void __init moxart_of_pll_clk_init(struct device_node *node) +{ + static void __iomem *base; + struct clk *clk, *ref_clk; + unsigned int mul; + const char *name = node->name; + const char *parent_name; + + of_property_read_string(node, "clock-output-names", &name); + parent_name = of_clk_get_parent_name(node, 0); + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s: of_iomap failed\n", node->full_name); + return; + } + + mul = readl(base + 0x30) >> 3 & 0x3f; + iounmap(base); + + ref_clk = of_clk_get(node, 0); + if (IS_ERR(ref_clk)) { + pr_err("%s: of_clk_get failed\n", node->full_name); + return; + } + + clk = clk_register_fixed_factor(NULL, name, parent_name, 0, mul, 1); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock\n", node->full_name); + return; + } + + clk_register_clkdev(clk, NULL, name); + of_clk_add_provider(node, of_clk_src_simple_get, clk); +} +CLK_OF_DECLARE(moxart_pll_clock, "moxa,moxart-pll-clock", + moxart_of_pll_clk_init); + +void __init moxart_of_apb_clk_init(struct device_node *node) +{ + static void __iomem *base; + struct clk *clk, *pll_clk; + unsigned int div, val; + unsigned int div_idx[] = { 2, 3, 4, 6, 8}; + const char *name = node->name; + const char *parent_name; + + of_property_read_string(node, "clock-output-names", &name); + parent_name = of_clk_get_parent_name(node, 0); + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s: of_iomap failed\n", node->full_name); + return; + } + + val = readl(base + 0xc) >> 4 & 0x7; + iounmap(base); + + if (val > 4) + val = 0; + div = div_idx[val] * 2; + + pll_clk = of_clk_get(node, 0); + if (IS_ERR(pll_clk)) { + pr_err("%s: of_clk_get failed\n", node->full_name); + return; + } + + clk = clk_register_fixed_factor(NULL, name, parent_name, 0, 1, div); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock\n", node->full_name); + return; + } + + clk_register_clkdev(clk, NULL, name); + of_clk_add_provider(node, of_clk_src_simple_get, clk); +} +CLK_OF_DECLARE(moxart_apb_clock, "moxa,moxart-apb-clock", + moxart_of_apb_clk_init); -- cgit v1.2.3 From 64d64c3573b3b6bf2d67fbb54d80bc73a1850f13 Mon Sep 17 00:00:00 2001 From: Tushar Behera Date: Thu, 26 Dec 2013 15:48:58 +0530 Subject: clk: clk-s2mps11: Refactor for including support for other MFD clocks The clocks in S2MPS11 and S5M8767 are managed in the same way, baring a difference in the register offset. It would be better to update existing S2MPS11 driver to support the clocks in S5M8767, rather than creating an almost duplicate driver altogether. Signed-off-by: Tushar Behera Reviewed-by: Tomasz Figa Reviewed-by: Yadwinder Singh Brar Signed-off-by: Mike Turquette --- drivers/clk/clk-s2mps11.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/clk-s2mps11.c b/drivers/clk/clk-s2mps11.c index 00a3abe103a5..43e25bb405ea 100644 --- a/drivers/clk/clk-s2mps11.c +++ b/drivers/clk/clk-s2mps11.c @@ -48,6 +48,7 @@ struct s2mps11_clk { struct clk_lookup *lookup; u32 mask; bool enabled; + unsigned int reg; }; static struct s2mps11_clk *to_s2mps11_clk(struct clk_hw *hw) @@ -61,7 +62,7 @@ static int s2mps11_clk_prepare(struct clk_hw *hw) int ret; ret = regmap_update_bits(s2mps11->iodev->regmap_pmic, - S2MPS11_REG_RTC_CTRL, + s2mps11->reg, s2mps11->mask, s2mps11->mask); if (!ret) s2mps11->enabled = true; @@ -74,7 +75,7 @@ static void s2mps11_clk_unprepare(struct clk_hw *hw) struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw); int ret; - ret = regmap_update_bits(s2mps11->iodev->regmap_pmic, S2MPS11_REG_RTC_CTRL, + ret = regmap_update_bits(s2mps11->iodev->regmap_pmic, s2mps11->reg, s2mps11->mask, ~s2mps11->mask); if (!ret) @@ -155,6 +156,7 @@ static int s2mps11_clk_probe(struct platform_device *pdev) struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent); struct s2mps11_clk *s2mps11_clks, *s2mps11_clk; struct device_node *clk_np = NULL; + unsigned int s2mps11_reg; int i, ret = 0; u32 val; @@ -169,13 +171,23 @@ static int s2mps11_clk_probe(struct platform_device *pdev) if (IS_ERR(clk_np)) return PTR_ERR(clk_np); + switch(platform_get_device_id(pdev)->driver_data) { + case S2MPS11X: + s2mps11_reg = S2MPS11_REG_RTC_CTRL; + break; + default: + dev_err(&pdev->dev, "Invalid device type\n"); + return -EINVAL; + }; + for (i = 0; i < S2MPS11_CLKS_NUM; i++, s2mps11_clk++) { s2mps11_clk->iodev = iodev; s2mps11_clk->hw.init = &s2mps11_clks_init[i]; s2mps11_clk->mask = 1 << i; + s2mps11_clk->reg = s2mps11_reg; ret = regmap_read(s2mps11_clk->iodev->regmap_pmic, - S2MPS11_REG_RTC_CTRL, &val); + s2mps11_clk->reg, &val); if (ret < 0) goto err_reg; @@ -241,7 +253,7 @@ static int s2mps11_clk_remove(struct platform_device *pdev) } static const struct platform_device_id s2mps11_clk_id[] = { - { "s2mps11-clk", 0}, + { "s2mps11-clk", S2MPS11X}, { }, }; MODULE_DEVICE_TABLE(platform, s2mps11_clk_id); -- cgit v1.2.3 From e8e6b840c4b646ce5aaef27875a22251ebc4e0d6 Mon Sep 17 00:00:00 2001 From: Tushar Behera Date: Thu, 26 Dec 2013 15:48:59 +0530 Subject: clk: clk-s2mps11: Add support for clocks in S5M8767 MFD Since clock operation within S2MPS11 and S5M8767 are similar, we can support both the devices within a single driver. Signed-off-by: Tushar Behera Reviewed-by: Tomasz Figa Reviewed-by: Yadwinder Singh Brar Signed-off-by: Mike Turquette --- drivers/clk/Kconfig | 6 ++++-- drivers/clk/clk-s2mps11.c | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 7641965d208d..da1b416aa579 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -65,10 +65,12 @@ config COMMON_CLK_SI570 clock generators. config COMMON_CLK_S2MPS11 - tristate "Clock driver for S2MPS11 MFD" + tristate "Clock driver for S2MPS11/S5M8767 MFD" depends on MFD_SEC_CORE ---help--- - This driver supports S2MPS11 crystal oscillator clock. + This driver supports S2MPS11/S5M8767 crystal oscillator clock. These + multi-function devices have 3 fixed-rate oscillators, clocked at + 32KHz each. config CLK_TWL6040 tristate "External McPDM functional clock from twl6040" diff --git a/drivers/clk/clk-s2mps11.c b/drivers/clk/clk-s2mps11.c index 43e25bb405ea..f4c1f0881b6c 100644 --- a/drivers/clk/clk-s2mps11.c +++ b/drivers/clk/clk-s2mps11.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #define s2mps11_name(a) (a->hw.init->name) @@ -175,6 +176,9 @@ static int s2mps11_clk_probe(struct platform_device *pdev) case S2MPS11X: s2mps11_reg = S2MPS11_REG_RTC_CTRL; break; + case S5M8767X: + s2mps11_reg = S5M8767_REG_CTRL1; + break; default: dev_err(&pdev->dev, "Invalid device type\n"); return -EINVAL; @@ -254,6 +258,7 @@ static int s2mps11_clk_remove(struct platform_device *pdev) static const struct platform_device_id s2mps11_clk_id[] = { { "s2mps11-clk", S2MPS11X}, + { "s5m8767-clk", S5M8767X}, { }, }; MODULE_DEVICE_TABLE(platform, s2mps11_clk_id); -- cgit v1.2.3 From 95bb9f515f5d8428a447b3afc131a99ddbdf4e8b Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Thu, 13 Mar 2014 16:08:01 -0500 Subject: clk: socfpga: Fix section mismatch warning WARNING: drivers/clk/socfpga/built-in.o(.data+0xc0): Section mismatch in reference from the variable socfpga_child_clocks to the function .init.text:socfpga_pll_init() The variable socfpga_child_clocks references the function __init socfpga_pll_init() If the reference is valid then annotate the variable with __init* or __refdata (see linux/init.h) or name the variable: *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console WARNING: drivers/clk/socfpga/built-in.o(.data+0x184): Section mismatch in reference from the variable socfpga_child_clocks to the function .init.text:socfpga_periph_init() The variable socfpga_child_clocks references the function __init socfpga_periph_init() If the reference is valid then annotate the variable with __init* or __refdata (see linux/init.h) or name the variable: *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console WARNING: drivers/clk/socfpga/built-in.o(.data+0x248): Section mismatch in reference from the variable socfpga_child_clocks to the function .init.text:socfpga_gate_init() The variable socfpga_child_clocks references the function __init socfpga_gate_init() If the reference is valid then annotate the variable with __init* or __refdata (see linux/init.h) or name the variable: *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console Reported-by: Mike Turquette Signed-off-by: Dinh Nguyen Signed-off-by: Mike Turquette --- drivers/clk/socfpga/clk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/socfpga/clk.c b/drivers/clk/socfpga/clk.c index 6217d5dc6644..35a960a993f9 100644 --- a/drivers/clk/socfpga/clk.c +++ b/drivers/clk/socfpga/clk.c @@ -28,7 +28,7 @@ void __iomem *clk_mgr_base_addr; -static struct of_device_id socfpga_child_clocks[] = { +static const struct of_device_id socfpga_child_clocks[] __initconst = { { .compatible = "altr,socfpga-pll-clock", socfpga_pll_init, }, { .compatible = "altr,socfpga-perip-clk", socfpga_periph_init, }, { .compatible = "altr,socfpga-gate-clk", socfpga_gate_init, }, -- cgit v1.2.3 From 16d1c8991cd6f25083ec43c15bed7ae30fbbe41e Mon Sep 17 00:00:00 2001 From: Haojian Zhuang Date: Tue, 24 Dec 2013 17:31:13 +0800 Subject: clk: hisi: assign missing clk to table The fixed rate and fixed factor clock isn't registered to clk table. Signed-off-by: Haojian Zhuang --- drivers/clk/hisilicon/clk.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/hisilicon/clk.c b/drivers/clk/hisilicon/clk.c index a3a7152c92d9..18fc21f0ea15 100644 --- a/drivers/clk/hisilicon/clk.c +++ b/drivers/clk/hisilicon/clk.c @@ -68,6 +68,7 @@ void __init hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *clks, __func__, clks[i].name); continue; } + clk_table[clks[i].id] = clk; } } @@ -87,6 +88,7 @@ void __init hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *clks, __func__, clks[i].name); continue; } + clk_table[clks[i].id] = clk; } } -- cgit v1.2.3 From d3e6573c48f4472147b37e92cb345271e04d34d9 Mon Sep 17 00:00:00 2001 From: Haojian Zhuang Date: Tue, 24 Dec 2013 17:33:54 +0800 Subject: clk: hip04: add clock driver Now only fixed rate clocks are appended into the clock driver. Signed-off-by: Haojian Zhuang --- drivers/clk/Makefile | 1 + drivers/clk/hisilicon/Makefile | 5 ++- drivers/clk/hisilicon/clk-hip04.c | 54 +++++++++++++++++++++++++++++++++ include/dt-bindings/clock/hip04-clock.h | 35 +++++++++++++++++++++ 4 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/hisilicon/clk-hip04.c create mode 100644 include/dt-bindings/clock/hip04-clock.h (limited to 'drivers') diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index a367a9831717..5134a79be320 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o obj-$(CONFIG_COMMON_CLK_AT91) += at91/ obj-$(CONFIG_ARCH_HI3xxx) += hisilicon/ +obj-$(CONFIG_ARCH_HIP04) += hisilicon/ obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_ARCH_MMP) += mmp/ diff --git a/drivers/clk/hisilicon/Makefile b/drivers/clk/hisilicon/Makefile index a049108341fc..40b33c6a8257 100644 --- a/drivers/clk/hisilicon/Makefile +++ b/drivers/clk/hisilicon/Makefile @@ -2,4 +2,7 @@ # Hisilicon Clock specific Makefile # -obj-y += clk.o clkgate-separated.o clk-hi3620.o +obj-y += clk.o clkgate-separated.o + +obj-$(CONFIG_ARCH_HI3xxx) += clk-hi3620.o +obj-$(CONFIG_ARCH_HIP04) += clk-hip04.o diff --git a/drivers/clk/hisilicon/clk-hip04.c b/drivers/clk/hisilicon/clk-hip04.c new file mode 100644 index 000000000000..bdc6cd05f4ca --- /dev/null +++ b/drivers/clk/hisilicon/clk-hip04.c @@ -0,0 +1,54 @@ +/* + * Hisilicon HiP04 clock driver + * + * Copyright (c) 2013-2014 Hisilicon Limited. + * Copyright (c) 2013-2014 Linaro Limited. + * + * Author: Haojian Zhuang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "clk.h" + +/* fixed rate clocks */ +static struct hisi_fixed_rate_clock hip04_fixed_rate_clks[] __initdata = { + { HIP04_OSC50M, "osc50m", NULL, CLK_IS_ROOT, 50000000, }, + { HIP04_CLK_50M, "clk50m", NULL, CLK_IS_ROOT, 50000000, }, + { HIP04_CLK_168M, "clk168m", NULL, CLK_IS_ROOT, 168750000, }, +}; + +static void __init hip04_clk_init(struct device_node *np) +{ + hisi_clk_init(np, HIP04_NR_CLKS); + + hisi_clk_register_fixed_rate(hip04_fixed_rate_clks, + ARRAY_SIZE(hip04_fixed_rate_clks), + NULL); +} +CLK_OF_DECLARE(hip04_clk, "hisilicon,hip04-clock", hip04_clk_init); diff --git a/include/dt-bindings/clock/hip04-clock.h b/include/dt-bindings/clock/hip04-clock.h new file mode 100644 index 000000000000..695e61cd1523 --- /dev/null +++ b/include/dt-bindings/clock/hip04-clock.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2013-2014 Hisilicon Limited. + * Copyright (c) 2013-2014 Linaro Limited. + * + * Author: Haojian Zhuang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef __DTS_HIP04_CLOCK_H +#define __DTS_HIP04_CLOCK_H + +#define HIP04_NONE_CLOCK 0 + +/* fixed rate & fixed factor clocks */ +#define HIP04_OSC50M 1 +#define HIP04_CLK_50M 2 +#define HIP04_CLK_168M 3 + +#define HIP04_NR_CLKS 64 + +#endif /* __DTS_HIP04_CLOCK_H */ -- cgit v1.2.3 From 75af25f581b1ffc63e06cb01547b3141d4cd5f58 Mon Sep 17 00:00:00 2001 From: Haojian Zhuang Date: Tue, 24 Dec 2013 21:38:26 +0800 Subject: clk: hisi: remove static variable Remove the static variable. So these common clock register helper could be used in more SoCs. Signed-off-by: Haojian Zhuang --- drivers/clk/hisilicon/clk-hi3620.c | 25 +++++---------- drivers/clk/hisilicon/clk-hip04.c | 8 +++-- drivers/clk/hisilicon/clk.c | 64 ++++++++++++++++++++++++++++---------- drivers/clk/hisilicon/clk.h | 17 ++++++---- 4 files changed, 72 insertions(+), 42 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/hisilicon/clk-hi3620.c b/drivers/clk/hisilicon/clk-hi3620.c index f24ad6a3a797..cc6dff19d6e4 100644 --- a/drivers/clk/hisilicon/clk-hi3620.c +++ b/drivers/clk/hisilicon/clk-hi3620.c @@ -210,33 +210,24 @@ static struct hisi_gate_clock hi3620_seperated_gate_clks[] __initdata = { static void __init hi3620_clk_init(struct device_node *np) { - void __iomem *base; + struct hisi_clock_data *clk_data; - if (np) { - base = of_iomap(np, 0); - if (!base) { - pr_err("failed to map Hi3620 clock registers\n"); - return; - } - } else { - pr_err("failed to find Hi3620 clock node in DTS\n"); + clk_data = hisi_clk_init(np, HI3620_NR_CLKS); + if (!clk_data) return; - } - - hisi_clk_init(np, HI3620_NR_CLKS); hisi_clk_register_fixed_rate(hi3620_fixed_rate_clks, ARRAY_SIZE(hi3620_fixed_rate_clks), - base); + clk_data); hisi_clk_register_fixed_factor(hi3620_fixed_factor_clks, ARRAY_SIZE(hi3620_fixed_factor_clks), - base); + clk_data); hisi_clk_register_mux(hi3620_mux_clks, ARRAY_SIZE(hi3620_mux_clks), - base); + clk_data); hisi_clk_register_divider(hi3620_div_clks, ARRAY_SIZE(hi3620_div_clks), - base); + clk_data); hisi_clk_register_gate_sep(hi3620_seperated_gate_clks, ARRAY_SIZE(hi3620_seperated_gate_clks), - base); + clk_data); } CLK_OF_DECLARE(hi3620_clk, "hisilicon,hi3620-clock", hi3620_clk_init); diff --git a/drivers/clk/hisilicon/clk-hip04.c b/drivers/clk/hisilicon/clk-hip04.c index bdc6cd05f4ca..132b57a0ce09 100644 --- a/drivers/clk/hisilicon/clk-hip04.c +++ b/drivers/clk/hisilicon/clk-hip04.c @@ -45,10 +45,14 @@ static struct hisi_fixed_rate_clock hip04_fixed_rate_clks[] __initdata = { static void __init hip04_clk_init(struct device_node *np) { - hisi_clk_init(np, HIP04_NR_CLKS); + struct hisi_clock_data *clk_data; + + clk_data = hisi_clk_init(np, HIP04_NR_CLKS); + if (!clk_data) + return; hisi_clk_register_fixed_rate(hip04_fixed_rate_clks, ARRAY_SIZE(hip04_fixed_rate_clks), - NULL); + clk_data); } CLK_OF_DECLARE(hip04_clk, "hisilicon,hip04-clock", hip04_clk_init); diff --git a/drivers/clk/hisilicon/clk.c b/drivers/clk/hisilicon/clk.c index 18fc21f0ea15..276f672e7b1a 100644 --- a/drivers/clk/hisilicon/clk.c +++ b/drivers/clk/hisilicon/clk.c @@ -37,23 +37,49 @@ #include "clk.h" static DEFINE_SPINLOCK(hisi_clk_lock); -static struct clk **clk_table; -static struct clk_onecell_data clk_data; -void __init hisi_clk_init(struct device_node *np, int nr_clks) +struct hisi_clock_data __init *hisi_clk_init(struct device_node *np, + int nr_clks) { + struct hisi_clock_data *clk_data; + struct clk **clk_table; + void __iomem *base; + + if (np) { + base = of_iomap(np, 0); + if (!base) { + pr_err("failed to map Hisilicon clock registers\n"); + goto err; + } + } else { + pr_err("failed to find Hisilicon clock node in DTS\n"); + goto err; + } + + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) { + pr_err("%s: could not allocate clock data\n", __func__); + goto err; + } + clk_data->base = base; + clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL); if (!clk_table) { pr_err("%s: could not allocate clock lookup table\n", __func__); - return; + goto err_data; } - clk_data.clks = clk_table; - clk_data.clk_num = nr_clks; - of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + clk_data->clk_data.clks = clk_table; + clk_data->clk_data.clk_num = nr_clks; + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data->clk_data); + return clk_data; +err_data: + kfree(clk_data); +err: + return NULL; } void __init hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *clks, - int nums, void __iomem *base) + int nums, struct hisi_clock_data *data) { struct clk *clk; int i; @@ -68,12 +94,13 @@ void __init hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *clks, __func__, clks[i].name); continue; } - clk_table[clks[i].id] = clk; + data->clk_data.clks[clks[i].id] = clk; } } void __init hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *clks, - int nums, void __iomem *base) + int nums, + struct hisi_clock_data *data) { struct clk *clk; int i; @@ -88,14 +115,15 @@ void __init hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *clks, __func__, clks[i].name); continue; } - clk_table[clks[i].id] = clk; + data->clk_data.clks[clks[i].id] = clk; } } void __init hisi_clk_register_mux(struct hisi_mux_clock *clks, - int nums, void __iomem *base) + int nums, struct hisi_clock_data *data) { struct clk *clk; + void __iomem *base = data->base; int i; for (i = 0; i < nums; i++) { @@ -113,14 +141,15 @@ void __init hisi_clk_register_mux(struct hisi_mux_clock *clks, if (clks[i].alias) clk_register_clkdev(clk, clks[i].alias, NULL); - clk_table[clks[i].id] = clk; + data->clk_data.clks[clks[i].id] = clk; } } void __init hisi_clk_register_divider(struct hisi_divider_clock *clks, - int nums, void __iomem *base) + int nums, struct hisi_clock_data *data) { struct clk *clk; + void __iomem *base = data->base; int i; for (i = 0; i < nums; i++) { @@ -141,14 +170,15 @@ void __init hisi_clk_register_divider(struct hisi_divider_clock *clks, if (clks[i].alias) clk_register_clkdev(clk, clks[i].alias, NULL); - clk_table[clks[i].id] = clk; + data->clk_data.clks[clks[i].id] = clk; } } void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *clks, - int nums, void __iomem *base) + int nums, struct hisi_clock_data *data) { struct clk *clk; + void __iomem *base = data->base; int i; for (i = 0; i < nums; i++) { @@ -168,6 +198,6 @@ void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *clks, if (clks[i].alias) clk_register_clkdev(clk, clks[i].alias, NULL); - clk_table[clks[i].id] = clk; + data->clk_data.clks[clks[i].id] = clk; } } diff --git a/drivers/clk/hisilicon/clk.h b/drivers/clk/hisilicon/clk.h index 4a6beebefb7a..43fa5da88f02 100644 --- a/drivers/clk/hisilicon/clk.h +++ b/drivers/clk/hisilicon/clk.h @@ -30,6 +30,11 @@ #include #include +struct hisi_clock_data { + struct clk_onecell_data clk_data; + void __iomem *base; +}; + struct hisi_fixed_rate_clock { unsigned int id; char *name; @@ -89,15 +94,15 @@ struct clk *hisi_register_clkgate_sep(struct device *, const char *, void __iomem *, u8, u8, spinlock_t *); -void __init hisi_clk_init(struct device_node *, int); +struct hisi_clock_data __init *hisi_clk_init(struct device_node *, int); void __init hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *, - int, void __iomem *); + int, struct hisi_clock_data *); void __init hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *, - int, void __iomem *); + int, struct hisi_clock_data *); void __init hisi_clk_register_mux(struct hisi_mux_clock *, int, - void __iomem *); + struct hisi_clock_data *); void __init hisi_clk_register_divider(struct hisi_divider_clock *, - int, void __iomem *); + int, struct hisi_clock_data *); void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *, - int, void __iomem *); + int, struct hisi_clock_data *); #endif /* __HISI_CLK_H */ -- cgit v1.2.3 From 5a8ddf26822dcf601a44d35efa8fe162cbc84e62 Mon Sep 17 00:00:00 2001 From: Emilio López Date: Wed, 19 Mar 2014 15:19:30 -0300 Subject: clk: sunxi: fix A20 PLL4 calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allwinner actually reworked the PLL4 on A20; now it's compatible with the sun4i PLL5/6 design previous to any divisions, as well as to the new PLL8 in sun7i. Signed-off-by: Emilio López Signed-off-by: Mike Turquette --- drivers/clk/sunxi/clk-sunxi.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 23baad9d934a..ef6ad52b7546 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -589,6 +589,12 @@ static const struct factors_data sun6i_a31_pll1_data __initconst = { .getter = sun6i_a31_get_pll1_factors, }; +static const struct factors_data sun7i_a20_pll4_data __initconst = { + .enable = 31, + .table = &sun4i_pll5_config, + .getter = sun4i_get_pll5_factors, +}; + static const struct factors_data sun4i_pll5_data __initconst = { .enable = 31, .table = &sun4i_pll5_config, @@ -1209,6 +1215,7 @@ free_clkdata: static const struct of_device_id clk_factors_match[] __initconst = { {.compatible = "allwinner,sun4i-a10-pll1-clk", .data = &sun4i_pll1_data,}, {.compatible = "allwinner,sun6i-a31-pll1-clk", .data = &sun6i_a31_pll1_data,}, + {.compatible = "allwinner,sun7i-a20-pll4-clk", .data = &sun7i_a20_pll4_data,}, {.compatible = "allwinner,sun6i-a31-pll6-clk", .data = &sun6i_a31_pll6_data,}, {.compatible = "allwinner,sun4i-a10-apb1-clk", .data = &sun4i_apb1_data,}, {.compatible = "allwinner,sun4i-a10-mod0-clk", .data = &sun4i_mod0_data,}, -- cgit v1.2.3 From 2226013972da1ec0a2aeb13a684180bb2b50e0f3 Mon Sep 17 00:00:00 2001 From: Emilio López Date: Wed, 19 Mar 2014 15:19:32 -0300 Subject: clk: sunxi: fix some calculations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some divisor calculations were misrounded, causing higher than requested rates on some clocks. Fix them up using DIV_ROUND_UP, and replace one homebrew instance of it as well with the right macro. Reported-by: Boris BREZILLON Signed-off-by: Emilio López Signed-off-by: Mike Turquette --- drivers/clk/sunxi/clk-sunxi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index ef6ad52b7546..aaec9d7867d0 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -299,7 +299,7 @@ static void sun4i_get_apb1_factors(u32 *freq, u32 parent_rate, if (parent_rate < *freq) *freq = parent_rate; - parent_rate = (parent_rate + (*freq - 1)) / *freq; + parent_rate = DIV_ROUND_UP(parent_rate, *freq); /* Invalid rate! */ if (parent_rate > 32) @@ -344,7 +344,7 @@ static void sun4i_get_mod0_factors(u32 *freq, u32 parent_rate, if (*freq > parent_rate) *freq = parent_rate; - div = parent_rate / *freq; + div = DIV_ROUND_UP(parent_rate, *freq); if (div < 16) calcp = 0; @@ -385,7 +385,7 @@ static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate, if (*freq > parent_rate) *freq = parent_rate; - div = parent_rate / *freq; + div = DIV_ROUND_UP(parent_rate, *freq); if (div < 32) calcp = 0; -- cgit v1.2.3 From 9ce71ca10fb8aeb900aeb319dde05750467554bf Mon Sep 17 00:00:00 2001 From: Emilio López Date: Wed, 19 Mar 2014 15:19:33 -0300 Subject: clk: sunxi: fix thinko in comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should read MOD0 and not MMC; MMC is just one example of a MOD0 clock. Signed-off-by: Emilio López Signed-off-by: Mike Turquette --- drivers/clk/sunxi/clk-sunxi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index aaec9d7867d0..bd7dc733c1ca 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -330,7 +330,7 @@ static void sun4i_get_apb1_factors(u32 *freq, u32 parent_rate, /** * sun4i_get_mod0_factors() - calculates m, n factors for MOD0-style clocks - * MMC rate is calculated as follows + * MOD0 rate is calculated as follows * rate = (parent_rate >> p) / (m + 1); */ -- cgit v1.2.3 From 419f612932674d5f72b1995eda52ca15d87361ef Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Tue, 11 Mar 2014 10:00:32 +0100 Subject: clk: at91: replace prog clk round_rate with determine_rate Implement the determine_rate callback to choose the best parent clk that fulfills the requested rate. Signed-off-by: Boris BREZILLON Signed-off-by: Mike Turquette --- drivers/clk/at91/clk-programmable.c | 56 ++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c index fd792b203eaf..eff701665f04 100644 --- a/drivers/clk/at91/clk-programmable.c +++ b/drivers/clk/at91/clk-programmable.c @@ -102,40 +102,40 @@ static unsigned long clk_programmable_recalc_rate(struct clk_hw *hw, return parent_rate >> prog->pres; } -static long clk_programmable_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static long clk_programmable_determine_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long *best_parent_rate, + struct clk **best_parent_clk) { - unsigned long best_rate = *parent_rate; - unsigned long best_diff; - unsigned long new_diff; - unsigned long cur_rate; - int shift = shift; - - if (rate > *parent_rate) - return *parent_rate; - else - best_diff = *parent_rate - rate; - - if (!best_diff) - return best_rate; + struct clk *parent = NULL; + long best_rate = -EINVAL; + unsigned long parent_rate; + unsigned long tmp_rate; + int shift; + int i; - for (shift = 1; shift < PROG_PRES_MASK; shift++) { - cur_rate = *parent_rate >> shift; + for (i = 0; i < __clk_get_num_parents(hw->clk); i++) { + parent = clk_get_parent_by_index(hw->clk, i); + if (!parent) + continue; - if (cur_rate > rate) - new_diff = cur_rate - rate; - else - new_diff = rate - cur_rate; + parent_rate = __clk_get_rate(parent); + for (shift = 0; shift < PROG_PRES_MASK; shift++) { + tmp_rate = parent_rate >> shift; + if (tmp_rate <= rate) + break; + } - if (!new_diff) - return cur_rate; + if (tmp_rate > rate) + continue; - if (new_diff < best_diff) { - best_diff = new_diff; - best_rate = cur_rate; + if (best_rate < 0 || (rate - tmp_rate) < (rate - best_rate)) { + best_rate = tmp_rate; + *best_parent_rate = parent_rate; + *best_parent_clk = parent; } - if (rate > cur_rate) + if (!best_rate) break; } @@ -228,7 +228,7 @@ static const struct clk_ops programmable_ops = { .prepare = clk_programmable_prepare, .is_prepared = clk_programmable_is_ready, .recalc_rate = clk_programmable_recalc_rate, - .round_rate = clk_programmable_round_rate, + .determine_rate = clk_programmable_determine_rate, .get_parent = clk_programmable_get_parent, .set_parent = clk_programmable_set_parent, .set_rate = clk_programmable_set_rate, -- cgit v1.2.3 From 693bb3d99d06e174c31471b58c8f0b97e1479b08 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Tue, 11 Mar 2014 10:00:33 +0100 Subject: clk: at91: propagate rate change on system clks System clks are just gates, and thus do not provide any rate operations. Authorize clk rate change to be propagated to system clk parents. Signed-off-by: Boris BREZILLON Signed-off-by: Mike Turquette --- drivers/clk/at91/clk-system.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c index 8f7c0434a09f..48c89fe0cfe8 100644 --- a/drivers/clk/at91/clk-system.c +++ b/drivers/clk/at91/clk-system.c @@ -84,7 +84,7 @@ at91_clk_register_system(struct at91_pmc *pmc, const char *name, * (see drivers/memory) which would request and enable the ddrck clock. * When this is done we will be able to remove CLK_IGNORE_UNUSED flag. */ - init.flags = CLK_IGNORE_UNUSED; + init.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED; sys->id = id; sys->hw.init = &init; -- cgit v1.2.3 From cce6db80a049b23f867f4afded70ca8027876a08 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Tue, 11 Mar 2014 10:00:34 +0100 Subject: clk: at91: fix programmable clk irq handling The PCKRDY bit is not set until the system clock is enabled. This patch moves the management of the ready status in the system clock driver. Signed-off-by: Boris BREZILLON Signed-off-by: Jean-Jacques Hiblot Signed-off-by: Mike Turquette --- drivers/clk/at91/clk-programmable.c | 108 +++++++++--------------------------- drivers/clk/at91/clk-system.c | 74 ++++++++++++++++++++---- 2 files changed, 89 insertions(+), 93 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c index eff701665f04..7bcc725f67af 100644 --- a/drivers/clk/at91/clk-programmable.c +++ b/drivers/clk/at91/clk-programmable.c @@ -13,12 +13,9 @@ #include #include #include -#include #include #include #include -#include -#include #include "pmc.h" @@ -38,68 +35,23 @@ struct clk_programmable_layout { struct clk_programmable { struct clk_hw hw; struct at91_pmc *pmc; - unsigned int irq; - wait_queue_head_t wait; u8 id; - u8 css; - u8 pres; - u8 slckmck; const struct clk_programmable_layout *layout; }; #define to_clk_programmable(hw) container_of(hw, struct clk_programmable, hw) - -static irqreturn_t clk_programmable_irq_handler(int irq, void *dev_id) -{ - struct clk_programmable *prog = (struct clk_programmable *)dev_id; - - wake_up(&prog->wait); - - return IRQ_HANDLED; -} - -static int clk_programmable_prepare(struct clk_hw *hw) -{ - u32 tmp; - struct clk_programmable *prog = to_clk_programmable(hw); - struct at91_pmc *pmc = prog->pmc; - const struct clk_programmable_layout *layout = prog->layout; - u8 id = prog->id; - u32 mask = PROG_STATUS_MASK(id); - - tmp = prog->css | (prog->pres << layout->pres_shift); - if (layout->have_slck_mck && prog->slckmck) - tmp |= AT91_PMC_CSSMCK_MCK; - - pmc_write(pmc, AT91_PMC_PCKR(id), tmp); - - while (!(pmc_read(pmc, AT91_PMC_SR) & mask)) - wait_event(prog->wait, pmc_read(pmc, AT91_PMC_SR) & mask); - - return 0; -} - -static int clk_programmable_is_ready(struct clk_hw *hw) -{ - struct clk_programmable *prog = to_clk_programmable(hw); - struct at91_pmc *pmc = prog->pmc; - - return !!(pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_PCKR(prog->id)); -} - static unsigned long clk_programmable_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - u32 tmp; + u32 pres; struct clk_programmable *prog = to_clk_programmable(hw); struct at91_pmc *pmc = prog->pmc; const struct clk_programmable_layout *layout = prog->layout; - tmp = pmc_read(pmc, AT91_PMC_PCKR(prog->id)); - prog->pres = (tmp >> layout->pres_shift) & PROG_PRES_MASK; - - return parent_rate >> prog->pres; + pres = (pmc_read(pmc, AT91_PMC_PCKR(prog->id)) >> layout->pres_shift) & + PROG_PRES_MASK; + return parent_rate >> pres; } static long clk_programmable_determine_rate(struct clk_hw *hw, @@ -146,17 +98,22 @@ static int clk_programmable_set_parent(struct clk_hw *hw, u8 index) { struct clk_programmable *prog = to_clk_programmable(hw); const struct clk_programmable_layout *layout = prog->layout; + struct at91_pmc *pmc = prog->pmc; + u32 tmp = pmc_read(pmc, AT91_PMC_PCKR(prog->id)) & ~layout->css_mask; + + if (layout->have_slck_mck) + tmp &= AT91_PMC_CSSMCK_MCK; + if (index > layout->css_mask) { if (index > PROG_MAX_RM9200_CSS && layout->have_slck_mck) { - prog->css = 0; - prog->slckmck = 1; + tmp |= AT91_PMC_CSSMCK_MCK; return 0; } else { return -EINVAL; } } - prog->css = index; + pmc_write(pmc, AT91_PMC_PCKR(prog->id), tmp | index); return 0; } @@ -169,13 +126,9 @@ static u8 clk_programmable_get_parent(struct clk_hw *hw) const struct clk_programmable_layout *layout = prog->layout; tmp = pmc_read(pmc, AT91_PMC_PCKR(prog->id)); - prog->css = tmp & layout->css_mask; - ret = prog->css; - if (layout->have_slck_mck) { - prog->slckmck = !!(tmp & AT91_PMC_CSSMCK_MCK); - if (prog->slckmck && !ret) - ret = PROG_MAX_RM9200_CSS + 1; - } + ret = tmp & layout->css_mask; + if (layout->have_slck_mck && (tmp & AT91_PMC_CSSMCK_MCK) && !ret) + ret = PROG_MAX_RM9200_CSS + 1; return ret; } @@ -184,11 +137,15 @@ static int clk_programmable_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk_programmable *prog = to_clk_programmable(hw); + struct at91_pmc *pmc = prog->pmc; + const struct clk_programmable_layout *layout = prog->layout; unsigned long best_rate = parent_rate; unsigned long best_diff; unsigned long new_diff; unsigned long cur_rate; int shift = 0; + u32 tmp = pmc_read(pmc, AT91_PMC_PCKR(prog->id)) & + ~(PROG_PRES_MASK << layout->pres_shift); if (rate > parent_rate) return parent_rate; @@ -196,7 +153,7 @@ static int clk_programmable_set_rate(struct clk_hw *hw, unsigned long rate, best_diff = parent_rate - rate; if (!best_diff) { - prog->pres = shift; + pmc_write(pmc, AT91_PMC_PCKR(prog->id), tmp | shift); return 0; } @@ -220,13 +177,13 @@ static int clk_programmable_set_rate(struct clk_hw *hw, unsigned long rate, break; } - prog->pres = shift; + pmc_write(pmc, AT91_PMC_PCKR(prog->id), + tmp | (shift << layout->pres_shift)); + return 0; } static const struct clk_ops programmable_ops = { - .prepare = clk_programmable_prepare, - .is_prepared = clk_programmable_is_ready, .recalc_rate = clk_programmable_recalc_rate, .determine_rate = clk_programmable_determine_rate, .get_parent = clk_programmable_get_parent, @@ -235,16 +192,14 @@ static const struct clk_ops programmable_ops = { }; static struct clk * __init -at91_clk_register_programmable(struct at91_pmc *pmc, unsigned int irq, +at91_clk_register_programmable(struct at91_pmc *pmc, const char *name, const char **parent_names, u8 num_parents, u8 id, const struct clk_programmable_layout *layout) { - int ret; struct clk_programmable *prog; struct clk *clk = NULL; struct clk_init_data init; - char irq_name[11]; if (id > PROG_ID_MAX) return ERR_PTR(-EINVAL); @@ -263,14 +218,6 @@ at91_clk_register_programmable(struct at91_pmc *pmc, unsigned int irq, prog->layout = layout; prog->hw.init = &init; prog->pmc = pmc; - prog->irq = irq; - init_waitqueue_head(&prog->wait); - irq_set_status_flags(prog->irq, IRQ_NOAUTOEN); - snprintf(irq_name, sizeof(irq_name), "clk-prog%d", id); - ret = request_irq(prog->irq, clk_programmable_irq_handler, - IRQF_TRIGGER_HIGH, irq_name, prog); - if (ret) - return ERR_PTR(ret); clk = clk_register(NULL, &prog->hw); if (IS_ERR(clk)) @@ -304,7 +251,6 @@ of_at91_clk_prog_setup(struct device_node *np, struct at91_pmc *pmc, int num; u32 id; int i; - unsigned int irq; struct clk *clk; int num_parents; const char *parent_names[PROG_SOURCE_MAX]; @@ -332,11 +278,7 @@ of_at91_clk_prog_setup(struct device_node *np, struct at91_pmc *pmc, if (of_property_read_string(np, "clock-output-names", &name)) name = progclknp->name; - irq = irq_of_parse_and_map(progclknp, 0); - if (!irq) - continue; - - clk = at91_clk_register_programmable(pmc, irq, name, + clk = at91_clk_register_programmable(pmc, name, parent_names, num_parents, id, layout); if (IS_ERR(clk)) diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c index 48c89fe0cfe8..8c96307d7363 100644 --- a/drivers/clk/at91/clk-system.c +++ b/drivers/clk/at91/clk-system.c @@ -14,6 +14,11 @@ #include #include #include +#include +#include +#include +#include +#include #include "pmc.h" @@ -25,19 +30,48 @@ struct clk_system { struct clk_hw hw; struct at91_pmc *pmc; + unsigned int irq; + wait_queue_head_t wait; u8 id; }; -static int clk_system_enable(struct clk_hw *hw) +static inline int is_pck(int id) +{ + 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 int clk_system_prepare(struct clk_hw *hw) { struct clk_system *sys = to_clk_system(hw); struct at91_pmc *pmc = sys->pmc; + u32 mask = 1 << sys->id; - pmc_write(pmc, AT91_PMC_SCER, 1 << sys->id); + pmc_write(pmc, AT91_PMC_SCER, mask); + + if (!is_pck(sys->id)) + return 0; + + while (!(pmc_read(pmc, AT91_PMC_SR) & mask)) { + if (sys->irq) { + enable_irq(sys->irq); + wait_event(sys->wait, + pmc_read(pmc, AT91_PMC_SR) & mask); + } else + cpu_relax(); + } return 0; } -static void clk_system_disable(struct clk_hw *hw) +static void clk_system_unprepare(struct clk_hw *hw) { struct clk_system *sys = to_clk_system(hw); struct at91_pmc *pmc = sys->pmc; @@ -45,27 +79,34 @@ static void clk_system_disable(struct clk_hw *hw) pmc_write(pmc, AT91_PMC_SCDR, 1 << sys->id); } -static int clk_system_is_enabled(struct clk_hw *hw) +static int clk_system_is_prepared(struct clk_hw *hw) { struct clk_system *sys = to_clk_system(hw); struct at91_pmc *pmc = sys->pmc; - return !!(pmc_read(pmc, AT91_PMC_SCSR) & (1 << sys->id)); + if (!(pmc_read(pmc, AT91_PMC_SCSR) & (1 << sys->id))) + return 0; + + if (!is_pck(sys->id)) + return 1; + + return !!(pmc_read(pmc, AT91_PMC_SR) & (1 << sys->id)); } static const struct clk_ops system_ops = { - .enable = clk_system_enable, - .disable = clk_system_disable, - .is_enabled = clk_system_is_enabled, + .prepare = clk_system_prepare, + .unprepare = clk_system_unprepare, + .is_prepared = clk_system_is_prepared, }; static struct clk * __init at91_clk_register_system(struct at91_pmc *pmc, const char *name, - const char *parent_name, u8 id) + const char *parent_name, u8 id, int irq) { struct clk_system *sys; struct clk *clk = NULL; struct clk_init_data init; + int ret; if (!parent_name || id > SYSTEM_MAX_ID) return ERR_PTR(-EINVAL); @@ -89,6 +130,15 @@ at91_clk_register_system(struct at91_pmc *pmc, const char *name, sys->id = id; sys->hw.init = &init; sys->pmc = pmc; + 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) + return ERR_PTR(ret); + } clk = clk_register(NULL, &sys->hw); if (IS_ERR(clk)) @@ -101,6 +151,7 @@ static void __init of_at91_clk_sys_setup(struct device_node *np, struct at91_pmc *pmc) { int num; + int irq = 0; u32 id; struct clk *clk; const char *name; @@ -118,9 +169,12 @@ of_at91_clk_sys_setup(struct device_node *np, struct at91_pmc *pmc) if (of_property_read_string(np, "clock-output-names", &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); - clk = at91_clk_register_system(pmc, name, parent_name, id); + clk = at91_clk_register_system(pmc, name, parent_name, id, irq); if (IS_ERR(clk)) continue; -- cgit v1.2.3 From 141c71dd2c00ca7d807138ece55a17c61085c793 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Tue, 11 Mar 2014 10:00:35 +0100 Subject: clk: at91: optimization of the set_rate callback Signed-off-by: Boris BREZILLON Signed-off-by: Jean-Jacques Hiblot Signed-off-by: Mike Turquette --- drivers/clk/at91/clk-programmable.c | 38 ++++++++----------------------------- 1 file changed, 8 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c index 7bcc725f67af..62e2509f9df1 100644 --- a/drivers/clk/at91/clk-programmable.c +++ b/drivers/clk/at91/clk-programmable.c @@ -139,43 +139,21 @@ static int clk_programmable_set_rate(struct clk_hw *hw, unsigned long rate, struct clk_programmable *prog = to_clk_programmable(hw); struct at91_pmc *pmc = prog->pmc; const struct clk_programmable_layout *layout = prog->layout; - unsigned long best_rate = parent_rate; - unsigned long best_diff; - unsigned long new_diff; - unsigned long cur_rate; + unsigned long div = parent_rate / rate; int shift = 0; u32 tmp = pmc_read(pmc, AT91_PMC_PCKR(prog->id)) & ~(PROG_PRES_MASK << layout->pres_shift); - if (rate > parent_rate) - return parent_rate; - else - best_diff = parent_rate - rate; + if (!div) + return -EINVAL; - if (!best_diff) { - pmc_write(pmc, AT91_PMC_PCKR(prog->id), tmp | shift); - return 0; - } - - for (shift = 1; shift < PROG_PRES_MASK; shift++) { - cur_rate = parent_rate >> shift; - - if (cur_rate > rate) - new_diff = cur_rate - rate; - else - new_diff = rate - cur_rate; - - if (!new_diff) - break; + shift = fls(div) - 1; - if (new_diff < best_diff) { - best_diff = new_diff; - best_rate = cur_rate; - } + if (div != (1< cur_rate) - break; - } + if (shift >= PROG_PRES_MASK) + return -EINVAL; pmc_write(pmc, AT91_PMC_PCKR(prog->id), tmp | (shift << layout->pres_shift)); -- cgit v1.2.3 From 00fa6e5d138a87f1b1bc8ae634c5f6fe513338c6 Mon Sep 17 00:00:00 2001 From: Tang Yuantian Date: Tue, 21 Jan 2014 09:32:45 +0800 Subject: clk: mpc85xx: Update the driver to align to new clock bindings The clock bindings for Freescale CoreNet platform are updated. So, the driver needs to be updated accordingly. The main changes include: - Added a new node to present the input system clock - Changed PLL and MUX's compatible string Signed-off-by: Tang Yuantian Acked-by: Scott Wood Signed-off-by: Mike Turquette --- drivers/clk/clk-ppc-corenet.c | 70 +++++++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/clk-ppc-corenet.c b/drivers/clk/clk-ppc-corenet.c index c4f76ed914b0..8b284be4efa4 100644 --- a/drivers/clk/clk-ppc-corenet.c +++ b/drivers/clk/clk-ppc-corenet.c @@ -27,7 +27,6 @@ struct cmux_clk { #define CLKSEL_ADJUST BIT(0) #define to_cmux_clk(p) container_of(p, struct cmux_clk, hw) -static void __iomem *base; static unsigned int clocks_per_pll; static int cmux_set_parent(struct clk_hw *hw, u8 idx) @@ -100,7 +99,11 @@ static void __init core_mux_init(struct device_node *np) pr_err("%s: could not allocate cmux_clk\n", __func__); goto err_name; } - cmux_clk->reg = base + offset; + cmux_clk->reg = of_iomap(np, 0); + if (!cmux_clk->reg) { + pr_err("%s: could not map register\n", __func__); + goto err_clk; + } node = of_find_compatible_node(NULL, NULL, "fsl,p4080-clockgen"); if (node && (offset >= 0x80)) @@ -143,38 +146,39 @@ err_name: static void __init core_pll_init(struct device_node *np) { - u32 offset, mult; + u32 mult; int i, rc, count; const char *clk_name, *parent_name; struct clk_onecell_data *onecell_data; struct clk **subclks; + void __iomem *base; - rc = of_property_read_u32(np, "reg", &offset); - if (rc) { - pr_err("%s: could not get reg property\n", np->name); + base = of_iomap(np, 0); + if (!base) { + pr_err("clk-ppc: iomap error\n"); return; } /* get the multiple of PLL */ - mult = ioread32be(base + offset); + mult = ioread32be(base); /* check if this PLL is disabled */ if (mult & PLL_KILL) { pr_debug("PLL:%s is disabled\n", np->name); - return; + goto err_map; } mult = (mult >> 1) & 0x3f; parent_name = of_clk_get_parent_name(np, 0); if (!parent_name) { pr_err("PLL: %s must have a parent\n", np->name); - return; + goto err_map; } count = of_property_count_strings(np, "clock-output-names"); if (count < 0 || count > 4) { pr_err("%s: clock is not supported\n", np->name); - return; + goto err_map; } /* output clock number per PLL */ @@ -183,7 +187,7 @@ static void __init core_pll_init(struct device_node *np) subclks = kzalloc(sizeof(struct clk *) * count, GFP_KERNEL); if (!subclks) { pr_err("%s: could not allocate subclks\n", __func__); - return; + goto err_map; } onecell_data = kzalloc(sizeof(struct clk_onecell_data), GFP_KERNEL); @@ -230,30 +234,52 @@ static void __init core_pll_init(struct device_node *np) goto err_cell; } + iounmap(base); return; err_cell: kfree(onecell_data); err_clks: kfree(subclks); +err_map: + iounmap(base); +} + +static void __init sysclk_init(struct device_node *node) +{ + struct clk *clk; + const char *clk_name = node->name; + struct device_node *np = of_get_parent(node); + u32 rate; + + if (!np) { + pr_err("ppc-clk: could not get parent node\n"); + return; + } + + if (of_property_read_u32(np, "clock-frequency", &rate)) { + of_node_put(node); + return; + } + + of_property_read_string(np, "clock-output-names", &clk_name); + + clk = clk_register_fixed_rate(NULL, clk_name, NULL, CLK_IS_ROOT, rate); + if (!IS_ERR(clk)) + of_clk_add_provider(np, of_clk_src_simple_get, clk); } static const struct of_device_id clk_match[] __initconst = { - { .compatible = "fixed-clock", .data = of_fixed_clk_setup, }, - { .compatible = "fsl,core-pll-clock", .data = core_pll_init, }, - { .compatible = "fsl,core-mux-clock", .data = core_mux_init, }, + { .compatible = "fsl,qoriq-sysclk-1.0", .data = sysclk_init, }, + { .compatible = "fsl,qoriq-sysclk-2.0", .data = sysclk_init, }, + { .compatible = "fsl,qoriq-core-pll-1.0", .data = core_pll_init, }, + { .compatible = "fsl,qoriq-core-pll-2.0", .data = core_pll_init, }, + { .compatible = "fsl,qoriq-core-mux-1.0", .data = core_mux_init, }, + { .compatible = "fsl,qoriq-core-mux-2.0", .data = core_mux_init, }, {} }; static int __init ppc_corenet_clk_probe(struct platform_device *pdev) { - struct device_node *np; - - np = pdev->dev.of_node; - base = of_iomap(np, 0); - if (!base) { - dev_err(&pdev->dev, "iomap error\n"); - return -ENOMEM; - } of_clk_init(clk_match); return 0; -- cgit v1.2.3 From 1771b10d605d26ccee771a7fb4b08718c124097a Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Mon, 24 Feb 2014 19:10:13 +0100 Subject: clk: respect the clock dependencies in of_clk_init Until now the clock providers were initialized in the order found in the device tree. This led to have the dependencies between the clocks not respected: children clocks could be initialized before their parent clocks. Instead of forcing each platform to manage its own initialization order, this patch adds this work inside the framework itself. Using the data of the device tree the of_clk_init function now delayed the initialization of a clock provider if its parent provider was not ready yet. The strict dependency check (all parents of a given clk must be initialized) was added by Boris BREZILLON Signed-off-by: Gregory CLEMENT Signed-off-by: Boris BREZILLON Signed-off-by: Mike Turquette --- drivers/clk/clk.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 895b3d204e22..e1a192240055 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2539,24 +2539,99 @@ const char *of_clk_get_parent_name(struct device_node *np, int index) } EXPORT_SYMBOL_GPL(of_clk_get_parent_name); +struct clock_provider { + of_clk_init_cb_t clk_init_cb; + struct device_node *np; + struct list_head node; +}; + +static LIST_HEAD(clk_provider_list); + +/* + * This function looks for a parent clock. If there is one, then it + * checks that the provider for this parent clock was initialized, in + * this case the parent clock will be ready. + */ +static int parent_ready(struct device_node *np) +{ + int i = 0; + + while (true) { + struct clk *clk = of_clk_get(np, i); + + /* this parent is ready we can check the next one */ + if (!IS_ERR(clk)) { + clk_put(clk); + i++; + continue; + } + + /* at least one parent is not ready, we exit now */ + if (PTR_ERR(clk) == -EPROBE_DEFER) + return 0; + + /* + * Here we make assumption that the device tree is + * written correctly. So an error means that there is + * no more parent. As we didn't exit yet, then the + * previous parent are ready. If there is no clock + * parent, no need to wait for them, then we can + * consider their absence as being ready + */ + return 1; + } +} + /** * of_clk_init() - Scan and init clock providers from the DT * @matches: array of compatible values and init functions for providers. * - * This function scans the device tree for matching clock providers and - * calls their initialization functions + * This function scans the device tree for matching clock providers + * and calls their initialization functions. It also do it by trying + * to follow the dependencies. */ void __init of_clk_init(const struct of_device_id *matches) { const struct of_device_id *match; struct device_node *np; + struct clock_provider *clk_provider, *next; + bool is_init_done; + bool force = false; if (!matches) matches = &__clk_of_table; + /* First prepare the list of the clocks providers */ for_each_matching_node_and_match(np, matches, &match) { - of_clk_init_cb_t clk_init_cb = match->data; - clk_init_cb(np); + struct clock_provider *parent = + kzalloc(sizeof(struct clock_provider), GFP_KERNEL); + + parent->clk_init_cb = match->data; + parent->np = np; + list_add(&parent->node, &clk_provider_list); + } + + while (!list_empty(&clk_provider_list)) { + is_init_done = false; + list_for_each_entry_safe(clk_provider, next, + &clk_provider_list, node) { + if (force || parent_ready(clk_provider->np)) { + clk_provider->clk_init_cb(clk_provider->np); + list_del(&clk_provider->node); + kfree(clk_provider); + is_init_done = true; + } + } + + /* + * We didn't managed to initialize any of the + * remaining providers during the last loop, so now we + * initialize all the remaining ones unconditionally + * in case the clock parent was not mandatory + */ + if (!is_init_done) + force = true; + } } #endif -- cgit v1.2.3 From f736386160f7d7419499f877b5abb1412ab4aa32 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 16 Jan 2014 16:12:55 +0100 Subject: clk: turn rate change failed warning into pr_debug If a rate change failed it's the opportunity of the caller to handle this. Do not spam the log with a message. Signed-off-by: Sascha Hauer Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Mike Turquette --- drivers/clk/clk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index e1a192240055..fb3c40b4fbe2 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1591,7 +1591,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate) /* notify that we are about to change rates */ fail_clk = clk_propagate_rate_change(top, PRE_RATE_CHANGE); if (fail_clk) { - pr_warn("%s: failed to set %s rate\n", __func__, + pr_debug("%s: failed to set %s rate\n", __func__, fail_clk->name); clk_propagate_rate_change(top, ABORT_RATE_CHANGE); ret = -EBUSY; -- cgit v1.2.3 From a665962e8f4484647e7a19b4d6329d42ed8bc804 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 7 Mar 2014 17:00:37 +0100 Subject: clk: shmobile: add CPG driver for rz-platforms Signed-off-by: Wolfram Sang Signed-off-by: Mike Turquette --- .../bindings/clock/renesas,rz-cpg-clocks.txt | 29 ++++++ drivers/clk/shmobile/Makefile | 1 + drivers/clk/shmobile/clk-rz.c | 103 +++++++++++++++++++++ 3 files changed, 133 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/renesas,rz-cpg-clocks.txt create mode 100644 drivers/clk/shmobile/clk-rz.c (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/clock/renesas,rz-cpg-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,rz-cpg-clocks.txt new file mode 100644 index 000000000000..98a257492522 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/renesas,rz-cpg-clocks.txt @@ -0,0 +1,29 @@ +* Renesas RZ Clock Pulse Generator (CPG) + +The CPG generates core clocks for the RZ SoCs. It includes the PLL, variable +CPU and GPU clocks, and several fixed ratio dividers. + +Required Properties: + + - compatible: Must be one of + - "renesas,r7s72100-cpg-clocks" for the r7s72100 CPG + - "renesas,rz-cpg-clocks" for the generic RZ CPG + - reg: Base address and length of the memory resource used by the CPG + - clocks: References to possible parent clocks. Order must match clock modes + in the datasheet. For the r7s72100, this is extal, usb_x1. + - #clock-cells: Must be 1 + - clock-output-names: The names of the clocks. Supported clocks are "pll", + "i", and "g" + + +Example +------- + + cpg_clocks: cpg_clocks@fcfe0000 { + #clock-cells = <1>; + compatible = "renesas,r7s72100-cpg-clocks", + "renesas,rz-cpg-clocks"; + reg = <0xfcfe0000 0x18>; + clocks = <&extal_clk>, <&usb_x1_clk>; + clock-output-names = "pll", "i", "g"; + }; diff --git a/drivers/clk/shmobile/Makefile b/drivers/clk/shmobile/Makefile index 9ecef140dba7..5404cb931ebf 100644 --- a/drivers/clk/shmobile/Makefile +++ b/drivers/clk/shmobile/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_ARCH_EMEV2) += clk-emev2.o +obj-$(CONFIG_ARCH_R7S72100) += clk-rz.o obj-$(CONFIG_ARCH_R8A7790) += clk-rcar-gen2.o obj-$(CONFIG_ARCH_R8A7791) += clk-rcar-gen2.o obj-$(CONFIG_ARCH_SHMOBILE_MULTI) += clk-div6.o diff --git a/drivers/clk/shmobile/clk-rz.c b/drivers/clk/shmobile/clk-rz.c new file mode 100644 index 000000000000..7e68e8630962 --- /dev/null +++ b/drivers/clk/shmobile/clk-rz.c @@ -0,0 +1,103 @@ +/* + * rz Core CPG Clocks + * + * Copyright (C) 2013 Ideas On Board SPRL + * Copyright (C) 2014 Wolfram Sang, Sang Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include +#include +#include +#include +#include +#include + +struct rz_cpg { + struct clk_onecell_data data; + void __iomem *reg; +}; + +#define CPG_FRQCR 0x10 +#define CPG_FRQCR2 0x14 + +/* ----------------------------------------------------------------------------- + * Initialization + */ + +static struct clk * __init +rz_cpg_register_clock(struct device_node *np, struct rz_cpg *cpg, const char *name) +{ + u32 val; + unsigned mult; + static const unsigned frqcr_tab[4] = { 3, 2, 0, 1 }; + + if (strcmp(name, "pll") == 0) { + /* FIXME: cpg_mode should be read from GPIO. But no GPIO support yet */ + unsigned cpg_mode = 0; /* hardcoded to EXTAL for now */ + const char *parent_name = of_clk_get_parent_name(np, cpg_mode); + + mult = cpg_mode ? (32 / 4) : 30; + + return clk_register_fixed_factor(NULL, name, parent_name, 0, mult, 1); + } + + /* If mapping regs failed, skip non-pll clocks. System will boot anyhow */ + if (!cpg->reg) + return ERR_PTR(-ENXIO); + + /* FIXME:"i" and "g" are variable clocks with non-integer dividers (e.g. 2/3) + * and the constraint that always g <= i. To get the rz platform started, + * let them run at fixed current speed and implement the details later. + */ + if (strcmp(name, "i") == 0) + val = (clk_readl(cpg->reg + CPG_FRQCR) >> 8) & 3; + else if (strcmp(name, "g") == 0) + val = clk_readl(cpg->reg + CPG_FRQCR2) & 3; + else + return ERR_PTR(-EINVAL); + + mult = frqcr_tab[val]; + return clk_register_fixed_factor(NULL, name, "pll", 0, mult, 3); +} + +static void __init rz_cpg_clocks_init(struct device_node *np) +{ + struct rz_cpg *cpg; + struct clk **clks; + unsigned i; + int num_clks; + + num_clks = of_property_count_strings(np, "clock-output-names"); + if (WARN(num_clks <= 0, "can't count CPG clocks\n")) + return; + + cpg = kzalloc(sizeof(*cpg), GFP_KERNEL); + clks = kzalloc(num_clks * sizeof(*clks), GFP_KERNEL); + BUG_ON(!cpg || !clks); + + cpg->data.clks = clks; + cpg->data.clk_num = num_clks; + + cpg->reg = of_iomap(np, 0); + + for (i = 0; i < num_clks; ++i) { + const char *name; + struct clk *clk; + + of_property_read_string_index(np, "clock-output-names", i, &name); + + clk = rz_cpg_register_clock(np, cpg, name); + if (IS_ERR(clk)) + pr_err("%s: failed to register %s %s clock (%ld)\n", + __func__, np->name, name, PTR_ERR(clk)); + else + cpg->data.clks[i] = clk; + } + + of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data); +} +CLK_OF_DECLARE(rz_cpg_clks, "renesas,rz-cpg-clocks", rz_cpg_clocks_init); -- cgit v1.2.3 From c115b13b85af69df0314556213db0b73cd8ea7e6 Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Mon, 10 Mar 2014 11:33:20 +0800 Subject: clk: hisilicon: fix warning from smatch drivers/clk/hisilicon/clk-hi3620.c:338 mmc_clk_delay() warn: always true condition '(para >= 0) => (0-u32max >= 0)' Reported-by: Dan Carpenter Signed-off-by: Zhangfei Gao Signed-off-by: Mike Turquette --- drivers/clk/hisilicon/clk-hi3620.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/hisilicon/clk-hi3620.c b/drivers/clk/hisilicon/clk-hi3620.c index 233eba22187a..339945d2503b 100644 --- a/drivers/clk/hisilicon/clk-hi3620.c +++ b/drivers/clk/hisilicon/clk-hi3620.c @@ -326,15 +326,14 @@ static u32 mmc_clk_delay(u32 val, u32 para, u32 off, u32 len) { u32 i; - if (para >= 0) { - for (i = 0; i < len; i++) { - if (para % 2) - val |= 1 << (off + i); - else - val &= ~(1 << (off + i)); - para = para >> 1; - } + for (i = 0; i < len; i++) { + if (para % 2) + val |= 1 << (off + i); + else + val &= ~(1 << (off + i)); + para = para >> 1; } + return val; } -- cgit v1.2.3 From 238e14055da87d0d012257788e39fe0df3a82226 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 21 Mar 2014 13:18:17 +0100 Subject: clk: s2mps11: Fix possible NULL pointer dereference If parent device does not have of_node set the s2mps11_clk_parse_dt() returned NULL. This NULL was later passed to of_clk_add_provider() which dereferenced it in pr_debug() call. Signed-off-by: Krzysztof Kozlowski Cc: Signed-off-by: Mike Turquette --- drivers/clk/clk-s2mps11.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/clk-s2mps11.c b/drivers/clk/clk-s2mps11.c index f4c1f0881b6c..10e66e90d81c 100644 --- a/drivers/clk/clk-s2mps11.c +++ b/drivers/clk/clk-s2mps11.c @@ -132,7 +132,7 @@ static struct device_node *s2mps11_clk_parse_dt(struct platform_device *pdev) int i; if (!iodev->dev->of_node) - return NULL; + return ERR_PTR(-EINVAL); clk_np = of_find_node_by_name(iodev->dev->of_node, "clocks"); if (!clk_np) { -- cgit v1.2.3 From afbd1a0c309e3d5cb581a3f7c879be97855fe72f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 21 Mar 2014 13:39:20 +0100 Subject: clk: s2mps11: Use of_get_child_by_name of_find_node_by_name() walks over all nodes and can thus walk outside of the parent node. Use of_get_child_by_name() instead. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mike Turquette --- drivers/clk/clk-s2mps11.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/clk-s2mps11.c b/drivers/clk/clk-s2mps11.c index 10e66e90d81c..f2f62a1bf61a 100644 --- a/drivers/clk/clk-s2mps11.c +++ b/drivers/clk/clk-s2mps11.c @@ -134,7 +134,7 @@ static struct device_node *s2mps11_clk_parse_dt(struct platform_device *pdev) if (!iodev->dev->of_node) return ERR_PTR(-EINVAL); - clk_np = of_find_node_by_name(iodev->dev->of_node, "clocks"); + clk_np = of_get_child_by_name(iodev->dev->of_node, "clocks"); if (!clk_np) { dev_err(&pdev->dev, "could not find clock sub-node\n"); return ERR_PTR(-EINVAL); -- cgit v1.2.3 From c646cbf10fb3347ecda290dfce96b813a423ca07 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 21 Mar 2014 06:43:56 -0500 Subject: clk: support hardware-specific debugfs entries Add a new clk_ops->debug_init method to allow a clock hardware driver to populate the clock's debugfs directory with entries beyond those common for every clock. Signed-off-by: Alex Elder Signed-off-by: Mike Turquette --- drivers/clk/clk.c | 4 ++++ include/linux/clk-provider.h | 8 ++++++++ 2 files changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index fb3c40b4fbe2..1fbcb2b107e7 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -277,6 +277,10 @@ static int clk_debug_create_one(struct clk *clk, struct dentry *pdentry) if (!d) goto err_out; + if (clk->ops->debug_init) + if (clk->ops->debug_init(clk->hw, clk->dentry)) + goto err_out; + ret = 0; goto out; diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 939533da93a7..511917416fb0 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -32,6 +32,7 @@ #define CLK_GET_ACCURACY_NOCACHE BIT(8) /* do not use the cached clk accuracy */ struct clk_hw; +struct dentry; /** * struct clk_ops - Callback operations for hardware clocks; these are to @@ -127,6 +128,12 @@ struct clk_hw; * separately via calls to .set_parent and .set_rate. * Returns 0 on success, -EERROR otherwise. * + * @debug_init: Set up type-specific debugfs entries for this clock. This + * is called once, after the debugfs directory entry for this + * clock has been created. The dentry pointer representing that + * directory is provided as an argument. Called with + * prepare_lock held. Returns 0 on success, -EERROR otherwise. + * * * The clk_enable/clk_disable and clk_prepare/clk_unprepare pairs allow * implementations to split any work between atomic (enable) and sleepable @@ -165,6 +172,7 @@ struct clk_ops { unsigned long (*recalc_accuracy)(struct clk_hw *hw, unsigned long parent_accuracy); void (*init)(struct clk_hw *hw); + int (*debug_init)(struct clk_hw *hw, struct dentry *dentry); }; /** -- cgit v1.2.3 From 94885faf9dbcc2ca704d60e7db2f2b87e0b0fe6e Mon Sep 17 00:00:00 2001 From: Gabriel FERNANDEZ Date: Thu, 27 Feb 2014 16:24:14 +0100 Subject: clk: st: Support for DIVMUX and PreDiv Clocks The patch supports the DIVMUX and PreDiv clocks used by ClockGenA(s) DIVMUX clock : Divider-Multiplexer-Gate inside ClockGenA(s) It includes support for each channel : 3-parent Multiplexer, Divider for each Parent, Gate to switch OFF the output channel. The clock is implemented using generic clocks implemented in the kernel clk_divider and clk_mux. PreDiv clock : Fixed Divider Clock used inside ClockGenA(s) to divide the oscillator clock by factor-of-16. The clock is implemented using generic clocks implemented in the kernel clk_divider. Signed-off-by: Pankaj Dev Signed-off-by: Gabriel Fernandez Signed-off-by: Mike Turquette --- drivers/clk/Makefile | 1 + drivers/clk/st/Makefile | 1 + drivers/clk/st/clkgen-mux.c | 529 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 531 insertions(+) create mode 100644 drivers/clk/st/Makefile create mode 100644 drivers/clk/st/clkgen-mux.c (limited to 'drivers') diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index b5221b712387..85a33e6d765c 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_ARCH_SHMOBILE_MULTI) += shmobile/ obj-$(CONFIG_ARCH_SIRF) += sirf/ obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/ obj-$(CONFIG_PLAT_SPEAR) += spear/ +obj-$(CONFIG_ARCH_STI) += st/ obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-$(CONFIG_ARCH_OMAP2PLUS) += ti/ diff --git a/drivers/clk/st/Makefile b/drivers/clk/st/Makefile new file mode 100644 index 000000000000..e42b370edef9 --- /dev/null +++ b/drivers/clk/st/Makefile @@ -0,0 +1 @@ +obj-y += clkgen-mux.o diff --git a/drivers/clk/st/clkgen-mux.c b/drivers/clk/st/clkgen-mux.c new file mode 100644 index 000000000000..65d702c2df8d --- /dev/null +++ b/drivers/clk/st/clkgen-mux.c @@ -0,0 +1,529 @@ +/* + * clkgen-mux.c: ST GEN-MUX Clock driver + * + * Copyright (C) 2014 STMicroelectronics (R&D) Limited + * + * Authors: Stephen Gallimore + * Pankaj Dev + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include + +static DEFINE_SPINLOCK(clkgena_divmux_lock); + +static const char ** __init clkgen_mux_get_parents(struct device_node *np, + int *num_parents) +{ + const char **parents; + int nparents, i; + + nparents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + if (WARN_ON(nparents <= 0)) + return ERR_PTR(-EINVAL); + + parents = kzalloc(nparents * sizeof(const char *), GFP_KERNEL); + if (!parents) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < nparents; i++) + parents[i] = of_clk_get_parent_name(np, i); + + *num_parents = nparents; + return parents; +} + +/** + * DOC: Clock mux with a programmable divider on each of its three inputs. + * The mux has an input setting which effectively gates its output. + * + * Traits of this clock: + * prepare - clk_(un)prepare only ensures parent is (un)prepared + * enable - clk_enable and clk_disable are functional & control gating + * rate - set rate is supported + * parent - set/get parent + */ + +#define NUM_INPUTS 3 + +struct clkgena_divmux { + struct clk_hw hw; + /* Subclassed mux and divider structures */ + struct clk_mux mux; + struct clk_divider div[NUM_INPUTS]; + /* Enable/running feedback register bits for each input */ + void __iomem *feedback_reg[NUM_INPUTS]; + int feedback_bit_idx; + + u8 muxsel; +}; + +#define to_clkgena_divmux(_hw) container_of(_hw, struct clkgena_divmux, hw) + +struct clkgena_divmux_data { + int num_outputs; + int mux_offset; + int mux_offset2; + int mux_start_bit; + int div_offsets[NUM_INPUTS]; + int fb_offsets[NUM_INPUTS]; + int fb_start_bit_idx; +}; + +#define CKGAX_CLKOPSRC_SWITCH_OFF 0x3 + +static int clkgena_divmux_is_running(struct clkgena_divmux *mux) +{ + u32 regval = readl(mux->feedback_reg[mux->muxsel]); + u32 running = regval & BIT(mux->feedback_bit_idx); + return !!running; +} + +static int clkgena_divmux_enable(struct clk_hw *hw) +{ + struct clkgena_divmux *genamux = to_clkgena_divmux(hw); + struct clk_hw *mux_hw = &genamux->mux.hw; + unsigned long timeout; + int ret = 0; + + mux_hw->clk = hw->clk; + + ret = clk_mux_ops.set_parent(mux_hw, genamux->muxsel); + if (ret) + return ret; + + timeout = jiffies + msecs_to_jiffies(10); + + while (!clkgena_divmux_is_running(genamux)) { + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + cpu_relax(); + } + + return 0; +} + +static void clkgena_divmux_disable(struct clk_hw *hw) +{ + struct clkgena_divmux *genamux = to_clkgena_divmux(hw); + struct clk_hw *mux_hw = &genamux->mux.hw; + + mux_hw->clk = hw->clk; + + clk_mux_ops.set_parent(mux_hw, CKGAX_CLKOPSRC_SWITCH_OFF); +} + +static int clkgena_divmux_is_enabled(struct clk_hw *hw) +{ + struct clkgena_divmux *genamux = to_clkgena_divmux(hw); + struct clk_hw *mux_hw = &genamux->mux.hw; + + mux_hw->clk = hw->clk; + + return (s8)clk_mux_ops.get_parent(mux_hw) > 0; +} + +u8 clkgena_divmux_get_parent(struct clk_hw *hw) +{ + struct clkgena_divmux *genamux = to_clkgena_divmux(hw); + struct clk_hw *mux_hw = &genamux->mux.hw; + + mux_hw->clk = hw->clk; + + genamux->muxsel = clk_mux_ops.get_parent(mux_hw); + if ((s8)genamux->muxsel < 0) { + pr_debug("%s: %s: Invalid parent, setting to default.\n", + __func__, __clk_get_name(hw->clk)); + genamux->muxsel = 0; + } + + return genamux->muxsel; +} + +static int clkgena_divmux_set_parent(struct clk_hw *hw, u8 index) +{ + struct clkgena_divmux *genamux = to_clkgena_divmux(hw); + + if (index >= CKGAX_CLKOPSRC_SWITCH_OFF) + return -EINVAL; + + genamux->muxsel = index; + + /* + * If the mux is already enabled, call enable directly to set the + * new mux position and wait for it to start running again. Otherwise + * do nothing. + */ + if (clkgena_divmux_is_enabled(hw)) + clkgena_divmux_enable(hw); + + return 0; +} + +unsigned long clkgena_divmux_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clkgena_divmux *genamux = to_clkgena_divmux(hw); + struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw; + + div_hw->clk = hw->clk; + + return clk_divider_ops.recalc_rate(div_hw, parent_rate); +} + +static int clkgena_divmux_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clkgena_divmux *genamux = to_clkgena_divmux(hw); + struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw; + + div_hw->clk = hw->clk; + + return clk_divider_ops.set_rate(div_hw, rate, parent_rate); +} + +static long clkgena_divmux_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clkgena_divmux *genamux = to_clkgena_divmux(hw); + struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw; + + div_hw->clk = hw->clk; + + return clk_divider_ops.round_rate(div_hw, rate, prate); +} + +static const struct clk_ops clkgena_divmux_ops = { + .enable = clkgena_divmux_enable, + .disable = clkgena_divmux_disable, + .is_enabled = clkgena_divmux_is_enabled, + .get_parent = clkgena_divmux_get_parent, + .set_parent = clkgena_divmux_set_parent, + .round_rate = clkgena_divmux_round_rate, + .recalc_rate = clkgena_divmux_recalc_rate, + .set_rate = clkgena_divmux_set_rate, +}; + +/** + * clk_register_genamux - register a genamux clock with the clock framework + */ +struct clk *clk_register_genamux(const char *name, + const char **parent_names, u8 num_parents, + void __iomem *reg, + const struct clkgena_divmux_data *muxdata, + u32 idx) +{ + /* + * Fixed constants across all ClockgenA variants + */ + const int mux_width = 2; + const int divider_width = 5; + struct clkgena_divmux *genamux; + struct clk *clk; + struct clk_init_data init; + int i; + + genamux = kzalloc(sizeof(*genamux), GFP_KERNEL); + if (!genamux) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &clkgena_divmux_ops; + init.flags = CLK_IS_BASIC; + init.parent_names = parent_names; + init.num_parents = num_parents; + + genamux->mux.lock = &clkgena_divmux_lock; + genamux->mux.mask = BIT(mux_width) - 1; + genamux->mux.shift = muxdata->mux_start_bit + (idx * mux_width); + if (genamux->mux.shift > 31) { + /* + * We have spilled into the second mux register so + * adjust the register address and the bit shift accordingly + */ + genamux->mux.reg = reg + muxdata->mux_offset2; + genamux->mux.shift -= 32; + } else { + genamux->mux.reg = reg + muxdata->mux_offset; + } + + for (i = 0; i < NUM_INPUTS; i++) { + /* + * Divider config for each input + */ + void __iomem *divbase = reg + muxdata->div_offsets[i]; + genamux->div[i].width = divider_width; + genamux->div[i].reg = divbase + (idx * sizeof(u32)); + + /* + * Mux enabled/running feedback register for each input. + */ + genamux->feedback_reg[i] = reg + muxdata->fb_offsets[i]; + } + + genamux->feedback_bit_idx = muxdata->fb_start_bit_idx + idx; + genamux->hw.init = &init; + + clk = clk_register(NULL, &genamux->hw); + if (IS_ERR(clk)) { + kfree(genamux); + goto err; + } + + pr_debug("%s: parent %s rate %lu\n", + __clk_get_name(clk), + __clk_get_name(clk_get_parent(clk)), + clk_get_rate(clk)); +err: + return clk; +} + +static struct clkgena_divmux_data st_divmux_c65hs = { + .num_outputs = 4, + .mux_offset = 0x14, + .mux_start_bit = 0, + .div_offsets = { 0x800, 0x900, 0xb00 }, + .fb_offsets = { 0x18, 0x1c, 0x20 }, + .fb_start_bit_idx = 0, +}; + +static struct clkgena_divmux_data st_divmux_c65ls = { + .num_outputs = 14, + .mux_offset = 0x14, + .mux_offset2 = 0x24, + .mux_start_bit = 8, + .div_offsets = { 0x810, 0xa10, 0xb10 }, + .fb_offsets = { 0x18, 0x1c, 0x20 }, + .fb_start_bit_idx = 4, +}; + +static struct clkgena_divmux_data st_divmux_c32odf0 = { + .num_outputs = 8, + .mux_offset = 0x1c, + .mux_start_bit = 0, + .div_offsets = { 0x800, 0x900, 0xa60 }, + .fb_offsets = { 0x2c, 0x24, 0x28 }, + .fb_start_bit_idx = 0, +}; + +static struct clkgena_divmux_data st_divmux_c32odf1 = { + .num_outputs = 8, + .mux_offset = 0x1c, + .mux_start_bit = 16, + .div_offsets = { 0x820, 0x980, 0xa80 }, + .fb_offsets = { 0x2c, 0x24, 0x28 }, + .fb_start_bit_idx = 8, +}; + +static struct clkgena_divmux_data st_divmux_c32odf2 = { + .num_outputs = 8, + .mux_offset = 0x20, + .mux_start_bit = 0, + .div_offsets = { 0x840, 0xa20, 0xb10 }, + .fb_offsets = { 0x2c, 0x24, 0x28 }, + .fb_start_bit_idx = 16, +}; + +static struct clkgena_divmux_data st_divmux_c32odf3 = { + .num_outputs = 8, + .mux_offset = 0x20, + .mux_start_bit = 16, + .div_offsets = { 0x860, 0xa40, 0xb30 }, + .fb_offsets = { 0x2c, 0x24, 0x28 }, + .fb_start_bit_idx = 24, +}; + +static struct of_device_id clkgena_divmux_of_match[] = { + { + .compatible = "st,clkgena-divmux-c65-hs", + .data = &st_divmux_c65hs, + }, + { + .compatible = "st,clkgena-divmux-c65-ls", + .data = &st_divmux_c65ls, + }, + { + .compatible = "st,clkgena-divmux-c32-odf0", + .data = &st_divmux_c32odf0, + }, + { + .compatible = "st,clkgena-divmux-c32-odf1", + .data = &st_divmux_c32odf1, + }, + { + .compatible = "st,clkgena-divmux-c32-odf2", + .data = &st_divmux_c32odf2, + }, + { + .compatible = "st,clkgena-divmux-c32-odf3", + .data = &st_divmux_c32odf3, + }, + {} +}; + +static void __iomem * __init clkgen_get_register_base( + struct device_node *np) +{ + struct device_node *pnode; + void __iomem *reg = NULL; + + pnode = of_get_parent(np); + if (!pnode) + return NULL; + + reg = of_iomap(pnode, 0); + + of_node_put(pnode); + return reg; +} + +void __init st_of_clkgena_divmux_setup(struct device_node *np) +{ + const struct of_device_id *match; + const struct clkgena_divmux_data *data; + struct clk_onecell_data *clk_data; + void __iomem *reg; + const char **parents; + int num_parents = 0, i; + + match = of_match_node(clkgena_divmux_of_match, np); + if (WARN_ON(!match)) + return; + + data = (struct clkgena_divmux_data *)match->data; + + reg = clkgen_get_register_base(np); + if (!reg) + return; + + parents = clkgen_mux_get_parents(np, &num_parents); + if (IS_ERR(parents)) + return; + + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) + goto err; + + clk_data->clk_num = data->num_outputs; + clk_data->clks = kzalloc(clk_data->clk_num * sizeof(struct clk *), + GFP_KERNEL); + + if (!clk_data->clks) + goto err; + + for (i = 0; i < clk_data->clk_num; i++) { + struct clk *clk; + const char *clk_name; + + if (of_property_read_string_index(np, "clock-output-names", + i, &clk_name)) + break; + + /* + * If we read an empty clock name then the output is unused + */ + if (*clk_name == '\0') + continue; + + clk = clk_register_genamux(clk_name, parents, num_parents, + reg, data, i); + + if (IS_ERR(clk)) + goto err; + + clk_data->clks[i] = clk; + } + + kfree(parents); + + of_clk_add_provider(np, of_clk_src_onecell_get, clk_data); + return; +err: + if (clk_data) + kfree(clk_data->clks); + + kfree(clk_data); + kfree(parents); +} +CLK_OF_DECLARE(clkgenadivmux, "st,clkgena-divmux", st_of_clkgena_divmux_setup); + +struct clkgena_prediv_data { + u32 offset; + u8 shift; + struct clk_div_table *table; +}; + +static struct clk_div_table prediv_table16[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 16 }, + { .div = 0 }, +}; + +static struct clkgena_prediv_data prediv_c65_data = { + .offset = 0x4c, + .shift = 31, + .table = prediv_table16, +}; + +static struct clkgena_prediv_data prediv_c32_data = { + .offset = 0x50, + .shift = 1, + .table = prediv_table16, +}; + +static struct of_device_id clkgena_prediv_of_match[] = { + { .compatible = "st,clkgena-prediv-c65", .data = &prediv_c65_data }, + { .compatible = "st,clkgena-prediv-c32", .data = &prediv_c32_data }, + {} +}; + +void __init st_of_clkgena_prediv_setup(struct device_node *np) +{ + const struct of_device_id *match; + void __iomem *reg; + const char *parent_name, *clk_name; + struct clk *clk; + struct clkgena_prediv_data *data; + + match = of_match_node(clkgena_prediv_of_match, np); + if (!match) { + pr_err("%s: No matching data\n", __func__); + return; + } + + data = (struct clkgena_prediv_data *)match->data; + + reg = clkgen_get_register_base(np); + if (!reg) + return; + + parent_name = of_clk_get_parent_name(np, 0); + if (!parent_name) + return; + + if (of_property_read_string_index(np, "clock-output-names", + 0, &clk_name)) + return; + + clk = clk_register_divider_table(NULL, clk_name, parent_name, 0, + reg + data->offset, data->shift, 1, + 0, data->table, NULL); + if (IS_ERR(clk)) + return; + + of_clk_add_provider(np, of_clk_src_simple_get, clk); + pr_debug("%s: parent %s rate %u\n", + __clk_get_name(clk), + __clk_get_name(clk_get_parent(clk)), + (unsigned int)clk_get_rate(clk)); + + return; +} +CLK_OF_DECLARE(clkgenaprediv, "st,clkgena-prediv", st_of_clkgena_prediv_setup); -- cgit v1.2.3 From b9b8e614b5805a99a5484c3d44fbfebaa8de4c65 Mon Sep 17 00:00:00 2001 From: Gabriel FERNANDEZ Date: Thu, 27 Feb 2014 16:24:15 +0100 Subject: clk: st: Support for PLLs inside ClockGenA(s) The patch supports the c65/c32 type PLLs used by ClockGenA(s) PLL clock : It includes support for all c65/c32 type PLLs inside ClockGenA(s) : implemented as Fixed Parent / Fixed Rate clock, with clock rate calculated reading H/w settings done at BOOT. c65 PLLs have 2 outputs : HS and LS c32 PLLs have 1-4 outputs : ODFx Signed-off-by: Pankaj Dev Signed-off-by: Gabriel Fernandez Signed-off-by: Mike Turquette --- drivers/clk/st/Makefile | 2 +- drivers/clk/st/clkgen-pll.c | 559 ++++++++++++++++++++++++++++++++++++++++++++ drivers/clk/st/clkgen.h | 48 ++++ 3 files changed, 608 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/st/clkgen-pll.c create mode 100644 drivers/clk/st/clkgen.h (limited to 'drivers') diff --git a/drivers/clk/st/Makefile b/drivers/clk/st/Makefile index e42b370edef9..b7b10f93dbe9 100644 --- a/drivers/clk/st/Makefile +++ b/drivers/clk/st/Makefile @@ -1 +1 @@ -obj-y += clkgen-mux.o +obj-y += clkgen-mux.o clkgen-pll.o diff --git a/drivers/clk/st/clkgen-pll.c b/drivers/clk/st/clkgen-pll.c new file mode 100644 index 000000000000..c6b38b05df1c --- /dev/null +++ b/drivers/clk/st/clkgen-pll.c @@ -0,0 +1,559 @@ +/* + * Copyright (C) 2014 STMicroelectronics (R&D) Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +/* + * Authors: + * Stephen Gallimore , + * Pankaj Dev . + */ + +#include +#include +#include + +#include "clkgen.h" + +static DEFINE_SPINLOCK(clkgena_c32_odf_lock); + +/* + * Common PLL configuration register bits for PLL800 and PLL1600 C65 + */ +#define C65_MDIV_PLL800_MASK (0xff) +#define C65_MDIV_PLL1600_MASK (0x7) +#define C65_NDIV_MASK (0xff) +#define C65_PDIV_MASK (0x7) + +/* + * PLL configuration register bits for PLL3200 C32 + */ +#define C32_NDIV_MASK (0xff) +#define C32_IDF_MASK (0x7) +#define C32_ODF_MASK (0x3f) +#define C32_LDF_MASK (0x7f) + +#define C32_MAX_ODFS (4) + +struct clkgen_pll_data { + struct clkgen_field pdn_status; + struct clkgen_field locked_status; + struct clkgen_field mdiv; + struct clkgen_field ndiv; + struct clkgen_field pdiv; + struct clkgen_field idf; + struct clkgen_field ldf; + unsigned int num_odfs; + struct clkgen_field odf[C32_MAX_ODFS]; + struct clkgen_field odf_gate[C32_MAX_ODFS]; + const struct clk_ops *ops; +}; + +static const struct clk_ops st_pll1600c65_ops; +static const struct clk_ops st_pll800c65_ops; +static const struct clk_ops stm_pll3200c32_ops; +static const struct clk_ops st_pll1200c32_ops; + +static struct clkgen_pll_data st_pll1600c65_ax = { + .pdn_status = CLKGEN_FIELD(0x0, 0x1, 19), + .locked_status = CLKGEN_FIELD(0x0, 0x1, 31), + .mdiv = CLKGEN_FIELD(0x0, C65_MDIV_PLL1600_MASK, 0), + .ndiv = CLKGEN_FIELD(0x0, C65_NDIV_MASK, 8), + .ops = &st_pll1600c65_ops +}; + +static struct clkgen_pll_data st_pll800c65_ax = { + .pdn_status = CLKGEN_FIELD(0x0, 0x1, 19), + .locked_status = CLKGEN_FIELD(0x0, 0x1, 31), + .mdiv = CLKGEN_FIELD(0x0, C65_MDIV_PLL800_MASK, 0), + .ndiv = CLKGEN_FIELD(0x0, C65_NDIV_MASK, 8), + .pdiv = CLKGEN_FIELD(0x0, C65_PDIV_MASK, 16), + .ops = &st_pll800c65_ops +}; + +static struct clkgen_pll_data st_pll3200c32_a1x_0 = { + .pdn_status = CLKGEN_FIELD(0x0, 0x1, 31), + .locked_status = CLKGEN_FIELD(0x4, 0x1, 31), + .ndiv = CLKGEN_FIELD(0x0, C32_NDIV_MASK, 0x0), + .idf = CLKGEN_FIELD(0x4, C32_IDF_MASK, 0x0), + .num_odfs = 4, + .odf = { CLKGEN_FIELD(0x54, C32_ODF_MASK, 4), + CLKGEN_FIELD(0x54, C32_ODF_MASK, 10), + CLKGEN_FIELD(0x54, C32_ODF_MASK, 16), + CLKGEN_FIELD(0x54, C32_ODF_MASK, 22) }, + .odf_gate = { CLKGEN_FIELD(0x54, 0x1, 0), + CLKGEN_FIELD(0x54, 0x1, 1), + CLKGEN_FIELD(0x54, 0x1, 2), + CLKGEN_FIELD(0x54, 0x1, 3) }, + .ops = &stm_pll3200c32_ops, +}; + +static struct clkgen_pll_data st_pll3200c32_a1x_1 = { + .pdn_status = CLKGEN_FIELD(0xC, 0x1, 31), + .locked_status = CLKGEN_FIELD(0x10, 0x1, 31), + .ndiv = CLKGEN_FIELD(0xC, C32_NDIV_MASK, 0x0), + .idf = CLKGEN_FIELD(0x10, C32_IDF_MASK, 0x0), + .num_odfs = 4, + .odf = { CLKGEN_FIELD(0x58, C32_ODF_MASK, 4), + CLKGEN_FIELD(0x58, C32_ODF_MASK, 10), + CLKGEN_FIELD(0x58, C32_ODF_MASK, 16), + CLKGEN_FIELD(0x58, C32_ODF_MASK, 22) }, + .odf_gate = { CLKGEN_FIELD(0x58, 0x1, 0), + CLKGEN_FIELD(0x58, 0x1, 1), + CLKGEN_FIELD(0x58, 0x1, 2), + CLKGEN_FIELD(0x58, 0x1, 3) }, + .ops = &stm_pll3200c32_ops, +}; + +/** + * DOC: Clock Generated by PLL, rate set and enabled by bootloader + * + * Traits of this clock: + * prepare - clk_(un)prepare only ensures parent is (un)prepared + * enable - clk_enable/disable only ensures parent is enabled + * rate - rate is fixed. No clk_set_rate support + * parent - fixed parent. No clk_set_parent support + */ + +/** + * PLL clock that is integrated in the ClockGenA instances on the STiH415 + * and STiH416. + * + * @hw: handle between common and hardware-specific interfaces. + * @type: PLL instance type. + * @regs_base: base of the PLL configuration register(s). + * + */ +struct clkgen_pll { + struct clk_hw hw; + struct clkgen_pll_data *data; + void __iomem *regs_base; +}; + +#define to_clkgen_pll(_hw) container_of(_hw, struct clkgen_pll, hw) + +static int clkgen_pll_is_locked(struct clk_hw *hw) +{ + struct clkgen_pll *pll = to_clkgen_pll(hw); + u32 locked = CLKGEN_READ(pll, locked_status); + + return !!locked; +} + +static int clkgen_pll_is_enabled(struct clk_hw *hw) +{ + struct clkgen_pll *pll = to_clkgen_pll(hw); + u32 poweroff = CLKGEN_READ(pll, pdn_status); + return !poweroff; +} + +unsigned long recalc_stm_pll800c65(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clkgen_pll *pll = to_clkgen_pll(hw); + unsigned long mdiv, ndiv, pdiv; + unsigned long rate; + uint64_t res; + + if (!clkgen_pll_is_enabled(hw) || !clkgen_pll_is_locked(hw)) + return 0; + + pdiv = CLKGEN_READ(pll, pdiv); + mdiv = CLKGEN_READ(pll, mdiv); + ndiv = CLKGEN_READ(pll, ndiv); + + if (!mdiv) + mdiv++; /* mdiv=0 or 1 => MDIV=1 */ + + res = (uint64_t)2 * (uint64_t)parent_rate * (uint64_t)ndiv; + rate = (unsigned long)div64_u64(res, mdiv * (1 << pdiv)); + + pr_debug("%s:%s rate %lu\n", __clk_get_name(hw->clk), __func__, rate); + + return rate; + +} + +unsigned long recalc_stm_pll1600c65(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clkgen_pll *pll = to_clkgen_pll(hw); + unsigned long mdiv, ndiv; + unsigned long rate; + + if (!clkgen_pll_is_enabled(hw) || !clkgen_pll_is_locked(hw)) + return 0; + + mdiv = CLKGEN_READ(pll, mdiv); + ndiv = CLKGEN_READ(pll, ndiv); + + if (!mdiv) + mdiv = 1; + + /* Note: input is divided by 1000 to avoid overflow */ + rate = ((2 * (parent_rate / 1000) * ndiv) / mdiv) * 1000; + + pr_debug("%s:%s rate %lu\n", __clk_get_name(hw->clk), __func__, rate); + + return rate; +} + +unsigned long recalc_stm_pll3200c32(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clkgen_pll *pll = to_clkgen_pll(hw); + unsigned long ndiv, idf; + unsigned long rate = 0; + + if (!clkgen_pll_is_enabled(hw) || !clkgen_pll_is_locked(hw)) + return 0; + + ndiv = CLKGEN_READ(pll, ndiv); + idf = CLKGEN_READ(pll, idf); + + if (idf) + /* Note: input is divided to avoid overflow */ + rate = ((2 * (parent_rate/1000) * ndiv) / idf) * 1000; + + pr_debug("%s:%s rate %lu\n", __clk_get_name(hw->clk), __func__, rate); + + return rate; +} + +unsigned long recalc_stm_pll1200c32(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clkgen_pll *pll = to_clkgen_pll(hw); + unsigned long odf, ldf, idf; + unsigned long rate; + + if (!clkgen_pll_is_enabled(hw) || !clkgen_pll_is_locked(hw)) + return 0; + + odf = CLKGEN_READ(pll, odf[0]); + ldf = CLKGEN_READ(pll, ldf); + idf = CLKGEN_READ(pll, idf); + + if (!idf) /* idf==0 means 1 */ + idf = 1; + if (!odf) /* odf==0 means 1 */ + odf = 1; + + /* Note: input is divided by 1000 to avoid overflow */ + rate = (((parent_rate / 1000) * ldf) / (odf * idf)) * 1000; + + pr_debug("%s:%s rate %lu\n", __clk_get_name(hw->clk), __func__, rate); + + return rate; +} + +static const struct clk_ops st_pll1600c65_ops = { + .is_enabled = clkgen_pll_is_enabled, + .recalc_rate = recalc_stm_pll1600c65, +}; + +static const struct clk_ops st_pll800c65_ops = { + .is_enabled = clkgen_pll_is_enabled, + .recalc_rate = recalc_stm_pll800c65, +}; + +static const struct clk_ops stm_pll3200c32_ops = { + .is_enabled = clkgen_pll_is_enabled, + .recalc_rate = recalc_stm_pll3200c32, +}; + +static const struct clk_ops st_pll1200c32_ops = { + .is_enabled = clkgen_pll_is_enabled, + .recalc_rate = recalc_stm_pll1200c32, +}; + +static struct clk * __init clkgen_pll_register(const char *parent_name, + struct clkgen_pll_data *pll_data, + void __iomem *reg, + const char *clk_name) +{ + struct clkgen_pll *pll; + struct clk *clk; + struct clk_init_data init; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + init.name = clk_name; + init.ops = pll_data->ops; + + init.flags = CLK_IS_BASIC; + init.parent_names = &parent_name; + init.num_parents = 1; + + pll->data = pll_data; + pll->regs_base = reg; + pll->hw.init = &init; + + clk = clk_register(NULL, &pll->hw); + if (IS_ERR(clk)) { + kfree(pll); + return clk; + } + + pr_debug("%s: parent %s rate %lu\n", + __clk_get_name(clk), + __clk_get_name(clk_get_parent(clk)), + clk_get_rate(clk)); + + return clk; +} + +static struct clk * __init clkgen_c65_lsdiv_register(const char *parent_name, + const char *clk_name) +{ + struct clk *clk; + + clk = clk_register_fixed_factor(NULL, clk_name, parent_name, 0, 1, 2); + if (IS_ERR(clk)) + return clk; + + pr_debug("%s: parent %s rate %lu\n", + __clk_get_name(clk), + __clk_get_name(clk_get_parent(clk)), + clk_get_rate(clk)); + return clk; +} + +static void __iomem * __init clkgen_get_register_base( + struct device_node *np) +{ + struct device_node *pnode; + void __iomem *reg = NULL; + + pnode = of_get_parent(np); + if (!pnode) + return NULL; + + reg = of_iomap(pnode, 0); + + of_node_put(pnode); + return reg; +} + +#define CLKGENAx_PLL0_OFFSET 0x0 +#define CLKGENAx_PLL1_OFFSET 0x4 + +static void __init clkgena_c65_pll_setup(struct device_node *np) +{ + const int num_pll_outputs = 3; + struct clk_onecell_data *clk_data; + const char *parent_name; + void __iomem *reg; + const char *clk_name; + + parent_name = of_clk_get_parent_name(np, 0); + if (!parent_name) + return; + + reg = clkgen_get_register_base(np); + if (!reg) + return; + + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) + return; + + clk_data->clk_num = num_pll_outputs; + clk_data->clks = kzalloc(clk_data->clk_num * sizeof(struct clk *), + GFP_KERNEL); + + if (!clk_data->clks) + goto err; + + if (of_property_read_string_index(np, "clock-output-names", + 0, &clk_name)) + goto err; + + /* + * PLL0 HS (high speed) output + */ + clk_data->clks[0] = clkgen_pll_register(parent_name, + &st_pll1600c65_ax, + reg + CLKGENAx_PLL0_OFFSET, + clk_name); + + if (IS_ERR(clk_data->clks[0])) + goto err; + + if (of_property_read_string_index(np, "clock-output-names", + 1, &clk_name)) + goto err; + + /* + * PLL0 LS (low speed) output, which is a fixed divide by 2 of the + * high speed output. + */ + clk_data->clks[1] = clkgen_c65_lsdiv_register(__clk_get_name + (clk_data->clks[0]), + clk_name); + + if (IS_ERR(clk_data->clks[1])) + goto err; + + if (of_property_read_string_index(np, "clock-output-names", + 2, &clk_name)) + goto err; + + /* + * PLL1 output + */ + clk_data->clks[2] = clkgen_pll_register(parent_name, + &st_pll800c65_ax, + reg + CLKGENAx_PLL1_OFFSET, + clk_name); + + if (IS_ERR(clk_data->clks[2])) + goto err; + + of_clk_add_provider(np, of_clk_src_onecell_get, clk_data); + return; + +err: + kfree(clk_data->clks); + kfree(clk_data); +} +CLK_OF_DECLARE(clkgena_c65_plls, + "st,clkgena-plls-c65", clkgena_c65_pll_setup); + +static struct clk * __init clkgen_odf_register(const char *parent_name, + void * __iomem reg, + struct clkgen_pll_data *pll_data, + int odf, + spinlock_t *odf_lock, + const char *odf_name) +{ + struct clk *clk; + unsigned long flags; + struct clk_gate *gate; + struct clk_divider *div; + + flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE; + + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) + return ERR_PTR(-ENOMEM); + + gate->flags = CLK_GATE_SET_TO_DISABLE; + gate->reg = reg + pll_data->odf_gate[odf].offset; + gate->bit_idx = pll_data->odf_gate[odf].shift; + gate->lock = odf_lock; + + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + return ERR_PTR(-ENOMEM); + + div->flags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO; + div->reg = reg + pll_data->odf[odf].offset; + div->shift = pll_data->odf[odf].shift; + div->width = fls(pll_data->odf[odf].mask); + div->lock = odf_lock; + + clk = clk_register_composite(NULL, odf_name, &parent_name, 1, + NULL, NULL, + &div->hw, &clk_divider_ops, + &gate->hw, &clk_gate_ops, + flags); + if (IS_ERR(clk)) + return clk; + + pr_debug("%s: parent %s rate %lu\n", + __clk_get_name(clk), + __clk_get_name(clk_get_parent(clk)), + clk_get_rate(clk)); + return clk; +} + +static struct of_device_id c32_pll_of_match[] = { + { + .compatible = "st,plls-c32-a1x-0", + .data = &st_pll3200c32_a1x_0, + }, + { + .compatible = "st,plls-c32-a1x-1", + .data = &st_pll3200c32_a1x_1, + }, + {} +}; + +static void __init clkgen_c32_pll_setup(struct device_node *np) +{ + const struct of_device_id *match; + struct clk *clk; + const char *parent_name, *pll_name; + void __iomem *pll_base; + int num_odfs, odf; + struct clk_onecell_data *clk_data; + struct clkgen_pll_data *data; + + match = of_match_node(c32_pll_of_match, np); + if (!match) { + pr_err("%s: No matching data\n", __func__); + return; + } + + data = (struct clkgen_pll_data *) match->data; + + parent_name = of_clk_get_parent_name(np, 0); + if (!parent_name) + return; + + pll_base = clkgen_get_register_base(np); + if (!pll_base) + return; + + clk = clkgen_pll_register(parent_name, data, pll_base, np->name); + if (IS_ERR(clk)) + return; + + pll_name = __clk_get_name(clk); + + num_odfs = data->num_odfs; + + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) + return; + + clk_data->clk_num = num_odfs; + clk_data->clks = kzalloc(clk_data->clk_num * sizeof(struct clk *), + GFP_KERNEL); + + if (!clk_data->clks) + goto err; + + for (odf = 0; odf < num_odfs; odf++) { + struct clk *clk; + const char *clk_name; + + if (of_property_read_string_index(np, "clock-output-names", + odf, &clk_name)) + return; + + clk = clkgen_odf_register(pll_name, pll_base, data, + odf, &clkgena_c32_odf_lock, clk_name); + if (IS_ERR(clk)) + goto err; + + clk_data->clks[odf] = clk; + } + + of_clk_add_provider(np, of_clk_src_onecell_get, clk_data); + return; + +err: + kfree(pll_name); + kfree(clk_data->clks); + kfree(clk_data); +} +CLK_OF_DECLARE(clkgen_c32_pll, "st,clkgen-plls-c32", clkgen_c32_pll_setup); diff --git a/drivers/clk/st/clkgen.h b/drivers/clk/st/clkgen.h new file mode 100644 index 000000000000..35c863295268 --- /dev/null +++ b/drivers/clk/st/clkgen.h @@ -0,0 +1,48 @@ +/************************************************************************ +File : Clock H/w specific Information + +Author: Pankaj Dev + +Copyright (C) 2014 STMicroelectronics +************************************************************************/ + +#ifndef __CLKGEN_INFO_H +#define __CLKGEN_INFO_H + +struct clkgen_field { + unsigned int offset; + unsigned int mask; + unsigned int shift; +}; + +static inline unsigned long clkgen_read(void __iomem *base, + struct clkgen_field *field) +{ + return (readl(base + field->offset) >> field->shift) & field->mask; +} + + +static inline void clkgen_write(void __iomem *base, struct clkgen_field *field, + unsigned long val) +{ + writel((readl(base + field->offset) & + ~(field->mask << field->shift)) | (val << field->shift), + base + field->offset); + + return; +} + +#define CLKGEN_FIELD(_offset, _mask, _shift) { \ + .offset = _offset, \ + .mask = _mask, \ + .shift = _shift, \ + } + +#define CLKGEN_READ(pll, field) clkgen_read(pll->regs_base, \ + &pll->data->field) + +#define CLKGEN_WRITE(pll, field, val) clkgen_write(pll->regs_base, \ + &pll->data->field, val) + +#endif /*__CLKGEN_INFO_H*/ + -- cgit v1.2.3 From 44993d384004fa9ca2dfcca86cddc436a28d6958 Mon Sep 17 00:00:00 2001 From: Gabriel FERNANDEZ Date: Thu, 27 Feb 2014 16:24:16 +0100 Subject: clk: st: Support for VCC-mux and MUX clocks The patch supports the VCC-mux and MUX clocks used by ClockGenC/F VCC-mux clock : Divider-Multiplexer-Gate inside ClockGenC/F It includes support for each channel : 4-parent Multiplexer, Post Divide by 1, 2, 4 or 8, Gate to switch OFF the output channel. The clock is implemented using generic clocks implemented in the kernel clk_divider, clk_mux, clk_gate and clk_composite (to combine all) MUX clock : 2-parent clock used inside ClockGenC/F. The clock is implemented using generic clocks implemented in the kernel clk_mux. Signed-off-by: Pankaj Dev Signed-off-by: Gabriel Fernandez Signed-off-by: Mike Turquette --- drivers/clk/st/clkgen-mux.c | 272 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 272 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/st/clkgen-mux.c b/drivers/clk/st/clkgen-mux.c index 65d702c2df8d..7ccff620a2fe 100644 --- a/drivers/clk/st/clkgen-mux.c +++ b/drivers/clk/st/clkgen-mux.c @@ -18,6 +18,7 @@ #include static DEFINE_SPINLOCK(clkgena_divmux_lock); +static DEFINE_SPINLOCK(clkgenf_lock); static const char ** __init clkgen_mux_get_parents(struct device_node *np, int *num_parents) @@ -527,3 +528,274 @@ void __init st_of_clkgena_prediv_setup(struct device_node *np) return; } CLK_OF_DECLARE(clkgenaprediv, "st,clkgena-prediv", st_of_clkgena_prediv_setup); + +struct clkgen_mux_data { + u32 offset; + u8 shift; + u8 width; + spinlock_t *lock; + unsigned long clk_flags; + u8 mux_flags; +}; + +static struct clkgen_mux_data clkgen_mux_c_vcc_hd_416 = { + .offset = 0, + .shift = 0, + .width = 1, +}; + +static struct clkgen_mux_data clkgen_mux_f_vcc_fvdp_416 = { + .offset = 0, + .shift = 0, + .width = 1, +}; + +static struct clkgen_mux_data clkgen_mux_f_vcc_hva_416 = { + .offset = 0, + .shift = 0, + .width = 1, +}; + +static struct clkgen_mux_data clkgen_mux_f_vcc_hd_416 = { + .offset = 0, + .shift = 16, + .width = 1, + .lock = &clkgenf_lock, +}; + +static struct clkgen_mux_data clkgen_mux_c_vcc_sd_416 = { + .offset = 0, + .shift = 17, + .width = 1, + .lock = &clkgenf_lock, +}; + +static struct of_device_id mux_of_match[] = { + { + .compatible = "st,stih416-clkgenc-vcc-hd", + .data = &clkgen_mux_c_vcc_hd_416, + }, + { + .compatible = "st,stih416-clkgenf-vcc-fvdp", + .data = &clkgen_mux_f_vcc_fvdp_416, + }, + { + .compatible = "st,stih416-clkgenf-vcc-hva", + .data = &clkgen_mux_f_vcc_hva_416, + }, + { + .compatible = "st,stih416-clkgenf-vcc-hd", + .data = &clkgen_mux_f_vcc_hd_416, + }, + { + .compatible = "st,stih416-clkgenf-vcc-sd", + .data = &clkgen_mux_c_vcc_sd_416, + }, + {} +}; + +void __init st_of_clkgen_mux_setup(struct device_node *np) +{ + const struct of_device_id *match; + struct clk *clk; + void __iomem *reg; + const char **parents; + int num_parents; + struct clkgen_mux_data *data; + + match = of_match_node(mux_of_match, np); + if (!match) { + pr_err("%s: No matching data\n", __func__); + return; + } + + data = (struct clkgen_mux_data *)match->data; + + reg = of_iomap(np, 0); + if (!reg) { + pr_err("%s: Failed to get base address\n", __func__); + return; + } + + parents = clkgen_mux_get_parents(np, &num_parents); + if (IS_ERR(parents)) { + pr_err("%s: Failed to get parents (%ld)\n", + __func__, PTR_ERR(parents)); + return; + } + + clk = clk_register_mux(NULL, np->name, parents, num_parents, + data->clk_flags | CLK_SET_RATE_PARENT, + reg + data->offset, + data->shift, data->width, data->mux_flags, + data->lock); + if (IS_ERR(clk)) + goto err; + + pr_debug("%s: parent %s rate %u\n", + __clk_get_name(clk), + __clk_get_name(clk_get_parent(clk)), + (unsigned int)clk_get_rate(clk)); + + of_clk_add_provider(np, of_clk_src_simple_get, clk); + +err: + kfree(parents); + + return; +} +CLK_OF_DECLARE(clkgen_mux, "st,clkgen-mux", st_of_clkgen_mux_setup); + +#define VCC_MAX_CHANNELS 16 + +#define VCC_GATE_OFFSET 0x0 +#define VCC_MUX_OFFSET 0x4 +#define VCC_DIV_OFFSET 0x8 + +struct clkgen_vcc_data { + spinlock_t *lock; + unsigned long clk_flags; +}; + +static struct clkgen_vcc_data st_clkgenc_vcc_416 = { + .clk_flags = CLK_SET_RATE_PARENT, +}; + +static struct clkgen_vcc_data st_clkgenf_vcc_416 = { + .lock = &clkgenf_lock, +}; + +static struct of_device_id vcc_of_match[] = { + { .compatible = "st,stih416-clkgenc", .data = &st_clkgenc_vcc_416 }, + { .compatible = "st,stih416-clkgenf", .data = &st_clkgenf_vcc_416 }, + {} +}; + +void __init st_of_clkgen_vcc_setup(struct device_node *np) +{ + const struct of_device_id *match; + void __iomem *reg; + const char **parents; + int num_parents, i; + struct clk_onecell_data *clk_data; + struct clkgen_vcc_data *data; + + match = of_match_node(vcc_of_match, np); + if (WARN_ON(!match)) + return; + data = (struct clkgen_vcc_data *)match->data; + + reg = of_iomap(np, 0); + if (!reg) + return; + + parents = clkgen_mux_get_parents(np, &num_parents); + if (IS_ERR(parents)) + return; + + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) + goto err; + + clk_data->clk_num = VCC_MAX_CHANNELS; + clk_data->clks = kzalloc(clk_data->clk_num * sizeof(struct clk *), + GFP_KERNEL); + + if (!clk_data->clks) + goto err; + + for (i = 0; i < clk_data->clk_num; i++) { + struct clk *clk; + const char *clk_name; + struct clk_gate *gate; + struct clk_divider *div; + struct clk_mux *mux; + + if (of_property_read_string_index(np, "clock-output-names", + i, &clk_name)) + break; + + /* + * If we read an empty clock name then the output is unused + */ + if (*clk_name == '\0') + continue; + + gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL); + if (!gate) + break; + + div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL); + if (!div) { + kfree(gate); + break; + } + + mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL); + if (!mux) { + kfree(gate); + kfree(div); + break; + } + + gate->reg = reg + VCC_GATE_OFFSET; + gate->bit_idx = i; + gate->flags = CLK_GATE_SET_TO_DISABLE; + gate->lock = data->lock; + + div->reg = reg + VCC_DIV_OFFSET; + div->shift = 2 * i; + div->width = 2; + div->flags = CLK_DIVIDER_POWER_OF_TWO; + + mux->reg = reg + VCC_MUX_OFFSET; + mux->shift = 2 * i; + mux->mask = 0x3; + + clk = clk_register_composite(NULL, clk_name, parents, + num_parents, + &mux->hw, &clk_mux_ops, + &div->hw, &clk_divider_ops, + &gate->hw, &clk_gate_ops, + data->clk_flags); + if (IS_ERR(clk)) { + kfree(gate); + kfree(div); + kfree(mux); + goto err; + } + + pr_debug("%s: parent %s rate %u\n", + __clk_get_name(clk), + __clk_get_name(clk_get_parent(clk)), + (unsigned int)clk_get_rate(clk)); + + clk_data->clks[i] = clk; + } + + kfree(parents); + + of_clk_add_provider(np, of_clk_src_onecell_get, clk_data); + return; + +err: + for (i = 0; i < clk_data->clk_num; i++) { + struct clk_composite *composite; + + if (!clk_data->clks[i]) + continue; + + composite = container_of(__clk_get_hw(clk_data->clks[i]), + struct clk_composite, hw); + kfree(container_of(composite->gate_hw, struct clk_gate, hw)); + kfree(container_of(composite->rate_hw, struct clk_divider, hw)); + kfree(container_of(composite->mux_hw, struct clk_mux, hw)); + } + + if (clk_data) + kfree(clk_data->clks); + + kfree(clk_data); + kfree(parents); +} +CLK_OF_DECLARE(clkgen_vcc, "st,clkgen-vcc", st_of_clkgen_vcc_setup); -- cgit v1.2.3 From 5f7aa9071e935c8c0e869306c7ef073df6c409f6 Mon Sep 17 00:00:00 2001 From: Gabriel FERNANDEZ Date: Thu, 27 Feb 2014 16:24:17 +0100 Subject: clk: st: Support for QUADFS inside ClockGenB/C/D/E/F The patch supports the 216/432/660 type Quad Frequency Synthesizers used by ClockGenB/C/D/E/F QUADFS clock : It includes support for all 216/432/660 type Quad Frequency Synthesizers : implemented as Fixed Parent / Rate / Gate clock, with clock rate calculated reading H/w settings done at BOOT. QuadFS have 4 outputs : chan0 chan1 chan2 chan3 Signed-off-by: Pankaj Dev Signed-off-by: Gabriel Fernandez Signed-off-by: Mike Turquette --- drivers/clk/st/Makefile | 2 +- drivers/clk/st/clkgen-fsyn.c | 1039 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1040 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/st/clkgen-fsyn.c (limited to 'drivers') diff --git a/drivers/clk/st/Makefile b/drivers/clk/st/Makefile index b7b10f93dbe9..c7455ffdbdf7 100644 --- a/drivers/clk/st/Makefile +++ b/drivers/clk/st/Makefile @@ -1 +1 @@ -obj-y += clkgen-mux.o clkgen-pll.o +obj-y += clkgen-mux.o clkgen-pll.o clkgen-fsyn.o diff --git a/drivers/clk/st/clkgen-fsyn.c b/drivers/clk/st/clkgen-fsyn.c new file mode 100644 index 000000000000..4f53ee0778d9 --- /dev/null +++ b/drivers/clk/st/clkgen-fsyn.c @@ -0,0 +1,1039 @@ +/* + * Copyright (C) 2014 STMicroelectronics R&D Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +/* + * Authors: + * Stephen Gallimore , + * Pankaj Dev . + */ + +#include +#include +#include + +#include "clkgen.h" + +/* + * Maximum input clock to the PLL before we divide it down by 2 + * although in reality in actual systems this has never been seen to + * be used. + */ +#define QUADFS_NDIV_THRESHOLD 30000000 + +#define PLL_BW_GOODREF (0L) +#define PLL_BW_VBADREF (1L) +#define PLL_BW_BADREF (2L) +#define PLL_BW_VGOODREF (3L) + +#define QUADFS_MAX_CHAN 4 + +struct stm_fs { + unsigned long ndiv; + unsigned long mdiv; + unsigned long pe; + unsigned long sdiv; + unsigned long nsdiv; +}; + +static struct stm_fs fs216c65_rtbl[] = { + { .mdiv = 0x1f, .pe = 0x0, .sdiv = 0x7, .nsdiv = 0 }, /* 312.5 Khz */ + { .mdiv = 0x17, .pe = 0x25ed, .sdiv = 0x1, .nsdiv = 0 }, /* 27 MHz */ + { .mdiv = 0x1a, .pe = 0x7b36, .sdiv = 0x2, .nsdiv = 1 }, /* 36.87 MHz */ + { .mdiv = 0x13, .pe = 0x0, .sdiv = 0x2, .nsdiv = 1 }, /* 48 MHz */ + { .mdiv = 0x11, .pe = 0x1c72, .sdiv = 0x1, .nsdiv = 1 }, /* 108 MHz */ +}; + +static struct stm_fs fs432c65_rtbl[] = { + { .mdiv = 0x1f, .pe = 0x0, .sdiv = 0x7, .nsdiv = 0 }, /* 625 Khz */ + { .mdiv = 0x11, .pe = 0x1c72, .sdiv = 0x2, .nsdiv = 1 }, /* 108 MHz */ + { .mdiv = 0x19, .pe = 0x121a, .sdiv = 0x0, .nsdiv = 1 }, /* 297 MHz */ +}; + +static struct stm_fs fs660c32_rtbl[] = { + { .mdiv = 0x01, .pe = 0x2aaa, .sdiv = 0x8, .nsdiv = 0 }, /* 600 KHz */ + { .mdiv = 0x02, .pe = 0x3d33, .sdiv = 0x0, .nsdiv = 0 }, /* 148.5 Mhz */ + { .mdiv = 0x13, .pe = 0x5bcc, .sdiv = 0x0, .nsdiv = 1 }, /* 297 Mhz */ + { .mdiv = 0x0e, .pe = 0x1025, .sdiv = 0x0, .nsdiv = 1 }, /* 333 Mhz */ + { .mdiv = 0x0b, .pe = 0x715f, .sdiv = 0x0, .nsdiv = 1 }, /* 350 Mhz */ +}; + +struct clkgen_quadfs_data { + bool reset_present; + bool bwfilter_present; + bool lockstatus_present; + bool nsdiv_present; + struct clkgen_field ndiv; + struct clkgen_field ref_bw; + struct clkgen_field nreset; + struct clkgen_field npda; + struct clkgen_field lock_status; + + struct clkgen_field nsb[QUADFS_MAX_CHAN]; + struct clkgen_field en[QUADFS_MAX_CHAN]; + struct clkgen_field mdiv[QUADFS_MAX_CHAN]; + struct clkgen_field pe[QUADFS_MAX_CHAN]; + struct clkgen_field sdiv[QUADFS_MAX_CHAN]; + struct clkgen_field nsdiv[QUADFS_MAX_CHAN]; + + const struct clk_ops *pll_ops; + struct stm_fs *rtbl; + u8 rtbl_cnt; + int (*get_rate)(unsigned long , struct stm_fs *, + unsigned long *); +}; + +static const struct clk_ops st_quadfs_pll_c65_ops; +static const struct clk_ops st_quadfs_pll_c32_ops; +static const struct clk_ops st_quadfs_fs216c65_ops; +static const struct clk_ops st_quadfs_fs432c65_ops; +static const struct clk_ops st_quadfs_fs660c32_ops; + +static int clk_fs216c65_get_rate(unsigned long, struct stm_fs *, + unsigned long *); +static int clk_fs432c65_get_rate(unsigned long, struct stm_fs *, + unsigned long *); +static int clk_fs660c32_dig_get_rate(unsigned long, struct stm_fs *, + unsigned long *); +/* + * Values for all of the standalone instances of this clock + * generator found in STiH415 and STiH416 SYSCFG register banks. Note + * that the individual channel standby control bits (nsb) are in the + * first register along with the PLL control bits. + */ +static struct clkgen_quadfs_data st_fs216c65_416 = { + /* 416 specific */ + .npda = CLKGEN_FIELD(0x0, 0x1, 14), + .nsb = { CLKGEN_FIELD(0x0, 0x1, 10), + CLKGEN_FIELD(0x0, 0x1, 11), + CLKGEN_FIELD(0x0, 0x1, 12), + CLKGEN_FIELD(0x0, 0x1, 13) }, + .nsdiv_present = true, + .nsdiv = { CLKGEN_FIELD(0x0, 0x1, 18), + CLKGEN_FIELD(0x0, 0x1, 19), + CLKGEN_FIELD(0x0, 0x1, 20), + CLKGEN_FIELD(0x0, 0x1, 21) }, + .mdiv = { CLKGEN_FIELD(0x4, 0x1f, 0), + CLKGEN_FIELD(0x14, 0x1f, 0), + CLKGEN_FIELD(0x24, 0x1f, 0), + CLKGEN_FIELD(0x34, 0x1f, 0) }, + .en = { CLKGEN_FIELD(0x10, 0x1, 0), + CLKGEN_FIELD(0x20, 0x1, 0), + CLKGEN_FIELD(0x30, 0x1, 0), + CLKGEN_FIELD(0x40, 0x1, 0) }, + .ndiv = CLKGEN_FIELD(0x0, 0x1, 15), + .bwfilter_present = true, + .ref_bw = CLKGEN_FIELD(0x0, 0x3, 16), + .pe = { CLKGEN_FIELD(0x8, 0xffff, 0), + CLKGEN_FIELD(0x18, 0xffff, 0), + CLKGEN_FIELD(0x28, 0xffff, 0), + CLKGEN_FIELD(0x38, 0xffff, 0) }, + .sdiv = { CLKGEN_FIELD(0xC, 0x7, 0), + CLKGEN_FIELD(0x1C, 0x7, 0), + CLKGEN_FIELD(0x2C, 0x7, 0), + CLKGEN_FIELD(0x3C, 0x7, 0) }, + .pll_ops = &st_quadfs_pll_c65_ops, + .rtbl = fs216c65_rtbl, + .rtbl_cnt = ARRAY_SIZE(fs216c65_rtbl), + .get_rate = clk_fs216c65_get_rate, +}; + +static struct clkgen_quadfs_data st_fs432c65_416 = { + .npda = CLKGEN_FIELD(0x0, 0x1, 14), + .nsb = { CLKGEN_FIELD(0x0, 0x1, 10), + CLKGEN_FIELD(0x0, 0x1, 11), + CLKGEN_FIELD(0x0, 0x1, 12), + CLKGEN_FIELD(0x0, 0x1, 13) }, + .nsdiv_present = true, + .nsdiv = { CLKGEN_FIELD(0x0, 0x1, 18), + CLKGEN_FIELD(0x0, 0x1, 19), + CLKGEN_FIELD(0x0, 0x1, 20), + CLKGEN_FIELD(0x0, 0x1, 21) }, + .mdiv = { CLKGEN_FIELD(0x4, 0x1f, 0), + CLKGEN_FIELD(0x14, 0x1f, 0), + CLKGEN_FIELD(0x24, 0x1f, 0), + CLKGEN_FIELD(0x34, 0x1f, 0) }, + .en = { CLKGEN_FIELD(0x10, 0x1, 0), + CLKGEN_FIELD(0x20, 0x1, 0), + CLKGEN_FIELD(0x30, 0x1, 0), + CLKGEN_FIELD(0x40, 0x1, 0) }, + .ndiv = CLKGEN_FIELD(0x0, 0x1, 15), + .bwfilter_present = true, + .ref_bw = CLKGEN_FIELD(0x0, 0x3, 16), + .pe = { CLKGEN_FIELD(0x8, 0xffff, 0), + CLKGEN_FIELD(0x18, 0xffff, 0), + CLKGEN_FIELD(0x28, 0xffff, 0), + CLKGEN_FIELD(0x38, 0xffff, 0) }, + .sdiv = { CLKGEN_FIELD(0xC, 0x7, 0), + CLKGEN_FIELD(0x1C, 0x7, 0), + CLKGEN_FIELD(0x2C, 0x7, 0), + CLKGEN_FIELD(0x3C, 0x7, 0) }, + .pll_ops = &st_quadfs_pll_c65_ops, + .rtbl = fs432c65_rtbl, + .rtbl_cnt = ARRAY_SIZE(fs432c65_rtbl), + .get_rate = clk_fs432c65_get_rate, +}; + +static struct clkgen_quadfs_data st_fs660c32_E_416 = { + .npda = CLKGEN_FIELD(0x0, 0x1, 14), + .nsb = { CLKGEN_FIELD(0x0, 0x1, 10), + CLKGEN_FIELD(0x0, 0x1, 11), + CLKGEN_FIELD(0x0, 0x1, 12), + CLKGEN_FIELD(0x0, 0x1, 13) }, + .nsdiv_present = true, + .nsdiv = { CLKGEN_FIELD(0x0, 0x1, 18), + CLKGEN_FIELD(0x0, 0x1, 19), + CLKGEN_FIELD(0x0, 0x1, 20), + CLKGEN_FIELD(0x0, 0x1, 21) }, + .mdiv = { CLKGEN_FIELD(0x4, 0x1f, 0), + CLKGEN_FIELD(0x14, 0x1f, 0), + CLKGEN_FIELD(0x24, 0x1f, 0), + CLKGEN_FIELD(0x34, 0x1f, 0) }, + .en = { CLKGEN_FIELD(0x10, 0x1, 0), + CLKGEN_FIELD(0x20, 0x1, 0), + CLKGEN_FIELD(0x30, 0x1, 0), + CLKGEN_FIELD(0x40, 0x1, 0) }, + .ndiv = CLKGEN_FIELD(0x0, 0x7, 15), + .pe = { CLKGEN_FIELD(0x8, 0x7fff, 0), + CLKGEN_FIELD(0x18, 0x7fff, 0), + CLKGEN_FIELD(0x28, 0x7fff, 0), + CLKGEN_FIELD(0x38, 0x7fff, 0) }, + .sdiv = { CLKGEN_FIELD(0xC, 0xf, 0), + CLKGEN_FIELD(0x1C, 0xf, 0), + CLKGEN_FIELD(0x2C, 0xf, 0), + CLKGEN_FIELD(0x3C, 0xf, 0) }, + .lockstatus_present = true, + .lock_status = CLKGEN_FIELD(0xAC, 0x1, 0), + .pll_ops = &st_quadfs_pll_c32_ops, + .rtbl = fs660c32_rtbl, + .rtbl_cnt = ARRAY_SIZE(fs660c32_rtbl), + .get_rate = clk_fs660c32_dig_get_rate, +}; + +static struct clkgen_quadfs_data st_fs660c32_F_416 = { + .npda = CLKGEN_FIELD(0x0, 0x1, 14), + .nsb = { CLKGEN_FIELD(0x0, 0x1, 10), + CLKGEN_FIELD(0x0, 0x1, 11), + CLKGEN_FIELD(0x0, 0x1, 12), + CLKGEN_FIELD(0x0, 0x1, 13) }, + .nsdiv_present = true, + .nsdiv = { CLKGEN_FIELD(0x0, 0x1, 18), + CLKGEN_FIELD(0x0, 0x1, 19), + CLKGEN_FIELD(0x0, 0x1, 20), + CLKGEN_FIELD(0x0, 0x1, 21) }, + .mdiv = { CLKGEN_FIELD(0x4, 0x1f, 0), + CLKGEN_FIELD(0x14, 0x1f, 0), + CLKGEN_FIELD(0x24, 0x1f, 0), + CLKGEN_FIELD(0x34, 0x1f, 0) }, + .en = { CLKGEN_FIELD(0x10, 0x1, 0), + CLKGEN_FIELD(0x20, 0x1, 0), + CLKGEN_FIELD(0x30, 0x1, 0), + CLKGEN_FIELD(0x40, 0x1, 0) }, + .ndiv = CLKGEN_FIELD(0x0, 0x7, 15), + .pe = { CLKGEN_FIELD(0x8, 0x7fff, 0), + CLKGEN_FIELD(0x18, 0x7fff, 0), + CLKGEN_FIELD(0x28, 0x7fff, 0), + CLKGEN_FIELD(0x38, 0x7fff, 0) }, + .sdiv = { CLKGEN_FIELD(0xC, 0xf, 0), + CLKGEN_FIELD(0x1C, 0xf, 0), + CLKGEN_FIELD(0x2C, 0xf, 0), + CLKGEN_FIELD(0x3C, 0xf, 0) }, + .lockstatus_present = true, + .lock_status = CLKGEN_FIELD(0xEC, 0x1, 0), + .pll_ops = &st_quadfs_pll_c32_ops, + .rtbl = fs660c32_rtbl, + .rtbl_cnt = ARRAY_SIZE(fs660c32_rtbl), + .get_rate = clk_fs660c32_dig_get_rate, +}; + +/** + * DOC: A Frequency Synthesizer that multiples its input clock by a fixed factor + * + * Traits of this clock: + * prepare - clk_(un)prepare only ensures parent is (un)prepared + * enable - clk_enable and clk_disable are functional & control the Fsyn + * rate - inherits rate from parent. set_rate/round_rate/recalc_rate + * parent - fixed parent. No clk_set_parent support + */ + +/** + * struct st_clk_quadfs_pll - A pll which outputs a fixed multiplier of + * its parent clock, found inside a type of + * ST quad channel frequency synthesizer block + * + * @hw: handle between common and hardware-specific interfaces. + * @ndiv: regmap field for the ndiv control. + * @regs_base: base address of the configuration registers. + * @lock: spinlock. + * + */ +struct st_clk_quadfs_pll { + struct clk_hw hw; + void __iomem *regs_base; + spinlock_t *lock; + struct clkgen_quadfs_data *data; + u32 ndiv; +}; + +#define to_quadfs_pll(_hw) container_of(_hw, struct st_clk_quadfs_pll, hw) + +static int quadfs_pll_enable(struct clk_hw *hw) +{ + struct st_clk_quadfs_pll *pll = to_quadfs_pll(hw); + unsigned long flags = 0, timeout = jiffies + msecs_to_jiffies(10); + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + /* + * Bring block out of reset if we have reset control. + */ + if (pll->data->reset_present) + CLKGEN_WRITE(pll, nreset, 1); + + /* + * Use a fixed input clock noise bandwidth filter for the moment + */ + if (pll->data->bwfilter_present) + CLKGEN_WRITE(pll, ref_bw, PLL_BW_GOODREF); + + + CLKGEN_WRITE(pll, ndiv, pll->ndiv); + + /* + * Power up the PLL + */ + CLKGEN_WRITE(pll, npda, 1); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + if (pll->data->lockstatus_present) + while (!CLKGEN_READ(pll, lock_status)) { + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + cpu_relax(); + } + + return 0; +} + +static void quadfs_pll_disable(struct clk_hw *hw) +{ + struct st_clk_quadfs_pll *pll = to_quadfs_pll(hw); + unsigned long flags = 0; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + /* + * Powerdown the PLL and then put block into soft reset if we have + * reset control. + */ + CLKGEN_WRITE(pll, npda, 0); + + if (pll->data->reset_present) + CLKGEN_WRITE(pll, nreset, 0); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); +} + +static int quadfs_pll_is_enabled(struct clk_hw *hw) +{ + struct st_clk_quadfs_pll *pll = to_quadfs_pll(hw); + u32 npda = CLKGEN_READ(pll, npda); + + return !!npda; +} + +int clk_fs660c32_vco_get_rate(unsigned long input, struct stm_fs *fs, + unsigned long *rate) +{ + unsigned long nd = fs->ndiv + 16; /* ndiv value */ + + *rate = input * nd; + + return 0; +} + +static unsigned long quadfs_pll_fs660c32_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct st_clk_quadfs_pll *pll = to_quadfs_pll(hw); + unsigned long rate = 0; + struct stm_fs params; + + params.ndiv = CLKGEN_READ(pll, ndiv); + if (clk_fs660c32_vco_get_rate(parent_rate, ¶ms, &rate)) + pr_err("%s:%s error calculating rate\n", + __clk_get_name(hw->clk), __func__); + + pll->ndiv = params.ndiv; + + return rate; +} + +int clk_fs660c32_vco_get_params(unsigned long input, + unsigned long output, struct stm_fs *fs) +{ +/* Formula + VCO frequency = (fin x ndiv) / pdiv + ndiv = VCOfreq * pdiv / fin + */ + unsigned long pdiv = 1, n; + + /* Output clock range: 384Mhz to 660Mhz */ + if (output < 384000000 || output > 660000000) + return -EINVAL; + + if (input > 40000000) + /* This means that PDIV would be 2 instead of 1. + Not supported today. */ + return -EINVAL; + + input /= 1000; + output /= 1000; + + n = output * pdiv / input; + if (n < 16) + n = 16; + fs->ndiv = n - 16; /* Converting formula value to reg value */ + + return 0; +} + +static long quadfs_pll_fs660c32_round_rate(struct clk_hw *hw, unsigned long rate + , unsigned long *prate) +{ + struct stm_fs params; + + if (!clk_fs660c32_vco_get_params(*prate, rate, ¶ms)) + clk_fs660c32_vco_get_rate(*prate, ¶ms, &rate); + + pr_debug("%s: %s new rate %ld [sdiv=0x%x,md=0x%x,pe=0x%x,nsdiv3=%u]\n", + __func__, __clk_get_name(hw->clk), + rate, (unsigned int)params.sdiv, + (unsigned int)params.mdiv, + (unsigned int)params.pe, (unsigned int)params.nsdiv); + + return rate; +} + +static int quadfs_pll_fs660c32_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct st_clk_quadfs_pll *pll = to_quadfs_pll(hw); + struct stm_fs params; + long hwrate = 0; + unsigned long flags = 0; + + if (!rate || !parent_rate) + return -EINVAL; + + if (!clk_fs660c32_vco_get_params(parent_rate, rate, ¶ms)) + clk_fs660c32_vco_get_rate(parent_rate, ¶ms, &hwrate); + + pr_debug("%s: %s new rate %ld [ndiv=0x%x]\n", + __func__, __clk_get_name(hw->clk), + hwrate, (unsigned int)params.ndiv); + + if (!hwrate) + return -EINVAL; + + pll->ndiv = params.ndiv; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + CLKGEN_WRITE(pll, ndiv, pll->ndiv); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + return 0; +} + +static const struct clk_ops st_quadfs_pll_c65_ops = { + .enable = quadfs_pll_enable, + .disable = quadfs_pll_disable, + .is_enabled = quadfs_pll_is_enabled, +}; + +static const struct clk_ops st_quadfs_pll_c32_ops = { + .enable = quadfs_pll_enable, + .disable = quadfs_pll_disable, + .is_enabled = quadfs_pll_is_enabled, + .recalc_rate = quadfs_pll_fs660c32_recalc_rate, + .round_rate = quadfs_pll_fs660c32_round_rate, + .set_rate = quadfs_pll_fs660c32_set_rate, +}; + +static struct clk * __init st_clk_register_quadfs_pll( + const char *name, const char *parent_name, + struct clkgen_quadfs_data *quadfs, void __iomem *reg, + spinlock_t *lock) +{ + struct st_clk_quadfs_pll *pll; + struct clk *clk; + struct clk_init_data init; + + /* + * Sanity check required pointers. + */ + if (WARN_ON(!name || !parent_name)) + return ERR_PTR(-EINVAL); + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = quadfs->pll_ops; + init.flags = CLK_IS_BASIC; + init.parent_names = &parent_name; + init.num_parents = 1; + + pll->data = quadfs; + pll->regs_base = reg; + pll->lock = lock; + pll->hw.init = &init; + + clk = clk_register(NULL, &pll->hw); + + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} + +/** + * DOC: A digital frequency synthesizer + * + * Traits of this clock: + * prepare - clk_(un)prepare only ensures parent is (un)prepared + * enable - clk_enable and clk_disable are functional + * rate - set rate is functional + * parent - fixed parent. No clk_set_parent support + */ + +/** + * struct st_clk_quadfs_fsynth - One clock output from a four channel digital + * frequency synthesizer (fsynth) block. + * + * @hw: handle between common and hardware-specific interfaces + * + * @nsb: regmap field in the output control register for the digital + * standby of this fsynth channel. This control is active low so + * the channel is in standby when the control bit is cleared. + * + * @nsdiv: regmap field in the output control register for + * for the optional divide by 3 of this fsynth channel. This control + * is active low so the divide by 3 is active when the control bit is + * cleared and the divide is bypassed when the bit is set. + */ +struct st_clk_quadfs_fsynth { + struct clk_hw hw; + void __iomem *regs_base; + spinlock_t *lock; + struct clkgen_quadfs_data *data; + + u32 chan; + /* + * Cached hardware values from set_rate so we can program the + * hardware in enable. There are two reasons for this: + * + * 1. The registers may not be writable until the parent has been + * enabled. + * + * 2. It restores the clock rate when a driver does an enable + * on PM restore, after a suspend to RAM has lost the hardware + * setup. + */ + u32 md; + u32 pe; + u32 sdiv; + u32 nsdiv; +}; + +#define to_quadfs_fsynth(_hw) \ + container_of(_hw, struct st_clk_quadfs_fsynth, hw) + +static void quadfs_fsynth_program_enable(struct st_clk_quadfs_fsynth *fs) +{ + /* + * Pulse the program enable register lsb to make the hardware take + * notice of the new md/pe values with a glitchless transition. + */ + CLKGEN_WRITE(fs, en[fs->chan], 1); + CLKGEN_WRITE(fs, en[fs->chan], 0); +} + +static void quadfs_fsynth_program_rate(struct st_clk_quadfs_fsynth *fs) +{ + unsigned long flags = 0; + + /* + * Ensure the md/pe parameters are ignored while we are + * reprogramming them so we can get a glitchless change + * when fine tuning the speed of a running clock. + */ + CLKGEN_WRITE(fs, en[fs->chan], 0); + + CLKGEN_WRITE(fs, mdiv[fs->chan], fs->md); + CLKGEN_WRITE(fs, pe[fs->chan], fs->pe); + CLKGEN_WRITE(fs, sdiv[fs->chan], fs->sdiv); + + if (fs->lock) + spin_lock_irqsave(fs->lock, flags); + + if (fs->data->nsdiv_present) + CLKGEN_WRITE(fs, nsdiv[fs->chan], fs->nsdiv); + + if (fs->lock) + spin_unlock_irqrestore(fs->lock, flags); +} + +static int quadfs_fsynth_enable(struct clk_hw *hw) +{ + struct st_clk_quadfs_fsynth *fs = to_quadfs_fsynth(hw); + unsigned long flags = 0; + + pr_debug("%s: %s\n", __func__, __clk_get_name(hw->clk)); + + quadfs_fsynth_program_rate(fs); + + if (fs->lock) + spin_lock_irqsave(fs->lock, flags); + + CLKGEN_WRITE(fs, nsb[fs->chan], 1); + + if (fs->lock) + spin_unlock_irqrestore(fs->lock, flags); + + quadfs_fsynth_program_enable(fs); + + return 0; +} + +static void quadfs_fsynth_disable(struct clk_hw *hw) +{ + struct st_clk_quadfs_fsynth *fs = to_quadfs_fsynth(hw); + unsigned long flags = 0; + + pr_debug("%s: %s\n", __func__, __clk_get_name(hw->clk)); + + if (fs->lock) + spin_lock_irqsave(fs->lock, flags); + + CLKGEN_WRITE(fs, nsb[fs->chan], 0); + + if (fs->lock) + spin_unlock_irqrestore(fs->lock, flags); +} + +static int quadfs_fsynth_is_enabled(struct clk_hw *hw) +{ + struct st_clk_quadfs_fsynth *fs = to_quadfs_fsynth(hw); + u32 nsb = CLKGEN_READ(fs, nsb[fs->chan]); + + pr_debug("%s: %s enable bit = 0x%x\n", + __func__, __clk_get_name(hw->clk), nsb); + + return !!nsb; +} + +#define P15 (uint64_t)(1 << 15) + +static int clk_fs216c65_get_rate(unsigned long input, struct stm_fs *fs, + unsigned long *rate) +{ + uint64_t res; + unsigned long ns; + unsigned long nd = 8; /* ndiv stuck at 0 => val = 8 */ + unsigned long s; + long m; + + m = fs->mdiv - 32; + s = 1 << (fs->sdiv + 1); + ns = (fs->nsdiv ? 1 : 3); + + res = (uint64_t)(s * ns * P15 * (uint64_t)(m + 33)); + res = res - (s * ns * fs->pe); + *rate = div64_u64(P15 * nd * input * 32, res); + + return 0; +} + +static int clk_fs432c65_get_rate(unsigned long input, struct stm_fs *fs, + unsigned long *rate) +{ + uint64_t res; + unsigned long nd = 16; /* ndiv value; stuck at 0 (30Mhz input) */ + long m; + unsigned long sd; + unsigned long ns; + + m = fs->mdiv - 32; + sd = 1 << (fs->sdiv + 1); + ns = (fs->nsdiv ? 1 : 3); + + res = (uint64_t)(sd * ns * P15 * (uint64_t)(m + 33)); + res = res - (sd * ns * fs->pe); + *rate = div64_u64(P15 * nd * input * 32, res); + + return 0; +} + +#define P20 (uint64_t)(1 << 20) + +static int clk_fs660c32_dig_get_rate(unsigned long input, + struct stm_fs *fs, unsigned long *rate) +{ + unsigned long s = (1 << fs->sdiv); + unsigned long ns; + uint64_t res; + + /* + * 'nsdiv' is a register value ('BIN') which is translated + * to a decimal value according to following rules. + * + * nsdiv ns.dec + * 0 3 + * 1 1 + */ + ns = (fs->nsdiv == 1) ? 1 : 3; + + res = (P20 * (32 + fs->mdiv) + 32 * fs->pe) * s * ns; + *rate = (unsigned long)div64_u64(input * P20 * 32, res); + + return 0; +} + +static int quadfs_fsynt_get_hw_value_for_recalc(struct st_clk_quadfs_fsynth *fs, + struct stm_fs *params) +{ + /* + * Get the initial hardware values for recalc_rate + */ + params->mdiv = CLKGEN_READ(fs, mdiv[fs->chan]); + params->pe = CLKGEN_READ(fs, pe[fs->chan]); + params->sdiv = CLKGEN_READ(fs, sdiv[fs->chan]); + + if (fs->data->nsdiv_present) + params->nsdiv = CLKGEN_READ(fs, nsdiv[fs->chan]); + else + params->nsdiv = 1; + + /* + * If All are NULL then assume no clock rate is programmed. + */ + if (!params->mdiv && !params->pe && !params->sdiv) + return 1; + + fs->md = params->mdiv; + fs->pe = params->pe; + fs->sdiv = params->sdiv; + fs->nsdiv = params->nsdiv; + + return 0; +} + +static long quadfs_find_best_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate, struct stm_fs *params) +{ + struct st_clk_quadfs_fsynth *fs = to_quadfs_fsynth(hw); + int (*clk_fs_get_rate)(unsigned long , + struct stm_fs *, unsigned long *); + struct stm_fs prev_params; + unsigned long prev_rate, rate = 0; + unsigned long diff_rate, prev_diff_rate = ~0; + int index; + + clk_fs_get_rate = fs->data->get_rate; + + for (index = 0; index < fs->data->rtbl_cnt; index++) { + prev_rate = rate; + + *params = fs->data->rtbl[index]; + prev_params = *params; + + clk_fs_get_rate(prate, &fs->data->rtbl[index], &rate); + + diff_rate = abs(drate - rate); + + if (diff_rate > prev_diff_rate) { + rate = prev_rate; + *params = prev_params; + break; + } + + prev_diff_rate = diff_rate; + + if (drate == rate) + return rate; + } + + + if (index == fs->data->rtbl_cnt) + *params = prev_params; + + return rate; +} + +static unsigned long quadfs_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct st_clk_quadfs_fsynth *fs = to_quadfs_fsynth(hw); + unsigned long rate = 0; + struct stm_fs params; + int (*clk_fs_get_rate)(unsigned long , + struct stm_fs *, unsigned long *); + + clk_fs_get_rate = fs->data->get_rate; + + if (quadfs_fsynt_get_hw_value_for_recalc(fs, ¶ms)) + return 0; + + if (clk_fs_get_rate(parent_rate, ¶ms, &rate)) { + pr_err("%s:%s error calculating rate\n", + __clk_get_name(hw->clk), __func__); + } + + pr_debug("%s:%s rate %lu\n", __clk_get_name(hw->clk), __func__, rate); + + return rate; +} + +static long quadfs_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct stm_fs params; + + rate = quadfs_find_best_rate(hw, rate, *prate, ¶ms); + + pr_debug("%s: %s new rate %ld [sdiv=0x%x,md=0x%x,pe=0x%x,nsdiv3=%u]\n", + __func__, __clk_get_name(hw->clk), + rate, (unsigned int)params.sdiv, (unsigned int)params.mdiv, + (unsigned int)params.pe, (unsigned int)params.nsdiv); + + return rate; +} + + +static void quadfs_program_and_enable(struct st_clk_quadfs_fsynth *fs, + struct stm_fs *params) +{ + fs->md = params->mdiv; + fs->pe = params->pe; + fs->sdiv = params->sdiv; + fs->nsdiv = params->nsdiv; + + /* + * In some integrations you can only change the fsynth programming when + * the parent entity containing it is enabled. + */ + quadfs_fsynth_program_rate(fs); + quadfs_fsynth_program_enable(fs); +} + +static int quadfs_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct st_clk_quadfs_fsynth *fs = to_quadfs_fsynth(hw); + struct stm_fs params; + long hwrate; + int uninitialized_var(i); + + if (!rate || !parent_rate) + return -EINVAL; + + memset(¶ms, 0, sizeof(struct stm_fs)); + + hwrate = quadfs_find_best_rate(hw, rate, parent_rate, ¶ms); + if (!hwrate) + return -EINVAL; + + quadfs_program_and_enable(fs, ¶ms); + + return 0; +} + + + +static const struct clk_ops st_quadfs_ops = { + .enable = quadfs_fsynth_enable, + .disable = quadfs_fsynth_disable, + .is_enabled = quadfs_fsynth_is_enabled, + .round_rate = quadfs_round_rate, + .set_rate = quadfs_set_rate, + .recalc_rate = quadfs_recalc_rate, +}; + +static struct clk * __init st_clk_register_quadfs_fsynth( + const char *name, const char *parent_name, + struct clkgen_quadfs_data *quadfs, void __iomem *reg, u32 chan, + spinlock_t *lock) +{ + struct st_clk_quadfs_fsynth *fs; + struct clk *clk; + struct clk_init_data init; + + /* + * Sanity check required pointers, note that nsdiv3 is optional. + */ + if (WARN_ON(!name || !parent_name)) + return ERR_PTR(-EINVAL); + + fs = kzalloc(sizeof(*fs), GFP_KERNEL); + if (!fs) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &st_quadfs_ops; + init.flags = CLK_GET_RATE_NOCACHE | CLK_IS_BASIC; + init.parent_names = &parent_name; + init.num_parents = 1; + + fs->data = quadfs; + fs->regs_base = reg; + fs->chan = chan; + fs->lock = lock; + fs->hw.init = &init; + + clk = clk_register(NULL, &fs->hw); + + if (IS_ERR(clk)) + kfree(fs); + + return clk; +} + +static struct of_device_id quadfs_of_match[] = { + { + .compatible = "st,stih416-quadfs216", + .data = (void *)&st_fs216c65_416 + }, + { + .compatible = "st,stih416-quadfs432", + .data = (void *)&st_fs432c65_416 + }, + { + .compatible = "st,stih416-quadfs660-E", + .data = (void *)&st_fs660c32_E_416 + }, + { + .compatible = "st,stih416-quadfs660-F", + .data = (void *)&st_fs660c32_F_416 + }, + {} +}; + +static void __init st_of_create_quadfs_fsynths( + struct device_node *np, const char *pll_name, + struct clkgen_quadfs_data *quadfs, void __iomem *reg, + spinlock_t *lock) +{ + struct clk_onecell_data *clk_data; + int fschan; + + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) + return; + + clk_data->clk_num = QUADFS_MAX_CHAN; + clk_data->clks = kzalloc(QUADFS_MAX_CHAN * sizeof(struct clk *), + GFP_KERNEL); + + if (!clk_data->clks) { + kfree(clk_data); + return; + } + + for (fschan = 0; fschan < QUADFS_MAX_CHAN; fschan++) { + struct clk *clk; + const char *clk_name; + + if (of_property_read_string_index(np, "clock-output-names", + fschan, &clk_name)) { + break; + } + + /* + * If we read an empty clock name then the channel is unused + */ + if (*clk_name == '\0') + continue; + + clk = st_clk_register_quadfs_fsynth(clk_name, pll_name, + quadfs, reg, fschan, lock); + + /* + * If there was an error registering this clock output, clean + * up and move on to the next one. + */ + if (!IS_ERR(clk)) { + clk_data->clks[fschan] = clk; + pr_debug("%s: parent %s rate %u\n", + __clk_get_name(clk), + __clk_get_name(clk_get_parent(clk)), + (unsigned int)clk_get_rate(clk)); + } + } + + of_clk_add_provider(np, of_clk_src_onecell_get, clk_data); +} + +static void __init st_of_quadfs_setup(struct device_node *np) +{ + const struct of_device_id *match; + struct clk *clk; + const char *pll_name, *clk_parent_name; + void __iomem *reg; + spinlock_t *lock; + + match = of_match_node(quadfs_of_match, np); + if (WARN_ON(!match)) + return; + + reg = of_iomap(np, 0); + if (!reg) + return; + + clk_parent_name = of_clk_get_parent_name(np, 0); + if (!clk_parent_name) + return; + + pll_name = kasprintf(GFP_KERNEL, "%s.pll", np->name); + if (!pll_name) + return; + + lock = kzalloc(sizeof(*lock), GFP_KERNEL); + if (!lock) + goto err_exit; + + spin_lock_init(lock); + + clk = st_clk_register_quadfs_pll(pll_name, clk_parent_name, + (struct clkgen_quadfs_data *) match->data, reg, lock); + if (IS_ERR(clk)) + goto err_exit; + else + pr_debug("%s: parent %s rate %u\n", + __clk_get_name(clk), + __clk_get_name(clk_get_parent(clk)), + (unsigned int)clk_get_rate(clk)); + + st_of_create_quadfs_fsynths(np, pll_name, + (struct clkgen_quadfs_data *)match->data, + reg, lock); + +err_exit: + kfree(pll_name); /* No longer need local copy of the PLL name */ +} +CLK_OF_DECLARE(quadfs, "st,quadfs", st_of_quadfs_setup); -- cgit v1.2.3 From ec8d27b41e3b513b15c4554d9bb02ba10e0861a4 Mon Sep 17 00:00:00 2001 From: Gabriel FERNANDEZ Date: Thu, 27 Feb 2014 16:24:18 +0100 Subject: clk: st: Support for ClockGenA9/DDR/GPU The patch added support for DT registration of ClockGenA9/DDR/GPU ClockgenA9/DDR : It includes c32 type PLL (also in ClockgenA1x), hence only CLK_OF_DECLARE implementation is required. ClockgenGPU : It includes c65 type PLL (also in ClockgenAx), hence only CLK_OF_DECLARE implementation is required. Signed-off-by: Pankaj Dev Signed-off-by: Gabriel Fernandez Signed-off-by: Mike Turquette --- drivers/clk/st/clkgen-pll.c | 139 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/st/clkgen-pll.c b/drivers/clk/st/clkgen-pll.c index c6b38b05df1c..bca0a0badbfa 100644 --- a/drivers/clk/st/clkgen-pll.c +++ b/drivers/clk/st/clkgen-pll.c @@ -110,6 +110,76 @@ static struct clkgen_pll_data st_pll3200c32_a1x_1 = { .ops = &stm_pll3200c32_ops, }; +/* 415 specific */ +static struct clkgen_pll_data st_pll3200c32_a9_415 = { + .pdn_status = CLKGEN_FIELD(0x0, 0x1, 0), + .locked_status = CLKGEN_FIELD(0x6C, 0x1, 0), + .ndiv = CLKGEN_FIELD(0x0, C32_NDIV_MASK, 9), + .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 22), + .num_odfs = 1, + .odf = { CLKGEN_FIELD(0x0, C32_ODF_MASK, 3) }, + .odf_gate = { CLKGEN_FIELD(0x0, 0x1, 28) }, + .ops = &stm_pll3200c32_ops, +}; + +static struct clkgen_pll_data st_pll3200c32_ddr_415 = { + .pdn_status = CLKGEN_FIELD(0x0, 0x1, 0), + .locked_status = CLKGEN_FIELD(0x100, 0x1, 0), + .ndiv = CLKGEN_FIELD(0x8, C32_NDIV_MASK, 0), + .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 25), + .num_odfs = 2, + .odf = { CLKGEN_FIELD(0x8, C32_ODF_MASK, 8), + CLKGEN_FIELD(0x8, C32_ODF_MASK, 14) }, + .odf_gate = { CLKGEN_FIELD(0x4, 0x1, 28), + CLKGEN_FIELD(0x4, 0x1, 29) }, + .ops = &stm_pll3200c32_ops, +}; + +static struct clkgen_pll_data st_pll1200c32_gpu_415 = { + .pdn_status = CLKGEN_FIELD(0x144, 0x1, 3), + .locked_status = CLKGEN_FIELD(0x168, 0x1, 0), + .ldf = CLKGEN_FIELD(0x0, C32_LDF_MASK, 3), + .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 0), + .num_odfs = 0, + .odf = { CLKGEN_FIELD(0x0, C32_ODF_MASK, 10) }, + .ops = &st_pll1200c32_ops, +}; + +/* 416 specific */ +static struct clkgen_pll_data st_pll3200c32_a9_416 = { + .pdn_status = CLKGEN_FIELD(0x0, 0x1, 0), + .locked_status = CLKGEN_FIELD(0x6C, 0x1, 0), + .ndiv = CLKGEN_FIELD(0x8, C32_NDIV_MASK, 0), + .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 25), + .num_odfs = 1, + .odf = { CLKGEN_FIELD(0x8, C32_ODF_MASK, 8) }, + .odf_gate = { CLKGEN_FIELD(0x4, 0x1, 28) }, + .ops = &stm_pll3200c32_ops, +}; + +static struct clkgen_pll_data st_pll3200c32_ddr_416 = { + .pdn_status = CLKGEN_FIELD(0x0, 0x1, 0), + .locked_status = CLKGEN_FIELD(0x10C, 0x1, 0), + .ndiv = CLKGEN_FIELD(0x8, C32_NDIV_MASK, 0), + .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 25), + .num_odfs = 2, + .odf = { CLKGEN_FIELD(0x8, C32_ODF_MASK, 8), + CLKGEN_FIELD(0x8, C32_ODF_MASK, 14) }, + .odf_gate = { CLKGEN_FIELD(0x4, 0x1, 28), + CLKGEN_FIELD(0x4, 0x1, 29) }, + .ops = &stm_pll3200c32_ops, +}; + +static struct clkgen_pll_data st_pll1200c32_gpu_416 = { + .pdn_status = CLKGEN_FIELD(0x8E4, 0x1, 3), + .locked_status = CLKGEN_FIELD(0x90C, 0x1, 0), + .ldf = CLKGEN_FIELD(0x0, C32_LDF_MASK, 3), + .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 0), + .num_odfs = 0, + .odf = { CLKGEN_FIELD(0x0, C32_ODF_MASK, 10) }, + .ops = &st_pll1200c32_ops, +}; + /** * DOC: Clock Generated by PLL, rate set and enabled by bootloader * @@ -484,6 +554,22 @@ static struct of_device_id c32_pll_of_match[] = { .compatible = "st,plls-c32-a1x-1", .data = &st_pll3200c32_a1x_1, }, + { + .compatible = "st,stih415-plls-c32-a9", + .data = &st_pll3200c32_a9_415, + }, + { + .compatible = "st,stih415-plls-c32-ddr", + .data = &st_pll3200c32_ddr_415, + }, + { + .compatible = "st,stih416-plls-c32-a9", + .data = &st_pll3200c32_a9_416, + }, + { + .compatible = "st,stih416-plls-c32-ddr", + .data = &st_pll3200c32_ddr_416, + }, {} }; @@ -557,3 +643,56 @@ err: kfree(clk_data); } CLK_OF_DECLARE(clkgen_c32_pll, "st,clkgen-plls-c32", clkgen_c32_pll_setup); + +static struct of_device_id c32_gpu_pll_of_match[] = { + { + .compatible = "st,stih415-gpu-pll-c32", + .data = &st_pll1200c32_gpu_415, + }, + { + .compatible = "st,stih416-gpu-pll-c32", + .data = &st_pll1200c32_gpu_416, + }, +}; + +static void __init clkgengpu_c32_pll_setup(struct device_node *np) +{ + const struct of_device_id *match; + struct clk *clk; + const char *parent_name; + void __iomem *reg; + const char *clk_name; + struct clkgen_pll_data *data; + + match = of_match_node(c32_gpu_pll_of_match, np); + if (!match) { + pr_err("%s: No matching data\n", __func__); + return; + } + + data = (struct clkgen_pll_data *)match->data; + + parent_name = of_clk_get_parent_name(np, 0); + if (!parent_name) + return; + + reg = clkgen_get_register_base(np); + if (!reg) + return; + + if (of_property_read_string_index(np, "clock-output-names", + 0, &clk_name)) + return; + + /* + * PLL 1200MHz output + */ + clk = clkgen_pll_register(parent_name, data, reg, clk_name); + + if (!IS_ERR(clk)) + of_clk_add_provider(np, of_clk_src_simple_get, clk); + + return; +} +CLK_OF_DECLARE(clkgengpu_c32_pll, + "st,clkgengpu-pll-c32", clkgengpu_c32_pll_setup); -- cgit v1.2.3 From ab35dc139aed41f5e527910193f0d3c3d73a36fc Mon Sep 17 00:00:00 2001 From: Gabriel FERNANDEZ Date: Thu, 27 Feb 2014 16:24:19 +0100 Subject: clk: st: Support for A9 MUX clocks The patch supports the A9-mux clocks used by ClockGenA9 A9-mux clock : Multiplexer inside ClockGenA9. A9 clock can be driven by either PLL or External clock (with an optional divide-by-2). This is implemented as 3-parent clock : PLL, Ext-clk OR Ext-clk/2 Signed-off-by: Pankaj Dev Signed-off-by: Mike Turquette --- drivers/clk/st/clkgen-mux.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/st/clkgen-mux.c b/drivers/clk/st/clkgen-mux.c index 7ccff620a2fe..a329906d1e81 100644 --- a/drivers/clk/st/clkgen-mux.c +++ b/drivers/clk/st/clkgen-mux.c @@ -570,6 +570,17 @@ static struct clkgen_mux_data clkgen_mux_c_vcc_sd_416 = { .lock = &clkgenf_lock, }; +static struct clkgen_mux_data stih415_a9_mux_data = { + .offset = 0, + .shift = 1, + .width = 2, +}; +static struct clkgen_mux_data stih416_a9_mux_data = { + .offset = 0, + .shift = 0, + .width = 2, +}; + static struct of_device_id mux_of_match[] = { { .compatible = "st,stih416-clkgenc-vcc-hd", @@ -591,6 +602,14 @@ static struct of_device_id mux_of_match[] = { .compatible = "st,stih416-clkgenf-vcc-sd", .data = &clkgen_mux_c_vcc_sd_416, }, + { + .compatible = "st,stih415-clkgen-a9-mux", + .data = &stih415_a9_mux_data, + }, + { + .compatible = "st,stih416-clkgen-a9-mux", + .data = &stih416_a9_mux_data, + }, {} }; -- cgit v1.2.3 From 7433ab43fa17094173f18a39f33c3a24d50b300f Mon Sep 17 00:00:00 2001 From: Chao Xie Date: Thu, 23 Jan 2014 10:47:40 +0800 Subject: clk: mmp: fix wrong mask when calculate denominator The code has typo when calculate denominator. It should use den_mask instead of num_mask. Signed-off-by: Chao Xie Signed-off-by: Mike Turquette --- drivers/clk/mmp/clk-frac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/mmp/clk-frac.c b/drivers/clk/mmp/clk-frac.c index 80c1dd15d15c..f6e7691c4efa 100644 --- a/drivers/clk/mmp/clk-frac.c +++ b/drivers/clk/mmp/clk-frac.c @@ -64,7 +64,7 @@ static unsigned long clk_factor_recalc_rate(struct clk_hw *hw, num = (val >> masks->num_shift) & masks->num_mask; /* calculate denominator */ - den = (val >> masks->den_shift) & masks->num_mask; + den = (val >> masks->den_shift) & masks->den_mask; if (!den) return 0; -- cgit v1.2.3 From c45693a6480cee906557a9fba533a9f3c224f91e Mon Sep 17 00:00:00 2001 From: Chao Xie Date: Thu, 23 Jan 2014 10:47:41 +0800 Subject: clk: mmp: fix the wrong calculation formula The formula is numerator/denominator = Fin / (Fout * factor) So Fout = Fin * denominator / (numerator * factor). Current clk_factor_round_rate and clk_factor_recalc_rate use wrong formula. This patch will fix them. Signed-off-by: Chao Xie Signed-off-by: Mike Turquette --- drivers/clk/mmp/clk-frac.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/mmp/clk-frac.c b/drivers/clk/mmp/clk-frac.c index f6e7691c4efa..5863a37ec84d 100644 --- a/drivers/clk/mmp/clk-frac.c +++ b/drivers/clk/mmp/clk-frac.c @@ -40,12 +40,12 @@ static long clk_factor_round_rate(struct clk_hw *hw, unsigned long drate, for (i = 0; i < factor->ftbl_cnt; i++) { prev_rate = rate; - rate = (((*prate / 10000) * factor->ftbl[i].num) / - (factor->ftbl[i].den * factor->masks->factor)) * 10000; + rate = (((*prate / 10000) * factor->ftbl[i].den) / + (factor->ftbl[i].num * factor->masks->factor)) * 10000; if (rate > drate) break; } - if (i == 0) + if ((i == 0) || (i == factor->ftbl_cnt)) return rate; else return prev_rate; @@ -85,8 +85,8 @@ static int clk_factor_set_rate(struct clk_hw *hw, unsigned long drate, for (i = 0; i < factor->ftbl_cnt; i++) { prev_rate = rate; - rate = (((prate / 10000) * factor->ftbl[i].num) / - (factor->ftbl[i].den * factor->masks->factor)) * 10000; + rate = (((prate / 10000) * factor->ftbl[i].den) / + (factor->ftbl[i].num * factor->masks->factor)) * 10000; if (rate > drate) break; } -- cgit v1.2.3 From 5d26c15d3ba98c31b24286dac7a390a307fcff29 Mon Sep 17 00:00:00 2001 From: Chao Xie Date: Thu, 23 Jan 2014 10:47:42 +0800 Subject: clk: mmp: try to use closer one when do round rate The orignal code will use the bigger rate between "previous rate" and "current rate" when caculate the rate. In fact, hardware cares about the closest one. So choose the closer rate between "previous rate" and "current rate". Signed-off-by: Chao Xie Signed-off-by: Mike Turquette --- drivers/clk/mmp/clk-frac.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/mmp/clk-frac.c b/drivers/clk/mmp/clk-frac.c index 5863a37ec84d..23a56f561812 100644 --- a/drivers/clk/mmp/clk-frac.c +++ b/drivers/clk/mmp/clk-frac.c @@ -45,10 +45,14 @@ static long clk_factor_round_rate(struct clk_hw *hw, unsigned long drate, if (rate > drate) break; } - if ((i == 0) || (i == factor->ftbl_cnt)) + if ((i == 0) || (i == factor->ftbl_cnt)) { return rate; - else - return prev_rate; + } else { + if ((drate - prev_rate) > (rate - drate)) + return rate; + else + return prev_rate; + } } static unsigned long clk_factor_recalc_rate(struct clk_hw *hw, -- cgit v1.2.3 From 7736692993308dfedc1aabbeb8b401fc00098de5 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Wed, 12 Feb 2014 22:16:11 +0800 Subject: clk: sirf: update copyright years to 2014 Signed-off-by: Barry Song Signed-off-by: Mike Turquette --- drivers/clk/sirf/clk-atlas6.c | 3 ++- drivers/clk/sirf/clk-common.c | 3 ++- drivers/clk/sirf/clk-prima2.c | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/sirf/clk-atlas6.c b/drivers/clk/sirf/clk-atlas6.c index f9f4a15a64ab..d63b76ca60c3 100644 --- a/drivers/clk/sirf/clk-atlas6.c +++ b/drivers/clk/sirf/clk-atlas6.c @@ -1,7 +1,8 @@ /* * Clock tree for CSR SiRFatlasVI * - * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * Copyright (c) 2011 - 2014 Cambridge Silicon Radio Limited, a CSR plc group + * company. * * Licensed under GPLv2 or later. */ diff --git a/drivers/clk/sirf/clk-common.c b/drivers/clk/sirf/clk-common.c index 7dde6a82f514..37af51c5f213 100644 --- a/drivers/clk/sirf/clk-common.c +++ b/drivers/clk/sirf/clk-common.c @@ -1,7 +1,8 @@ /* * common clks module for all SiRF SoCs * - * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * Copyright (c) 2011 - 2014 Cambridge Silicon Radio Limited, a CSR plc group + * company. * * Licensed under GPLv2 or later. */ diff --git a/drivers/clk/sirf/clk-prima2.c b/drivers/clk/sirf/clk-prima2.c index 7adc5c70c7ff..6968e2ebcd8a 100644 --- a/drivers/clk/sirf/clk-prima2.c +++ b/drivers/clk/sirf/clk-prima2.c @@ -1,7 +1,8 @@ /* * Clock tree for CSR SiRFprimaII * - * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * Copyright (c) 2011 - 2014 Cambridge Silicon Radio Limited, a CSR plc group + * company. * * Licensed under GPLv2 or later. */ -- cgit v1.2.3 From 3f6d439f202243a1594f2e1b86620af0d342bbe6 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 27 Mar 2014 11:43:32 +0100 Subject: clk: reverse default clk provider initialization order in of_clk_init() This restores the default clocks registration order as parsed from devicetree, i.e. as before commit 1771b10d605d26ccee771a7fb4b08718 "clk: respect the clock dependencies in of_clk_init", for when there is no explicit parent clock dependencies between clock providers specified in the device tree. It prevents regressions (boot failure, division by 0 errors) on imx and exynos platforms. Signed-off-by: Sylwester Nawrocki Tested-by: Fabio Estevam Signed-off-by: Mike Turquette --- drivers/clk/clk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 1fbcb2b107e7..6b44022753cb 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2612,7 +2612,7 @@ void __init of_clk_init(const struct of_device_id *matches) parent->clk_init_cb = match->data; parent->np = np; - list_add(&parent->node, &clk_provider_list); + list_add_tail(&parent->node, &clk_provider_list); } while (!list_empty(&clk_provider_list)) { -- cgit v1.2.3 From e5ca8fb4cca90706e115f65097a775795415eca5 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 27 Mar 2014 12:08:36 +0100 Subject: clk: Fix minor errors in of_clk_init() function comments Signed-off-by: Sylwester Nawrocki Signed-off-by: Mike Turquette --- drivers/clk/clk.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 6b44022753cb..dff0373f53c1 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2591,7 +2591,7 @@ static int parent_ready(struct device_node *np) * @matches: array of compatible values and init functions for providers. * * This function scans the device tree for matching clock providers - * and calls their initialization functions. It also do it by trying + * and calls their initialization functions. It also does it by trying * to follow the dependencies. */ void __init of_clk_init(const struct of_device_id *matches) @@ -2628,7 +2628,7 @@ void __init of_clk_init(const struct of_device_id *matches) } /* - * We didn't managed to initialize any of the + * We didn't manage to initialize any of the * remaining providers during the last loop, so now we * initialize all the remaining ones unconditionally * in case the clock parent was not mandatory -- cgit v1.2.3 From 365b01869bca1c9d5ecb05be7857739fa18a9b8c Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 31 Mar 2014 15:50:34 +0100 Subject: clk: shmobile: rcar-gen2: fix lb/sd0/sd1/sdh clock parent to pll1 The clock generator for rcar-gen2 has the lb, sdh, sd0 and sd1 clocks parented to pll1_div2 where the hardware diagram shows these to be directly fed from pll1. This fixes the initial rate for sdh0 clock to be 97.5MHz instead of the reported 48MHz where the manual says the default register values are for 97.5MHz. Signed-off-by: Ben Dooks Acked-by: Laurent Pinchart Signed-off-by: Mike Turquette --- drivers/clk/shmobile/clk-rcar-gen2.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/shmobile/clk-rcar-gen2.c b/drivers/clk/shmobile/clk-rcar-gen2.c index dd272a0d1446..a32eddd32589 100644 --- a/drivers/clk/shmobile/clk-rcar-gen2.c +++ b/drivers/clk/shmobile/clk-rcar-gen2.c @@ -210,22 +210,22 @@ rcar_gen2_cpg_register_clock(struct device_node *np, struct rcar_gen2_cpg *cpg, parent_name = "main"; mult = config->pll3_mult; } else if (!strcmp(name, "lb")) { - parent_name = "pll1_div2"; + parent_name = "pll1"; div = cpg_mode & BIT(18) ? 36 : 24; } else if (!strcmp(name, "qspi")) { parent_name = "pll1_div2"; div = (cpg_mode & (BIT(3) | BIT(2) | BIT(1))) == BIT(2) ? 8 : 10; } else if (!strcmp(name, "sdh")) { - parent_name = "pll1_div2"; + parent_name = "pll1"; table = cpg_sdh_div_table; shift = 8; } else if (!strcmp(name, "sd0")) { - parent_name = "pll1_div2"; + parent_name = "pll1"; table = cpg_sd01_div_table; shift = 4; } else if (!strcmp(name, "sd1")) { - parent_name = "pll1_div2"; + parent_name = "pll1"; table = cpg_sd01_div_table; shift = 0; } else if (!strcmp(name, "z")) { -- cgit v1.2.3 From e44df332f30bf3040c60c1ed6674d1431fdb48b9 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 31 Mar 2014 18:47:27 +0100 Subject: clk: shmobile: fix setting paretn clock rate If the driver needs to change a clock rate, then it must be propogated through the MSTP clock to the parent clock (such as shdi0 -> sd0). Without this we cannot up-rate default clocks which are really slow (such as the mmcif1 which defaults to 12MHz where it could be running at 97MHz) Signed-off-by: Ben Dooks Acked-by: Laurent Pinchart Signed-off-by: Mike Turquette --- drivers/clk/shmobile/clk-mstp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/shmobile/clk-mstp.c b/drivers/clk/shmobile/clk-mstp.c index 42d5912b1d25..2e5810c88d11 100644 --- a/drivers/clk/shmobile/clk-mstp.c +++ b/drivers/clk/shmobile/clk-mstp.c @@ -137,7 +137,7 @@ cpg_mstp_clock_register(const char *name, const char *parent_name, init.name = name; init.ops = &cpg_mstp_clock_ops; - init.flags = CLK_IS_BASIC; + init.flags = CLK_IS_BASIC | CLK_SET_RATE_PARENT; init.parent_names = &parent_name; init.num_parents = 1; -- cgit v1.2.3