From 9268beb5c2301b6651f0cc78734a1c0d15b3fcb1 Mon Sep 17 00:00:00 2001 From: Soren Brinkmann Date: Thu, 17 Apr 2014 11:40:52 -0700 Subject: clk: zynq: Leave debug clocks in bootup state Make sure debug clocks stay enabled if the bootloader enabled them. Otherwise debug HW may crash due to bus-hangs caused by stopped clocks. Signed-off-by: Soren Brinkmann Signed-off-by: Michal Simek --- drivers/clk/zynq/clkc.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/clk') diff --git a/drivers/clk/zynq/clkc.c b/drivers/clk/zynq/clkc.c index 52c09afdcfb7..246cf1226eaa 100644 --- a/drivers/clk/zynq/clkc.c +++ b/drivers/clk/zynq/clkc.c @@ -53,6 +53,9 @@ static void __iomem *zynq_clkc_base; #define NUM_MIO_PINS 54 +#define DBG_CLK_CTRL_CLKACT_TRC BIT(0) +#define DBG_CLK_CTRL_CPU_1XCLKACT BIT(1) + enum zynq_clk { armpll, ddrpll, iopll, cpu_6or4x, cpu_3or2x, cpu_2x, cpu_1x, @@ -499,6 +502,15 @@ static void __init zynq_clk_setup(struct device_node *np) clk_output_name[cpu_1x], 0, SLCR_DBG_CLK_CTRL, 1, 0, &dbgclk_lock); + /* leave debug clocks in the state the bootloader set them up to */ + tmp = clk_readl(SLCR_DBG_CLK_CTRL); + if (tmp & DBG_CLK_CTRL_CLKACT_TRC) + if (clk_prepare_enable(clks[dbg_trc])) + pr_warn("%s: trace clk enable failed\n", __func__); + if (tmp & DBG_CLK_CTRL_CPU_1XCLKACT) + if (clk_prepare_enable(clks[dbg_apb])) + pr_warn("%s: debug APB clk enable failed\n", __func__); + /* One gated clock for all APER clocks. */ clks[dma] = clk_register_gate(NULL, clk_output_name[dma], clk_output_name[cpu_2x], 0, SLCR_APER_CLK_CTRL, 0, 0, -- cgit v1.2.3 From 66ecbfea762ad28bd108db76775483b491068b92 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 22 Apr 2014 23:26:08 +0200 Subject: clk: mvebu: add Orion5x clock driver This commit adds a core clock driver for the Orion5x SoC, with support for the tclk, the CPU frequency and the DDR frequency. All the details about the Sample-At-Reset register were extracted from the U-Boot sources for Orion5x. Note that Orion5x does not have gatable clocks, so this core clock driver is sufficient to support clocking on Orion5x platforms. Signed-off-by: Thomas Petazzoni Acked-by: Sebastian Hesselbarth Link: https://lkml.kernel.org/r/1398202002-28530-5-git-send-email-thomas.petazzoni@free-electrons.com Cc: Mike Turquette Signed-off-by: Jason Cooper --- .../devicetree/bindings/clock/mvebu-core-clock.txt | 8 + drivers/clk/mvebu/Kconfig | 4 + drivers/clk/mvebu/Makefile | 1 + drivers/clk/mvebu/orion.c | 210 +++++++++++++++++++++ 4 files changed, 223 insertions(+) create mode 100644 drivers/clk/mvebu/orion.c (limited to 'drivers/clk') diff --git a/Documentation/devicetree/bindings/clock/mvebu-core-clock.txt b/Documentation/devicetree/bindings/clock/mvebu-core-clock.txt index 307a503c5db8..dc5ea5b22da9 100644 --- a/Documentation/devicetree/bindings/clock/mvebu-core-clock.txt +++ b/Documentation/devicetree/bindings/clock/mvebu-core-clock.txt @@ -29,6 +29,11 @@ The following is a list of provided IDs and clock names on Kirkwood and Dove: 2 = l2clk (L2 Cache clock derived from CPU0 clock) 3 = ddrclk (DDR controller clock derived from CPU0 clock) +The following is a list of provided IDs and clock names on Orion5x: + 0 = tclk (Internal Bus clock) + 1 = cpuclk (CPU0 clock) + 2 = ddrclk (DDR controller clock derived from CPU0 clock) + Required properties: - compatible : shall be one of the following: "marvell,armada-370-core-clock" - For Armada 370 SoC core clocks @@ -38,6 +43,9 @@ Required properties: "marvell,dove-core-clock" - for Dove SoC core clocks "marvell,kirkwood-core-clock" - for Kirkwood SoC (except mv88f6180) "marvell,mv88f6180-core-clock" - for Kirkwood MV88f6180 SoC + "marvell,mv88f5182-core-clock" - for Orion MV88F5182 SoC + "marvell,mv88f5281-core-clock" - for Orion MV88F5281 SoC + "marvell,mv88f6183-core-clock" - for Orion MV88F6183 SoC - reg : shall be the register address of the Sample-At-Reset (SAR) register - #clock-cells : from common clock binding; shall be set to 1 diff --git a/drivers/clk/mvebu/Kconfig b/drivers/clk/mvebu/Kconfig index 693f7be129f1..3b34dba9178d 100644 --- a/drivers/clk/mvebu/Kconfig +++ b/drivers/clk/mvebu/Kconfig @@ -34,3 +34,7 @@ config DOVE_CLK config KIRKWOOD_CLK bool select MVEBU_CLK_COMMON + +config ORION_CLK + bool + select MVEBU_CLK_COMMON diff --git a/drivers/clk/mvebu/Makefile b/drivers/clk/mvebu/Makefile index 4c66162fb0b4..a9a56fc01901 100644 --- a/drivers/clk/mvebu/Makefile +++ b/drivers/clk/mvebu/Makefile @@ -8,3 +8,4 @@ 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 +obj-$(CONFIG_ORION_CLK) += orion.o diff --git a/drivers/clk/mvebu/orion.c b/drivers/clk/mvebu/orion.c new file mode 100644 index 000000000000..fd129566c1ce --- /dev/null +++ b/drivers/clk/mvebu/orion.c @@ -0,0 +1,210 @@ +/* + * Marvell Orion SoC clocks + * + * Copyright (C) 2014 Thomas Petazzoni + * + * Thomas Petazzoni + * + * 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" + +static const struct coreclk_ratio orion_coreclk_ratios[] __initconst = { + { .id = 0, .name = "ddrclk", } +}; + +/* + * Orion 5182 + */ + +#define SAR_MV88F5182_TCLK_FREQ 8 +#define SAR_MV88F5182_TCLK_FREQ_MASK 0x3 + +static u32 __init mv88f5182_get_tclk_freq(void __iomem *sar) +{ + u32 opt = (readl(sar) >> SAR_MV88F5182_TCLK_FREQ) & + SAR_MV88F5182_TCLK_FREQ_MASK; + if (opt == 1) + return 150000000; + else if (opt == 2) + return 166666667; + else + return 0; +} + +#define SAR_MV88F5182_CPU_FREQ 4 +#define SAR_MV88F5182_CPU_FREQ_MASK 0xf + +static u32 __init mv88f5182_get_cpu_freq(void __iomem *sar) +{ + u32 opt = (readl(sar) >> SAR_MV88F5182_CPU_FREQ) & + SAR_MV88F5182_CPU_FREQ_MASK; + if (opt == 0) + return 333333333; + else if (opt == 1 || opt == 2) + return 400000000; + else if (opt == 3) + return 500000000; + else + return 0; +} + +static void __init mv88f5182_get_clk_ratio(void __iomem *sar, int id, + int *mult, int *div) +{ + u32 opt = (readl(sar) >> SAR_MV88F5182_CPU_FREQ) & + SAR_MV88F5182_CPU_FREQ_MASK; + if (opt == 0 || opt == 1) { + *mult = 1; + *div = 2; + } else if (opt == 2 || opt == 3) { + *mult = 1; + *div = 3; + } else { + *mult = 0; + *div = 1; + } +} + +static const struct coreclk_soc_desc mv88f5182_coreclks = { + .get_tclk_freq = mv88f5182_get_tclk_freq, + .get_cpu_freq = mv88f5182_get_cpu_freq, + .get_clk_ratio = mv88f5182_get_clk_ratio, + .ratios = orion_coreclk_ratios, + .num_ratios = ARRAY_SIZE(orion_coreclk_ratios), +}; + +static void __init mv88f5182_clk_init(struct device_node *np) +{ + return mvebu_coreclk_setup(np, &mv88f5182_coreclks); +} + +CLK_OF_DECLARE(mv88f5182_clk, "marvell,mv88f5182-core-clock", mv88f5182_clk_init); + +/* + * Orion 5281 + */ + +static u32 __init mv88f5281_get_tclk_freq(void __iomem *sar) +{ + /* On 5281, tclk is always 166 Mhz */ + return 166666667; +} + +#define SAR_MV88F5281_CPU_FREQ 4 +#define SAR_MV88F5281_CPU_FREQ_MASK 0xf + +static u32 __init mv88f5281_get_cpu_freq(void __iomem *sar) +{ + u32 opt = (readl(sar) >> SAR_MV88F5281_CPU_FREQ) & + SAR_MV88F5281_CPU_FREQ_MASK; + if (opt == 1 || opt == 2) + return 400000000; + else if (opt == 3) + return 500000000; + else + return 0; +} + +static void __init mv88f5281_get_clk_ratio(void __iomem *sar, int id, + int *mult, int *div) +{ + u32 opt = (readl(sar) >> SAR_MV88F5281_CPU_FREQ) & + SAR_MV88F5281_CPU_FREQ_MASK; + if (opt == 1) { + *mult = 1; + *div = 2; + } else if (opt == 2 || opt == 3) { + *mult = 1; + *div = 3; + } else { + *mult = 0; + *div = 1; + } +} + +static const struct coreclk_soc_desc mv88f5281_coreclks = { + .get_tclk_freq = mv88f5281_get_tclk_freq, + .get_cpu_freq = mv88f5281_get_cpu_freq, + .get_clk_ratio = mv88f5281_get_clk_ratio, + .ratios = orion_coreclk_ratios, + .num_ratios = ARRAY_SIZE(orion_coreclk_ratios), +}; + +static void __init mv88f5281_clk_init(struct device_node *np) +{ + return mvebu_coreclk_setup(np, &mv88f5281_coreclks); +} + +CLK_OF_DECLARE(mv88f5281_clk, "marvell,mv88f5281-core-clock", mv88f5281_clk_init); + +/* + * Orion 6183 + */ + +#define SAR_MV88F6183_TCLK_FREQ 9 +#define SAR_MV88F6183_TCLK_FREQ_MASK 0x1 + +static u32 __init mv88f6183_get_tclk_freq(void __iomem *sar) +{ + u32 opt = (readl(sar) >> SAR_MV88F6183_TCLK_FREQ) & + SAR_MV88F6183_TCLK_FREQ_MASK; + if (opt == 0) + return 133333333; + else if (opt == 1) + return 166666667; + else + return 0; +} + +#define SAR_MV88F6183_CPU_FREQ 1 +#define SAR_MV88F6183_CPU_FREQ_MASK 0x3f + +static u32 __init mv88f6183_get_cpu_freq(void __iomem *sar) +{ + u32 opt = (readl(sar) >> SAR_MV88F6183_CPU_FREQ) & + SAR_MV88F6183_CPU_FREQ_MASK; + if (opt == 9) + return 333333333; + else if (opt == 17) + return 400000000; + else + return 0; +} + +static void __init mv88f6183_get_clk_ratio(void __iomem *sar, int id, + int *mult, int *div) +{ + u32 opt = (readl(sar) >> SAR_MV88F6183_CPU_FREQ) & + SAR_MV88F6183_CPU_FREQ_MASK; + if (opt == 9 || opt == 17) { + *mult = 1; + *div = 2; + } else { + *mult = 0; + *div = 1; + } +} + +static const struct coreclk_soc_desc mv88f6183_coreclks = { + .get_tclk_freq = mv88f6183_get_tclk_freq, + .get_cpu_freq = mv88f6183_get_cpu_freq, + .get_clk_ratio = mv88f6183_get_clk_ratio, + .ratios = orion_coreclk_ratios, + .num_ratios = ARRAY_SIZE(orion_coreclk_ratios), +}; + + +static void __init mv88f6183_clk_init(struct device_node *np) +{ + return mvebu_coreclk_setup(np, &mv88f6183_coreclks); +} + +CLK_OF_DECLARE(mv88f6183_clk, "marvell,mv88f6183-core-clock", mv88f6183_clk_init); -- cgit v1.2.3 From 774b514390b1eb8476bc759262790762bd1ef45a Mon Sep 17 00:00:00 2001 From: Maxime COQUELIN Date: Wed, 29 Jan 2014 17:24:07 +0100 Subject: clk: divider: Add round to closest divider In some cases, we want to be able to round the divider to the closest one, instead than rounding up. This patch adds a new CLK_DIVIDER_ROUND_CLOSEST flag to specify the divider has to round to closest div, keeping rounding up as de default behaviour. Signed-off-by: Maxime Coquelin Signed-off-by: Mike Turquette --- drivers/clk/clk-divider.c | 69 ++++++++++++++++++++++++++++++++++++++++++-- include/linux/clk-provider.h | 3 ++ 2 files changed, 70 insertions(+), 2 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 4637697c139f..c57294563a98 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -43,6 +43,17 @@ static unsigned int _get_table_maxdiv(const struct clk_div_table *table) return maxdiv; } +static unsigned int _get_table_mindiv(const struct clk_div_table *table) +{ + unsigned int mindiv = UINT_MAX; + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->div < mindiv) + mindiv = clkt->div; + return mindiv; +} + static unsigned int _get_maxdiv(struct clk_divider *divider) { if (divider->flags & CLK_DIVIDER_ONE_BASED) @@ -162,6 +173,24 @@ static int _round_up_table(const struct clk_div_table *table, int div) return up; } +static int _round_down_table(const struct clk_div_table *table, int div) +{ + const struct clk_div_table *clkt; + int down = _get_table_mindiv(table); + + for (clkt = table; clkt->div; clkt++) { + if (clkt->div == div) + return clkt->div; + else if (clkt->div > div) + continue; + + if ((div - clkt->div) < (div - down)) + down = clkt->div; + } + + return down; +} + static int _div_round_up(struct clk_divider *divider, unsigned long parent_rate, unsigned long rate) { @@ -175,6 +204,42 @@ static int _div_round_up(struct clk_divider *divider, return div; } +static int _div_round_closest(struct clk_divider *divider, + unsigned long parent_rate, unsigned long rate) +{ + int up, down, div; + + up = down = div = DIV_ROUND_CLOSEST(parent_rate, rate); + + if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) { + up = __roundup_pow_of_two(div); + down = __rounddown_pow_of_two(div); + } else if (divider->table) { + up = _round_up_table(divider->table, div); + down = _round_down_table(divider->table, div); + } + + return (up - div) <= (div - down) ? up : down; +} + +static int _div_round(struct clk_divider *divider, unsigned long parent_rate, + unsigned long rate) +{ + if (divider->flags & CLK_DIVIDER_ROUND_CLOSEST) + return _div_round_closest(divider, parent_rate, rate); + + return _div_round_up(divider, parent_rate, rate); +} + +static bool _is_best_div(struct clk_divider *divider, + int rate, int now, int best) +{ + if (divider->flags & CLK_DIVIDER_ROUND_CLOSEST) + return abs(rate - now) < abs(rate - best); + + return now <= rate && now > best; +} + static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, unsigned long *best_parent_rate) { @@ -190,7 +255,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) { parent_rate = *best_parent_rate; - bestdiv = _div_round_up(divider, parent_rate, rate); + bestdiv = _div_round(divider, parent_rate, rate); bestdiv = bestdiv == 0 ? 1 : bestdiv; bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv; return bestdiv; @@ -217,7 +282,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 = DIV_ROUND_UP(parent_rate, i); - if (now <= rate && now > best) { + if (_is_best_div(divider, rate, now, best)) { bestdiv = i; best = now; *best_parent_rate = parent_rate; diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 511917416fb0..59e2eb58f555 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -312,6 +312,8 @@ struct clk_div_table { * of this register, and mask of divider bits are in higher 16-bit of this * register. While setting the divider bits, higher 16-bit should also be * updated to indicate changing divider bits. + * CLK_DIVIDER_ROUND_CLOSEST - Makes the best calculated divider to be rounded + * to the closest integer instead of the up one. */ struct clk_divider { struct clk_hw hw; @@ -327,6 +329,7 @@ struct clk_divider { #define CLK_DIVIDER_POWER_OF_TWO BIT(1) #define CLK_DIVIDER_ALLOW_ZERO BIT(2) #define CLK_DIVIDER_HIWORD_MASK BIT(3) +#define CLK_DIVIDER_ROUND_CLOSEST BIT(4) extern const struct clk_ops clk_divider_ops; struct clk *clk_register_divider(struct device *dev, const char *name, -- cgit v1.2.3 From 0e2de78ecdc842218a2b6c422bb4a8ff4d85d11f Mon Sep 17 00:00:00 2001 From: Maxime COQUELIN Date: Wed, 29 Jan 2014 17:24:08 +0100 Subject: clk: divider: Optimize clk_divider_bestdiv loop Currently, the for-loop used to try all the different dividers to find the one that best fit tries all the values from 1 to max_div, incrementing by one. In case of power-of-two, or table based divider, the loop isn't optimal. Instead of incrementing by one, this patch provides directly the next divider. Signed-off-by: Maxime Coquelin Signed-off-by: Mike Turquette --- drivers/clk/clk-divider.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'drivers/clk') diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index c57294563a98..b3c83966be18 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -240,6 +240,18 @@ static bool _is_best_div(struct clk_divider *divider, return now <= rate && now > best; } +static int _next_div(struct clk_divider *divider, int div) +{ + div++; + + if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) + return __roundup_pow_of_two(div); + if (divider->table) + return _round_up_table(divider->table, div); + + return div; +} + static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, unsigned long *best_parent_rate) { @@ -267,7 +279,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, */ maxdiv = min(ULONG_MAX / rate, maxdiv); - for (i = 1; i <= maxdiv; i++) { + for (i = 1; i <= maxdiv; i = _next_div(divider, i)) { if (!_is_valid_div(divider, i)) continue; if (rate * i == parent_rate_saved) { -- cgit v1.2.3 From 4bac65ca7d86b7e219138706e9f687753ccbfee9 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 21 Apr 2014 16:11:37 -0500 Subject: clk: bcm281xx: warn if ccu_wait_bit() fails Don't let a failure of ccu_wait_bit() go unnoticed. Signed-off-by: Alex Elder Signed-off-by: Mike Turquette --- drivers/clk/bcm/clk-kona.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/clk') diff --git a/drivers/clk/bcm/clk-kona.c b/drivers/clk/bcm/clk-kona.c index db11a87449f2..4b4e35aeb778 100644 --- a/drivers/clk/bcm/clk-kona.c +++ b/drivers/clk/bcm/clk-kona.c @@ -207,6 +207,9 @@ __ccu_wait_bit(struct ccu_data *ccu, u32 reg_offset, u32 bit, bool want) return true; udelay(1); } + pr_warn("%s: %s/0x%04x bit %u was never %s\n", __func__, + ccu->name, reg_offset, bit, want ? "set" : "clear"); + return false; } -- cgit v1.2.3 From e756325206ebf284d00410077ea43eb53de8e9bf Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 21 Apr 2014 16:11:38 -0500 Subject: clk: bcm281xx: use init_data.name for clock name Use the init_data.name field to hold the name of a Kona clock rather than duplicating it. Signed-off-by: Alex Elder Signed-off-by: Mike Turquette --- drivers/clk/bcm/clk-kona-setup.c | 6 +++--- drivers/clk/bcm/clk-kona.c | 17 ++++++++++------- drivers/clk/bcm/clk-kona.h | 3 +-- 3 files changed, 14 insertions(+), 12 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/bcm/clk-kona-setup.c b/drivers/clk/bcm/clk-kona-setup.c index 54a06526f64f..9e7a9c11c951 100644 --- a/drivers/clk/bcm/clk-kona-setup.c +++ b/drivers/clk/bcm/clk-kona-setup.c @@ -64,7 +64,7 @@ static bool peri_clk_data_offsets_valid(struct kona_clk *bcm_clk) BUG_ON(bcm_clk->type != bcm_clk_peri); peri = bcm_clk->u.peri; - name = bcm_clk->name; + name = bcm_clk->init_data.name; range = bcm_clk->ccu->range; limit = range - sizeof(u32); @@ -330,7 +330,7 @@ peri_clk_data_valid(struct kona_clk *bcm_clk) return false; peri = bcm_clk->u.peri; - name = bcm_clk->name; + name = bcm_clk->init_data.name; gate = &peri->gate; if (gate_exists(gate) && !gate_valid(gate, "gate", name)) return false; @@ -631,7 +631,7 @@ struct clk *kona_clk_setup(struct ccu_data *ccu, const char *name, return NULL; } bcm_clk->ccu = ccu; - bcm_clk->name = name; + bcm_clk->init_data.name = name; init_data = &bcm_clk->init_data; init_data->name = name; diff --git a/drivers/clk/bcm/clk-kona.c b/drivers/clk/bcm/clk-kona.c index 4b4e35aeb778..bf0b70e87014 100644 --- a/drivers/clk/bcm/clk-kona.c +++ b/drivers/clk/bcm/clk-kona.c @@ -809,7 +809,7 @@ static int kona_peri_clk_enable(struct clk_hw *hw) struct kona_clk *bcm_clk = to_kona_clk(hw); struct bcm_clk_gate *gate = &bcm_clk->u.peri->gate; - return clk_gate(bcm_clk->ccu, bcm_clk->name, gate, true); + return clk_gate(bcm_clk->ccu, bcm_clk->init_data.name, gate, true); } static void kona_peri_clk_disable(struct clk_hw *hw) @@ -817,7 +817,7 @@ static void kona_peri_clk_disable(struct clk_hw *hw) struct kona_clk *bcm_clk = to_kona_clk(hw); struct bcm_clk_gate *gate = &bcm_clk->u.peri->gate; - (void)clk_gate(bcm_clk->ccu, bcm_clk->name, gate, false); + (void)clk_gate(bcm_clk->ccu, bcm_clk->init_data.name, gate, false); } static int kona_peri_clk_is_enabled(struct clk_hw *hw) @@ -875,12 +875,13 @@ static int kona_peri_clk_set_parent(struct clk_hw *hw, u8 index) ret = selector_write(bcm_clk->ccu, &data->gate, sel, trig, index); if (ret == -ENXIO) { - pr_err("%s: gating failure for %s\n", __func__, bcm_clk->name); + pr_err("%s: gating failure for %s\n", __func__, + bcm_clk->init_data.name); ret = -EIO; /* Don't proliferate weird errors */ } else if (ret == -EIO) { pr_err("%s: %strigger failed for %s\n", __func__, trig == &data->pre_trig ? "pre-" : "", - bcm_clk->name); + bcm_clk->init_data.name); } return ret; @@ -939,10 +940,12 @@ static int kona_peri_clk_set_rate(struct clk_hw *hw, unsigned long rate, ret = divider_write(bcm_clk->ccu, &data->gate, &data->div, &data->trig, scaled_div); if (ret == -ENXIO) { - pr_err("%s: gating failure for %s\n", __func__, bcm_clk->name); + pr_err("%s: gating failure for %s\n", __func__, + bcm_clk->init_data.name); ret = -EIO; /* Don't proliferate weird errors */ } else if (ret == -EIO) { - pr_err("%s: trigger failed for %s\n", __func__, bcm_clk->name); + pr_err("%s: trigger failed for %s\n", __func__, + bcm_clk->init_data.name); } return ret; @@ -964,7 +967,7 @@ static bool __peri_clk_init(struct kona_clk *bcm_clk) { struct ccu_data *ccu = bcm_clk->ccu; struct peri_clk_data *peri = bcm_clk->u.peri; - const char *name = bcm_clk->name; + const char *name = bcm_clk->init_data.name; struct bcm_clk_trig *trig; BUG_ON(bcm_clk->type != bcm_clk_peri); diff --git a/drivers/clk/bcm/clk-kona.h b/drivers/clk/bcm/clk-kona.h index dee690951bb6..1a7eba4ac33a 100644 --- a/drivers/clk/bcm/clk-kona.h +++ b/drivers/clk/bcm/clk-kona.h @@ -373,8 +373,7 @@ struct peri_clk_data { struct kona_clk { struct clk_hw hw; - struct clk_init_data init_data; - const char *name; /* name of this clock */ + struct clk_init_data init_data; /* includes name of this clock */ struct ccu_data *ccu; /* ccu this clock is associated with */ enum bcm_clk_type type; union { -- cgit v1.2.3 From 9d3d87c750f30af0e8e268c122ffec4489bc9638 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 21 Apr 2014 16:11:39 -0500 Subject: clk: bcm281xx: change some symbol names As I developed the bcm281xx clock code I understood there were restrictions on device tree "compatible" strings names, and as a result "bcm11351" was used in places despite the part family being more properly called "bcm281xx". This can be a little confusing. In some cases I went to far and things using "bcm11351" when that was not necessary. This patch remedies this. It renames the symbol used to define the "compatible" string (but not its value) so it uses "BCM281XX". Similarly, the name names provided to the CLK_OF_DECLARE() macro are changed, hoping to minimize the number of places that the confusing "11351" string is used. Signed-off-by: Alex Elder Signed-off-by: Mike Turquette --- drivers/clk/bcm/clk-bcm281xx.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/bcm/clk-bcm281xx.c b/drivers/clk/bcm/clk-bcm281xx.c index 3c66de696aeb..bb947afadb88 100644 --- a/drivers/clk/bcm/clk-bcm281xx.c +++ b/drivers/clk/bcm/clk-bcm281xx.c @@ -15,12 +15,17 @@ #include "clk-kona.h" #include "dt-bindings/clock/bcm281xx.h" -/* bcm11351 CCU device tree "compatible" strings */ -#define BCM11351_DT_ROOT_CCU_COMPAT "brcm,bcm11351-root-ccu" -#define BCM11351_DT_AON_CCU_COMPAT "brcm,bcm11351-aon-ccu" -#define BCM11351_DT_HUB_CCU_COMPAT "brcm,bcm11351-hub-ccu" -#define BCM11351_DT_MASTER_CCU_COMPAT "brcm,bcm11351-master-ccu" -#define BCM11351_DT_SLAVE_CCU_COMPAT "brcm,bcm11351-slave-ccu" +/* + * These are the bcm281xx CCU device tree "compatible" strings. + * We're stuck with using "bcm11351" in the string because wild + * cards aren't allowed, and that name was the first one defined + * in this family of devices. + */ +#define BCM281XX_DT_ROOT_CCU_COMPAT "brcm,bcm11351-root-ccu" +#define BCM281XX_DT_AON_CCU_COMPAT "brcm,bcm11351-aon-ccu" +#define BCM281XX_DT_HUB_CCU_COMPAT "brcm,bcm11351-hub-ccu" +#define BCM281XX_DT_MASTER_CCU_COMPAT "brcm,bcm11351-master-ccu" +#define BCM281XX_DT_SLAVE_CCU_COMPAT "brcm,bcm11351-slave-ccu" /* Root CCU clocks */ @@ -404,13 +409,13 @@ static void __init kona_dt_slave_ccu_setup(struct device_node *node) kona_dt_ccu_setup(node, bcm281xx_slave_ccu_clks_setup); } -CLK_OF_DECLARE(bcm11351_root_ccu, BCM11351_DT_ROOT_CCU_COMPAT, +CLK_OF_DECLARE(bcm281xx_root_ccu, BCM281XX_DT_ROOT_CCU_COMPAT, kona_dt_root_ccu_setup); -CLK_OF_DECLARE(bcm11351_aon_ccu, BCM11351_DT_AON_CCU_COMPAT, +CLK_OF_DECLARE(bcm281xx_aon_ccu, BCM281XX_DT_AON_CCU_COMPAT, kona_dt_aon_ccu_setup); -CLK_OF_DECLARE(bcm11351_hub_ccu, BCM11351_DT_HUB_CCU_COMPAT, +CLK_OF_DECLARE(bcm281xx_hub_ccu, BCM281XX_DT_HUB_CCU_COMPAT, kona_dt_hub_ccu_setup); -CLK_OF_DECLARE(bcm11351_master_ccu, BCM11351_DT_MASTER_CCU_COMPAT, +CLK_OF_DECLARE(bcm281xx_master_ccu, BCM281XX_DT_MASTER_CCU_COMPAT, kona_dt_master_ccu_setup); -CLK_OF_DECLARE(bcm11351_slave_ccu, BCM11351_DT_SLAVE_CCU_COMPAT, +CLK_OF_DECLARE(bcm281xx_slave_ccu, BCM281XX_DT_SLAVE_CCU_COMPAT, kona_dt_slave_ccu_setup); -- cgit v1.2.3 From b12151ca5cd76e5ed9c75ef02b2f5d2aa5b45808 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 21 Apr 2014 16:11:40 -0500 Subject: clk: bcm281xx: initialize CCU structures statically We know up front how many CCU's we'll support, so there's no need to allocate their data structures dynamically. Define a macro KONA_CCU_COMMON() to simplify the initialization of many of the fields in a ccu_data structure. Pass the address of a statically defined CCU structure to kona_dt_ccu_setup() rather than having that function allocate one. We also know at build time how many clocks a given CCU will provide, though the number of of them for each CCU is different. Record the number of clocks we need in the CCU's clk_onecell_data struct (which is used when we register the CCU with the common clock code as a clock provider). Rename that struct field "clk_data" (because "data" alone gets a little confusing). Use the known clock count to move the allocation of each CCU's clocks array into ccu_clks_setup() rather than having each CCU's setup callback function do it. (The real motivation behind all of this is that we'll be doing some static initialization of some additional CCU-specific data soon.) Signed-off-by: Alex Elder Signed-off-by: Mike Turquette --- drivers/clk/bcm/clk-bcm281xx.c | 144 ++++++++++++++++----------------------- drivers/clk/bcm/clk-kona-setup.c | 47 ++++++------- drivers/clk/bcm/clk-kona.c | 4 +- drivers/clk/bcm/clk-kona.h | 21 ++++-- 4 files changed, 97 insertions(+), 119 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/bcm/clk-bcm281xx.c b/drivers/clk/bcm/clk-bcm281xx.c index bb947afadb88..d72f2ae01493 100644 --- a/drivers/clk/bcm/clk-bcm281xx.c +++ b/drivers/clk/bcm/clk-bcm281xx.c @@ -15,6 +15,9 @@ #include "clk-kona.h" #include "dt-bindings/clock/bcm281xx.h" +#define BCM281XX_CCU_COMMON(_name, _ucase_name) \ + KONA_CCU_COMMON(BCM281XX, _name, _ucase_name) + /* * These are the bcm281xx CCU device tree "compatible" strings. * We're stuck with using "bcm11351" in the string because wild @@ -27,7 +30,7 @@ #define BCM281XX_DT_MASTER_CCU_COMPAT "brcm,bcm11351-master-ccu" #define BCM281XX_DT_SLAVE_CCU_COMPAT "brcm,bcm11351-slave-ccu" -/* Root CCU clocks */ +/* Root CCU */ static struct peri_clk_data frac_1m_data = { .gate = HW_SW_GATE(0x214, 16, 0, 1), @@ -36,7 +39,11 @@ static struct peri_clk_data frac_1m_data = { .clocks = CLOCKS("ref_crystal"), }; -/* AON CCU clocks */ +static struct ccu_data root_ccu_data = { + BCM281XX_CCU_COMMON(root, ROOT), +}; + +/* AON CCU */ static struct peri_clk_data hub_timer_data = { .gate = HW_SW_GATE(0x0414, 16, 0, 1), @@ -65,7 +72,11 @@ static struct peri_clk_data pmu_bsc_var_data = { .trig = TRIGGER(0x0a40, 2), }; -/* Hub CCU clocks */ +static struct ccu_data aon_ccu_data = { + BCM281XX_CCU_COMMON(aon, AON), +}; + +/* Hub CCU */ static struct peri_clk_data tmon_1m_data = { .gate = HW_SW_GATE(0x04a4, 18, 2, 3), @@ -75,7 +86,11 @@ static struct peri_clk_data tmon_1m_data = { .trig = TRIGGER(0x0e84, 1), }; -/* Master CCU clocks */ +static struct ccu_data hub_ccu_data = { + BCM281XX_CCU_COMMON(hub, HUB), +}; + +/* Master CCU */ static struct peri_clk_data sdio1_data = { .gate = HW_SW_GATE(0x0358, 18, 2, 3), @@ -158,7 +173,11 @@ static struct peri_clk_data hsic2_12m_data = { .trig = TRIGGER(0x0afc, 5), }; -/* Slave CCU clocks */ +static struct ccu_data master_ccu_data = { + BCM281XX_CCU_COMMON(master, MASTER), +}; + +/* Slave CCU */ static struct peri_clk_data uartb_data = { .gate = HW_SW_GATE(0x0400, 18, 2, 3), @@ -266,6 +285,10 @@ static struct peri_clk_data pwm_data = { .trig = TRIGGER(0x0afc, 15), }; +static struct ccu_data slave_ccu_data = { + BCM281XX_CCU_COMMON(slave, SLAVE), +}; + /* * CCU setup routines * @@ -277,107 +300,52 @@ static struct peri_clk_data pwm_data = { */ static int __init bcm281xx_root_ccu_clks_setup(struct ccu_data *ccu) { - struct clk **clks; - size_t count = BCM281XX_ROOT_CCU_CLOCK_COUNT; - - clks = kzalloc(count * sizeof(*clks), GFP_KERNEL); - if (!clks) { - pr_err("%s: failed to allocate root clocks\n", __func__); - return -ENOMEM; - } - ccu->data.clks = clks; - ccu->data.clk_num = count; - - PERI_CLK_SETUP(clks, ccu, BCM281XX_ROOT_CCU_FRAC_1M, frac_1m); + PERI_CLK_SETUP(ccu, BCM281XX_ROOT_CCU_FRAC_1M, frac_1m); return 0; } static int __init bcm281xx_aon_ccu_clks_setup(struct ccu_data *ccu) { - struct clk **clks; - size_t count = BCM281XX_AON_CCU_CLOCK_COUNT; - - clks = kzalloc(count * sizeof(*clks), GFP_KERNEL); - if (!clks) { - pr_err("%s: failed to allocate aon clocks\n", __func__); - return -ENOMEM; - } - ccu->data.clks = clks; - ccu->data.clk_num = count; - - PERI_CLK_SETUP(clks, ccu, BCM281XX_AON_CCU_HUB_TIMER, hub_timer); - PERI_CLK_SETUP(clks, ccu, BCM281XX_AON_CCU_PMU_BSC, pmu_bsc); - PERI_CLK_SETUP(clks, ccu, BCM281XX_AON_CCU_PMU_BSC_VAR, pmu_bsc_var); + PERI_CLK_SETUP(ccu, BCM281XX_AON_CCU_HUB_TIMER, hub_timer); + PERI_CLK_SETUP(ccu, BCM281XX_AON_CCU_PMU_BSC, pmu_bsc); + PERI_CLK_SETUP(ccu, BCM281XX_AON_CCU_PMU_BSC_VAR, pmu_bsc_var); return 0; } static int __init bcm281xx_hub_ccu_clks_setup(struct ccu_data *ccu) { - struct clk **clks; - size_t count = BCM281XX_HUB_CCU_CLOCK_COUNT; - - clks = kzalloc(count * sizeof(*clks), GFP_KERNEL); - if (!clks) { - pr_err("%s: failed to allocate hub clocks\n", __func__); - return -ENOMEM; - } - ccu->data.clks = clks; - ccu->data.clk_num = count; - - PERI_CLK_SETUP(clks, ccu, BCM281XX_HUB_CCU_TMON_1M, tmon_1m); + PERI_CLK_SETUP(ccu, BCM281XX_HUB_CCU_TMON_1M, tmon_1m); return 0; } static int __init bcm281xx_master_ccu_clks_setup(struct ccu_data *ccu) { - struct clk **clks; - size_t count = BCM281XX_MASTER_CCU_CLOCK_COUNT; - - clks = kzalloc(count * sizeof(*clks), GFP_KERNEL); - if (!clks) { - pr_err("%s: failed to allocate master clocks\n", __func__); - return -ENOMEM; - } - ccu->data.clks = clks; - ccu->data.clk_num = count; - - PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_SDIO1, sdio1); - PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_SDIO2, sdio2); - PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_SDIO3, sdio3); - PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_SDIO4, sdio4); - PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_USB_IC, usb_ic); - PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_HSIC2_48M, hsic2_48m); - PERI_CLK_SETUP(clks, ccu, BCM281XX_MASTER_CCU_HSIC2_12M, hsic2_12m); + PERI_CLK_SETUP(ccu, BCM281XX_MASTER_CCU_SDIO1, sdio1); + PERI_CLK_SETUP(ccu, BCM281XX_MASTER_CCU_SDIO2, sdio2); + PERI_CLK_SETUP(ccu, BCM281XX_MASTER_CCU_SDIO3, sdio3); + PERI_CLK_SETUP(ccu, BCM281XX_MASTER_CCU_SDIO4, sdio4); + PERI_CLK_SETUP(ccu, BCM281XX_MASTER_CCU_USB_IC, usb_ic); + PERI_CLK_SETUP(ccu, BCM281XX_MASTER_CCU_HSIC2_48M, hsic2_48m); + PERI_CLK_SETUP(ccu, BCM281XX_MASTER_CCU_HSIC2_12M, hsic2_12m); return 0; } static int __init bcm281xx_slave_ccu_clks_setup(struct ccu_data *ccu) { - struct clk **clks; - size_t count = BCM281XX_SLAVE_CCU_CLOCK_COUNT; - - clks = kzalloc(count * sizeof(*clks), GFP_KERNEL); - if (!clks) { - pr_err("%s: failed to allocate slave clocks\n", __func__); - return -ENOMEM; - } - ccu->data.clks = clks; - ccu->data.clk_num = count; - - PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_UARTB, uartb); - PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_UARTB2, uartb2); - PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_UARTB3, uartb3); - PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_UARTB4, uartb4); - PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_SSP0, ssp0); - PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_SSP2, ssp2); - PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_BSC1, bsc1); - PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_BSC2, bsc2); - PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_BSC3, bsc3); - PERI_CLK_SETUP(clks, ccu, BCM281XX_SLAVE_CCU_PWM, pwm); + PERI_CLK_SETUP(ccu, BCM281XX_SLAVE_CCU_UARTB, uartb); + PERI_CLK_SETUP(ccu, BCM281XX_SLAVE_CCU_UARTB2, uartb2); + PERI_CLK_SETUP(ccu, BCM281XX_SLAVE_CCU_UARTB3, uartb3); + PERI_CLK_SETUP(ccu, BCM281XX_SLAVE_CCU_UARTB4, uartb4); + PERI_CLK_SETUP(ccu, BCM281XX_SLAVE_CCU_SSP0, ssp0); + PERI_CLK_SETUP(ccu, BCM281XX_SLAVE_CCU_SSP2, ssp2); + PERI_CLK_SETUP(ccu, BCM281XX_SLAVE_CCU_BSC1, bsc1); + PERI_CLK_SETUP(ccu, BCM281XX_SLAVE_CCU_BSC2, bsc2); + PERI_CLK_SETUP(ccu, BCM281XX_SLAVE_CCU_BSC3, bsc3); + PERI_CLK_SETUP(ccu, BCM281XX_SLAVE_CCU_PWM, pwm); return 0; } @@ -386,27 +354,29 @@ static int __init bcm281xx_slave_ccu_clks_setup(struct ccu_data *ccu) static void __init kona_dt_root_ccu_setup(struct device_node *node) { - kona_dt_ccu_setup(node, bcm281xx_root_ccu_clks_setup); + kona_dt_ccu_setup(&root_ccu_data, node, bcm281xx_root_ccu_clks_setup); } static void __init kona_dt_aon_ccu_setup(struct device_node *node) { - kona_dt_ccu_setup(node, bcm281xx_aon_ccu_clks_setup); + kona_dt_ccu_setup(&aon_ccu_data, node, bcm281xx_aon_ccu_clks_setup); } static void __init kona_dt_hub_ccu_setup(struct device_node *node) { - kona_dt_ccu_setup(node, bcm281xx_hub_ccu_clks_setup); + kona_dt_ccu_setup(&hub_ccu_data, node, bcm281xx_hub_ccu_clks_setup); } static void __init kona_dt_master_ccu_setup(struct device_node *node) { - kona_dt_ccu_setup(node, bcm281xx_master_ccu_clks_setup); + kona_dt_ccu_setup(&master_ccu_data, node, + bcm281xx_master_ccu_clks_setup); } static void __init kona_dt_slave_ccu_setup(struct device_node *node) { - kona_dt_ccu_setup(node, bcm281xx_slave_ccu_clks_setup); + kona_dt_ccu_setup(&slave_ccu_data, node, + bcm281xx_slave_ccu_clks_setup); } CLK_OF_DECLARE(bcm281xx_root_ccu, BCM281XX_DT_ROOT_CCU_COMPAT, diff --git a/drivers/clk/bcm/clk-kona-setup.c b/drivers/clk/bcm/clk-kona-setup.c index 9e7a9c11c951..4d1ca5372eff 100644 --- a/drivers/clk/bcm/clk-kona-setup.c +++ b/drivers/clk/bcm/clk-kona-setup.c @@ -675,50 +675,49 @@ static void ccu_clks_teardown(struct ccu_data *ccu) { u32 i; - for (i = 0; i < ccu->data.clk_num; i++) - kona_clk_teardown(ccu->data.clks[i]); - kfree(ccu->data.clks); + for (i = 0; i < ccu->clk_data.clk_num; i++) + kona_clk_teardown(ccu->clk_data.clks[i]); + kfree(ccu->clk_data.clks); } static void kona_ccu_teardown(struct ccu_data *ccu) { - if (!ccu) - return; - + kfree(ccu->clk_data.clks); + ccu->clk_data.clks = NULL; if (!ccu->base) - goto done; + return; of_clk_del_provider(ccu->node); /* safe if never added */ ccu_clks_teardown(ccu); list_del(&ccu->links); of_node_put(ccu->node); + ccu->node = NULL; iounmap(ccu->base); -done: - kfree(ccu->name); - kfree(ccu); + ccu->base = NULL; } /* * Set up a CCU. Call the provided ccu_clks_setup callback to * initialize the array of clocks provided by the CCU. */ -void __init kona_dt_ccu_setup(struct device_node *node, +void __init kona_dt_ccu_setup(struct ccu_data *ccu, + struct device_node *node, int (*ccu_clks_setup)(struct ccu_data *)) { - struct ccu_data *ccu; struct resource res = { 0 }; resource_size_t range; int ret; - ccu = kzalloc(sizeof(*ccu), GFP_KERNEL); - if (ccu) - ccu->name = kstrdup(node->name, GFP_KERNEL); - if (!ccu || !ccu->name) { - pr_err("%s: unable to allocate CCU struct for %s\n", - __func__, node->name); - kfree(ccu); + if (ccu->clk_data.clk_num) { + size_t size; - return; + size = ccu->clk_data.clk_num * sizeof(*ccu->clk_data.clks); + ccu->clk_data.clks = kzalloc(size, GFP_KERNEL); + if (!ccu->clk_data.clks) { + pr_err("%s: unable to allocate %u clocks for %s\n", + __func__, ccu->clk_data.clk_num, node->name); + return; + } } ret = of_address_to_resource(node, 0, &res); @@ -742,18 +741,14 @@ void __init kona_dt_ccu_setup(struct device_node *node, node->name); goto out_err; } - - spin_lock_init(&ccu->lock); - INIT_LIST_HEAD(&ccu->links); ccu->node = of_node_get(node); - list_add_tail(&ccu->links, &ccu_list); - /* Set up clocks array (in ccu->data) */ + /* Set up clocks array (in ccu->clk_data) */ if (ccu_clks_setup(ccu)) goto out_err; - ret = of_clk_add_provider(node, of_clk_src_onecell_get, &ccu->data); + ret = of_clk_add_provider(node, of_clk_src_onecell_get, &ccu->clk_data); if (ret) { pr_err("%s: error adding ccu %s as provider (%d)\n", __func__, node->name, ret); diff --git a/drivers/clk/bcm/clk-kona.c b/drivers/clk/bcm/clk-kona.c index bf0b70e87014..f8bc6bc0784f 100644 --- a/drivers/clk/bcm/clk-kona.c +++ b/drivers/clk/bcm/clk-kona.c @@ -1020,13 +1020,13 @@ bool __init kona_ccu_init(struct ccu_data *ccu) { unsigned long flags; unsigned int which; - struct clk **clks = ccu->data.clks; + struct clk **clks = ccu->clk_data.clks; bool success = true; flags = ccu_lock(ccu); __ccu_write_enable(ccu); - for (which = 0; which < ccu->data.clk_num; which++) { + for (which = 0; which < ccu->clk_data.clk_num; which++) { struct kona_clk *bcm_clk; if (!clks[which]) diff --git a/drivers/clk/bcm/clk-kona.h b/drivers/clk/bcm/clk-kona.h index 1a7eba4ac33a..108c2647ca28 100644 --- a/drivers/clk/bcm/clk-kona.h +++ b/drivers/clk/bcm/clk-kona.h @@ -85,11 +85,20 @@ struct ccu_data { bool write_enabled; /* write access is currently enabled */ struct list_head links; /* for ccu_list */ struct device_node *node; - struct clk_onecell_data data; + struct clk_onecell_data clk_data; const char *name; u32 range; /* byte range of address space */ }; +/* Initialization for common fields in a Kona ccu_data structure */ +#define KONA_CCU_COMMON(_prefix, _name, _ucase_name) \ + .name = #_name "_ccu", \ + .lock = __SPIN_LOCK_UNLOCKED(_name ## _ccu_data.lock), \ + .links = LIST_HEAD_INIT(_name ## _ccu_data.links), \ + .clk_data = { \ + .clk_num = _prefix ## _ ## _ucase_name ## _CCU_CLOCK_COUNT, \ + } + /* * Gating control and status is managed by a 32-bit gate register. * @@ -390,8 +399,11 @@ extern struct clk_ops kona_peri_clk_ops; /* Help functions */ -#define PERI_CLK_SETUP(clks, ccu, id, name) \ - clks[id] = kona_clk_setup(ccu, #name, bcm_clk_peri, &name ## _data) +#define KONA_CLK_SETUP(_ccu, _type, _name) \ + kona_clk_setup((_ccu), #_name, bcm_clk_## _type, &_name ## _data) + +#define PERI_CLK_SETUP(_ccu, _id, _name) \ + (_ccu)->clk_data.clks[_id] = KONA_CLK_SETUP((_ccu), peri, _name) /* Externally visible functions */ @@ -402,7 +414,8 @@ extern u64 scaled_div_build(struct bcm_clk_div *div, u32 div_value, extern struct clk *kona_clk_setup(struct ccu_data *ccu, const char *name, enum bcm_clk_type type, void *data); -extern void __init kona_dt_ccu_setup(struct device_node *node, +extern void __init kona_dt_ccu_setup(struct ccu_data *ccu, + struct device_node *node, int (*ccu_clks_setup)(struct ccu_data *)); extern bool __init kona_ccu_init(struct ccu_data *ccu); -- cgit v1.2.3 From 03548ec06ad3ec75d5b212fa832e4e617334ea09 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 21 Apr 2014 16:11:41 -0500 Subject: clk: bcm281xx: define CCU clock data statically Rather than "manually" setting up each CCU's clock entries at run time, define a flexible array of generic Kona clock structures within the CCU structure itself. Each of these entries contains generic kona clock information (like its CCU pointer and clock framework initialization data). Each also has a pointer to a structure contianing clock type-dependent initialization data (like register definitions). Since we'll iterate over these arrays we need to be sure they have slots for all potential clock index values. (E.g. for the root CCU we must have at least BCM281XX_ROOT_CCU_CLOCK_COUNT slots.) To ensure this we always define an extra entry and fill it using the special initializer LAST_KONA_CLK. Just about everything we need to know about a clock can be defined statically. As a result, kona_clk_setup() can be changed to take just a kona_clk structure as its argument, and peri_clk_setup() can be simplified. With the information pre-defined we are also able to handle most clock setup genericially. We can do away with the CCU-specific callback functions that previously were needed to set up the entries in CCU's clock array. Move the definition of the ccu_data structure down in "clk-kona.h" to avoid a forward dependency. Signed-off-by: Alex Elder Signed-off-by: Mike Turquette --- drivers/clk/bcm/clk-bcm281xx.c | 132 +++++++++++++++++++-------------------- drivers/clk/bcm/clk-kona-setup.c | 60 ++++++++---------- drivers/clk/bcm/clk-kona.h | 84 +++++++++++++------------ 3 files changed, 133 insertions(+), 143 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/bcm/clk-bcm281xx.c b/drivers/clk/bcm/clk-bcm281xx.c index d72f2ae01493..71a65a49f1a4 100644 --- a/drivers/clk/bcm/clk-bcm281xx.c +++ b/drivers/clk/bcm/clk-bcm281xx.c @@ -41,6 +41,11 @@ static struct peri_clk_data frac_1m_data = { static struct ccu_data root_ccu_data = { BCM281XX_CCU_COMMON(root, ROOT), + .kona_clks = { + [BCM281XX_ROOT_CCU_FRAC_1M] = + KONA_CLK(root, frac_1m, peri), + [BCM281XX_ROOT_CCU_CLOCK_COUNT] = LAST_KONA_CLK, + }, }; /* AON CCU */ @@ -74,6 +79,15 @@ static struct peri_clk_data pmu_bsc_var_data = { static struct ccu_data aon_ccu_data = { BCM281XX_CCU_COMMON(aon, AON), + .kona_clks = { + [BCM281XX_AON_CCU_HUB_TIMER] = + KONA_CLK(aon, hub_timer, peri), + [BCM281XX_AON_CCU_PMU_BSC] = + KONA_CLK(aon, pmu_bsc, peri), + [BCM281XX_AON_CCU_PMU_BSC_VAR] = + KONA_CLK(aon, pmu_bsc_var, peri), + [BCM281XX_AON_CCU_CLOCK_COUNT] = LAST_KONA_CLK, + }, }; /* Hub CCU */ @@ -88,6 +102,11 @@ static struct peri_clk_data tmon_1m_data = { static struct ccu_data hub_ccu_data = { BCM281XX_CCU_COMMON(hub, HUB), + .kona_clks = { + [BCM281XX_HUB_CCU_TMON_1M] = + KONA_CLK(hub, tmon_1m, peri), + [BCM281XX_HUB_CCU_CLOCK_COUNT] = LAST_KONA_CLK, + }, }; /* Master CCU */ @@ -175,6 +194,23 @@ static struct peri_clk_data hsic2_12m_data = { static struct ccu_data master_ccu_data = { BCM281XX_CCU_COMMON(master, MASTER), + .kona_clks = { + [BCM281XX_MASTER_CCU_SDIO1] = + KONA_CLK(master, sdio1, peri), + [BCM281XX_MASTER_CCU_SDIO2] = + KONA_CLK(master, sdio2, peri), + [BCM281XX_MASTER_CCU_SDIO3] = + KONA_CLK(master, sdio3, peri), + [BCM281XX_MASTER_CCU_SDIO4] = + KONA_CLK(master, sdio4, peri), + [BCM281XX_MASTER_CCU_USB_IC] = + KONA_CLK(master, usb_ic, peri), + [BCM281XX_MASTER_CCU_HSIC2_48M] = + KONA_CLK(master, hsic2_48m, peri), + [BCM281XX_MASTER_CCU_HSIC2_12M] = + KONA_CLK(master, hsic2_12m, peri), + [BCM281XX_MASTER_CCU_CLOCK_COUNT] = LAST_KONA_CLK, + }, }; /* Slave CCU */ @@ -287,96 +323,56 @@ static struct peri_clk_data pwm_data = { static struct ccu_data slave_ccu_data = { BCM281XX_CCU_COMMON(slave, SLAVE), + .kona_clks = { + [BCM281XX_SLAVE_CCU_UARTB] = + KONA_CLK(slave, uartb, peri), + [BCM281XX_SLAVE_CCU_UARTB2] = + KONA_CLK(slave, uartb2, peri), + [BCM281XX_SLAVE_CCU_UARTB3] = + KONA_CLK(slave, uartb3, peri), + [BCM281XX_SLAVE_CCU_UARTB4] = + KONA_CLK(slave, uartb4, peri), + [BCM281XX_SLAVE_CCU_SSP0] = + KONA_CLK(slave, ssp0, peri), + [BCM281XX_SLAVE_CCU_SSP2] = + KONA_CLK(slave, ssp2, peri), + [BCM281XX_SLAVE_CCU_BSC1] = + KONA_CLK(slave, bsc1, peri), + [BCM281XX_SLAVE_CCU_BSC2] = + KONA_CLK(slave, bsc2, peri), + [BCM281XX_SLAVE_CCU_BSC3] = + KONA_CLK(slave, bsc3, peri), + [BCM281XX_SLAVE_CCU_PWM] = + KONA_CLK(slave, pwm, peri), + [BCM281XX_SLAVE_CCU_CLOCK_COUNT] = LAST_KONA_CLK, + }, }; -/* - * CCU setup routines - * - * These are called from kona_dt_ccu_setup() to initialize the array - * of clocks provided by the CCU. Once allocated, the entries in - * the array are initialized by calling kona_clk_setup() with the - * initialization data for each clock. They return 0 if successful - * or an error code otherwise. - */ -static int __init bcm281xx_root_ccu_clks_setup(struct ccu_data *ccu) -{ - PERI_CLK_SETUP(ccu, BCM281XX_ROOT_CCU_FRAC_1M, frac_1m); - - return 0; -} - -static int __init bcm281xx_aon_ccu_clks_setup(struct ccu_data *ccu) -{ - PERI_CLK_SETUP(ccu, BCM281XX_AON_CCU_HUB_TIMER, hub_timer); - PERI_CLK_SETUP(ccu, BCM281XX_AON_CCU_PMU_BSC, pmu_bsc); - PERI_CLK_SETUP(ccu, BCM281XX_AON_CCU_PMU_BSC_VAR, pmu_bsc_var); - - return 0; -} - -static int __init bcm281xx_hub_ccu_clks_setup(struct ccu_data *ccu) -{ - PERI_CLK_SETUP(ccu, BCM281XX_HUB_CCU_TMON_1M, tmon_1m); - - return 0; -} - -static int __init bcm281xx_master_ccu_clks_setup(struct ccu_data *ccu) -{ - PERI_CLK_SETUP(ccu, BCM281XX_MASTER_CCU_SDIO1, sdio1); - PERI_CLK_SETUP(ccu, BCM281XX_MASTER_CCU_SDIO2, sdio2); - PERI_CLK_SETUP(ccu, BCM281XX_MASTER_CCU_SDIO3, sdio3); - PERI_CLK_SETUP(ccu, BCM281XX_MASTER_CCU_SDIO4, sdio4); - PERI_CLK_SETUP(ccu, BCM281XX_MASTER_CCU_USB_IC, usb_ic); - PERI_CLK_SETUP(ccu, BCM281XX_MASTER_CCU_HSIC2_48M, hsic2_48m); - PERI_CLK_SETUP(ccu, BCM281XX_MASTER_CCU_HSIC2_12M, hsic2_12m); - - return 0; -} - -static int __init bcm281xx_slave_ccu_clks_setup(struct ccu_data *ccu) -{ - PERI_CLK_SETUP(ccu, BCM281XX_SLAVE_CCU_UARTB, uartb); - PERI_CLK_SETUP(ccu, BCM281XX_SLAVE_CCU_UARTB2, uartb2); - PERI_CLK_SETUP(ccu, BCM281XX_SLAVE_CCU_UARTB3, uartb3); - PERI_CLK_SETUP(ccu, BCM281XX_SLAVE_CCU_UARTB4, uartb4); - PERI_CLK_SETUP(ccu, BCM281XX_SLAVE_CCU_SSP0, ssp0); - PERI_CLK_SETUP(ccu, BCM281XX_SLAVE_CCU_SSP2, ssp2); - PERI_CLK_SETUP(ccu, BCM281XX_SLAVE_CCU_BSC1, bsc1); - PERI_CLK_SETUP(ccu, BCM281XX_SLAVE_CCU_BSC2, bsc2); - PERI_CLK_SETUP(ccu, BCM281XX_SLAVE_CCU_BSC3, bsc3); - PERI_CLK_SETUP(ccu, BCM281XX_SLAVE_CCU_PWM, pwm); - - return 0; -} - /* Device tree match table callback functions */ static void __init kona_dt_root_ccu_setup(struct device_node *node) { - kona_dt_ccu_setup(&root_ccu_data, node, bcm281xx_root_ccu_clks_setup); + kona_dt_ccu_setup(&root_ccu_data, node); } static void __init kona_dt_aon_ccu_setup(struct device_node *node) { - kona_dt_ccu_setup(&aon_ccu_data, node, bcm281xx_aon_ccu_clks_setup); + kona_dt_ccu_setup(&aon_ccu_data, node); } static void __init kona_dt_hub_ccu_setup(struct device_node *node) { - kona_dt_ccu_setup(&hub_ccu_data, node, bcm281xx_hub_ccu_clks_setup); + kona_dt_ccu_setup(&hub_ccu_data, node); } static void __init kona_dt_master_ccu_setup(struct device_node *node) { - kona_dt_ccu_setup(&master_ccu_data, node, - bcm281xx_master_ccu_clks_setup); + kona_dt_ccu_setup(&master_ccu_data, node); } static void __init kona_dt_slave_ccu_setup(struct device_node *node) { - kona_dt_ccu_setup(&slave_ccu_data, node, - bcm281xx_slave_ccu_clks_setup); + kona_dt_ccu_setup(&slave_ccu_data, node); } CLK_OF_DECLARE(bcm281xx_root_ccu, BCM281XX_DT_ROOT_CCU_COMPAT, diff --git a/drivers/clk/bcm/clk-kona-setup.c b/drivers/clk/bcm/clk-kona-setup.c index 4d1ca5372eff..825a2f2ab052 100644 --- a/drivers/clk/bcm/clk-kona-setup.c +++ b/drivers/clk/bcm/clk-kona-setup.c @@ -567,7 +567,6 @@ static void peri_clk_teardown(struct peri_clk_data *data, struct clk_init_data *init_data) { clk_sel_teardown(&data->sel, init_data); - init_data->ops = NULL; } /* @@ -576,10 +575,9 @@ static void peri_clk_teardown(struct peri_clk_data *data, * that can be assigned if the clock has one or more parent clocks * associated with it. */ -static int peri_clk_setup(struct ccu_data *ccu, struct peri_clk_data *data, - struct clk_init_data *init_data) +static int +peri_clk_setup(struct peri_clk_data *data, struct clk_init_data *init_data) { - init_data->ops = &kona_peri_clk_ops; init_data->flags = CLK_IGNORE_UNUSED; return clk_sel_setup(data->clocks, &data->sel, init_data); @@ -617,39 +615,26 @@ static void kona_clk_teardown(struct clk *clk) bcm_clk_teardown(bcm_clk); } -struct clk *kona_clk_setup(struct ccu_data *ccu, const char *name, - enum bcm_clk_type type, void *data) +struct clk *kona_clk_setup(struct kona_clk *bcm_clk) { - struct kona_clk *bcm_clk; - struct clk_init_data *init_data; + struct clk_init_data *init_data = &bcm_clk->init_data; struct clk *clk = NULL; - bcm_clk = kzalloc(sizeof(*bcm_clk), GFP_KERNEL); - if (!bcm_clk) { - pr_err("%s: failed to allocate bcm_clk for %s\n", __func__, - name); - return NULL; - } - bcm_clk->ccu = ccu; - bcm_clk->init_data.name = name; - - init_data = &bcm_clk->init_data; - init_data->name = name; - switch (type) { + switch (bcm_clk->type) { case bcm_clk_peri: - if (peri_clk_setup(ccu, data, init_data)) - goto out_free; + if (peri_clk_setup(bcm_clk->u.data, init_data)) + return NULL; break; default: - data = NULL; - break; + pr_err("%s: clock type %d invalid for %s\n", __func__, + (int)bcm_clk->type, init_data->name); + return NULL; } - bcm_clk->type = type; - bcm_clk->u.data = data; /* Make sure everything makes sense before we set it up */ if (!kona_clk_valid(bcm_clk)) { - pr_err("%s: clock data invalid for %s\n", __func__, name); + pr_err("%s: clock data invalid for %s\n", __func__, + init_data->name); goto out_teardown; } @@ -657,7 +642,7 @@ struct clk *kona_clk_setup(struct ccu_data *ccu, const char *name, clk = clk_register(NULL, &bcm_clk->hw); if (IS_ERR(clk)) { pr_err("%s: error registering clock %s (%ld)\n", __func__, - name, PTR_ERR(clk)); + init_data->name, PTR_ERR(clk)); goto out_teardown; } BUG_ON(!clk); @@ -665,8 +650,6 @@ struct clk *kona_clk_setup(struct ccu_data *ccu, const char *name, return clk; out_teardown: bcm_clk_teardown(bcm_clk); -out_free: - kfree(bcm_clk); return NULL; } @@ -701,11 +684,11 @@ static void kona_ccu_teardown(struct ccu_data *ccu) * initialize the array of clocks provided by the CCU. */ void __init kona_dt_ccu_setup(struct ccu_data *ccu, - struct device_node *node, - int (*ccu_clks_setup)(struct ccu_data *)) + struct device_node *node) { struct resource res = { 0 }; resource_size_t range; + unsigned int i; int ret; if (ccu->clk_data.clk_num) { @@ -744,9 +727,16 @@ void __init kona_dt_ccu_setup(struct ccu_data *ccu, ccu->node = of_node_get(node); list_add_tail(&ccu->links, &ccu_list); - /* Set up clocks array (in ccu->clk_data) */ - if (ccu_clks_setup(ccu)) - goto out_err; + /* + * Set up each defined kona clock and save the result in + * the clock framework clock array (in ccu->data). Then + * register as a provider for these clocks. + */ + for (i = 0; i < ccu->clk_data.clk_num; i++) { + if (!ccu->kona_clks[i].ccu) + continue; + ccu->clk_data.clks[i] = kona_clk_setup(&ccu->kona_clks[i]); + } ret = of_clk_add_provider(node, of_clk_src_onecell_get, &ccu->clk_data); if (ret) { diff --git a/drivers/clk/bcm/clk-kona.h b/drivers/clk/bcm/clk-kona.h index 108c2647ca28..05d74779a564 100644 --- a/drivers/clk/bcm/clk-kona.h +++ b/drivers/clk/bcm/clk-kona.h @@ -70,35 +70,6 @@ enum bcm_clk_type { bcm_clk_peri }; -/* - * Each CCU defines a mapped area of memory containing registers - * used to manage clocks implemented by the CCU. Access to memory - * within the CCU's space is serialized by a spinlock. Before any - * (other) address can be written, a special access "password" value - * must be written to its WR_ACCESS register (located at the base - * address of the range). We keep track of the name of each CCU as - * it is set up, and maintain them in a list. - */ -struct ccu_data { - void __iomem *base; /* base of mapped address space */ - spinlock_t lock; /* serialization lock */ - bool write_enabled; /* write access is currently enabled */ - struct list_head links; /* for ccu_list */ - struct device_node *node; - struct clk_onecell_data clk_data; - const char *name; - u32 range; /* byte range of address space */ -}; - -/* Initialization for common fields in a Kona ccu_data structure */ -#define KONA_CCU_COMMON(_prefix, _name, _ucase_name) \ - .name = #_name "_ccu", \ - .lock = __SPIN_LOCK_UNLOCKED(_name ## _ccu_data.lock), \ - .links = LIST_HEAD_INIT(_name ## _ccu_data.links), \ - .clk_data = { \ - .clk_num = _prefix ## _ ## _ucase_name ## _CCU_CLOCK_COUNT, \ - } - /* * Gating control and status is managed by a 32-bit gate register. * @@ -393,17 +364,52 @@ struct kona_clk { #define to_kona_clk(_hw) \ container_of(_hw, struct kona_clk, hw) -/* Exported globals */ +/* Initialization macro for an entry in a CCU's kona_clks[] array. */ +#define KONA_CLK(_ccu_name, _clk_name, _type) \ + { \ + .init_data = { \ + .name = #_clk_name, \ + .ops = &kona_ ## _type ## _clk_ops, \ + }, \ + .ccu = &_ccu_name ## _ccu_data, \ + .type = bcm_clk_ ## _type, \ + .u.data = &_clk_name ## _data, \ + } +#define LAST_KONA_CLK { .type = bcm_clk_none } -extern struct clk_ops kona_peri_clk_ops; +/* + * Each CCU defines a mapped area of memory containing registers + * used to manage clocks implemented by the CCU. Access to memory + * within the CCU's space is serialized by a spinlock. Before any + * (other) address can be written, a special access "password" value + * must be written to its WR_ACCESS register (located at the base + * address of the range). We keep track of the name of each CCU as + * it is set up, and maintain them in a list. + */ +struct ccu_data { + void __iomem *base; /* base of mapped address space */ + spinlock_t lock; /* serialization lock */ + bool write_enabled; /* write access is currently enabled */ + struct list_head links; /* for ccu_list */ + struct device_node *node; + struct clk_onecell_data clk_data; + const char *name; + u32 range; /* byte range of address space */ + struct kona_clk kona_clks[]; /* must be last */ +}; -/* Help functions */ +/* Initialization for common fields in a Kona ccu_data structure */ +#define KONA_CCU_COMMON(_prefix, _name, _ccuname) \ + .name = #_name "_ccu", \ + .lock = __SPIN_LOCK_UNLOCKED(_name ## _ccu_data.lock), \ + .links = LIST_HEAD_INIT(_name ## _ccu_data.links), \ + .clk_data = { \ + .clk_num = _prefix ## _ ## _ccuname ## _CCU_CLOCK_COUNT, \ + } -#define KONA_CLK_SETUP(_ccu, _type, _name) \ - kona_clk_setup((_ccu), #_name, bcm_clk_## _type, &_name ## _data) +/* Exported globals */ -#define PERI_CLK_SETUP(_ccu, _id, _name) \ - (_ccu)->clk_data.clks[_id] = KONA_CLK_SETUP((_ccu), peri, _name) +extern struct clk_ops kona_peri_clk_ops; /* Externally visible functions */ @@ -412,11 +418,9 @@ extern u64 scaled_div_max(struct bcm_clk_div *div); extern u64 scaled_div_build(struct bcm_clk_div *div, u32 div_value, u32 billionths); -extern struct clk *kona_clk_setup(struct ccu_data *ccu, const char *name, - enum bcm_clk_type type, void *data); +extern struct clk *kona_clk_setup(struct kona_clk *bcm_clk); extern void __init kona_dt_ccu_setup(struct ccu_data *ccu, - struct device_node *node, - int (*ccu_clks_setup)(struct ccu_data *)); + struct device_node *node); extern bool __init kona_ccu_init(struct ccu_data *ccu); #endif /* _CLK_KONA_H */ -- cgit v1.2.3 From a597faccc7eedd406313e880ed05ff75bc522910 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 21 Apr 2014 16:11:42 -0500 Subject: clk: bcm281xx: add clock policy support Add support for CCU policy engine control, and also for setting the mask bits for bus clocks that require a policy change to get activated. This includes adding validity checking framework for CCUs, to validate the policy fields if defined. Signed-off-by: Alex Elder Signed-off-by: Mike Turquette --- drivers/clk/bcm/clk-kona-setup.c | 92 +++++++++++++++++++++++ drivers/clk/bcm/clk-kona.c | 155 +++++++++++++++++++++++++++++++++++++++ drivers/clk/bcm/clk-kona.h | 71 ++++++++++++++++++ 3 files changed, 318 insertions(+) (limited to 'drivers/clk') diff --git a/drivers/clk/bcm/clk-kona-setup.c b/drivers/clk/bcm/clk-kona-setup.c index 825a2f2ab052..773ad7c7dd59 100644 --- a/drivers/clk/bcm/clk-kona-setup.c +++ b/drivers/clk/bcm/clk-kona-setup.c @@ -25,6 +25,31 @@ LIST_HEAD(ccu_list); /* The list of set up CCUs */ /* Validity checking */ +static bool ccu_data_offsets_valid(struct ccu_data *ccu) +{ + struct ccu_policy *ccu_policy = &ccu->policy; + u32 limit; + + limit = ccu->range - sizeof(u32); + limit = round_down(limit, sizeof(u32)); + if (ccu_policy_exists(ccu_policy)) { + if (ccu_policy->enable.offset > limit) { + pr_err("%s: bad policy enable offset for %s " + "(%u > %u)\n", __func__, + ccu->name, ccu_policy->enable.offset, limit); + return false; + } + if (ccu_policy->control.offset > limit) { + pr_err("%s: bad policy control offset for %s " + "(%u > %u)\n", __func__, + ccu->name, ccu_policy->control.offset, limit); + return false; + } + } + + return true; +} + static bool clk_requires_trigger(struct kona_clk *bcm_clk) { struct peri_clk_data *peri = bcm_clk->u.peri; @@ -54,6 +79,7 @@ static bool clk_requires_trigger(struct kona_clk *bcm_clk) static bool peri_clk_data_offsets_valid(struct kona_clk *bcm_clk) { struct peri_clk_data *peri; + struct bcm_clk_policy *policy; struct bcm_clk_gate *gate; struct bcm_clk_div *div; struct bcm_clk_sel *sel; @@ -70,6 +96,15 @@ static bool peri_clk_data_offsets_valid(struct kona_clk *bcm_clk) limit = range - sizeof(u32); limit = round_down(limit, sizeof(u32)); + policy = &peri->policy; + if (policy_exists(policy)) { + if (policy->offset > limit) { + pr_err("%s: bad policy offset for %s (%u > %u)\n", + __func__, name, policy->offset, limit); + return false; + } + } + gate = &peri->gate; if (gate_exists(gate)) { if (gate->offset > limit) { @@ -167,6 +202,36 @@ static bool bitfield_valid(u32 shift, u32 width, const char *field_name, return true; } +static bool +ccu_policy_valid(struct ccu_policy *ccu_policy, const char *ccu_name) +{ + struct bcm_lvm_en *enable = &ccu_policy->enable; + struct bcm_policy_ctl *control; + + if (!bit_posn_valid(enable->bit, "policy enable", ccu_name)) + return false; + + control = &ccu_policy->control; + if (!bit_posn_valid(control->go_bit, "policy control GO", ccu_name)) + return false; + + if (!bit_posn_valid(control->atl_bit, "policy control ATL", ccu_name)) + return false; + + if (!bit_posn_valid(control->ac_bit, "policy control AC", ccu_name)) + return false; + + return true; +} + +static bool policy_valid(struct bcm_clk_policy *policy, const char *clock_name) +{ + if (!bit_posn_valid(policy->bit, "policy", clock_name)) + return false; + + return true; +} + /* * All gates, if defined, have a status bit, and for hardware-only * gates, that's it. Gates that can be software controlled also @@ -312,6 +377,7 @@ static bool peri_clk_data_valid(struct kona_clk *bcm_clk) { struct peri_clk_data *peri; + struct bcm_clk_policy *policy; struct bcm_clk_gate *gate; struct bcm_clk_sel *sel; struct bcm_clk_div *div; @@ -331,6 +397,11 @@ peri_clk_data_valid(struct kona_clk *bcm_clk) peri = bcm_clk->u.peri; name = bcm_clk->init_data.name; + + policy = &peri->policy; + if (policy_exists(policy) && !policy_valid(policy, name)) + return false; + gate = &peri->gate; if (gate_exists(gate) && !gate_valid(gate, "gate", name)) return false; @@ -679,6 +750,21 @@ static void kona_ccu_teardown(struct ccu_data *ccu) ccu->base = NULL; } +static bool ccu_data_valid(struct ccu_data *ccu) +{ + struct ccu_policy *ccu_policy; + + if (!ccu_data_offsets_valid(ccu)) + return false; + + ccu_policy = &ccu->policy; + if (ccu_policy_exists(ccu_policy)) + if (!ccu_policy_valid(ccu_policy, ccu->name)) + return false; + + return true; +} + /* * Set up a CCU. Call the provided ccu_clks_setup callback to * initialize the array of clocks provided by the CCU. @@ -718,6 +804,12 @@ void __init kona_dt_ccu_setup(struct ccu_data *ccu, } ccu->range = (u32)range; + + if (!ccu_data_valid(ccu)) { + pr_err("%s: ccu data not valid for %s\n", __func__, node->name); + goto out_err; + } + ccu->base = ioremap(res.start, ccu->range); if (!ccu->base) { pr_err("%s: unable to map CCU registers for %s\n", __func__, diff --git a/drivers/clk/bcm/clk-kona.c b/drivers/clk/bcm/clk-kona.c index f8bc6bc0784f..7d90c34d1336 100644 --- a/drivers/clk/bcm/clk-kona.c +++ b/drivers/clk/bcm/clk-kona.c @@ -16,6 +16,14 @@ #include +/* + * "Policies" affect the frequencies of bus clocks provided by a + * CCU. (I believe these polices are named "Deep Sleep", "Economy", + * "Normal", and "Turbo".) A lower policy number has lower power + * consumption, and policy 2 is the default. + */ +#define CCU_POLICY_COUNT 4 + #define CCU_ACCESS_PASSWORD 0xA5A500 #define CLK_GATE_DELAY_LOOP 2000 @@ -213,6 +221,148 @@ __ccu_wait_bit(struct ccu_data *ccu, u32 reg_offset, u32 bit, bool want) return false; } +/* Policy operations */ + +static bool __ccu_policy_engine_start(struct ccu_data *ccu, bool sync) +{ + struct bcm_policy_ctl *control = &ccu->policy.control; + u32 offset; + u32 go_bit; + u32 mask; + bool ret; + + /* If we don't need to control policy for this CCU, we're done. */ + if (!policy_ctl_exists(control)) + return true; + + offset = control->offset; + go_bit = control->go_bit; + + /* Ensure we're not busy before we start */ + ret = __ccu_wait_bit(ccu, offset, go_bit, false); + if (!ret) { + pr_err("%s: ccu %s policy engine wouldn't go idle\n", + __func__, ccu->name); + return false; + } + + /* + * If it's a synchronous request, we'll wait for the voltage + * and frequency of the active load to stabilize before + * returning. To do this we select the active load by + * setting the ATL bit. + * + * An asynchronous request instead ramps the voltage in the + * background, and when that process stabilizes, the target + * load is copied to the active load and the CCU frequency + * is switched. We do this by selecting the target load + * (ATL bit clear) and setting the request auto-copy (AC bit + * set). + * + * Note, we do NOT read-modify-write this register. + */ + mask = (u32)1 << go_bit; + if (sync) + mask |= 1 << control->atl_bit; + else + mask |= 1 << control->ac_bit; + __ccu_write(ccu, offset, mask); + + /* Wait for indication that operation is complete. */ + ret = __ccu_wait_bit(ccu, offset, go_bit, false); + if (!ret) + pr_err("%s: ccu %s policy engine never started\n", + __func__, ccu->name); + + return ret; +} + +static bool __ccu_policy_engine_stop(struct ccu_data *ccu) +{ + struct bcm_lvm_en *enable = &ccu->policy.enable; + u32 offset; + u32 enable_bit; + bool ret; + + /* If we don't need to control policy for this CCU, we're done. */ + if (!policy_lvm_en_exists(enable)) + return true; + + /* Ensure we're not busy before we start */ + offset = enable->offset; + enable_bit = enable->bit; + ret = __ccu_wait_bit(ccu, offset, enable_bit, false); + if (!ret) { + pr_err("%s: ccu %s policy engine already stopped\n", + __func__, ccu->name); + return false; + } + + /* Now set the bit to stop the engine (NO read-modify-write) */ + __ccu_write(ccu, offset, (u32)1 << enable_bit); + + /* Wait for indication that it has stopped. */ + ret = __ccu_wait_bit(ccu, offset, enable_bit, false); + if (!ret) + pr_err("%s: ccu %s policy engine never stopped\n", + __func__, ccu->name); + + return ret; +} + +/* + * A CCU has four operating conditions ("policies"), and some clocks + * can be disabled or enabled based on which policy is currently in + * effect. Such clocks have a bit in a "policy mask" register for + * each policy indicating whether the clock is enabled for that + * policy or not. The bit position for a clock is the same for all + * four registers, and the 32-bit registers are at consecutive + * addresses. + */ +static bool policy_init(struct ccu_data *ccu, struct bcm_clk_policy *policy) +{ + u32 offset; + u32 mask; + int i; + bool ret; + + if (!policy_exists(policy)) + return true; + + /* + * We need to stop the CCU policy engine to allow update + * of our policy bits. + */ + if (!__ccu_policy_engine_stop(ccu)) { + pr_err("%s: unable to stop CCU %s policy engine\n", + __func__, ccu->name); + return false; + } + + /* + * For now, if a clock defines its policy bit we just mark + * it "enabled" for all four policies. + */ + offset = policy->offset; + mask = (u32)1 << policy->bit; + for (i = 0; i < CCU_POLICY_COUNT; i++) { + u32 reg_val; + + reg_val = __ccu_read(ccu, offset); + reg_val |= mask; + __ccu_write(ccu, offset, reg_val); + offset += sizeof(u32); + } + + /* We're done updating; fire up the policy engine again. */ + ret = __ccu_policy_engine_start(ccu, true); + if (!ret) + pr_err("%s: unable to restart CCU %s policy engine\n", + __func__, ccu->name); + + return ret; +} + /* Gate operations */ /* Determine whether a clock is gated. CCU lock must be held. */ @@ -972,6 +1122,11 @@ static bool __peri_clk_init(struct kona_clk *bcm_clk) BUG_ON(bcm_clk->type != bcm_clk_peri); + if (!policy_init(ccu, &peri->policy)) { + pr_err("%s: error initializing policy for %s\n", + __func__, name); + return false; + } if (!gate_init(ccu, &peri->gate)) { pr_err("%s: error initializing gate for %s\n", __func__, name); return false; diff --git a/drivers/clk/bcm/clk-kona.h b/drivers/clk/bcm/clk-kona.h index 05d74779a564..4bcba1331561 100644 --- a/drivers/clk/bcm/clk-kona.h +++ b/drivers/clk/bcm/clk-kona.h @@ -43,8 +43,14 @@ #define FLAG_FLIP(obj, type, flag) ((obj)->flags ^= FLAG(type, flag)) #define FLAG_TEST(obj, type, flag) (!!((obj)->flags & FLAG(type, flag))) +/* CCU field state tests */ + +#define ccu_policy_exists(ccu_policy) ((ccu_policy)->enable.offset != 0) + /* Clock field state tests */ +#define policy_exists(policy) ((policy)->offset != 0) + #define gate_exists(gate) FLAG_TEST(gate, GATE, EXISTS) #define gate_is_enabled(gate) FLAG_TEST(gate, GATE, ENABLED) #define gate_is_hw_controllable(gate) FLAG_TEST(gate, GATE, HW) @@ -62,6 +68,9 @@ #define selector_exists(sel) ((sel)->width != 0) #define trigger_exists(trig) FLAG_TEST(trig, TRIG, EXISTS) +#define policy_lvm_en_exists(enable) ((enable)->offset != 0) +#define policy_ctl_exists(control) ((control)->offset != 0) + /* Clock type, used to tell common block what it's part of */ enum bcm_clk_type { bcm_clk_none, /* undefined clock type */ @@ -70,6 +79,27 @@ enum bcm_clk_type { bcm_clk_peri }; +/* + * CCU policy control for clocks. Clocks can be enabled or disabled + * based on the CCU policy in effect. One bit in each policy mask + * register (one per CCU policy) represents whether the clock is + * enabled when that policy is effect or not. The CCU policy engine + * must be stopped to update these bits, and must be restarted again + * afterward. + */ +struct bcm_clk_policy { + u32 offset; /* first policy mask register offset */ + u32 bit; /* bit used in all mask registers */ +}; + +/* Policy initialization macro */ + +#define POLICY(_offset, _bit) \ + { \ + .offset = (_offset), \ + .bit = (_bit), \ + } + /* * Gating control and status is managed by a 32-bit gate register. * @@ -340,6 +370,7 @@ struct bcm_clk_trig { } struct peri_clk_data { + struct bcm_clk_policy policy; struct bcm_clk_gate gate; struct bcm_clk_trig pre_trig; struct bcm_clk_div pre_div; @@ -377,6 +408,45 @@ struct kona_clk { } #define LAST_KONA_CLK { .type = bcm_clk_none } +/* + * CCU policy control. To enable software update of the policy + * tables the CCU policy engine must be stopped by setting the + * software update enable bit (LVM_EN). After an update the engine + * is restarted using the GO bit and either the GO_ATL or GO_AC bit. + */ +struct bcm_lvm_en { + u32 offset; /* LVM_EN register offset */ + u32 bit; /* POLICY_CONFIG_EN bit in register */ +}; + +/* Policy enable initialization macro */ +#define CCU_LVM_EN(_offset, _bit) \ + { \ + .offset = (_offset), \ + .bit = (_bit), \ + } + +struct bcm_policy_ctl { + u32 offset; /* POLICY_CTL register offset */ + u32 go_bit; + u32 atl_bit; /* GO, GO_ATL, and GO_AC bits */ + u32 ac_bit; +}; + +/* Policy control initialization macro */ +#define CCU_POLICY_CTL(_offset, _go_bit, _ac_bit, _atl_bit) \ + { \ + .offset = (_offset), \ + .go_bit = (_go_bit), \ + .ac_bit = (_ac_bit), \ + .atl_bit = (_atl_bit), \ + } + +struct ccu_policy { + struct bcm_lvm_en enable; + struct bcm_policy_ctl control; +}; + /* * Each CCU defines a mapped area of memory containing registers * used to manage clocks implemented by the CCU. Access to memory @@ -390,6 +460,7 @@ struct ccu_data { void __iomem *base; /* base of mapped address space */ spinlock_t lock; /* serialization lock */ bool write_enabled; /* write access is currently enabled */ + struct ccu_policy policy; struct list_head links; /* for ccu_list */ struct device_node *node; struct clk_onecell_data clk_data; -- cgit v1.2.3 From dc613840a625bfad38141d2d8bbdb0c7bc3d45eb Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 21 Apr 2014 16:11:43 -0500 Subject: clk: bcm281xx: add clock hysteresis support Add support for clock gate hysteresis control. For now, if it's defined for a clock, it's enabled. Signed-off-by: Alex Elder Signed-off-by: Mike Turquette --- drivers/clk/bcm/clk-kona-setup.c | 30 ++++++++++++++++++++++++++++++ drivers/clk/bcm/clk-kona.c | 33 +++++++++++++++++++++++++++++++++ drivers/clk/bcm/clk-kona.h | 19 +++++++++++++++++++ 3 files changed, 82 insertions(+) (limited to 'drivers/clk') diff --git a/drivers/clk/bcm/clk-kona-setup.c b/drivers/clk/bcm/clk-kona-setup.c index 773ad7c7dd59..e5aededdd322 100644 --- a/drivers/clk/bcm/clk-kona-setup.c +++ b/drivers/clk/bcm/clk-kona-setup.c @@ -81,6 +81,7 @@ static bool peri_clk_data_offsets_valid(struct kona_clk *bcm_clk) struct peri_clk_data *peri; struct bcm_clk_policy *policy; struct bcm_clk_gate *gate; + struct bcm_clk_hyst *hyst; struct bcm_clk_div *div; struct bcm_clk_sel *sel; struct bcm_clk_trig *trig; @@ -106,12 +107,25 @@ static bool peri_clk_data_offsets_valid(struct kona_clk *bcm_clk) } gate = &peri->gate; + hyst = &peri->hyst; if (gate_exists(gate)) { if (gate->offset > limit) { pr_err("%s: bad gate offset for %s (%u > %u)\n", __func__, name, gate->offset, limit); return false; } + + if (hyst_exists(hyst)) { + if (hyst->offset > limit) { + pr_err("%s: bad hysteresis offset for %s " + "(%u > %u)\n", __func__, + name, hyst->offset, limit); + return false; + } + } + } else if (hyst_exists(hyst)) { + pr_err("%s: hysteresis but no gate for %s\n", __func__, name); + return false; } div = &peri->div; @@ -261,6 +275,17 @@ static bool gate_valid(struct bcm_clk_gate *gate, const char *field_name, return true; } +static bool hyst_valid(struct bcm_clk_hyst *hyst, const char *clock_name) +{ + if (!bit_posn_valid(hyst->en_bit, "hysteresis enable", clock_name)) + return false; + + if (!bit_posn_valid(hyst->val_bit, "hysteresis value", clock_name)) + return false; + + return true; +} + /* * A selector bitfield must be valid. Its parent_sel array must * also be reasonable for the field. @@ -379,6 +404,7 @@ peri_clk_data_valid(struct kona_clk *bcm_clk) struct peri_clk_data *peri; struct bcm_clk_policy *policy; struct bcm_clk_gate *gate; + struct bcm_clk_hyst *hyst; struct bcm_clk_sel *sel; struct bcm_clk_div *div; struct bcm_clk_div *pre_div; @@ -406,6 +432,10 @@ peri_clk_data_valid(struct kona_clk *bcm_clk) if (gate_exists(gate) && !gate_valid(gate, "gate", name)) return false; + hyst = &peri->hyst; + if (hyst_exists(hyst) && !hyst_valid(hyst, name)) + return false; + sel = &peri->sel; if (selector_exists(sel)) { if (!sel_valid(sel, "selector", name)) diff --git a/drivers/clk/bcm/clk-kona.c b/drivers/clk/bcm/clk-kona.c index 7d90c34d1336..d603c4e22fca 100644 --- a/drivers/clk/bcm/clk-kona.c +++ b/drivers/clk/bcm/clk-kona.c @@ -527,6 +527,35 @@ static int clk_gate(struct ccu_data *ccu, const char *name, return -EIO; } +/* Hysteresis operations */ + +/* + * If a clock gate requires a turn-off delay it will have + * "hysteresis" register bits defined. The first, if set, enables + * the delay; and if enabled, the second bit determines whether the + * delay is "low" or "high" (1 means high). For now, if it's + * defined for a clock, we set it. + */ +static bool hyst_init(struct ccu_data *ccu, struct bcm_clk_hyst *hyst) +{ + u32 offset; + u32 reg_val; + u32 mask; + + if (!hyst_exists(hyst)) + return true; + + offset = hyst->offset; + mask = (u32)1 << hyst->en_bit; + mask |= (u32)1 << hyst->val_bit; + + reg_val = __ccu_read(ccu, offset); + reg_val |= mask; + __ccu_write(ccu, offset, reg_val); + + return true; +} + /* Trigger operations */ /* @@ -1131,6 +1160,10 @@ static bool __peri_clk_init(struct kona_clk *bcm_clk) pr_err("%s: error initializing gate for %s\n", __func__, name); return false; } + if (!hyst_init(ccu, &peri->hyst)) { + pr_err("%s: error initializing hyst for %s\n", __func__, name); + return false; + } if (!div_init(ccu, &peri->gate, &peri->div, &peri->trig)) { pr_err("%s: error initializing divider for %s\n", __func__, name); diff --git a/drivers/clk/bcm/clk-kona.h b/drivers/clk/bcm/clk-kona.h index 4bcba1331561..2537b3072910 100644 --- a/drivers/clk/bcm/clk-kona.h +++ b/drivers/clk/bcm/clk-kona.h @@ -60,6 +60,8 @@ #define gate_flip_enabled(gate) FLAG_FLIP(gate, GATE, ENABLED) +#define hyst_exists(hyst) ((hyst)->offset != 0) + #define divider_exists(div) FLAG_TEST(div, DIV, EXISTS) #define divider_is_fixed(div) FLAG_TEST(div, DIV, FIXED) #define divider_has_fraction(div) (!divider_is_fixed(div) && \ @@ -205,6 +207,22 @@ struct bcm_clk_gate { .flags = FLAG(GATE, HW)|FLAG(GATE, EXISTS), \ } +/* Gate hysteresis for clocks */ +struct bcm_clk_hyst { + u32 offset; /* hyst register offset (normally CLKGATE) */ + u32 en_bit; /* bit used to enable hysteresis */ + u32 val_bit; /* if enabled: 0 = low delay; 1 = high delay */ +}; + +/* Hysteresis initialization macro */ + +#define HYST(_offset, _en_bit, _val_bit) \ + { \ + .offset = (_offset), \ + .en_bit = (_en_bit), \ + .val_bit = (_val_bit), \ + } + /* * Each clock can have zero, one, or two dividers which change the * output rate of the clock. Each divider can be either fixed or @@ -372,6 +390,7 @@ struct bcm_clk_trig { struct peri_clk_data { struct bcm_clk_policy policy; struct bcm_clk_gate gate; + struct bcm_clk_hyst hyst; struct bcm_clk_trig pre_trig; struct bcm_clk_div pre_div; struct bcm_clk_trig trig; -- cgit v1.2.3 From 0bdab78ba69f057c99e72b9887a997888e7fce15 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 21 Apr 2014 16:26:23 -0500 Subject: clk: bcm281xx: move compatible string definitions The Broadcom 281xx clock code uses a #define for the compatible string for it's clock control units (CCUs). Rather than defining those in the C source file, define them in the header file that's shared by both the code and the device tree source file (along with all the clock ids). Signed-off-by: Alex Elder Signed-off-by: Mike Turquette --- drivers/clk/bcm/clk-bcm281xx.c | 12 ------------ include/dt-bindings/clock/bcm281xx.h | 12 ++++++++++++ 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/bcm/clk-bcm281xx.c b/drivers/clk/bcm/clk-bcm281xx.c index 71a65a49f1a4..502a487d62c5 100644 --- a/drivers/clk/bcm/clk-bcm281xx.c +++ b/drivers/clk/bcm/clk-bcm281xx.c @@ -18,18 +18,6 @@ #define BCM281XX_CCU_COMMON(_name, _ucase_name) \ KONA_CCU_COMMON(BCM281XX, _name, _ucase_name) -/* - * These are the bcm281xx CCU device tree "compatible" strings. - * We're stuck with using "bcm11351" in the string because wild - * cards aren't allowed, and that name was the first one defined - * in this family of devices. - */ -#define BCM281XX_DT_ROOT_CCU_COMPAT "brcm,bcm11351-root-ccu" -#define BCM281XX_DT_AON_CCU_COMPAT "brcm,bcm11351-aon-ccu" -#define BCM281XX_DT_HUB_CCU_COMPAT "brcm,bcm11351-hub-ccu" -#define BCM281XX_DT_MASTER_CCU_COMPAT "brcm,bcm11351-master-ccu" -#define BCM281XX_DT_SLAVE_CCU_COMPAT "brcm,bcm11351-slave-ccu" - /* Root CCU */ static struct peri_clk_data frac_1m_data = { diff --git a/include/dt-bindings/clock/bcm281xx.h b/include/dt-bindings/clock/bcm281xx.h index e0096940886d..a763460cf1af 100644 --- a/include/dt-bindings/clock/bcm281xx.h +++ b/include/dt-bindings/clock/bcm281xx.h @@ -20,6 +20,18 @@ * the clock control units (CCUs) on Broadcom BCM281XX family SoCs. */ +/* + * These are the bcm281xx CCU device tree "compatible" strings. + * We're stuck with using "bcm11351" in the string because wild + * cards aren't allowed, and that name was the first one defined + * in this family of devices. + */ +#define BCM281XX_DT_ROOT_CCU_COMPAT "brcm,bcm11351-root-ccu" +#define BCM281XX_DT_AON_CCU_COMPAT "brcm,bcm11351-aon-ccu" +#define BCM281XX_DT_HUB_CCU_COMPAT "brcm,bcm11351-hub-ccu" +#define BCM281XX_DT_MASTER_CCU_COMPAT "brcm,bcm11351-master-ccu" +#define BCM281XX_DT_SLAVE_CCU_COMPAT "brcm,bcm11351-slave-ccu" + /* root CCU clock ids */ #define BCM281XX_ROOT_CCU_FRAC_1M 0 -- cgit v1.2.3 From 7d3723ba8c19a9d23a240f8e99010193e5623b06 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 21 Apr 2014 16:26:26 -0500 Subject: clk: bcm21664: use common clock framework Define the set of CCUs and provided clocks sufficient to satisfy the needs of all the existing clock references for BCM21664. Replace the "fake" fixed-rate clocks used previously with "real" ones. Note that only the minimal set of these clocks and CCUs is defined here. More clock definitions will need to be added as required by the addition of additional drivers. Signed-off-by: Alex Elder Signed-off-by: Mike Turquette --- drivers/clk/bcm/Kconfig | 2 +- drivers/clk/bcm/Makefile | 1 + drivers/clk/bcm/clk-bcm21664.c | 290 +++++++++++++++++++++++++++++++++++ include/dt-bindings/clock/bcm21664.h | 62 ++++++++ 4 files changed, 354 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/bcm/clk-bcm21664.c create mode 100644 include/dt-bindings/clock/bcm21664.h (limited to 'drivers/clk') diff --git a/drivers/clk/bcm/Kconfig b/drivers/clk/bcm/Kconfig index a7262fb8ce55..75506e53075b 100644 --- a/drivers/clk/bcm/Kconfig +++ b/drivers/clk/bcm/Kconfig @@ -6,4 +6,4 @@ config CLK_BCM_KONA help Enable common clock framework support for Broadcom SoCs using "Kona" style clock control units, including those - in the BCM281xx family. + in the BCM281xx and BCM21664 families. diff --git a/drivers/clk/bcm/Makefile b/drivers/clk/bcm/Makefile index cf93359aa862..6297d05a9a10 100644 --- a/drivers/clk/bcm/Makefile +++ b/drivers/clk/bcm/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_CLK_BCM_KONA) += clk-kona.o obj-$(CONFIG_CLK_BCM_KONA) += clk-kona-setup.o obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281xx.o +obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm21664.o diff --git a/drivers/clk/bcm/clk-bcm21664.c b/drivers/clk/bcm/clk-bcm21664.c new file mode 100644 index 000000000000..eeae4cad2281 --- /dev/null +++ b/drivers/clk/bcm/clk-bcm21664.c @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * Copyright 2014 Linaro 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "clk-kona.h" +#include "dt-bindings/clock/bcm21664.h" + +#define BCM21664_CCU_COMMON(_name, _capname) \ + KONA_CCU_COMMON(BCM21664, _name, _capname) + +/* Root CCU */ + +static struct peri_clk_data frac_1m_data = { + .gate = HW_SW_GATE(0x214, 16, 0, 1), + .clocks = CLOCKS("ref_crystal"), +}; + +static struct ccu_data root_ccu_data = { + BCM21664_CCU_COMMON(root, ROOT), + /* no policy control */ + .kona_clks = { + [BCM21664_ROOT_CCU_FRAC_1M] = + KONA_CLK(root, frac_1m, peri), + [BCM21664_ROOT_CCU_CLOCK_COUNT] = LAST_KONA_CLK, + }, +}; + +/* AON CCU */ + +static struct peri_clk_data hub_timer_data = { + .gate = HW_SW_GATE(0x0414, 16, 0, 1), + .hyst = HYST(0x0414, 8, 9), + .clocks = CLOCKS("bbl_32k", + "frac_1m", + "dft_19_5m"), + .sel = SELECTOR(0x0a10, 0, 2), + .trig = TRIGGER(0x0a40, 4), +}; + +static struct ccu_data aon_ccu_data = { + BCM21664_CCU_COMMON(aon, AON), + .policy = { + .enable = CCU_LVM_EN(0x0034, 0), + .control = CCU_POLICY_CTL(0x000c, 0, 1, 2), + }, + .kona_clks = { + [BCM21664_AON_CCU_HUB_TIMER] = + KONA_CLK(aon, hub_timer, peri), + [BCM21664_AON_CCU_CLOCK_COUNT] = LAST_KONA_CLK, + }, +}; + +/* Master CCU */ + +static struct peri_clk_data sdio1_data = { + .gate = HW_SW_GATE(0x0358, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_52m", + "ref_52m", + "var_96m", + "ref_96m"), + .sel = SELECTOR(0x0a28, 0, 3), + .div = DIVIDER(0x0a28, 4, 14), + .trig = TRIGGER(0x0afc, 9), +}; + +static struct peri_clk_data sdio2_data = { + .gate = HW_SW_GATE(0x035c, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_52m", + "ref_52m", + "var_96m", + "ref_96m"), + .sel = SELECTOR(0x0a2c, 0, 3), + .div = DIVIDER(0x0a2c, 4, 14), + .trig = TRIGGER(0x0afc, 10), +}; + +static struct peri_clk_data sdio3_data = { + .gate = HW_SW_GATE(0x0364, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_52m", + "ref_52m", + "var_96m", + "ref_96m"), + .sel = SELECTOR(0x0a34, 0, 3), + .div = DIVIDER(0x0a34, 4, 14), + .trig = TRIGGER(0x0afc, 12), +}; + +static struct peri_clk_data sdio4_data = { + .gate = HW_SW_GATE(0x0360, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_52m", + "ref_52m", + "var_96m", + "ref_96m"), + .sel = SELECTOR(0x0a30, 0, 3), + .div = DIVIDER(0x0a30, 4, 14), + .trig = TRIGGER(0x0afc, 11), +}; + +static struct peri_clk_data sdio1_sleep_data = { + .clocks = CLOCKS("ref_32k"), /* Verify */ + .gate = HW_SW_GATE(0x0358, 18, 2, 3), +}; + +static struct peri_clk_data sdio2_sleep_data = { + .clocks = CLOCKS("ref_32k"), /* Verify */ + .gate = HW_SW_GATE(0x035c, 18, 2, 3), +}; + +static struct peri_clk_data sdio3_sleep_data = { + .clocks = CLOCKS("ref_32k"), /* Verify */ + .gate = HW_SW_GATE(0x0364, 18, 2, 3), +}; + +static struct peri_clk_data sdio4_sleep_data = { + .clocks = CLOCKS("ref_32k"), /* Verify */ + .gate = HW_SW_GATE(0x0360, 18, 2, 3), +}; + +static struct ccu_data master_ccu_data = { + BCM21664_CCU_COMMON(master, MASTER), + .policy = { + .enable = CCU_LVM_EN(0x0034, 0), + .control = CCU_POLICY_CTL(0x000c, 0, 1, 2), + }, + .kona_clks = { + [BCM21664_MASTER_CCU_SDIO1] = + KONA_CLK(master, sdio1, peri), + [BCM21664_MASTER_CCU_SDIO2] = + KONA_CLK(master, sdio2, peri), + [BCM21664_MASTER_CCU_SDIO3] = + KONA_CLK(master, sdio3, peri), + [BCM21664_MASTER_CCU_SDIO4] = + KONA_CLK(master, sdio4, peri), + [BCM21664_MASTER_CCU_SDIO1_SLEEP] = + KONA_CLK(master, sdio1_sleep, peri), + [BCM21664_MASTER_CCU_SDIO2_SLEEP] = + KONA_CLK(master, sdio2_sleep, peri), + [BCM21664_MASTER_CCU_SDIO3_SLEEP] = + KONA_CLK(master, sdio3_sleep, peri), + [BCM21664_MASTER_CCU_SDIO4_SLEEP] = + KONA_CLK(master, sdio4_sleep, peri), + [BCM21664_MASTER_CCU_CLOCK_COUNT] = LAST_KONA_CLK, + }, +}; + +/* Slave CCU */ + +static struct peri_clk_data uartb_data = { + .gate = HW_SW_GATE(0x0400, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_156m", + "ref_156m"), + .sel = SELECTOR(0x0a10, 0, 2), + .div = FRAC_DIVIDER(0x0a10, 4, 12, 8), + .trig = TRIGGER(0x0afc, 2), +}; + +static struct peri_clk_data uartb2_data = { + .gate = HW_SW_GATE(0x0404, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_156m", + "ref_156m"), + .sel = SELECTOR(0x0a14, 0, 2), + .div = FRAC_DIVIDER(0x0a14, 4, 12, 8), + .trig = TRIGGER(0x0afc, 3), +}; + +static struct peri_clk_data uartb3_data = { + .gate = HW_SW_GATE(0x0408, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_156m", + "ref_156m"), + .sel = SELECTOR(0x0a18, 0, 2), + .div = FRAC_DIVIDER(0x0a18, 4, 12, 8), + .trig = TRIGGER(0x0afc, 4), +}; + +static struct peri_clk_data bsc1_data = { + .gate = HW_SW_GATE(0x0458, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_104m", + "ref_104m", + "var_13m", + "ref_13m"), + .sel = SELECTOR(0x0a64, 0, 3), + .trig = TRIGGER(0x0afc, 23), +}; + +static struct peri_clk_data bsc2_data = { + .gate = HW_SW_GATE(0x045c, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_104m", + "ref_104m", + "var_13m", + "ref_13m"), + .sel = SELECTOR(0x0a68, 0, 3), + .trig = TRIGGER(0x0afc, 24), +}; + +static struct peri_clk_data bsc3_data = { + .gate = HW_SW_GATE(0x0470, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_104m", + "ref_104m", + "var_13m", + "ref_13m"), + .sel = SELECTOR(0x0a7c, 0, 3), + .trig = TRIGGER(0x0afc, 18), +}; + +static struct peri_clk_data bsc4_data = { + .gate = HW_SW_GATE(0x0474, 18, 2, 3), + .clocks = CLOCKS("ref_crystal", + "var_104m", + "ref_104m", + "var_13m", + "ref_13m"), + .sel = SELECTOR(0x0a80, 0, 3), + .trig = TRIGGER(0x0afc, 19), +}; + +static struct ccu_data slave_ccu_data = { + BCM21664_CCU_COMMON(slave, SLAVE), + .policy = { + .enable = CCU_LVM_EN(0x0034, 0), + .control = CCU_POLICY_CTL(0x000c, 0, 1, 2), + }, + .kona_clks = { + [BCM21664_SLAVE_CCU_UARTB] = + KONA_CLK(slave, uartb, peri), + [BCM21664_SLAVE_CCU_UARTB2] = + KONA_CLK(slave, uartb2, peri), + [BCM21664_SLAVE_CCU_UARTB3] = + KONA_CLK(slave, uartb3, peri), + [BCM21664_SLAVE_CCU_BSC1] = + KONA_CLK(slave, bsc1, peri), + [BCM21664_SLAVE_CCU_BSC2] = + KONA_CLK(slave, bsc2, peri), + [BCM21664_SLAVE_CCU_BSC3] = + KONA_CLK(slave, bsc3, peri), + [BCM21664_SLAVE_CCU_BSC4] = + KONA_CLK(slave, bsc4, peri), + [BCM21664_SLAVE_CCU_CLOCK_COUNT] = LAST_KONA_CLK, + }, +}; + +/* Device tree match table callback functions */ + +static void __init kona_dt_root_ccu_setup(struct device_node *node) +{ + kona_dt_ccu_setup(&root_ccu_data, node); +} + +static void __init kona_dt_aon_ccu_setup(struct device_node *node) +{ + kona_dt_ccu_setup(&aon_ccu_data, node); +} + +static void __init kona_dt_master_ccu_setup(struct device_node *node) +{ + kona_dt_ccu_setup(&master_ccu_data, node); +} + +static void __init kona_dt_slave_ccu_setup(struct device_node *node) +{ + kona_dt_ccu_setup(&slave_ccu_data, node); +} + +CLK_OF_DECLARE(bcm21664_root_ccu, BCM21664_DT_ROOT_CCU_COMPAT, + kona_dt_root_ccu_setup); +CLK_OF_DECLARE(bcm21664_aon_ccu, BCM21664_DT_AON_CCU_COMPAT, + kona_dt_aon_ccu_setup); +CLK_OF_DECLARE(bcm21664_master_ccu, BCM21664_DT_MASTER_CCU_COMPAT, + kona_dt_master_ccu_setup); +CLK_OF_DECLARE(bcm21664_slave_ccu, BCM21664_DT_SLAVE_CCU_COMPAT, + kona_dt_slave_ccu_setup); diff --git a/include/dt-bindings/clock/bcm21664.h b/include/dt-bindings/clock/bcm21664.h new file mode 100644 index 000000000000..5a7f0e4750a8 --- /dev/null +++ b/include/dt-bindings/clock/bcm21664.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2013 Broadcom Corporation + * Copyright 2013 Linaro 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CLOCK_BCM21664_H +#define _CLOCK_BCM21664_H + +/* + * This file defines the values used to specify clocks provided by + * the clock control units (CCUs) on Broadcom BCM21664 family SoCs. + */ + +/* bcm21664 CCU device tree "compatible" strings */ +#define BCM21664_DT_ROOT_CCU_COMPAT "brcm,bcm21664-root-ccu" +#define BCM21664_DT_AON_CCU_COMPAT "brcm,bcm21664-aon-ccu" +#define BCM21664_DT_MASTER_CCU_COMPAT "brcm,bcm21664-master-ccu" +#define BCM21664_DT_SLAVE_CCU_COMPAT "brcm,bcm21664-slave-ccu" + +/* root CCU clock ids */ + +#define BCM21664_ROOT_CCU_FRAC_1M 0 +#define BCM21664_ROOT_CCU_CLOCK_COUNT 1 + +/* aon CCU clock ids */ + +#define BCM21664_AON_CCU_HUB_TIMER 0 +#define BCM21664_AON_CCU_CLOCK_COUNT 1 + +/* master CCU clock ids */ + +#define BCM21664_MASTER_CCU_SDIO1 0 +#define BCM21664_MASTER_CCU_SDIO2 1 +#define BCM21664_MASTER_CCU_SDIO3 2 +#define BCM21664_MASTER_CCU_SDIO4 3 +#define BCM21664_MASTER_CCU_SDIO1_SLEEP 4 +#define BCM21664_MASTER_CCU_SDIO2_SLEEP 5 +#define BCM21664_MASTER_CCU_SDIO3_SLEEP 6 +#define BCM21664_MASTER_CCU_SDIO4_SLEEP 7 +#define BCM21664_MASTER_CCU_CLOCK_COUNT 8 + +/* slave CCU clock ids */ + +#define BCM21664_SLAVE_CCU_UARTB 0 +#define BCM21664_SLAVE_CCU_UARTB2 1 +#define BCM21664_SLAVE_CCU_UARTB3 2 +#define BCM21664_SLAVE_CCU_BSC1 3 +#define BCM21664_SLAVE_CCU_BSC2 4 +#define BCM21664_SLAVE_CCU_BSC3 5 +#define BCM21664_SLAVE_CCU_BSC4 6 +#define BCM21664_SLAVE_CCU_CLOCK_COUNT 7 + +#endif /* _CLOCK_BCM21664_H */ -- cgit v1.2.3 From 86a612349fa58467cd63b30748114ec377d61807 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 26 Mar 2014 16:06:35 -0700 Subject: clk: Don't check for missing ops in clk_set_parent() We dereference clk->ops during clock registration so this check for NULL ops can't possibly ever be true. Signed-off-by: Stephen Boyd Signed-off-by: Mike Turquette --- drivers/clk/clk.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 7cf2c093cc54..453cf3d210d2 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1716,9 +1716,6 @@ int clk_set_parent(struct clk *clk, struct clk *parent) if (!clk) return 0; - if (!clk->ops) - return -EINVAL; - /* verify ops for for multi-parent clks */ if ((clk->num_parents > 1) && (!clk->ops->set_parent)) return -ENOSYS; -- cgit v1.2.3 From 8f2c2db132cf5f4ffb4b9702ddb7e6bc5a343814 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 26 Mar 2014 16:06:36 -0700 Subject: clk: Consolidate recalc rate logic The same if-else statement exists four times to recalculate the rate of a clock. Consolidate this logic into a single function to save some lines. Signed-off-by: Stephen Boyd Signed-off-by: Mike Turquette --- drivers/clk/clk.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 453cf3d210d2..3c02be74fd7c 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1115,6 +1115,13 @@ long clk_get_accuracy(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_get_accuracy); +static unsigned long clk_recalc(struct clk *clk, unsigned long parent_rate) +{ + if (clk->ops->recalc_rate) + return clk->ops->recalc_rate(clk->hw, parent_rate); + return parent_rate; +} + /** * __clk_recalc_rates * @clk: first clk in the subtree @@ -1140,10 +1147,7 @@ static void __clk_recalc_rates(struct clk *clk, unsigned long msg) if (clk->parent) parent_rate = clk->parent->rate; - if (clk->ops->recalc_rate) - clk->rate = clk->ops->recalc_rate(clk->hw, parent_rate); - else - clk->rate = parent_rate; + clk->rate = clk_recalc(clk, parent_rate); /* * ignore NOTIFY_STOP and NOTIFY_BAD return values for POST_RATE_CHANGE @@ -1334,10 +1338,7 @@ static int __clk_speculate_rates(struct clk *clk, unsigned long parent_rate) unsigned long new_rate; int ret = NOTIFY_DONE; - if (clk->ops->recalc_rate) - new_rate = clk->ops->recalc_rate(clk->hw, parent_rate); - else - new_rate = parent_rate; + new_rate = clk_recalc(clk, parent_rate); /* abort rate change if a driver returns NOTIFY_BAD or NOTIFY_STOP */ if (clk->notifier_count) @@ -1373,10 +1374,7 @@ static void clk_calc_subtree(struct clk *clk, unsigned long new_rate, new_parent->new_child = clk; hlist_for_each_entry(child, &clk->children, child_node) { - if (child->ops->recalc_rate) - child->new_rate = child->ops->recalc_rate(child->hw, new_rate); - else - child->new_rate = new_rate; + child->new_rate = clk_recalc(child, new_rate); clk_calc_subtree(child, child->new_rate, NULL, 0); } } @@ -1524,10 +1522,7 @@ static void clk_change_rate(struct clk *clk) if (!skip_set_rate && clk->ops->set_rate) clk->ops->set_rate(clk->hw, clk->new_rate, best_parent_rate); - if (clk->ops->recalc_rate) - clk->rate = clk->ops->recalc_rate(clk->hw, best_parent_rate); - else - clk->rate = best_parent_rate; + clk->rate = clk_recalc(clk, best_parent_rate); if (clk->notifier_count && old_rate != clk->rate) __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate); -- cgit v1.2.3 From 63589e92c2d975cc63222e5bd4a9a1fa2a1187ac Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 26 Mar 2014 16:06:37 -0700 Subject: clk: Ignore error and NULL pointers passed to clk_{unprepare, disable}() This simplifies error paths in drivers that use optional clocks by allowing the NULL or error pointer to be passed unconditionally. Signed-off-by: Stephen Boyd Signed-off-by: Mike Turquette --- drivers/clk/clk.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 3c02be74fd7c..4d562206d62f 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -822,6 +822,9 @@ void __clk_unprepare(struct clk *clk) */ void clk_unprepare(struct clk *clk) { + if (IS_ERR_OR_NULL(clk)) + return; + clk_prepare_lock(); __clk_unprepare(clk); clk_prepare_unlock(); @@ -883,9 +886,6 @@ static void __clk_disable(struct clk *clk) if (!clk) return; - if (WARN_ON(IS_ERR(clk))) - return; - if (WARN_ON(clk->enable_count == 0)) return; @@ -914,6 +914,9 @@ void clk_disable(struct clk *clk) { unsigned long flags; + if (IS_ERR_OR_NULL(clk)) + return; + flags = clk_enable_lock(); __clk_disable(clk); clk_enable_unlock(flags); -- cgit v1.2.3 From 49fc825f0cc221768c5711b8ec6a71bd49f2663e Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 21 Mar 2014 17:59:37 -0700 Subject: clk: qcom: Consolidate common probe code Most of the probe code is the same between all the different clock controllers. Consolidate the code into a common.c file. This makes changes to the common probe parts easier and reduces chances for bugs. Signed-off-by: Stephen Boyd Signed-off-by: Mike Turquette --- drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/common.c | 99 +++++++++++++++++++++++++++++++++++++++++ drivers/clk/qcom/common.h | 34 ++++++++++++++ drivers/clk/qcom/gcc-msm8660.c | 87 +++++------------------------------- drivers/clk/qcom/gcc-msm8960.c | 77 +++++--------------------------- drivers/clk/qcom/gcc-msm8974.c | 77 +++++--------------------------- drivers/clk/qcom/mmcc-msm8960.c | 78 +++++--------------------------- drivers/clk/qcom/mmcc-msm8974.c | 80 +++++++-------------------------- 8 files changed, 196 insertions(+), 337 deletions(-) create mode 100644 drivers/clk/qcom/common.c create mode 100644 drivers/clk/qcom/common.h (limited to 'drivers/clk') diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index f60db2ef1aee..689e05bf4f95 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_COMMON_CLK_QCOM) += clk-qcom.o +clk-qcom-y += common.o clk-qcom-y += clk-regmap.o clk-qcom-y += clk-pll.o clk-qcom-y += clk-rcg.o diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c new file mode 100644 index 000000000000..86b45fba5f90 --- /dev/null +++ b/drivers/clk/qcom/common.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#include +#include +#include +#include +#include + +#include "common.h" +#include "clk-regmap.h" +#include "reset.h" + +struct qcom_cc { + struct qcom_reset_controller reset; + struct clk_onecell_data data; + struct clk *clks[]; +}; + +int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc) +{ + void __iomem *base; + struct resource *res; + int i, ret; + struct device *dev = &pdev->dev; + struct clk *clk; + struct clk_onecell_data *data; + struct clk **clks; + struct regmap *regmap; + struct qcom_reset_controller *reset; + struct qcom_cc *cc; + size_t num_clks = desc->num_clks; + struct clk_regmap **rclks = desc->clks; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + regmap = devm_regmap_init_mmio(dev, base, desc->config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks, + GFP_KERNEL); + if (!cc) + return -ENOMEM; + + clks = cc->clks; + data = &cc->data; + data->clks = clks; + data->clk_num = num_clks; + + for (i = 0; i < num_clks; i++) { + if (!rclks[i]) + continue; + clk = devm_clk_register_regmap(dev, rclks[i]); + if (IS_ERR(clk)) + return PTR_ERR(clk); + clks[i] = clk; + } + + ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data); + if (ret) + return ret; + + reset = &cc->reset; + reset->rcdev.of_node = dev->of_node; + reset->rcdev.ops = &qcom_reset_ops; + reset->rcdev.owner = dev->driver->owner; + reset->rcdev.nr_resets = desc->num_resets; + reset->regmap = regmap; + reset->reset_map = desc->resets; + platform_set_drvdata(pdev, &reset->rcdev); + + ret = reset_controller_register(&reset->rcdev); + if (ret) + of_clk_del_provider(dev->of_node); + + return ret; +} +EXPORT_SYMBOL_GPL(qcom_cc_probe); + +void qcom_cc_remove(struct platform_device *pdev) +{ + of_clk_del_provider(pdev->dev.of_node); + reset_controller_unregister(platform_get_drvdata(pdev)); +} +EXPORT_SYMBOL_GPL(qcom_cc_remove); diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h new file mode 100644 index 000000000000..2c3cfc860348 --- /dev/null +++ b/drivers/clk/qcom/common.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ +#ifndef __QCOM_CLK_COMMON_H__ +#define __QCOM_CLK_COMMON_H__ + +struct platform_device; +struct regmap_config; +struct clk_regmap; +struct qcom_reset_map; + +struct qcom_cc_desc { + const struct regmap_config *config; + struct clk_regmap **clks; + size_t num_clks; + const struct qcom_reset_map *resets; + size_t num_resets; +}; + +extern int qcom_cc_probe(struct platform_device *pdev, + const struct qcom_cc_desc *desc); + +extern void qcom_cc_remove(struct platform_device *pdev); + +#endif diff --git a/drivers/clk/qcom/gcc-msm8660.c b/drivers/clk/qcom/gcc-msm8660.c index bc0b7f1fcfbe..44bc6fa64d78 100644 --- a/drivers/clk/qcom/gcc-msm8660.c +++ b/drivers/clk/qcom/gcc-msm8660.c @@ -25,6 +25,7 @@ #include #include +#include "common.h" #include "clk-regmap.h" #include "clk-pll.h" #include "clk-rcg.h" @@ -2701,94 +2702,28 @@ static const struct regmap_config gcc_msm8660_regmap_config = { .fast_io = true, }; +static const struct qcom_cc_desc gcc_msm8660_desc = { + .config = &gcc_msm8660_regmap_config, + .clks = gcc_msm8660_clks, + .num_clks = ARRAY_SIZE(gcc_msm8660_clks), + .resets = gcc_msm8660_resets, + .num_resets = ARRAY_SIZE(gcc_msm8660_resets), +}; + static const struct of_device_id gcc_msm8660_match_table[] = { { .compatible = "qcom,gcc-msm8660" }, { } }; MODULE_DEVICE_TABLE(of, gcc_msm8660_match_table); -struct qcom_cc { - struct qcom_reset_controller reset; - struct clk_onecell_data data; - struct clk *clks[]; -}; - static int gcc_msm8660_probe(struct platform_device *pdev) { - void __iomem *base; - struct resource *res; - int i, ret; - struct device *dev = &pdev->dev; - struct clk *clk; - struct clk_onecell_data *data; - struct clk **clks; - struct regmap *regmap; - size_t num_clks; - struct qcom_reset_controller *reset; - struct qcom_cc *cc; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - regmap = devm_regmap_init_mmio(dev, base, &gcc_msm8660_regmap_config); - if (IS_ERR(regmap)) - return PTR_ERR(regmap); - - num_clks = ARRAY_SIZE(gcc_msm8660_clks); - cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks, - GFP_KERNEL); - if (!cc) - return -ENOMEM; - - clks = cc->clks; - data = &cc->data; - data->clks = clks; - data->clk_num = num_clks; - - /* Temporary until RPM clocks supported */ - clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 19200000); - if (IS_ERR(clk)) - return PTR_ERR(clk); - - clk = clk_register_fixed_rate(dev, "pxo", NULL, CLK_IS_ROOT, 27000000); - if (IS_ERR(clk)) - return PTR_ERR(clk); - - for (i = 0; i < num_clks; i++) { - if (!gcc_msm8660_clks[i]) - continue; - clk = devm_clk_register_regmap(dev, gcc_msm8660_clks[i]); - if (IS_ERR(clk)) - return PTR_ERR(clk); - clks[i] = clk; - } - - ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data); - if (ret) - return ret; - - reset = &cc->reset; - reset->rcdev.of_node = dev->of_node; - reset->rcdev.ops = &qcom_reset_ops, - reset->rcdev.owner = THIS_MODULE, - reset->rcdev.nr_resets = ARRAY_SIZE(gcc_msm8660_resets), - reset->regmap = regmap; - reset->reset_map = gcc_msm8660_resets, - platform_set_drvdata(pdev, &reset->rcdev); - - ret = reset_controller_register(&reset->rcdev); - if (ret) - of_clk_del_provider(dev->of_node); - - return ret; + return qcom_cc_probe(pdev, &gcc_msm8660_desc); } static int gcc_msm8660_remove(struct platform_device *pdev) { - of_clk_del_provider(pdev->dev.of_node); - reset_controller_unregister(platform_get_drvdata(pdev)); + qcom_cc_remove(pdev); return 0; } diff --git a/drivers/clk/qcom/gcc-msm8960.c b/drivers/clk/qcom/gcc-msm8960.c index fd446ab2fd98..633b019891bb 100644 --- a/drivers/clk/qcom/gcc-msm8960.c +++ b/drivers/clk/qcom/gcc-msm8960.c @@ -25,6 +25,7 @@ #include #include +#include "common.h" #include "clk-regmap.h" #include "clk-pll.h" #include "clk-rcg.h" @@ -2875,51 +2876,24 @@ static const struct regmap_config gcc_msm8960_regmap_config = { .fast_io = true, }; +static const struct qcom_cc_desc gcc_msm8960_desc = { + .config = &gcc_msm8960_regmap_config, + .clks = gcc_msm8960_clks, + .num_clks = ARRAY_SIZE(gcc_msm8960_clks), + .resets = gcc_msm8960_resets, + .num_resets = ARRAY_SIZE(gcc_msm8960_resets), +}; + static const struct of_device_id gcc_msm8960_match_table[] = { { .compatible = "qcom,gcc-msm8960" }, { } }; MODULE_DEVICE_TABLE(of, gcc_msm8960_match_table); -struct qcom_cc { - struct qcom_reset_controller reset; - struct clk_onecell_data data; - struct clk *clks[]; -}; - static int gcc_msm8960_probe(struct platform_device *pdev) { - void __iomem *base; - struct resource *res; - int i, ret; - struct device *dev = &pdev->dev; struct clk *clk; - struct clk_onecell_data *data; - struct clk **clks; - struct regmap *regmap; - size_t num_clks; - struct qcom_reset_controller *reset; - struct qcom_cc *cc; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - regmap = devm_regmap_init_mmio(dev, base, &gcc_msm8960_regmap_config); - if (IS_ERR(regmap)) - return PTR_ERR(regmap); - - num_clks = ARRAY_SIZE(gcc_msm8960_clks); - cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks, - GFP_KERNEL); - if (!cc) - return -ENOMEM; - - clks = cc->clks; - data = &cc->data; - data->clks = clks; - data->clk_num = num_clks; + struct device *dev = &pdev->dev; /* Temporary until RPM clocks supported */ clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 19200000); @@ -2930,39 +2904,12 @@ static int gcc_msm8960_probe(struct platform_device *pdev) if (IS_ERR(clk)) return PTR_ERR(clk); - for (i = 0; i < num_clks; i++) { - if (!gcc_msm8960_clks[i]) - continue; - clk = devm_clk_register_regmap(dev, gcc_msm8960_clks[i]); - if (IS_ERR(clk)) - return PTR_ERR(clk); - clks[i] = clk; - } - - ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data); - if (ret) - return ret; - - reset = &cc->reset; - reset->rcdev.of_node = dev->of_node; - reset->rcdev.ops = &qcom_reset_ops, - reset->rcdev.owner = THIS_MODULE, - reset->rcdev.nr_resets = ARRAY_SIZE(gcc_msm8960_resets), - reset->regmap = regmap; - reset->reset_map = gcc_msm8960_resets, - platform_set_drvdata(pdev, &reset->rcdev); - - ret = reset_controller_register(&reset->rcdev); - if (ret) - of_clk_del_provider(dev->of_node); - - return ret; + return qcom_cc_probe(pdev, &gcc_msm8960_desc); } static int gcc_msm8960_remove(struct platform_device *pdev) { - of_clk_del_provider(pdev->dev.of_node); - reset_controller_unregister(platform_get_drvdata(pdev)); + qcom_cc_remove(pdev); return 0; } diff --git a/drivers/clk/qcom/gcc-msm8974.c b/drivers/clk/qcom/gcc-msm8974.c index 51d457e2b959..0d1edc1e9b31 100644 --- a/drivers/clk/qcom/gcc-msm8974.c +++ b/drivers/clk/qcom/gcc-msm8974.c @@ -25,6 +25,7 @@ #include #include +#include "common.h" #include "clk-regmap.h" #include "clk-pll.h" #include "clk-rcg.h" @@ -2574,51 +2575,24 @@ static const struct regmap_config gcc_msm8974_regmap_config = { .fast_io = true, }; +static const struct qcom_cc_desc gcc_msm8974_desc = { + .config = &gcc_msm8974_regmap_config, + .clks = gcc_msm8974_clocks, + .num_clks = ARRAY_SIZE(gcc_msm8974_clocks), + .resets = gcc_msm8974_resets, + .num_resets = ARRAY_SIZE(gcc_msm8974_resets), +}; + static const struct of_device_id gcc_msm8974_match_table[] = { { .compatible = "qcom,gcc-msm8974" }, { } }; MODULE_DEVICE_TABLE(of, gcc_msm8974_match_table); -struct qcom_cc { - struct qcom_reset_controller reset; - struct clk_onecell_data data; - struct clk *clks[]; -}; - static int gcc_msm8974_probe(struct platform_device *pdev) { - void __iomem *base; - struct resource *res; - int i, ret; - struct device *dev = &pdev->dev; struct clk *clk; - struct clk_onecell_data *data; - struct clk **clks; - struct regmap *regmap; - size_t num_clks; - struct qcom_reset_controller *reset; - struct qcom_cc *cc; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - regmap = devm_regmap_init_mmio(dev, base, &gcc_msm8974_regmap_config); - if (IS_ERR(regmap)) - return PTR_ERR(regmap); - - num_clks = ARRAY_SIZE(gcc_msm8974_clocks); - cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks, - GFP_KERNEL); - if (!cc) - return -ENOMEM; - - clks = cc->clks; - data = &cc->data; - data->clks = clks; - data->clk_num = num_clks; + struct device *dev = &pdev->dev; /* Temporary until RPM clocks supported */ clk = clk_register_fixed_rate(dev, "xo", NULL, CLK_IS_ROOT, 19200000); @@ -2631,39 +2605,12 @@ static int gcc_msm8974_probe(struct platform_device *pdev) if (IS_ERR(clk)) return PTR_ERR(clk); - for (i = 0; i < num_clks; i++) { - if (!gcc_msm8974_clocks[i]) - continue; - clk = devm_clk_register_regmap(dev, gcc_msm8974_clocks[i]); - if (IS_ERR(clk)) - return PTR_ERR(clk); - clks[i] = clk; - } - - ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data); - if (ret) - return ret; - - reset = &cc->reset; - reset->rcdev.of_node = dev->of_node; - reset->rcdev.ops = &qcom_reset_ops, - reset->rcdev.owner = THIS_MODULE, - reset->rcdev.nr_resets = ARRAY_SIZE(gcc_msm8974_resets), - reset->regmap = regmap; - reset->reset_map = gcc_msm8974_resets, - platform_set_drvdata(pdev, &reset->rcdev); - - ret = reset_controller_register(&reset->rcdev); - if (ret) - of_clk_del_provider(dev->of_node); - - return ret; + return qcom_cc_probe(pdev, &gcc_msm8974_desc); } static int gcc_msm8974_remove(struct platform_device *pdev) { - of_clk_del_provider(pdev->dev.of_node); - reset_controller_unregister(platform_get_drvdata(pdev)); + qcom_cc_remove(pdev); return 0; } diff --git a/drivers/clk/qcom/mmcc-msm8960.c b/drivers/clk/qcom/mmcc-msm8960.c index f9b59c7e48e9..12f3c0b64fcd 100644 --- a/drivers/clk/qcom/mmcc-msm8960.c +++ b/drivers/clk/qcom/mmcc-msm8960.c @@ -26,6 +26,7 @@ #include #include +#include "common.h" #include "clk-regmap.h" #include "clk-pll.h" #include "clk-rcg.h" @@ -2222,85 +2223,28 @@ static const struct regmap_config mmcc_msm8960_regmap_config = { .fast_io = true, }; +static const struct qcom_cc_desc mmcc_msm8960_desc = { + .config = &mmcc_msm8960_regmap_config, + .clks = mmcc_msm8960_clks, + .num_clks = ARRAY_SIZE(mmcc_msm8960_clks), + .resets = mmcc_msm8960_resets, + .num_resets = ARRAY_SIZE(mmcc_msm8960_resets), +}; + static const struct of_device_id mmcc_msm8960_match_table[] = { { .compatible = "qcom,mmcc-msm8960" }, { } }; MODULE_DEVICE_TABLE(of, mmcc_msm8960_match_table); -struct qcom_cc { - struct qcom_reset_controller reset; - struct clk_onecell_data data; - struct clk *clks[]; -}; - static int mmcc_msm8960_probe(struct platform_device *pdev) { - void __iomem *base; - struct resource *res; - int i, ret; - struct device *dev = &pdev->dev; - struct clk *clk; - struct clk_onecell_data *data; - struct clk **clks; - struct regmap *regmap; - size_t num_clks; - struct qcom_reset_controller *reset; - struct qcom_cc *cc; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - regmap = devm_regmap_init_mmio(dev, base, &mmcc_msm8960_regmap_config); - if (IS_ERR(regmap)) - return PTR_ERR(regmap); - - num_clks = ARRAY_SIZE(mmcc_msm8960_clks); - cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks, - GFP_KERNEL); - if (!cc) - return -ENOMEM; - - clks = cc->clks; - data = &cc->data; - data->clks = clks; - data->clk_num = num_clks; - - for (i = 0; i < num_clks; i++) { - if (!mmcc_msm8960_clks[i]) - continue; - clk = devm_clk_register_regmap(dev, mmcc_msm8960_clks[i]); - if (IS_ERR(clk)) - return PTR_ERR(clk); - clks[i] = clk; - } - - ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data); - if (ret) - return ret; - - reset = &cc->reset; - reset->rcdev.of_node = dev->of_node; - reset->rcdev.ops = &qcom_reset_ops, - reset->rcdev.owner = THIS_MODULE, - reset->rcdev.nr_resets = ARRAY_SIZE(mmcc_msm8960_resets), - reset->regmap = regmap; - reset->reset_map = mmcc_msm8960_resets, - platform_set_drvdata(pdev, &reset->rcdev); - - ret = reset_controller_register(&reset->rcdev); - if (ret) - of_clk_del_provider(dev->of_node); - - return ret; + return qcom_cc_probe(pdev, &mmcc_msm8960_desc); } static int mmcc_msm8960_remove(struct platform_device *pdev) { - of_clk_del_provider(pdev->dev.of_node); - reset_controller_unregister(platform_get_drvdata(pdev)); + qcom_cc_remove(pdev); return 0; } diff --git a/drivers/clk/qcom/mmcc-msm8974.c b/drivers/clk/qcom/mmcc-msm8974.c index c95774514b81..60b7c24a5cd6 100644 --- a/drivers/clk/qcom/mmcc-msm8974.c +++ b/drivers/clk/qcom/mmcc-msm8974.c @@ -25,6 +25,7 @@ #include #include +#include "common.h" #include "clk-regmap.h" #include "clk-pll.h" #include "clk-rcg.h" @@ -2524,88 +2525,39 @@ static const struct regmap_config mmcc_msm8974_regmap_config = { .fast_io = true, }; +static const struct qcom_cc_desc mmcc_msm8974_desc = { + .config = &mmcc_msm8974_regmap_config, + .clks = mmcc_msm8974_clocks, + .num_clks = ARRAY_SIZE(mmcc_msm8974_clocks), + .resets = mmcc_msm8974_resets, + .num_resets = ARRAY_SIZE(mmcc_msm8974_resets), +}; + static const struct of_device_id mmcc_msm8974_match_table[] = { { .compatible = "qcom,mmcc-msm8974" }, { } }; MODULE_DEVICE_TABLE(of, mmcc_msm8974_match_table); -struct qcom_cc { - struct qcom_reset_controller reset; - struct clk_onecell_data data; - struct clk *clks[]; -}; - static int mmcc_msm8974_probe(struct platform_device *pdev) { - void __iomem *base; - struct resource *res; - int i, ret; - struct device *dev = &pdev->dev; - struct clk *clk; - struct clk_onecell_data *data; - struct clk **clks; + int ret; struct regmap *regmap; - size_t num_clks; - struct qcom_reset_controller *reset; - struct qcom_cc *cc; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - regmap = devm_regmap_init_mmio(dev, base, &mmcc_msm8974_regmap_config); - if (IS_ERR(regmap)) - return PTR_ERR(regmap); - - num_clks = ARRAY_SIZE(mmcc_msm8974_clocks); - cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks, - GFP_KERNEL); - if (!cc) - return -ENOMEM; - - clks = cc->clks; - data = &cc->data; - data->clks = clks; - data->clk_num = num_clks; - - clk_pll_configure_sr_hpm_lp(&mmpll1, regmap, &mmpll1_config, true); - clk_pll_configure_sr_hpm_lp(&mmpll3, regmap, &mmpll3_config, false); - for (i = 0; i < num_clks; i++) { - if (!mmcc_msm8974_clocks[i]) - continue; - clk = devm_clk_register_regmap(dev, mmcc_msm8974_clocks[i]); - if (IS_ERR(clk)) - return PTR_ERR(clk); - clks[i] = clk; - } - - ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data); + ret = qcom_cc_probe(pdev, &mmcc_msm8974_desc); if (ret) return ret; - reset = &cc->reset; - reset->rcdev.of_node = dev->of_node; - reset->rcdev.ops = &qcom_reset_ops, - reset->rcdev.owner = THIS_MODULE, - reset->rcdev.nr_resets = ARRAY_SIZE(mmcc_msm8974_resets), - reset->regmap = regmap; - reset->reset_map = mmcc_msm8974_resets, - platform_set_drvdata(pdev, &reset->rcdev); - - ret = reset_controller_register(&reset->rcdev); - if (ret) - of_clk_del_provider(dev->of_node); + regmap = dev_get_regmap(&pdev->dev, NULL); + clk_pll_configure_sr_hpm_lp(&mmpll1, regmap, &mmpll1_config, true); + clk_pll_configure_sr_hpm_lp(&mmpll3, regmap, &mmpll3_config, false); - return ret; + return 0; } static int mmcc_msm8974_remove(struct platform_device *pdev) { - of_clk_del_provider(pdev->dev.of_node); - reset_controller_unregister(platform_get_drvdata(pdev)); + qcom_cc_remove(pdev); return 0; } -- cgit v1.2.3 From 2d85a713dca8b1c96e78e9f17c6a9bc88d11515a Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Fri, 4 Apr 2014 11:31:29 -0500 Subject: clk: qcom: Add basic support for APQ8064 global clock controller clocks The APQ8064 and MSM8960 share a significant amount of clock data and code between the two SoCs. Rather than duplicating the data we just add support for a unqiue APQ8064 clock table into the MSM8960 code. For now add just enough clocks to get a basic serial port going on an APQ8064 device. Signed-off-by: Kumar Gala Reviewed-by: Stephen Boyd Signed-off-by: Mike Turquette [mturquette@linaro.org: trivial conflict due to missing ipq8064 support] --- .../devicetree/bindings/clock/qcom,gcc.txt | 1 + drivers/clk/qcom/Kconfig | 4 +-- drivers/clk/qcom/gcc-msm8960.c | 30 +++++++++++++++++++--- 3 files changed, 30 insertions(+), 5 deletions(-) (limited to 'drivers/clk') diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc.txt b/Documentation/devicetree/bindings/clock/qcom,gcc.txt index 767401f42871..7b7104e8cb1e 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc.txt +++ b/Documentation/devicetree/bindings/clock/qcom,gcc.txt @@ -4,6 +4,7 @@ Qualcomm Global Clock & Reset Controller Binding Required properties : - compatible : shall contain only one of the following: + "qcom,gcc-apq8064" "qcom,gcc-msm8660" "qcom,gcc-msm8960" "qcom,gcc-msm8974" diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index 995bcfa021a4..7f696b7d4422 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -13,10 +13,10 @@ config MSM_GCC_8660 i2c, USB, SD/eMMC, etc. config MSM_GCC_8960 - tristate "MSM8960 Global Clock Controller" + tristate "APQ8064/MSM8960 Global Clock Controller" depends on COMMON_CLK_QCOM help - Support for the global clock controller on msm8960 devices. + Support for the global clock controller on apq8064/msm8960 devices. Say Y if you want to use peripheral devices such as UART, SPI, i2c, USB, SD/eMMC, SATA, PCIe, etc. diff --git a/drivers/clk/qcom/gcc-msm8960.c b/drivers/clk/qcom/gcc-msm8960.c index 633b019891bb..8e2b6ddcb8e4 100644 --- a/drivers/clk/qcom/gcc-msm8960.c +++ b/drivers/clk/qcom/gcc-msm8960.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -2868,6 +2868,16 @@ static const struct qcom_reset_map gcc_msm8960_resets[] = { [RIVA_RESET] = { 0x35e0 }, }; +static struct clk_regmap *gcc_apq8064_clks[] = { + [PLL8] = &pll8.clkr, + [PLL8_VOTE] = &pll8_vote, + [GSBI7_UART_SRC] = &gsbi7_uart_src.clkr, + [GSBI7_UART_CLK] = &gsbi7_uart_clk.clkr, + [GSBI7_QUP_SRC] = &gsbi7_qup_src.clkr, + [GSBI7_QUP_CLK] = &gsbi7_qup_clk.clkr, + [GSBI7_H_CLK] = &gsbi7_h_clk.clkr, +}; + static const struct regmap_config gcc_msm8960_regmap_config = { .reg_bits = 32, .reg_stride = 4, @@ -2884,8 +2894,17 @@ static const struct qcom_cc_desc gcc_msm8960_desc = { .num_resets = ARRAY_SIZE(gcc_msm8960_resets), }; +static const struct qcom_cc_desc gcc_apq8064_desc = { + .config = &gcc_msm8960_regmap_config, + .clks = gcc_apq8064_clks, + .num_clks = ARRAY_SIZE(gcc_apq8064_clks), + .resets = gcc_msm8960_resets, + .num_resets = ARRAY_SIZE(gcc_msm8960_resets), +}; + static const struct of_device_id gcc_msm8960_match_table[] = { - { .compatible = "qcom,gcc-msm8960" }, + { .compatible = "qcom,gcc-msm8960", .data = &gcc_msm8960_desc }, + { .compatible = "qcom,gcc-apq8064", .data = &gcc_apq8064_desc }, { } }; MODULE_DEVICE_TABLE(of, gcc_msm8960_match_table); @@ -2894,6 +2913,11 @@ static int gcc_msm8960_probe(struct platform_device *pdev) { struct clk *clk; struct device *dev = &pdev->dev; + const struct of_device_id *match; + + match = of_match_device(gcc_msm8960_match_table, &pdev->dev); + if (!match) + return -EINVAL; /* Temporary until RPM clocks supported */ clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 19200000); @@ -2904,7 +2928,7 @@ static int gcc_msm8960_probe(struct platform_device *pdev) if (IS_ERR(clk)) return PTR_ERR(clk); - return qcom_cc_probe(pdev, &gcc_msm8960_desc); + return qcom_cc_probe(pdev, match->data); } static int gcc_msm8960_remove(struct platform_device *pdev) -- cgit v1.2.3 From 2c07e3c7dd487fa40983aa20704b2755c9aac477 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Fri, 4 Apr 2014 11:32:56 -0500 Subject: clk: qcom: Various fixes for MSM8960's global clock controller * Remove CE2_SLEEP_CLK, doesn't exist on 8960 family SoCs * Fix incorrect offset for PMIC_SSBI2_RESET * Fix typo: SIC_TIC -> SPS_TIC_H SFAB_ADM0_M2_A_CLK -> SFAB_ADM0_M2_H_CLK * Fix naming convention: SFAB_CFPB_S_HCLK -> SFAB_CFPB_S_H_CLK SATA_SRC_CLK -> SATA_CLK_SRC Signed-off-by: Kumar Gala Reviewed-by: Stephen Boyd Signed-off-by: Mike Turquette --- drivers/clk/qcom/gcc-msm8960.c | 4 ++-- include/dt-bindings/clock/qcom,gcc-msm8960.h | 7 +++---- include/dt-bindings/reset/qcom,gcc-msm8960.h | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/qcom/gcc-msm8960.c b/drivers/clk/qcom/gcc-msm8960.c index 8e2b6ddcb8e4..f4ffd91901f8 100644 --- a/drivers/clk/qcom/gcc-msm8960.c +++ b/drivers/clk/qcom/gcc-msm8960.c @@ -2810,7 +2810,7 @@ static const struct qcom_reset_map gcc_msm8960_resets[] = { [PPSS_PROC_RESET] = { 0x2594, 1 }, [PPSS_RESET] = { 0x2594}, [DMA_BAM_RESET] = { 0x25c0, 7 }, - [SIC_TIC_RESET] = { 0x2600, 7 }, + [SPS_TIC_H_RESET] = { 0x2600, 7 }, [SLIMBUS_H_RESET] = { 0x2620, 7 }, [SFAB_CFPB_M_RESET] = { 0x2680, 7 }, [SFAB_CFPB_S_RESET] = { 0x26c0, 7 }, @@ -2823,7 +2823,7 @@ static const struct qcom_reset_map gcc_msm8960_resets[] = { [SFAB_SFPB_M_RESET] = { 0x2780, 7 }, [SFAB_SFPB_S_RESET] = { 0x27a0, 7 }, [RPM_PROC_RESET] = { 0x27c0, 7 }, - [PMIC_SSBI2_RESET] = { 0x270c, 12 }, + [PMIC_SSBI2_RESET] = { 0x280c, 12 }, [SDC1_RESET] = { 0x2830 }, [SDC2_RESET] = { 0x2850 }, [SDC3_RESET] = { 0x2870 }, diff --git a/include/dt-bindings/clock/qcom,gcc-msm8960.h b/include/dt-bindings/clock/qcom,gcc-msm8960.h index 03bbf49d43b7..f9f547146a15 100644 --- a/include/dt-bindings/clock/qcom,gcc-msm8960.h +++ b/include/dt-bindings/clock/qcom,gcc-msm8960.h @@ -51,7 +51,7 @@ #define QDSS_TSCTR_CLK 34 #define SFAB_ADM0_M0_A_CLK 35 #define SFAB_ADM0_M1_A_CLK 36 -#define SFAB_ADM0_M2_A_CLK 37 +#define SFAB_ADM0_M2_H_CLK 37 #define ADM0_CLK 38 #define ADM0_PBUS_CLK 39 #define MSS_XPU_CLK 40 @@ -99,7 +99,7 @@ #define CFPB2_H_CLK 82 #define SFAB_CFPB_M_H_CLK 83 #define CFPB_MASTER_H_CLK 84 -#define SFAB_CFPB_S_HCLK 85 +#define SFAB_CFPB_S_H_CLK 85 #define CFPB_SPLITTER_H_CLK 86 #define TSIF_H_CLK 87 #define TSIF_INACTIVITY_TIMERS_CLK 88 @@ -110,7 +110,6 @@ #define CE1_SLEEP_CLK 93 #define CE2_H_CLK 94 #define CE2_CORE_CLK 95 -#define CE2_SLEEP_CLK 96 #define SFPB_H_CLK_SRC 97 #define SFPB_H_CLK 98 #define SFAB_SFPB_M_H_CLK 99 @@ -252,7 +251,7 @@ #define MSS_S_H_CLK 235 #define MSS_CXO_SRC_CLK 236 #define SATA_H_CLK 237 -#define SATA_SRC_CLK 238 +#define SATA_CLK_SRC 238 #define SATA_RXOOB_CLK 239 #define SATA_PMALIVE_CLK 240 #define SATA_PHY_REF_CLK 241 diff --git a/include/dt-bindings/reset/qcom,gcc-msm8960.h b/include/dt-bindings/reset/qcom,gcc-msm8960.h index a840e680323c..07edd0e65eed 100644 --- a/include/dt-bindings/reset/qcom,gcc-msm8960.h +++ b/include/dt-bindings/reset/qcom,gcc-msm8960.h @@ -58,7 +58,7 @@ #define PPSS_PROC_RESET 41 #define PPSS_RESET 42 #define DMA_BAM_RESET 43 -#define SIC_TIC_RESET 44 +#define SPS_TIC_H_RESET 44 #define SLIMBUS_H_RESET 45 #define SFAB_CFPB_M_RESET 46 #define SFAB_CFPB_S_RESET 47 -- cgit v1.2.3 From 862b728387aef3a3776ad2a261e484aff36c5e67 Mon Sep 17 00:00:00 2001 From: Emilio López Date: Fri, 2 May 2014 17:57:15 +0200 Subject: clk: sunxi: factors: automatic reparenting support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit implements .determine_rate, so that our factor clocks can be reparented when needed. Signed-off-by: Emilio López Signed-off-by: Hans de Goede Acked-by: Maxime Ripard Signed-off-by: Mike Turquette --- drivers/clk/sunxi/clk-factors.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c index 9e232644f07e..3806d97e529b 100644 --- a/drivers/clk/sunxi/clk-factors.c +++ b/drivers/clk/sunxi/clk-factors.c @@ -77,6 +77,41 @@ static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate, return rate; } +static long clk_factors_determine_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *best_parent_rate, + struct clk **best_parent_p) +{ + struct clk *clk = hw->clk, *parent, *best_parent = NULL; + int i, num_parents; + unsigned long parent_rate, best = 0, child_rate, best_child_rate = 0; + + /* find the parent that can help provide the fastest rate <= rate */ + num_parents = __clk_get_num_parents(clk); + for (i = 0; i < num_parents; i++) { + parent = clk_get_parent_by_index(clk, i); + if (!parent) + continue; + if (__clk_get_flags(clk) & CLK_SET_RATE_PARENT) + parent_rate = __clk_round_rate(parent, rate); + else + parent_rate = __clk_get_rate(parent); + + child_rate = clk_factors_round_rate(hw, rate, &parent_rate); + + if (child_rate <= rate && child_rate > best_child_rate) { + best_parent = parent; + best = parent_rate; + best_child_rate = child_rate; + } + } + + if (best_parent) + *best_parent_p = best_parent; + *best_parent_rate = best; + + return best_child_rate; +} + static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { @@ -113,6 +148,7 @@ static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate, } const struct clk_ops clk_factors_ops = { + .determine_rate = clk_factors_determine_rate, .recalc_rate = clk_factors_recalc_rate, .round_rate = clk_factors_round_rate, .set_rate = clk_factors_set_rate, -- cgit v1.2.3 From 95713978b0a2929b72933235bb07c0a793e71afa Mon Sep 17 00:00:00 2001 From: Emilio López Date: Fri, 2 May 2014 17:57:16 +0200 Subject: clk: sunxi: Implement MMC phase control MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HdG: add header exporting clk_sunxi_mmc_phase_control Signed-off-by: Emilio López Signed-off-by: Hans de Goede Signed-off-by: Mike Turquette --- drivers/clk/sunxi/clk-sunxi.c | 36 ++++++++++++++++++++++++++++++++++++ include/linux/clk/sunxi.h | 22 ++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 include/linux/clk/sunxi.h (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index bd7dc733c1ca..59f90401b900 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -506,6 +506,42 @@ CLK_OF_DECLARE(sun7i_a20_gmac, "allwinner,sun7i-a20-gmac-clk", +/** + * clk_sunxi_mmc_phase_control() - configures MMC clock phase control + */ + +void clk_sunxi_mmc_phase_control(struct clk_hw *hw, u8 sample, u8 output) +{ + #define to_clk_composite(_hw) container_of(_hw, struct clk_composite, hw) + #define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw) + + struct clk_composite *composite = to_clk_composite(hw); + struct clk_hw *rate_hw = composite->rate_hw; + struct clk_factors *factors = to_clk_factors(rate_hw); + unsigned long flags = 0; + u32 reg; + + if (factors->lock) + spin_lock_irqsave(factors->lock, flags); + + reg = readl(factors->reg); + + /* set sample clock phase control */ + reg &= ~(0x7 << 20); + reg |= ((sample & 0x7) << 20); + + /* set output clock phase control */ + reg &= ~(0x7 << 8); + reg |= ((output & 0x7) << 8); + + writel(reg, factors->reg); + + if (factors->lock) + spin_unlock_irqrestore(factors->lock, flags); +} +EXPORT_SYMBOL(clk_sunxi_mmc_phase_control); + + /** * sunxi_factors_clk_setup() - Setup function for factor clocks */ diff --git a/include/linux/clk/sunxi.h b/include/linux/clk/sunxi.h new file mode 100644 index 000000000000..1ef5c899e458 --- /dev/null +++ b/include/linux/clk/sunxi.h @@ -0,0 +1,22 @@ +/* + * Copyright 2013 - Hans de Goede + * + * 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. + */ + +#ifndef __LINUX_CLK_SUNXI_H_ +#define __LINUX_CLK_SUNXI_H_ + +#include + +void clk_sunxi_mmc_phase_control(struct clk_hw *hw, u8 sample, u8 output); + +#endif -- cgit v1.2.3 From 156342a1e5719e50e001dec0ee8dfefefebb7606 Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Mon, 21 Apr 2014 11:35:21 +0800 Subject: clk: hisi: use clk_register_mux_table in hisi_clk_register_mux Platform hix5hd2 use mux table, so use clk_register_mux_table instead Signed-off-by: Zhangfei Gao Signed-off-by: Haojian Zhuang --- drivers/clk/hisilicon/clk.c | 13 ++++++++----- drivers/clk/hisilicon/clk.h | 1 + 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/hisilicon/clk.c b/drivers/clk/hisilicon/clk.c index 276f672e7b1a..269938852f4f 100644 --- a/drivers/clk/hisilicon/clk.c +++ b/drivers/clk/hisilicon/clk.c @@ -127,11 +127,14 @@ void __init hisi_clk_register_mux(struct hisi_mux_clock *clks, int i; for (i = 0; i < nums; i++) { - clk = clk_register_mux(NULL, clks[i].name, clks[i].parent_names, - clks[i].num_parents, clks[i].flags, - base + clks[i].offset, clks[i].shift, - clks[i].width, clks[i].mux_flags, - &hisi_clk_lock); + u32 mask = BIT(clks[i].width) - 1; + + clk = clk_register_mux_table(NULL, clks[i].name, + clks[i].parent_names, + clks[i].num_parents, clks[i].flags, + base + clks[i].offset, clks[i].shift, + mask, clks[i].mux_flags, + clks[i].table, &hisi_clk_lock); if (IS_ERR(clk)) { pr_err("%s: failed to register clock %s\n", __func__, clks[i].name); diff --git a/drivers/clk/hisilicon/clk.h b/drivers/clk/hisilicon/clk.h index 43fa5da88f02..bc39a6aa475e 100644 --- a/drivers/clk/hisilicon/clk.h +++ b/drivers/clk/hisilicon/clk.h @@ -62,6 +62,7 @@ struct hisi_mux_clock { u8 shift; u8 width; u8 mux_flags; + u32 *table; const char *alias; }; -- cgit v1.2.3 From 8b9dcb6cb7b69e6990bc4134546be5059342916c Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Tue, 22 Apr 2014 15:42:47 +0800 Subject: clk: hisi: add hisi_clk_register_gate Add hisi_clk_register_gate register clk gate table Signed-off-by: Zhangfei Gao Signed-off-by: Haojian Zhuang --- drivers/clk/hisilicon/clk.c | 28 ++++++++++++++++++++++++++++ drivers/clk/hisilicon/clk.h | 2 ++ 2 files changed, 30 insertions(+) (limited to 'drivers/clk') diff --git a/drivers/clk/hisilicon/clk.c b/drivers/clk/hisilicon/clk.c index 269938852f4f..a078e84f7b05 100644 --- a/drivers/clk/hisilicon/clk.c +++ b/drivers/clk/hisilicon/clk.c @@ -177,6 +177,34 @@ void __init hisi_clk_register_divider(struct hisi_divider_clock *clks, } } +void __init hisi_clk_register_gate(struct hisi_gate_clock *clks, + int nums, struct hisi_clock_data *data) +{ + struct clk *clk; + void __iomem *base = data->base; + int i; + + for (i = 0; i < nums; i++) { + clk = clk_register_gate(NULL, clks[i].name, + clks[i].parent_name, + clks[i].flags, + base + clks[i].offset, + clks[i].bit_idx, + clks[i].gate_flags, + &hisi_clk_lock); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s\n", + __func__, clks[i].name); + continue; + } + + if (clks[i].alias) + clk_register_clkdev(clk, clks[i].alias, NULL); + + data->clk_data.clks[clks[i].id] = clk; + } +} + void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *clks, int nums, struct hisi_clock_data *data) { diff --git a/drivers/clk/hisilicon/clk.h b/drivers/clk/hisilicon/clk.h index bc39a6aa475e..31083ffc0650 100644 --- a/drivers/clk/hisilicon/clk.h +++ b/drivers/clk/hisilicon/clk.h @@ -104,6 +104,8 @@ void __init hisi_clk_register_mux(struct hisi_mux_clock *, int, struct hisi_clock_data *); void __init hisi_clk_register_divider(struct hisi_divider_clock *, int, struct hisi_clock_data *); +void __init hisi_clk_register_gate(struct hisi_gate_clock *, + int, struct hisi_clock_data *); void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *, int, struct hisi_clock_data *); #endif /* __HISI_CLK_H */ -- cgit v1.2.3 From 5efaf09021a5817e5a274aa2d2fad8d92d12ed92 Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Mon, 21 Apr 2014 12:07:27 +0800 Subject: clk: hisi: add clk-hix5hd2.c Signed-off-by: Haifeng Yan Signed-off-by: Zhangfei Gao Signed-off-by: Haojian Zhuang --- .../devicetree/bindings/clock/hix5hd2-clock.txt | 31 +++++++ drivers/clk/Makefile | 1 + drivers/clk/hisilicon/Makefile | 1 + drivers/clk/hisilicon/clk-hix5hd2.c | 101 +++++++++++++++++++++ include/dt-bindings/clock/hix5hd2-clock.h | 58 ++++++++++++ 5 files changed, 192 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/hix5hd2-clock.txt create mode 100644 drivers/clk/hisilicon/clk-hix5hd2.c create mode 100644 include/dt-bindings/clock/hix5hd2-clock.h (limited to 'drivers/clk') diff --git a/Documentation/devicetree/bindings/clock/hix5hd2-clock.txt b/Documentation/devicetree/bindings/clock/hix5hd2-clock.txt new file mode 100644 index 000000000000..7894a64887cb --- /dev/null +++ b/Documentation/devicetree/bindings/clock/hix5hd2-clock.txt @@ -0,0 +1,31 @@ +* Hisilicon Hix5hd2 Clock Controller + +The hix5hd2 clock controller generates and supplies clock to various +controllers within the hix5hd2 SoC. + +Required Properties: + +- compatible: should be "hisilicon,hix5hd2-clock" +- reg: Address and length of the register set +- #clock-cells: Should be <1> + +Each clock is assigned an identifier and client nodes use this identifier +to specify the clock which they consume. + +All these identifier could be found in . + +Examples: + clock: clock@f8a22000 { + compatible = "hisilicon,hix5hd2-clock"; + reg = <0xf8a22000 0x1000>; + #clock-cells = <1>; + }; + + uart0: uart@f8b00000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0xf8b00000 0x1000>; + interrupts = <0 49 4>; + clocks = <&clock HIX5HD2_FIXED_83M>; + clock-names = "apb_pclk"; + status = "disabled"; + }; diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 5f8a28735c96..3e6e577c01db 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_COMMON_CLK_AT91) += at91/ obj-$(CONFIG_ARCH_BCM_MOBILE) += bcm/ obj-$(CONFIG_ARCH_HI3xxx) += hisilicon/ obj-$(CONFIG_ARCH_HIP04) += hisilicon/ +obj-$(CONFIG_ARCH_HIX5HD2) += 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 40b33c6a8257..038c02f4d0e7 100644 --- a/drivers/clk/hisilicon/Makefile +++ b/drivers/clk/hisilicon/Makefile @@ -6,3 +6,4 @@ obj-y += clk.o clkgate-separated.o obj-$(CONFIG_ARCH_HI3xxx) += clk-hi3620.o obj-$(CONFIG_ARCH_HIP04) += clk-hip04.o +obj-$(CONFIG_ARCH_HIX5HD2) += clk-hix5hd2.o diff --git a/drivers/clk/hisilicon/clk-hix5hd2.c b/drivers/clk/hisilicon/clk-hix5hd2.c new file mode 100644 index 000000000000..e5fcfb4e32ef --- /dev/null +++ b/drivers/clk/hisilicon/clk-hix5hd2.c @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2014 Linaro Ltd. + * Copyright (c) 2014 Hisilicon Limited. + * + * 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. + */ + +#include +#include +#include "clk.h" + +static struct hisi_fixed_rate_clock hix5hd2_fixed_rate_clks[] __initdata = { + { HIX5HD2_FIXED_1200M, "1200m", NULL, CLK_IS_ROOT, 1200000000, }, + { HIX5HD2_FIXED_400M, "400m", NULL, CLK_IS_ROOT, 400000000, }, + { HIX5HD2_FIXED_48M, "48m", NULL, CLK_IS_ROOT, 48000000, }, + { HIX5HD2_FIXED_24M, "24m", NULL, CLK_IS_ROOT, 24000000, }, + { HIX5HD2_FIXED_600M, "600m", NULL, CLK_IS_ROOT, 600000000, }, + { HIX5HD2_FIXED_300M, "300m", NULL, CLK_IS_ROOT, 300000000, }, + { HIX5HD2_FIXED_75M, "75m", NULL, CLK_IS_ROOT, 75000000, }, + { HIX5HD2_FIXED_200M, "200m", NULL, CLK_IS_ROOT, 200000000, }, + { HIX5HD2_FIXED_100M, "100m", NULL, CLK_IS_ROOT, 100000000, }, + { HIX5HD2_FIXED_40M, "40m", NULL, CLK_IS_ROOT, 40000000, }, + { HIX5HD2_FIXED_150M, "150m", NULL, CLK_IS_ROOT, 150000000, }, + { HIX5HD2_FIXED_1728M, "1728m", NULL, CLK_IS_ROOT, 1728000000, }, + { HIX5HD2_FIXED_28P8M, "28p8m", NULL, CLK_IS_ROOT, 28000000, }, + { HIX5HD2_FIXED_432M, "432m", NULL, CLK_IS_ROOT, 432000000, }, + { HIX5HD2_FIXED_345P6M, "345p6m", NULL, CLK_IS_ROOT, 345000000, }, + { HIX5HD2_FIXED_288M, "288m", NULL, CLK_IS_ROOT, 288000000, }, + { HIX5HD2_FIXED_60M, "60m", NULL, CLK_IS_ROOT, 60000000, }, + { HIX5HD2_FIXED_750M, "750m", NULL, CLK_IS_ROOT, 750000000, }, + { HIX5HD2_FIXED_500M, "500m", NULL, CLK_IS_ROOT, 500000000, }, + { HIX5HD2_FIXED_54M, "54m", NULL, CLK_IS_ROOT, 54000000, }, + { HIX5HD2_FIXED_27M, "27m", NULL, CLK_IS_ROOT, 27000000, }, + { HIX5HD2_FIXED_1500M, "1500m", NULL, CLK_IS_ROOT, 1500000000, }, + { HIX5HD2_FIXED_375M, "375m", NULL, CLK_IS_ROOT, 375000000, }, + { HIX5HD2_FIXED_187M, "187m", NULL, CLK_IS_ROOT, 187000000, }, + { HIX5HD2_FIXED_250M, "250m", NULL, CLK_IS_ROOT, 250000000, }, + { HIX5HD2_FIXED_125M, "125m", NULL, CLK_IS_ROOT, 125000000, }, + { HIX5HD2_FIXED_2P02M, "2m", NULL, CLK_IS_ROOT, 2000000, }, + { HIX5HD2_FIXED_50M, "50m", NULL, CLK_IS_ROOT, 50000000, }, + { HIX5HD2_FIXED_25M, "25m", NULL, CLK_IS_ROOT, 25000000, }, + { HIX5HD2_FIXED_83M, "83m", NULL, CLK_IS_ROOT, 83333333, }, +}; + +static const char *sfc_mux_p[] __initconst = { + "24m", "150m", "200m", "100m", "75m", }; +static u32 sfc_mux_table[] = {0, 4, 5, 6, 7}; + +static const char *sdio1_mux_p[] __initconst = { + "75m", "100m", "50m", "15m", }; +static u32 sdio1_mux_table[] = {0, 1, 2, 3}; + +static const char *fephy_mux_p[] __initconst = { "25m", "125m"}; +static u32 fephy_mux_table[] = {0, 1}; + + +static struct hisi_mux_clock hix5hd2_mux_clks[] __initdata = { + { HIX5HD2_SFC_MUX, "sfc_mux", sfc_mux_p, ARRAY_SIZE(sfc_mux_p), + CLK_SET_RATE_PARENT, 0x5c, 8, 3, 0, sfc_mux_table, }, + { HIX5HD2_MMC_MUX, "mmc_mux", sdio1_mux_p, ARRAY_SIZE(sdio1_mux_p), + CLK_SET_RATE_PARENT, 0xa0, 8, 2, 0, sdio1_mux_table, }, + { HIX5HD2_FEPHY_MUX, "fephy_mux", + fephy_mux_p, ARRAY_SIZE(fephy_mux_p), + CLK_SET_RATE_PARENT, 0x120, 8, 2, 0, fephy_mux_table, }, +}; + +static struct hisi_gate_clock hix5hd2_gate_clks[] __initdata = { + /*sfc*/ + { HIX5HD2_SFC_CLK, "clk_sfc", "sfc_mux", + CLK_SET_RATE_PARENT, 0x5c, 0, 0, }, + { HIX5HD2_SFC_RST, "rst_sfc", "clk_sfc", + CLK_SET_RATE_PARENT, 0x5c, 4, CLK_GATE_SET_TO_DISABLE, }, + /*sdio1*/ + { HIX5HD2_MMC_BIU_CLK, "clk_mmc_biu", "200m", + CLK_SET_RATE_PARENT, 0xa0, 0, 0, }, + { HIX5HD2_MMC_CIU_CLK, "clk_mmc_ciu", "mmc_mux", + CLK_SET_RATE_PARENT, 0xa0, 1, 0, }, + { HIX5HD2_MMC_CIU_RST, "rst_mmc_ciu", "clk_mmc_ciu", + CLK_SET_RATE_PARENT, 0xa0, 4, CLK_GATE_SET_TO_DISABLE, }, +}; + +static void __init hix5hd2_clk_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data; + + clk_data = hisi_clk_init(np, HIX5HD2_NR_CLKS); + if (!clk_data) + return; + + hisi_clk_register_fixed_rate(hix5hd2_fixed_rate_clks, + ARRAY_SIZE(hix5hd2_fixed_rate_clks), + clk_data); + hisi_clk_register_mux(hix5hd2_mux_clks, ARRAY_SIZE(hix5hd2_mux_clks), + clk_data); + hisi_clk_register_gate(hix5hd2_gate_clks, + ARRAY_SIZE(hix5hd2_gate_clks), clk_data); +} + +CLK_OF_DECLARE(hix5hd2_clk, "hisilicon,hix5hd2-clock", hix5hd2_clk_init); diff --git a/include/dt-bindings/clock/hix5hd2-clock.h b/include/dt-bindings/clock/hix5hd2-clock.h new file mode 100644 index 000000000000..aad579a75802 --- /dev/null +++ b/include/dt-bindings/clock/hix5hd2-clock.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2014 Linaro Ltd. + * Copyright (c) 2014 Hisilicon Limited. + * + * 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. + */ + +#ifndef __DTS_HIX5HD2_CLOCK_H +#define __DTS_HIX5HD2_CLOCK_H + +/* fixed rate */ +#define HIX5HD2_FIXED_1200M 1 +#define HIX5HD2_FIXED_400M 2 +#define HIX5HD2_FIXED_48M 3 +#define HIX5HD2_FIXED_24M 4 +#define HIX5HD2_FIXED_600M 5 +#define HIX5HD2_FIXED_300M 6 +#define HIX5HD2_FIXED_75M 7 +#define HIX5HD2_FIXED_200M 8 +#define HIX5HD2_FIXED_100M 9 +#define HIX5HD2_FIXED_40M 10 +#define HIX5HD2_FIXED_150M 11 +#define HIX5HD2_FIXED_1728M 12 +#define HIX5HD2_FIXED_28P8M 13 +#define HIX5HD2_FIXED_432M 14 +#define HIX5HD2_FIXED_345P6M 15 +#define HIX5HD2_FIXED_288M 16 +#define HIX5HD2_FIXED_60M 17 +#define HIX5HD2_FIXED_750M 18 +#define HIX5HD2_FIXED_500M 19 +#define HIX5HD2_FIXED_54M 20 +#define HIX5HD2_FIXED_27M 21 +#define HIX5HD2_FIXED_1500M 22 +#define HIX5HD2_FIXED_375M 23 +#define HIX5HD2_FIXED_187M 24 +#define HIX5HD2_FIXED_250M 25 +#define HIX5HD2_FIXED_125M 26 +#define HIX5HD2_FIXED_2P02M 27 +#define HIX5HD2_FIXED_50M 28 +#define HIX5HD2_FIXED_25M 29 +#define HIX5HD2_FIXED_83M 30 + +/* mux clocks */ +#define HIX5HD2_SFC_MUX 64 +#define HIX5HD2_MMC_MUX 65 +#define HIX5HD2_FEPHY_MUX 66 + +/* gate clocks */ +#define HIX5HD2_SFC_RST 128 +#define HIX5HD2_SFC_CLK 129 +#define HIX5HD2_MMC_CIU_CLK 130 +#define HIX5HD2_MMC_BIU_CLK 131 +#define HIX5HD2_MMC_CIU_RST 132 + +#define HIX5HD2_NR_CLKS 256 +#endif /* __DTS_HIX5HD2_CLOCK_H */ -- cgit v1.2.3 From 0691bb1b5a1865b3bbc9b7ce6e26eff546abb1cf Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Mon, 12 May 2014 12:27:22 -0500 Subject: clk: socfpga: add divider registers to the main pll outputs The C0(mpu_clk), C1(main_clk), and C2(dbg_base_clk) outputs from the main PLL go through a pre-divider before coming into the system. These registers were hidden for the CycloneV platform, but are now used for the ArriaV platform. This patch updates the clock driver to read the div-reg property for the socfpga-periph-clk clocks. Also moves the div_mask define to clk.h for re-use. Signed-off-by: Dinh Nguyen --- drivers/clk/socfpga/clk-gate.c | 1 - drivers/clk/socfpga/clk-periph.c | 22 +++++++++++++++++++--- drivers/clk/socfpga/clk.h | 4 ++++ 3 files changed, 23 insertions(+), 4 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/socfpga/clk-gate.c b/drivers/clk/socfpga/clk-gate.c index 501d513bf890..dd3a78c64795 100644 --- a/drivers/clk/socfpga/clk-gate.c +++ b/drivers/clk/socfpga/clk-gate.c @@ -32,7 +32,6 @@ #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) diff --git a/drivers/clk/socfpga/clk-periph.c b/drivers/clk/socfpga/clk-periph.c index 81623a3736f9..46531c34ec9b 100644 --- a/drivers/clk/socfpga/clk-periph.c +++ b/drivers/clk/socfpga/clk-periph.c @@ -29,12 +29,18 @@ 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; + u32 div, val; - if (socfpgaclk->fixed_div) + if (socfpgaclk->fixed_div) { div = socfpgaclk->fixed_div; - else + } else { + if (socfpgaclk->div_reg) { + val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift; + val &= div_mask(socfpgaclk->width); + parent_rate /= (val + 1); + } div = ((readl(socfpgaclk->hw.reg) & 0x1ff) + 1); + } return parent_rate / div; } @@ -54,6 +60,7 @@ static __init void __socfpga_periph_init(struct device_node *node, struct clk_init_data init; int rc; u32 fixed_div; + u32 div_reg[3]; of_property_read_u32(node, "reg", ®); @@ -63,6 +70,15 @@ static __init void __socfpga_periph_init(struct device_node *node, periph_clk->hw.reg = clk_mgr_base_addr + reg; + rc = of_property_read_u32_array(node, "div-reg", div_reg, 3); + if (!rc) { + periph_clk->div_reg = clk_mgr_base_addr + div_reg[0]; + periph_clk->shift = div_reg[1]; + periph_clk->width = div_reg[2]; + } else { + periph_clk->div_reg = 0; + } + rc = of_property_read_u32(node, "fixed-divider", &fixed_div); if (rc) periph_clk->fixed_div = 0; diff --git a/drivers/clk/socfpga/clk.h b/drivers/clk/socfpga/clk.h index d2e54019c94f..d291f60c46e1 100644 --- a/drivers/clk/socfpga/clk.h +++ b/drivers/clk/socfpga/clk.h @@ -27,6 +27,7 @@ #define CLKMGR_PERPLL_SRC 0xAC #define SOCFPGA_MAX_PARENTS 3 +#define div_mask(width) ((1 << (width)) - 1) extern void __iomem *clk_mgr_base_addr; @@ -52,6 +53,9 @@ struct socfpga_periph_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 */ }; #endif /* SOCFPGA_CLK_H */ -- cgit v1.2.3 From 7b42a997bfb93c6ae0709f34ec8e2860757804b5 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Fri, 18 Apr 2014 08:05:50 +0900 Subject: clk: shmobile: r8a7779: Add clocks support The R8A7779 SoC has several clocks that are too custom to be supported in a generic driver. Those clocks are all fixed rate clocks with multiplier and divisor set according to boot mode configuration. Based on work for R-Car Gen2 SoCs by Laurent Pinchart. Cc: devicetree@vger.kernel.org Acked-by: Laurent Pinchart Signed-off-by: Simon Horman Signed-off-by: Mike Turquette --- .../bindings/clock/renesas,r8a7779-cpg-clocks.txt | 27 ++++ drivers/clk/shmobile/Makefile | 1 + drivers/clk/shmobile/clk-r8a7779.c | 180 +++++++++++++++++++++ include/linux/clk/shmobile.h | 3 + 4 files changed, 211 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/renesas,r8a7779-cpg-clocks.txt create mode 100644 drivers/clk/shmobile/clk-r8a7779.c (limited to 'drivers/clk') diff --git a/Documentation/devicetree/bindings/clock/renesas,r8a7779-cpg-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,r8a7779-cpg-clocks.txt new file mode 100644 index 000000000000..ed3c8cb12f4e --- /dev/null +++ b/Documentation/devicetree/bindings/clock/renesas,r8a7779-cpg-clocks.txt @@ -0,0 +1,27 @@ +* Renesas R8A7779 Clock Pulse Generator (CPG) + +The CPG generates core clocks for the R8A7779. It includes one PLL and +several fixed ratio dividers + +Required Properties: + + - compatible: Must be "renesas,r8a7779-cpg-clocks" + - reg: Base address and length of the memory resource used by the CPG + + - clocks: Reference to the parent clock + - #clock-cells: Must be 1 + - clock-output-names: The names of the clocks. Supported clocks are "plla", + "z", "zs", "s", "s1", "p", "b", "out". + + +Example +------- + + cpg_clocks: cpg_clocks@ffc80000 { + compatible = "renesas,r8a7779-cpg-clocks"; + reg = <0 0xffc80000 0 0x30>; + clocks = <&extal_clk>; + #clock-cells = <1>; + clock-output-names = "plla", "z", "zs", "s", "s1", "p", + "b", "out"; + }; diff --git a/drivers/clk/shmobile/Makefile b/drivers/clk/shmobile/Makefile index 5404cb931ebf..bdf342daefa5 100644 --- a/drivers/clk/shmobile/Makefile +++ b/drivers/clk/shmobile/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_ARCH_EMEV2) += clk-emev2.o obj-$(CONFIG_ARCH_R7S72100) += clk-rz.o +obj-$(CONFIG_ARCH_R8A7779) += clk-r8a7779.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-r8a7779.c b/drivers/clk/shmobile/clk-r8a7779.c new file mode 100644 index 000000000000..652ecacb6daf --- /dev/null +++ b/drivers/clk/shmobile/clk-r8a7779.c @@ -0,0 +1,180 @@ +/* + * r8a7779 Core CPG Clocks + * + * Copyright (C) 2013, 2014 Horms Solutions Ltd. + * + * Contact: Simon Horman + * + * 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 +#include +#include + +#include + +#define CPG_NUM_CLOCKS (R8A7779_CLK_OUT + 1) + +struct r8a7779_cpg { + struct clk_onecell_data data; + spinlock_t lock; + void __iomem *reg; +}; + +/* ----------------------------------------------------------------------------- + * CPG Clock Data + */ + +/* + * MD1 = 1 MD1 = 0 + * (PLLA = 1500) (PLLA = 1600) + * (MHz) (MHz) + *------------------------------------------------+-------------------- + * clkz 1000 (2/3) 800 (1/2) + * clkzs 250 (1/6) 200 (1/8) + * clki 750 (1/2) 800 (1/2) + * clks 250 (1/6) 200 (1/8) + * clks1 125 (1/12) 100 (1/16) + * clks3 187.5 (1/8) 200 (1/8) + * clks4 93.7 (1/16) 100 (1/16) + * clkp 62.5 (1/24) 50 (1/32) + * clkg 62.5 (1/24) 66.6 (1/24) + * clkb, CLKOUT + * (MD2 = 0) 62.5 (1/24) 66.6 (1/24) + * (MD2 = 1) 41.6 (1/36) 50 (1/32) + */ + +#define CPG_CLK_CONFIG_INDEX(md) (((md) & (BIT(2)|BIT(1))) >> 1) + +struct cpg_clk_config { + unsigned int z_mult; + unsigned int z_div; + unsigned int zs_and_s_div; + unsigned int s1_div; + unsigned int p_div; + unsigned int b_and_out_div; +}; + +static const struct cpg_clk_config cpg_clk_configs[4] __initconst = { + { 1, 2, 8, 16, 32, 24 }, + { 2, 3, 6, 12, 24, 24 }, + { 1, 2, 8, 16, 32, 32 }, + { 2, 3, 6, 12, 24, 36 }, +}; + +/* + * MD PLLA Ratio + * 12 11 + *------------------------ + * 0 0 x42 + * 0 1 x48 + * 1 0 x56 + * 1 1 x64 + */ + +#define CPG_PLLA_MULT_INDEX(md) (((md) & (BIT(12)|BIT(11))) >> 11) + +static const unsigned int cpg_plla_mult[4] __initconst = { 42, 48, 56, 64 }; + +/* ----------------------------------------------------------------------------- + * Initialization + */ + +static u32 cpg_mode __initdata; + +static struct clk * __init +r8a7779_cpg_register_clock(struct device_node *np, struct r8a7779_cpg *cpg, + const struct cpg_clk_config *config, + unsigned int plla_mult, const char *name) +{ + const char *parent_name = "plla"; + unsigned int mult = 1; + unsigned int div = 1; + + if (!strcmp(name, "plla")) { + parent_name = of_clk_get_parent_name(np, 0); + mult = plla_mult; + } else if (!strcmp(name, "z")) { + div = config->z_div; + mult = config->z_mult; + } else if (!strcmp(name, "zs") || !strcmp(name, "s")) { + div = config->zs_and_s_div; + } else if (!strcmp(name, "s1")) { + div = config->s1_div; + } else if (!strcmp(name, "p")) { + div = config->p_div; + } else if (!strcmp(name, "b") || !strcmp(name, "out")) { + div = config->b_and_out_div; + } else { + return ERR_PTR(-EINVAL); + } + + return clk_register_fixed_factor(NULL, name, parent_name, 0, mult, div); +} + +static void __init r8a7779_cpg_clocks_init(struct device_node *np) +{ + const struct cpg_clk_config *config; + struct r8a7779_cpg *cpg; + struct clk **clks; + unsigned int i, plla_mult; + int num_clks; + + num_clks = of_property_count_strings(np, "clock-output-names"); + if (num_clks < 0) { + pr_err("%s: failed to count clocks\n", __func__); + return; + } + + cpg = kzalloc(sizeof(*cpg), GFP_KERNEL); + clks = kzalloc(CPG_NUM_CLOCKS * sizeof(*clks), GFP_KERNEL); + if (cpg == NULL || clks == NULL) { + /* We're leaking memory on purpose, there's no point in cleaning + * up as the system won't boot anyway. + */ + return; + } + + spin_lock_init(&cpg->lock); + + cpg->data.clks = clks; + cpg->data.clk_num = num_clks; + + config = &cpg_clk_configs[CPG_CLK_CONFIG_INDEX(cpg_mode)]; + plla_mult = cpg_plla_mult[CPG_PLLA_MULT_INDEX(cpg_mode)]; + + 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 = r8a7779_cpg_register_clock(np, cpg, config, + plla_mult, 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(r8a7779_cpg_clks, "renesas,r8a7779-cpg-clocks", + r8a7779_cpg_clocks_init); + +void __init r8a7779_clocks_init(u32 mode) +{ + cpg_mode = mode; + + of_clk_init(NULL); +} diff --git a/include/linux/clk/shmobile.h b/include/linux/clk/shmobile.h index f9bf080a1123..9f8a14041dd5 100644 --- a/include/linux/clk/shmobile.h +++ b/include/linux/clk/shmobile.h @@ -1,7 +1,9 @@ /* * Copyright 2013 Ideas On Board SPRL + * Copyright 2013, 2014 Horms Solutions Ltd. * * Contact: Laurent Pinchart + * Contact: Simon Horman * * 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 @@ -14,6 +16,7 @@ #include +void r8a7779_clocks_init(u32 mode); void rcar_gen2_clocks_init(u32 mode); #endif -- cgit v1.2.3 From a97181adf1502128e2945b4fef2591249c565467 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 12 May 2014 14:04:47 +0200 Subject: clk: sunxi: Fixup clk_sunxi_mmc_phase_control to take a clk rather then a hw_clk __clk_get_hw is supposed to be used by clk providers, not clk consumers. Signed-off-by: Hans de Goede Reviewed-by: Ulf Hansson Signed-off-by: Mike Turquette --- drivers/clk/sunxi/clk-sunxi.c | 3 ++- include/linux/clk/sunxi.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 59f90401b900..4cc2b2a5aa75 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -510,11 +510,12 @@ CLK_OF_DECLARE(sun7i_a20_gmac, "allwinner,sun7i-a20-gmac-clk", * clk_sunxi_mmc_phase_control() - configures MMC clock phase control */ -void clk_sunxi_mmc_phase_control(struct clk_hw *hw, u8 sample, u8 output) +void clk_sunxi_mmc_phase_control(struct clk *clk, u8 sample, u8 output) { #define to_clk_composite(_hw) container_of(_hw, struct clk_composite, hw) #define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw) + struct clk_hw *hw = __clk_get_hw(clk); struct clk_composite *composite = to_clk_composite(hw); struct clk_hw *rate_hw = composite->rate_hw; struct clk_factors *factors = to_clk_factors(rate_hw); diff --git a/include/linux/clk/sunxi.h b/include/linux/clk/sunxi.h index 1ef5c899e458..aed28c4451d9 100644 --- a/include/linux/clk/sunxi.h +++ b/include/linux/clk/sunxi.h @@ -17,6 +17,6 @@ #include -void clk_sunxi_mmc_phase_control(struct clk_hw *hw, u8 sample, u8 output); +void clk_sunxi_mmc_phase_control(struct clk *clk, u8 sample, u8 output); #endif -- cgit v1.2.3 From a218d7fa3f7dbc0d3a168f1ccd13b21313d85dab Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 8 May 2014 16:56:16 +0200 Subject: clk/versatile: export symbols for impd1 The impd1 code on mach-integrator can be a loadable module, so we have to export icst_clk_register, integrator_impd1_clk_init and integrator_impd1_clk_exit. Signed-off-by: Arnd Bergmann Acked-by: Linus Walleij Signed-off-by: Mike Turquette --- drivers/clk/versatile/clk-icst.c | 1 + drivers/clk/versatile/clk-impd1.c | 2 ++ 2 files changed, 3 insertions(+) (limited to 'drivers/clk') diff --git a/drivers/clk/versatile/clk-icst.c b/drivers/clk/versatile/clk-icst.c index a820b0cfcf57..7f3868a227c8 100644 --- a/drivers/clk/versatile/clk-icst.c +++ b/drivers/clk/versatile/clk-icst.c @@ -160,3 +160,4 @@ struct clk *icst_clk_register(struct device *dev, return clk; } +EXPORT_SYMBOL_GPL(icst_clk_register); diff --git a/drivers/clk/versatile/clk-impd1.c b/drivers/clk/versatile/clk-impd1.c index 31b44f025f9e..264d8d59ce31 100644 --- a/drivers/clk/versatile/clk-impd1.c +++ b/drivers/clk/versatile/clk-impd1.c @@ -133,6 +133,7 @@ void integrator_impd1_clk_init(void __iomem *base, unsigned int id) for (i = 0; i < ARRAY_SIZE(imc->clks); i++) clkdev_add(imc->clks[i]); } +EXPORT_SYMBOL_GPL(integrator_impd1_clk_init); void integrator_impd1_clk_exit(unsigned int id) { @@ -155,3 +156,4 @@ void integrator_impd1_clk_exit(unsigned int id) kfree(imc->vco2name); kfree(imc->vco1name); } +EXPORT_SYMBOL_GPL(integrator_impd1_clk_exit); -- cgit v1.2.3 From d15998e0957abea410159333995cab27ec568fed Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 16 May 2014 10:05:14 -0700 Subject: clk: qcom: Fix msm8660 GCC probe When consolidating the msm8660 GCC probe code I forgot to keep around these temporary clock registrations. Put them back so the clock tree is not entirely orphaned. Fixes: 49fc825f0cc2 (clk: qcom: Consolidate common probe code) Signed-off-by: Stephen Boyd Signed-off-by: Mike Turquette --- drivers/clk/qcom/gcc-msm8660.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/clk') diff --git a/drivers/clk/qcom/gcc-msm8660.c b/drivers/clk/qcom/gcc-msm8660.c index 44bc6fa64d78..0c4b727ae429 100644 --- a/drivers/clk/qcom/gcc-msm8660.c +++ b/drivers/clk/qcom/gcc-msm8660.c @@ -2718,6 +2718,18 @@ MODULE_DEVICE_TABLE(of, gcc_msm8660_match_table); static int gcc_msm8660_probe(struct platform_device *pdev) { + struct clk *clk; + struct device *dev = &pdev->dev; + + /* Temporary until RPM clocks supported */ + clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 19200000); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + clk = clk_register_fixed_rate(dev, "pxo", NULL, CLK_IS_ROOT, 27000000); + if (IS_ERR(clk)) + return PTR_ERR(clk); + return qcom_cc_probe(pdev, &gcc_msm8660_desc); } -- cgit v1.2.3 From 44731f5db44087f54d4e1ed6a460c75552fadcf7 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 3 Apr 2014 17:05:02 +0200 Subject: clk: si570: Fix email address specifiction Just fix missing ">" in the email. Signed-off-by: Michal Simek --- drivers/clk/clk-si570.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/clk') diff --git a/drivers/clk/clk-si570.c b/drivers/clk/clk-si570.c index 4bbbe32585ec..fc167b3f8919 100644 --- a/drivers/clk/clk-si570.c +++ b/drivers/clk/clk-si570.c @@ -526,6 +526,6 @@ static struct i2c_driver si570_driver = { module_i2c_driver(si570_driver); MODULE_AUTHOR("Guenter Roeck "); -MODULE_AUTHOR("Soeren Brinkmann "); MODULE_DESCRIPTION("Si570 driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 222cb1bf180e413e1816aaf4166e4f56e72201d8 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 15 Apr 2014 10:29:45 +0200 Subject: clk: impd1: add pclk clocks The IM-PD1 PrimeCells all have pclk assignments though this clock cannot be controlled, and we need to provide this as a dummy clock for the PL061 GPIO driver to probe, so let's assign it to all the cells on the board. Signed-off-by: Linus Walleij Signed-off-by: Mike Turquette --- drivers/clk/versatile/clk-impd1.c | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/versatile/clk-impd1.c b/drivers/clk/versatile/clk-impd1.c index 264d8d59ce31..1cc1330dc570 100644 --- a/drivers/clk/versatile/clk-impd1.c +++ b/drivers/clk/versatile/clk-impd1.c @@ -20,6 +20,8 @@ #define IMPD1_LOCK 0x08 struct impd1_clk { + char *pclkname; + struct clk *pclk; char *vco1name; struct clk *vco1clk; char *vco2name; @@ -31,7 +33,7 @@ struct impd1_clk { struct clk *spiclk; char *scname; struct clk *scclk; - struct clk_lookup *clks[6]; + struct clk_lookup *clks[15]; }; /* One entry for each connected IM-PD1 LM */ @@ -86,6 +88,7 @@ void integrator_impd1_clk_init(void __iomem *base, unsigned int id) { struct impd1_clk *imc; struct clk *clk; + struct clk *pclk; int i; if (id > 3) { @@ -94,11 +97,18 @@ void integrator_impd1_clk_init(void __iomem *base, unsigned int id) } imc = &impd1_clks[id]; + /* Register the fixed rate PCLK */ + imc->pclkname = kasprintf(GFP_KERNEL, "lm%x-pclk", id); + pclk = clk_register_fixed_rate(NULL, imc->pclkname, NULL, + CLK_IS_ROOT, 0); + imc->pclk = pclk; + imc->vco1name = kasprintf(GFP_KERNEL, "lm%x-vco1", id); clk = icst_clk_register(NULL, &impd1_icst1_desc, imc->vco1name, NULL, base); imc->vco1clk = clk; - imc->clks[0] = clkdev_alloc(clk, NULL, "lm%x:01000", id); + imc->clks[0] = clkdev_alloc(pclk, "apb_pclk", "lm%x:01000", id); + imc->clks[1] = clkdev_alloc(clk, NULL, "lm%x:01000", id); /* VCO2 is also called "CLK2" */ imc->vco2name = kasprintf(GFP_KERNEL, "lm%x-vco2", id); @@ -107,28 +117,38 @@ void integrator_impd1_clk_init(void __iomem *base, unsigned int id) imc->vco2clk = clk; /* MMCI uses CLK2 right off */ - imc->clks[1] = clkdev_alloc(clk, NULL, "lm%x:00700", id); + imc->clks[2] = clkdev_alloc(pclk, "apb_pclk", "lm%x:00700", id); + imc->clks[3] = clkdev_alloc(clk, NULL, "lm%x:00700", id); /* UART reference clock divides CLK2 by a fixed factor 4 */ imc->uartname = kasprintf(GFP_KERNEL, "lm%x-uartclk", id); clk = clk_register_fixed_factor(NULL, imc->uartname, imc->vco2name, CLK_IGNORE_UNUSED, 1, 4); imc->uartclk = clk; - imc->clks[2] = clkdev_alloc(clk, NULL, "lm%x:00100", id); - imc->clks[3] = clkdev_alloc(clk, NULL, "lm%x:00200", id); + imc->clks[4] = clkdev_alloc(pclk, "apb_pclk", "lm%x:00100", id); + imc->clks[5] = clkdev_alloc(clk, NULL, "lm%x:00100", id); + imc->clks[6] = clkdev_alloc(pclk, "apb_pclk", "lm%x:00200", id); + imc->clks[7] = clkdev_alloc(clk, NULL, "lm%x:00200", id); /* SPI PL022 clock divides CLK2 by a fixed factor 64 */ imc->spiname = kasprintf(GFP_KERNEL, "lm%x-spiclk", id); clk = clk_register_fixed_factor(NULL, imc->spiname, imc->vco2name, CLK_IGNORE_UNUSED, 1, 64); - imc->clks[4] = clkdev_alloc(clk, NULL, "lm%x:00300", id); + imc->clks[8] = clkdev_alloc(pclk, "apb_pclk", "lm%x:00300", id); + imc->clks[9] = clkdev_alloc(clk, NULL, "lm%x:00300", id); + + /* The GPIO blocks and AACI have only PCLK */ + imc->clks[10] = clkdev_alloc(pclk, "apb_pclk", "lm%x:00400", id); + imc->clks[11] = clkdev_alloc(pclk, "apb_pclk", "lm%x:00500", id); + imc->clks[12] = clkdev_alloc(pclk, "apb_pclk", "lm%x:00800", id); /* Smart Card clock divides CLK2 by a fixed factor 4 */ imc->scname = kasprintf(GFP_KERNEL, "lm%x-scclk", id); clk = clk_register_fixed_factor(NULL, imc->scname, imc->vco2name, CLK_IGNORE_UNUSED, 1, 4); imc->scclk = clk; - imc->clks[5] = clkdev_alloc(clk, NULL, "lm%x:00600", id); + imc->clks[13] = clkdev_alloc(pclk, "apb_pclk", "lm%x:00600", id); + imc->clks[14] = clkdev_alloc(clk, NULL, "lm%x:00600", id); for (i = 0; i < ARRAY_SIZE(imc->clks); i++) clkdev_add(imc->clks[i]); @@ -150,10 +170,12 @@ void integrator_impd1_clk_exit(unsigned int id) clk_unregister(imc->uartclk); clk_unregister(imc->vco2clk); clk_unregister(imc->vco1clk); + clk_unregister(imc->pclk); kfree(imc->scname); kfree(imc->spiname); kfree(imc->uartname); kfree(imc->vco2name); kfree(imc->vco1name); + kfree(imc->pclkname); } EXPORT_SYMBOL_GPL(integrator_impd1_clk_exit); -- cgit v1.2.3 From 7f05e28f9dd3eb7a3f27c6f50b715995dc7a88c5 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 19 May 2014 19:22:50 +0200 Subject: clk: Add of_clk_get_by_clkspec() helper This patch adds of_clk_get_by_clkspec() helper function, which does only a struct clk lookup from the clock providers. It is used in the subsequent patch where parsing of a clock from device tree and the lookup from providers needed to be split. Signed-off-by: Sylwester Nawrocki Signed-off-by: Mike Turquette --- drivers/clk/clk.h | 1 + drivers/clk/clkdev.c | 34 +++++++++++++++++++++++++++------- 2 files changed, 28 insertions(+), 7 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/clk.h b/drivers/clk/clk.h index 795cc9f0dac0..c798138f023f 100644 --- a/drivers/clk/clk.h +++ b/drivers/clk/clk.h @@ -10,6 +10,7 @@ */ #if defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK) +struct clk *of_clk_get_by_clkspec(struct of_phandle_args *clkspec); struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec); void of_clk_lock(void); void of_clk_unlock(void); diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c index a360b2eca5cb..f890b901c6bc 100644 --- a/drivers/clk/clkdev.c +++ b/drivers/clk/clkdev.c @@ -27,6 +27,32 @@ static LIST_HEAD(clocks); static DEFINE_MUTEX(clocks_mutex); #if defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK) + +/** + * of_clk_get_by_clkspec() - Lookup a clock form a clock provider + * @clkspec: pointer to a clock specifier data structure + * + * This function looks up a struct clk from the registered list of clock + * providers, an input is a clock specifier data structure as returned + * from the of_parse_phandle_with_args() function call. + */ +struct clk *of_clk_get_by_clkspec(struct of_phandle_args *clkspec) +{ + struct clk *clk; + + if (!clkspec) + return ERR_PTR(-EINVAL); + + of_clk_lock(); + clk = __of_clk_get_from_provider(clkspec); + + if (!IS_ERR(clk) && !__clk_get(clk)) + clk = ERR_PTR(-ENOENT); + + of_clk_unlock(); + return clk; +} + struct clk *of_clk_get(struct device_node *np, int index) { struct of_phandle_args clkspec; @@ -41,13 +67,7 @@ struct clk *of_clk_get(struct device_node *np, int index) if (rc) return ERR_PTR(rc); - of_clk_lock(); - clk = __of_clk_get_from_provider(&clkspec); - - if (!IS_ERR(clk) && !__clk_get(clk)) - clk = ERR_PTR(-ENOENT); - - of_clk_unlock(); + clk = of_clk_get_by_clkspec(&clkspec); of_node_put(clkspec.np); return clk; } -- cgit v1.2.3 From bb178da701382a230e26d90cf94e8a24b280e0d9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 22 May 2014 20:02:14 +0200 Subject: clk: shmobile: mstp: Fix the is_enabled() operation The MSTP[SC]R registers have clock stop bits, not clock enable bits. The bit value should thus be inverted in the is_enabled() operation. Signed-off-by: Laurent Pinchart Signed-off-by: Geert Uytterhoeven Signed-off-by: Mike Turquette --- drivers/clk/shmobile/clk-mstp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/clk') diff --git a/drivers/clk/shmobile/clk-mstp.c b/drivers/clk/shmobile/clk-mstp.c index 1f6324e29a80..2d2fe773ac81 100644 --- a/drivers/clk/shmobile/clk-mstp.c +++ b/drivers/clk/shmobile/clk-mstp.c @@ -112,7 +112,7 @@ static int cpg_mstp_clock_is_enabled(struct clk_hw *hw) else value = clk_readl(group->smstpcr); - return !!(value & BIT(clock->bit_index)); + return !(value & BIT(clock->bit_index)); } static const struct clk_ops cpg_mstp_clock_ops = { -- cgit v1.2.3 From c675a00c2d666c8e90da335eafbbae81201d53f7 Mon Sep 17 00:00:00 2001 From: Anders Berg Date: Thu, 15 May 2014 15:42:25 +0200 Subject: clk: Add clock driver for AXM55xx SoC Add clk driver to support clock blocks found on the AXM55xx devices. The driver provides clock implementations for three different types of clock devices on the AXM55xx device: PLL clock, a clock divider and a clock mux. Signed-off-by: Anders Berg Cc: Mark Rutland Signed-off-by: Mike Turquette --- .../devicetree/bindings/clock/lsi,axm5516-clks.txt | 29 + drivers/clk/Makefile | 1 + drivers/clk/clk-axm5516.c | 615 +++++++++++++++++++++ 3 files changed, 645 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/lsi,axm5516-clks.txt create mode 100644 drivers/clk/clk-axm5516.c (limited to 'drivers/clk') diff --git a/Documentation/devicetree/bindings/clock/lsi,axm5516-clks.txt b/Documentation/devicetree/bindings/clock/lsi,axm5516-clks.txt new file mode 100644 index 000000000000..3ce97cfe999b --- /dev/null +++ b/Documentation/devicetree/bindings/clock/lsi,axm5516-clks.txt @@ -0,0 +1,29 @@ +AXM5516 clock driver bindings +----------------------------- + +Required properties : +- compatible : shall contain "lsi,axm5516-clks" +- reg : shall contain base register location and length +- #clock-cells : shall contain 1 + +The consumer specifies the desired clock by having the clock ID in its "clocks" +phandle cell. See for the list of +supported clock IDs. + +Example: + + clks: clock-controller@2010020000 { + compatible = "lsi,axm5516-clks"; + #clock-cells = <1>; + reg = <0x20 0x10020000 0 0x20000>; + }; + + serial0: uart@2010080000 { + compatible = "arm,pl011", "arm,primecell"; + reg = <0x20 0x10080000 0 0x1000>; + interrupts = ; + clocks = <&clks AXXIA_CLK_PER>; + clock-names = "apb_pclk"; + }; + }; + diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 3e6e577c01db..f30357aa5471 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-composite.o # hardware specific clock types # please keep this section sorted lexicographically by file/directory path name obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o +obj-$(CONFIG_ARCH_AXXIA) += clk-axm5516.o obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o diff --git a/drivers/clk/clk-axm5516.c b/drivers/clk/clk-axm5516.c new file mode 100644 index 000000000000..d2f1e119b450 --- /dev/null +++ b/drivers/clk/clk-axm5516.c @@ -0,0 +1,615 @@ +/* + * drivers/clk/clk-axm5516.c + * + * Provides clock implementations for three different types of clock devices on + * the Axxia device: PLL clock, a clock divider and a clock mux. + * + * Copyright (C) 2014 LSI Corporation + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/** + * struct axxia_clk - Common struct to all Axxia clocks. + * @hw: clk_hw for the common clk framework + * @regmap: Regmap for the clock control registers + */ +struct axxia_clk { + struct clk_hw hw; + struct regmap *regmap; +}; +#define to_axxia_clk(_hw) container_of(_hw, struct axxia_clk, hw) + +/** + * struct axxia_pllclk - Axxia PLL generated clock. + * @aclk: Common struct + * @reg: Offset into regmap for PLL control register + */ +struct axxia_pllclk { + struct axxia_clk aclk; + u32 reg; +}; +#define to_axxia_pllclk(_aclk) container_of(_aclk, struct axxia_pllclk, aclk) + +/** + * axxia_pllclk_recalc - Calculate the PLL generated clock rate given the + * parent clock rate. + */ +static unsigned long +axxia_pllclk_recalc(struct clk_hw *hw, unsigned long parent_rate) +{ + struct axxia_clk *aclk = to_axxia_clk(hw); + struct axxia_pllclk *pll = to_axxia_pllclk(aclk); + unsigned long rate, fbdiv, refdiv, postdiv; + u32 control; + + regmap_read(aclk->regmap, pll->reg, &control); + postdiv = ((control >> 0) & 0xf) + 1; + fbdiv = ((control >> 4) & 0xfff) + 3; + refdiv = ((control >> 16) & 0x1f) + 1; + rate = (parent_rate / (refdiv * postdiv)) * fbdiv; + + return rate; +} + +static const struct clk_ops axxia_pllclk_ops = { + .recalc_rate = axxia_pllclk_recalc, +}; + +/** + * struct axxia_divclk - Axxia clock divider + * @aclk: Common struct + * @reg: Offset into regmap for PLL control register + * @shift: Bit position for divider value + * @width: Number of bits in divider value + */ +struct axxia_divclk { + struct axxia_clk aclk; + u32 reg; + u32 shift; + u32 width; +}; +#define to_axxia_divclk(_aclk) container_of(_aclk, struct axxia_divclk, aclk) + +/** + * axxia_divclk_recalc_rate - Calculate clock divider output rage + */ +static unsigned long +axxia_divclk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + struct axxia_clk *aclk = to_axxia_clk(hw); + struct axxia_divclk *divclk = to_axxia_divclk(aclk); + u32 ctrl, div; + + regmap_read(aclk->regmap, divclk->reg, &ctrl); + div = 1 + ((ctrl >> divclk->shift) & ((1 << divclk->width)-1)); + + return parent_rate / div; +} + +static const struct clk_ops axxia_divclk_ops = { + .recalc_rate = axxia_divclk_recalc_rate, +}; + +/** + * struct axxia_clkmux - Axxia clock mux + * @aclk: Common struct + * @reg: Offset into regmap for PLL control register + * @shift: Bit position for selection value + * @width: Number of bits in selection value + */ +struct axxia_clkmux { + struct axxia_clk aclk; + u32 reg; + u32 shift; + u32 width; +}; +#define to_axxia_clkmux(_aclk) container_of(_aclk, struct axxia_clkmux, aclk) + +/** + * axxia_clkmux_get_parent - Return the index of selected parent clock + */ +static u8 axxia_clkmux_get_parent(struct clk_hw *hw) +{ + struct axxia_clk *aclk = to_axxia_clk(hw); + struct axxia_clkmux *mux = to_axxia_clkmux(aclk); + u32 ctrl, parent; + + regmap_read(aclk->regmap, mux->reg, &ctrl); + parent = (ctrl >> mux->shift) & ((1 << mux->width) - 1); + + return (u8) parent; +} + +static const struct clk_ops axxia_clkmux_ops = { + .get_parent = axxia_clkmux_get_parent, +}; + + +/* + * PLLs + */ + +static struct axxia_pllclk clk_fab_pll = { + .aclk.hw.init = &(struct clk_init_data){ + .name = "clk_fab_pll", + .parent_names = (const char *[]){ + "clk_ref0" + }, + .num_parents = 1, + .ops = &axxia_pllclk_ops, + }, + .reg = 0x01800, +}; + +static struct axxia_pllclk clk_cpu_pll = { + .aclk.hw.init = &(struct clk_init_data){ + .name = "clk_cpu_pll", + .parent_names = (const char *[]){ + "clk_ref0" + }, + .num_parents = 1, + .ops = &axxia_pllclk_ops, + }, + .reg = 0x02000, +}; + +static struct axxia_pllclk clk_sys_pll = { + .aclk.hw.init = &(struct clk_init_data){ + .name = "clk_sys_pll", + .parent_names = (const char *[]){ + "clk_ref0" + }, + .num_parents = 1, + .ops = &axxia_pllclk_ops, + }, + .reg = 0x02800, +}; + +static struct axxia_pllclk clk_sm0_pll = { + .aclk.hw.init = &(struct clk_init_data){ + .name = "clk_sm0_pll", + .parent_names = (const char *[]){ + "clk_ref2" + }, + .num_parents = 1, + .ops = &axxia_pllclk_ops, + }, + .reg = 0x03000, +}; + +static struct axxia_pllclk clk_sm1_pll = { + .aclk.hw.init = &(struct clk_init_data){ + .name = "clk_sm1_pll", + .parent_names = (const char *[]){ + "clk_ref1" + }, + .num_parents = 1, + .ops = &axxia_pllclk_ops, + }, + .reg = 0x03800, +}; + +/* + * Clock dividers + */ + +static struct axxia_divclk clk_cpu0_div = { + .aclk.hw.init = &(struct clk_init_data){ + .name = "clk_cpu0_div", + .parent_names = (const char *[]){ + "clk_cpu_pll" + }, + .num_parents = 1, + .ops = &axxia_divclk_ops, + }, + .reg = 0x10008, + .shift = 0, + .width = 4, +}; + +static struct axxia_divclk clk_cpu1_div = { + .aclk.hw.init = &(struct clk_init_data){ + .name = "clk_cpu1_div", + .parent_names = (const char *[]){ + "clk_cpu_pll" + }, + .num_parents = 1, + .ops = &axxia_divclk_ops, + }, + .reg = 0x10008, + .shift = 4, + .width = 4, +}; + +static struct axxia_divclk clk_cpu2_div = { + .aclk.hw.init = &(struct clk_init_data){ + .name = "clk_cpu2_div", + .parent_names = (const char *[]){ + "clk_cpu_pll" + }, + .num_parents = 1, + .ops = &axxia_divclk_ops, + }, + .reg = 0x10008, + .shift = 8, + .width = 4, +}; + +static struct axxia_divclk clk_cpu3_div = { + .aclk.hw.init = &(struct clk_init_data){ + .name = "clk_cpu3_div", + .parent_names = (const char *[]){ + "clk_cpu_pll" + }, + .num_parents = 1, + .ops = &axxia_divclk_ops, + }, + .reg = 0x10008, + .shift = 12, + .width = 4, +}; + +static struct axxia_divclk clk_nrcp_div = { + .aclk.hw.init = &(struct clk_init_data){ + .name = "clk_nrcp_div", + .parent_names = (const char *[]){ + "clk_sys_pll" + }, + .num_parents = 1, + .ops = &axxia_divclk_ops, + }, + .reg = 0x1000c, + .shift = 0, + .width = 4, +}; + +static struct axxia_divclk clk_sys_div = { + .aclk.hw.init = &(struct clk_init_data){ + .name = "clk_sys_div", + .parent_names = (const char *[]){ + "clk_sys_pll" + }, + .num_parents = 1, + .ops = &axxia_divclk_ops, + }, + .reg = 0x1000c, + .shift = 4, + .width = 4, +}; + +static struct axxia_divclk clk_fab_div = { + .aclk.hw.init = &(struct clk_init_data){ + .name = "clk_fab_div", + .parent_names = (const char *[]){ + "clk_fab_pll" + }, + .num_parents = 1, + .ops = &axxia_divclk_ops, + }, + .reg = 0x1000c, + .shift = 8, + .width = 4, +}; + +static struct axxia_divclk clk_per_div = { + .aclk.hw.init = &(struct clk_init_data){ + .name = "clk_per_div", + .parent_names = (const char *[]){ + "clk_sm1_pll" + }, + .num_parents = 1, + .flags = CLK_IS_BASIC, + .ops = &axxia_divclk_ops, + }, + .reg = 0x1000c, + .shift = 12, + .width = 4, +}; + +static struct axxia_divclk clk_mmc_div = { + .aclk.hw.init = &(struct clk_init_data){ + .name = "clk_mmc_div", + .parent_names = (const char *[]){ + "clk_sm1_pll" + }, + .num_parents = 1, + .flags = CLK_IS_BASIC, + .ops = &axxia_divclk_ops, + }, + .reg = 0x1000c, + .shift = 16, + .width = 4, +}; + +/* + * Clock MUXes + */ + +static struct axxia_clkmux clk_cpu0_mux = { + .aclk.hw.init = &(struct clk_init_data){ + .name = "clk_cpu0", + .parent_names = (const char *[]){ + "clk_ref0", + "clk_cpu_pll", + "clk_cpu0_div", + "clk_cpu0_div" + }, + .num_parents = 4, + .ops = &axxia_clkmux_ops, + }, + .reg = 0x10000, + .shift = 0, + .width = 2, +}; + +static struct axxia_clkmux clk_cpu1_mux = { + .aclk.hw.init = &(struct clk_init_data){ + .name = "clk_cpu1", + .parent_names = (const char *[]){ + "clk_ref0", + "clk_cpu_pll", + "clk_cpu1_div", + "clk_cpu1_div" + }, + .num_parents = 4, + .ops = &axxia_clkmux_ops, + }, + .reg = 0x10000, + .shift = 2, + .width = 2, +}; + +static struct axxia_clkmux clk_cpu2_mux = { + .aclk.hw.init = &(struct clk_init_data){ + .name = "clk_cpu2", + .parent_names = (const char *[]){ + "clk_ref0", + "clk_cpu_pll", + "clk_cpu2_div", + "clk_cpu2_div" + }, + .num_parents = 4, + .ops = &axxia_clkmux_ops, + }, + .reg = 0x10000, + .shift = 4, + .width = 2, +}; + +static struct axxia_clkmux clk_cpu3_mux = { + .aclk.hw.init = &(struct clk_init_data){ + .name = "clk_cpu3", + .parent_names = (const char *[]){ + "clk_ref0", + "clk_cpu_pll", + "clk_cpu3_div", + "clk_cpu3_div" + }, + .num_parents = 4, + .ops = &axxia_clkmux_ops, + }, + .reg = 0x10000, + .shift = 6, + .width = 2, +}; + +static struct axxia_clkmux clk_nrcp_mux = { + .aclk.hw.init = &(struct clk_init_data){ + .name = "clk_nrcp", + .parent_names = (const char *[]){ + "clk_ref0", + "clk_sys_pll", + "clk_nrcp_div", + "clk_nrcp_div" + }, + .num_parents = 4, + .ops = &axxia_clkmux_ops, + }, + .reg = 0x10004, + .shift = 0, + .width = 2, +}; + +static struct axxia_clkmux clk_sys_mux = { + .aclk.hw.init = &(struct clk_init_data){ + .name = "clk_sys", + .parent_names = (const char *[]){ + "clk_ref0", + "clk_sys_pll", + "clk_sys_div", + "clk_sys_div" + }, + .num_parents = 4, + .ops = &axxia_clkmux_ops, + }, + .reg = 0x10004, + .shift = 2, + .width = 2, +}; + +static struct axxia_clkmux clk_fab_mux = { + .aclk.hw.init = &(struct clk_init_data){ + .name = "clk_fab", + .parent_names = (const char *[]){ + "clk_ref0", + "clk_fab_pll", + "clk_fab_div", + "clk_fab_div" + }, + .num_parents = 4, + .ops = &axxia_clkmux_ops, + }, + .reg = 0x10004, + .shift = 4, + .width = 2, +}; + +static struct axxia_clkmux clk_per_mux = { + .aclk.hw.init = &(struct clk_init_data){ + .name = "clk_per", + .parent_names = (const char *[]){ + "clk_ref1", + "clk_per_div" + }, + .num_parents = 2, + .ops = &axxia_clkmux_ops, + }, + .reg = 0x10004, + .shift = 6, + .width = 1, +}; + +static struct axxia_clkmux clk_mmc_mux = { + .aclk.hw.init = &(struct clk_init_data){ + .name = "clk_mmc", + .parent_names = (const char *[]){ + "clk_ref1", + "clk_mmc_div" + }, + .num_parents = 2, + .ops = &axxia_clkmux_ops, + }, + .reg = 0x10004, + .shift = 9, + .width = 1, +}; + +/* Table of all supported clocks indexed by the clock identifiers from the + * device tree binding + */ +static struct axxia_clk *axmclk_clocks[] = { + [AXXIA_CLK_FAB_PLL] = &clk_fab_pll.aclk, + [AXXIA_CLK_CPU_PLL] = &clk_cpu_pll.aclk, + [AXXIA_CLK_SYS_PLL] = &clk_sys_pll.aclk, + [AXXIA_CLK_SM0_PLL] = &clk_sm0_pll.aclk, + [AXXIA_CLK_SM1_PLL] = &clk_sm1_pll.aclk, + [AXXIA_CLK_FAB_DIV] = &clk_fab_div.aclk, + [AXXIA_CLK_SYS_DIV] = &clk_sys_div.aclk, + [AXXIA_CLK_NRCP_DIV] = &clk_nrcp_div.aclk, + [AXXIA_CLK_CPU0_DIV] = &clk_cpu0_div.aclk, + [AXXIA_CLK_CPU1_DIV] = &clk_cpu1_div.aclk, + [AXXIA_CLK_CPU2_DIV] = &clk_cpu2_div.aclk, + [AXXIA_CLK_CPU3_DIV] = &clk_cpu3_div.aclk, + [AXXIA_CLK_PER_DIV] = &clk_per_div.aclk, + [AXXIA_CLK_MMC_DIV] = &clk_mmc_div.aclk, + [AXXIA_CLK_FAB] = &clk_fab_mux.aclk, + [AXXIA_CLK_SYS] = &clk_sys_mux.aclk, + [AXXIA_CLK_NRCP] = &clk_nrcp_mux.aclk, + [AXXIA_CLK_CPU0] = &clk_cpu0_mux.aclk, + [AXXIA_CLK_CPU1] = &clk_cpu1_mux.aclk, + [AXXIA_CLK_CPU2] = &clk_cpu2_mux.aclk, + [AXXIA_CLK_CPU3] = &clk_cpu3_mux.aclk, + [AXXIA_CLK_PER] = &clk_per_mux.aclk, + [AXXIA_CLK_MMC] = &clk_mmc_mux.aclk, +}; + +static const struct regmap_config axmclk_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1fffc, + .fast_io = true, +}; + +static const struct of_device_id axmclk_match_table[] = { + { .compatible = "lsi,axm5516-clks" }, + { } +}; +MODULE_DEVICE_TABLE(of, axmclk_match_table); + +struct axmclk_priv { + struct clk_onecell_data onecell; + struct clk *clks[]; +}; + +static int axmclk_probe(struct platform_device *pdev) +{ + void __iomem *base; + struct resource *res; + int i, ret; + struct device *dev = &pdev->dev; + struct clk *clk; + struct regmap *regmap; + size_t num_clks; + struct axmclk_priv *priv; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + regmap = devm_regmap_init_mmio(dev, base, &axmclk_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + num_clks = ARRAY_SIZE(axmclk_clocks); + pr_info("axmclk: supporting %u clocks\n", num_clks); + priv = devm_kzalloc(dev, sizeof(*priv) + sizeof(*priv->clks) * num_clks, + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->onecell.clks = priv->clks; + priv->onecell.clk_num = num_clks; + + /* Update each entry with the allocated regmap and register the clock + * with the common clock framework + */ + for (i = 0; i < num_clks; i++) { + axmclk_clocks[i]->regmap = regmap; + clk = devm_clk_register(dev, &axmclk_clocks[i]->hw); + if (IS_ERR(clk)) + return PTR_ERR(clk); + priv->clks[i] = clk; + } + + ret = of_clk_add_provider(dev->of_node, + of_clk_src_onecell_get, &priv->onecell); + + return ret; +} + +static int axmclk_remove(struct platform_device *pdev) +{ + of_clk_del_provider(pdev->dev.of_node); + return 0; +} + +static struct platform_driver axmclk_driver = { + .probe = axmclk_probe, + .remove = axmclk_remove, + .driver = { + .name = "clk-axm5516", + .owner = THIS_MODULE, + .of_match_table = axmclk_match_table, + }, +}; + +static int __init axmclk_init(void) +{ + return platform_driver_register(&axmclk_driver); +} +core_initcall(axmclk_init); + +static void __exit axmclk_exit(void) +{ + platform_driver_unregister(&axmclk_driver); +} +module_exit(axmclk_exit); + +MODULE_DESCRIPTION("AXM5516 clock driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:clk-axm5516"); -- cgit v1.2.3 From 2cfe16748bec853cb6b83d19546dfd226898b222 Mon Sep 17 00:00:00 2001 From: Jim Lin Date: Wed, 14 May 2014 17:32:57 -0700 Subject: clk: tegra: Enable hardware control of PLLE Enable hardware control of PLLE spread-spectrum, IDDQ, and enable controls when enabling PLLE. The hardware (e.g. XUSB) using PLLE will use these controls for power-saving optimizations. Signed-off-by: Jim Lin Signed-off-by: Andrew Bresticker Signed-off-by: Mike Turquette --- drivers/clk/tegra/clk-pll.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) (limited to 'drivers/clk') diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index e1769addf435..39e0959b61bd 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -96,10 +96,20 @@ (PLLE_SS_MAX_VAL | PLLE_SS_INC_VAL | PLLE_SS_INCINTRV_VAL) #define PLLE_AUX_PLLP_SEL BIT(2) +#define PLLE_AUX_USE_LOCKDET BIT(3) #define PLLE_AUX_ENABLE_SWCTL BIT(4) +#define PLLE_AUX_SS_SWCTL BIT(6) #define PLLE_AUX_SEQ_ENABLE BIT(24) +#define PLLE_AUX_SEQ_START_STATE BIT(25) #define PLLE_AUX_PLLRE_SEL BIT(28) +#define XUSBIO_PLL_CFG0 0x51c +#define XUSBIO_PLL_CFG0_PADPLL_RESET_SWCTL BIT(0) +#define XUSBIO_PLL_CFG0_CLK_ENABLE_SWCTL BIT(2) +#define XUSBIO_PLL_CFG0_PADPLL_USE_LOCKDET BIT(6) +#define XUSBIO_PLL_CFG0_SEQ_ENABLE BIT(24) +#define XUSBIO_PLL_CFG0_SEQ_START_STATE BIT(25) + #define PLLE_MISC_PLLE_PTS BIT(8) #define PLLE_MISC_IDDQ_SW_VALUE BIT(13) #define PLLE_MISC_IDDQ_SW_CTRL BIT(14) @@ -1318,7 +1328,28 @@ static int clk_plle_tegra114_enable(struct clk_hw *hw) pll_writel(val, PLLE_SS_CTRL, pll); udelay(1); - /* TODO: enable hw control of xusb brick pll */ + /* Enable hw control of xusb brick pll */ + val = pll_readl_misc(pll); + val &= ~PLLE_MISC_IDDQ_SW_CTRL; + pll_writel_misc(val, pll); + + val = pll_readl(pll->params->aux_reg, pll); + val |= (PLLE_AUX_USE_LOCKDET | PLLE_AUX_SEQ_START_STATE); + val &= ~(PLLE_AUX_ENABLE_SWCTL | PLLE_AUX_SS_SWCTL); + pll_writel(val, pll->params->aux_reg, pll); + udelay(1); + val |= PLLE_AUX_SEQ_ENABLE; + pll_writel(val, pll->params->aux_reg, pll); + + val = pll_readl(XUSBIO_PLL_CFG0, pll); + val |= (XUSBIO_PLL_CFG0_PADPLL_USE_LOCKDET | + XUSBIO_PLL_CFG0_SEQ_START_STATE); + val &= ~(XUSBIO_PLL_CFG0_CLK_ENABLE_SWCTL | + XUSBIO_PLL_CFG0_PADPLL_RESET_SWCTL); + pll_writel(val, XUSBIO_PLL_CFG0, pll); + udelay(1); + val |= XUSBIO_PLL_CFG0_SEQ_ENABLE; + pll_writel(val, XUSBIO_PLL_CFG0, pll); out: if (pll->lock) -- cgit v1.2.3 From 9d61707b1f83324fc30918787cb6ef101997ecbd Mon Sep 17 00:00:00 2001 From: Jim Lin Date: Wed, 14 May 2014 17:32:58 -0700 Subject: clk: tegra: Fix xusb_fs_src mux The parent-to-index mapping for xusb_fs_src is incorrect. Fix it by adding a mux table. Signed-off-by: Jim Lin Signed-off-by: Andrew Bresticker Signed-off-by: Mike Turquette --- drivers/clk/tegra/clk-tegra-periph.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/clk') diff --git a/drivers/clk/tegra/clk-tegra-periph.c b/drivers/clk/tegra/clk-tegra-periph.c index 1fa5c3f33b20..a4063bc25208 100644 --- a/drivers/clk/tegra/clk-tegra-periph.c +++ b/drivers/clk/tegra/clk-tegra-periph.c @@ -329,7 +329,9 @@ static u32 mux_clkm_pllp_pllc_pllre_idx[] = { static const char *mux_clkm_48M_pllp_480M[] = { "clk_m", "pll_u_48M", "pll_p", "pll_u_480M" }; -#define mux_clkm_48M_pllp_480M_idx NULL +static u32 mux_clkm_48M_pllp_480M_idx[] = { + [0] = 0, [1] = 2, [2] = 4, [3] = 6, +}; static const char *mux_clkm_pllre_clk32_480M_pllc_ref[] = { "clk_m", "pll_re_out", "clk_32k", "pll_u_480M", "pll_c", "pll_ref" -- cgit v1.2.3 From 5c992afcf8e4f91fac05d39b86c7f7922a50145c Mon Sep 17 00:00:00 2001 From: Andrew Bresticker Date: Wed, 14 May 2014 17:32:59 -0700 Subject: clk: tegra: Fix xusb_hs_src clock hierarchy Currently the Tegra1x4 clock init code hard-codes the mux setting for xusb_hs_src and treats it as a fixed-factor clock. It is, however, a mux which can be parented by either xusb_ss_src/2 or pll_u_60M. Add the fixed-factor clock xusb_ss_div2 and put an entry in periph_clks[] for the xusb_hs_src mux. Signed-off-by: Andrew Bresticker Signed-off-by: Mike Turquette --- drivers/clk/tegra/clk-id.h | 1 + drivers/clk/tegra/clk-tegra-periph.c | 6 ++++++ drivers/clk/tegra/clk-tegra114.c | 15 +++++---------- drivers/clk/tegra/clk-tegra124.c | 15 +++++---------- include/dt-bindings/clock/tegra114-car.h | 3 ++- include/dt-bindings/clock/tegra124-car.h | 3 ++- 6 files changed, 21 insertions(+), 22 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/tegra/clk-id.h b/drivers/clk/tegra/clk-id.h index c39613c519af..0011d547a9f7 100644 --- a/drivers/clk/tegra/clk-id.h +++ b/drivers/clk/tegra/clk-id.h @@ -233,6 +233,7 @@ enum clk_id { tegra_clk_xusb_hs_src, tegra_clk_xusb_ss, tegra_clk_xusb_ss_src, + tegra_clk_xusb_ss_div2, tegra_clk_max, }; diff --git a/drivers/clk/tegra/clk-tegra-periph.c b/drivers/clk/tegra/clk-tegra-periph.c index a4063bc25208..adf6b814b5bc 100644 --- a/drivers/clk/tegra/clk-tegra-periph.c +++ b/drivers/clk/tegra/clk-tegra-periph.c @@ -340,6 +340,11 @@ static u32 mux_clkm_pllre_clk32_480M_pllc_ref_idx[] = { [0] = 0, [1] = 1, [2] = 3, [3] = 3, [4] = 4, [5] = 7, }; +static const char *mux_ss_60M[] = { + "xusb_ss_div2", "pll_u_60M" +}; +#define mux_ss_60M_idx NULL + static const char *mux_d_audio_clk[] = { "pll_a_out0", "pll_p", "clk_m", "spdif_in_sync", "i2s0_sync", "i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync", "vimclk_sync", @@ -501,6 +506,7 @@ static struct tegra_periph_init_data periph_clks[] = { XUSB("xusb_falcon_src", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_FALCON_SRC, 143, TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_falcon_src), XUSB("xusb_fs_src", mux_clkm_48M_pllp_480M, CLK_SOURCE_XUSB_FS_SRC, 143, TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_fs_src), XUSB("xusb_ss_src", mux_clkm_pllre_clk32_480M_pllc_ref, CLK_SOURCE_XUSB_SS_SRC, 143, TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_ss_src), + NODIV("xusb_hs_src", mux_ss_60M, CLK_SOURCE_XUSB_SS_SRC, 25, MASK(1), 143, TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_hs_src, NULL), XUSB("xusb_dev_src", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_DEV_SRC, 95, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_dev_src), }; diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c index 80431f0fb268..841f54fb8c8b 100644 --- a/drivers/clk/tegra/clk-tegra114.c +++ b/drivers/clk/tegra/clk-tegra114.c @@ -142,7 +142,6 @@ #define UTMIPLL_HW_PWRDN_CFG0_IDDQ_SWCTL BIT(0) #define CLK_SOURCE_CSITE 0x1d4 -#define CLK_SOURCE_XUSB_SS_SRC 0x610 #define CLK_SOURCE_EMC 0x19c /* PLLM override registers */ @@ -834,6 +833,7 @@ static struct tegra_clk tegra114_clks[tegra_clk_max] __initdata = { [tegra_clk_xusb_falcon_src] = { .dt_id = TEGRA114_CLK_XUSB_FALCON_SRC, .present = true }, [tegra_clk_xusb_fs_src] = { .dt_id = TEGRA114_CLK_XUSB_FS_SRC, .present = true }, [tegra_clk_xusb_ss_src] = { .dt_id = TEGRA114_CLK_XUSB_SS_SRC, .present = true }, + [tegra_clk_xusb_ss_div2] = { .dt_id = TEGRA114_CLK_XUSB_SS_DIV2, .present = true}, [tegra_clk_xusb_dev_src] = { .dt_id = TEGRA114_CLK_XUSB_DEV_SRC, .present = true }, [tegra_clk_xusb_dev] = { .dt_id = TEGRA114_CLK_XUSB_DEV, .present = true }, [tegra_clk_xusb_hs_src] = { .dt_id = TEGRA114_CLK_XUSB_HS_SRC, .present = true }, @@ -1182,16 +1182,11 @@ static __init void tegra114_periph_clk_init(void __iomem *clk_base, void __iomem *pmc_base) { struct clk *clk; - u32 val; - - /* xusb_hs_src */ - val = readl(clk_base + CLK_SOURCE_XUSB_SS_SRC); - val |= BIT(25); /* always select PLLU_60M */ - writel(val, clk_base + CLK_SOURCE_XUSB_SS_SRC); - clk = clk_register_fixed_factor(NULL, "xusb_hs_src", "pll_u_60M", 0, - 1, 1); - clks[TEGRA114_CLK_XUSB_HS_SRC] = clk; + /* xusb_ss_div2 */ + clk = clk_register_fixed_factor(NULL, "xusb_ss_div2", "xusb_ss_src", 0, + 1, 2); + clks[TEGRA114_CLK_XUSB_SS_DIV2] = clk; /* dsia mux */ clk = clk_register_mux(NULL, "dsia_mux", mux_plld_out0_plld2_out0, diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c index cc37c342c4cb..0dce8c08eb91 100644 --- a/drivers/clk/tegra/clk-tegra124.c +++ b/drivers/clk/tegra/clk-tegra124.c @@ -30,7 +30,6 @@ #define CLK_SOURCE_CSITE 0x1d4 #define CLK_SOURCE_EMC 0x19c -#define CLK_SOURCE_XUSB_SS_SRC 0x610 #define PLLC_BASE 0x80 #define PLLC_OUT 0x84 @@ -925,6 +924,7 @@ static struct tegra_clk tegra124_clks[tegra_clk_max] __initdata = { [tegra_clk_xusb_falcon_src] = { .dt_id = TEGRA124_CLK_XUSB_FALCON_SRC, .present = true }, [tegra_clk_xusb_fs_src] = { .dt_id = TEGRA124_CLK_XUSB_FS_SRC, .present = true }, [tegra_clk_xusb_ss_src] = { .dt_id = TEGRA124_CLK_XUSB_SS_SRC, .present = true }, + [tegra_clk_xusb_ss_div2] = { .dt_id = TEGRA124_CLK_XUSB_SS_DIV2, .present = true }, [tegra_clk_xusb_dev_src] = { .dt_id = TEGRA124_CLK_XUSB_DEV_SRC, .present = true }, [tegra_clk_xusb_dev] = { .dt_id = TEGRA124_CLK_XUSB_DEV, .present = true }, [tegra_clk_xusb_hs_src] = { .dt_id = TEGRA124_CLK_XUSB_HS_SRC, .present = true }, @@ -1105,16 +1105,11 @@ static __init void tegra124_periph_clk_init(void __iomem *clk_base, void __iomem *pmc_base) { struct clk *clk; - u32 val; - - /* xusb_hs_src */ - val = readl(clk_base + CLK_SOURCE_XUSB_SS_SRC); - val |= BIT(25); /* always select PLLU_60M */ - writel(val, clk_base + CLK_SOURCE_XUSB_SS_SRC); - clk = clk_register_fixed_factor(NULL, "xusb_hs_src", "pll_u_60M", 0, - 1, 1); - clks[TEGRA124_CLK_XUSB_HS_SRC] = clk; + /* xusb_ss_div2 */ + clk = clk_register_fixed_factor(NULL, "xusb_ss_div2", "xusb_ss_src", 0, + 1, 2); + clks[TEGRA124_CLK_XUSB_SS_DIV2] = clk; /* dsia mux */ clk = clk_register_mux(NULL, "dsia_mux", mux_plld_out0_plld2_out0, diff --git a/include/dt-bindings/clock/tegra114-car.h b/include/dt-bindings/clock/tegra114-car.h index 6d0d8d8ef31e..fc12621fb432 100644 --- a/include/dt-bindings/clock/tegra114-car.h +++ b/include/dt-bindings/clock/tegra114-car.h @@ -337,6 +337,7 @@ #define TEGRA114_CLK_CLK_OUT_3_MUX 308 #define TEGRA114_CLK_DSIA_MUX 309 #define TEGRA114_CLK_DSIB_MUX 310 -#define TEGRA114_CLK_CLK_MAX 311 +#define TEGRA114_CLK_XUSB_SS_DIV2 311 +#define TEGRA114_CLK_CLK_MAX 312 #endif /* _DT_BINDINGS_CLOCK_TEGRA114_CAR_H */ diff --git a/include/dt-bindings/clock/tegra124-car.h b/include/dt-bindings/clock/tegra124-car.h index 433528ab5161..8a4c5892890f 100644 --- a/include/dt-bindings/clock/tegra124-car.h +++ b/include/dt-bindings/clock/tegra124-car.h @@ -336,6 +336,7 @@ #define TEGRA124_CLK_DSIA_MUX 309 #define TEGRA124_CLK_DSIB_MUX 310 #define TEGRA124_CLK_SOR0_LVDS 311 -#define TEGRA124_CLK_CLK_MAX 312 +#define TEGRA124_CLK_XUSB_SS_DIV2 312 +#define TEGRA124_CLK_CLK_MAX 313 #endif /* _DT_BINDINGS_CLOCK_TEGRA124_CAR_H */ -- cgit v1.2.3 From 4a7f10d67b7a015036823856d6669b1f75362ba0 Mon Sep 17 00:00:00 2001 From: Andrew Bresticker Date: Wed, 14 May 2014 17:33:00 -0700 Subject: clk: tegra: Initialize xusb clocks Initialize the XUSB-related clocks with appropriate parents and rates for both Tegra114 and Tegra124. Signed-off-by: Jim Lin Signed-off-by: Andrew Bresticker Signed-off-by: Mike Turquette --- drivers/clk/tegra/clk-tegra114.c | 7 ++++++- drivers/clk/tegra/clk-tegra124.c | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers/clk') diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c index 841f54fb8c8b..b9c8ba258ef0 100644 --- a/drivers/clk/tegra/clk-tegra114.c +++ b/drivers/clk/tegra/clk-tegra114.c @@ -1296,7 +1296,12 @@ static struct tegra_clk_init_table init_table[] __initdata = { {TEGRA114_CLK_GR3D, TEGRA114_CLK_PLL_C2, 300000000, 0}, {TEGRA114_CLK_DSIALP, TEGRA114_CLK_PLL_P, 68000000, 0}, {TEGRA114_CLK_DSIBLP, TEGRA114_CLK_PLL_P, 68000000, 0}, - + {TEGRA114_CLK_PLL_RE_VCO, TEGRA114_CLK_CLK_MAX, 612000000, 0}, + {TEGRA114_CLK_XUSB_SS_SRC, TEGRA114_CLK_PLL_RE_OUT, 122400000, 0}, + {TEGRA114_CLK_XUSB_FS_SRC, TEGRA114_CLK_PLL_U_48M, 48000000, 0}, + {TEGRA114_CLK_XUSB_HS_SRC, TEGRA114_CLK_XUSB_SS_DIV2, 61200000, 0}, + {TEGRA114_CLK_XUSB_FALCON_SRC, TEGRA114_CLK_PLL_P, 204000000, 0}, + {TEGRA114_CLK_XUSB_HOST_SRC, TEGRA114_CLK_PLL_P, 102000000, 0}, /* This MUST be the last entry. */ {TEGRA114_CLK_CLK_MAX, TEGRA114_CLK_CLK_MAX, 0, 0}, }; diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c index 0dce8c08eb91..80efe51fdcdf 100644 --- a/drivers/clk/tegra/clk-tegra124.c +++ b/drivers/clk/tegra/clk-tegra124.c @@ -1363,6 +1363,12 @@ static struct tegra_clk_init_table init_table[] __initdata = { {TEGRA124_CLK_SBC4, TEGRA124_CLK_PLL_P, 12000000, 1}, {TEGRA124_CLK_TSEC, TEGRA124_CLK_PLL_C3, 0, 0}, {TEGRA124_CLK_MSENC, TEGRA124_CLK_PLL_C3, 0, 0}, + {TEGRA124_CLK_PLL_RE_VCO, TEGRA124_CLK_CLK_MAX, 672000000, 0}, + {TEGRA124_CLK_XUSB_SS_SRC, TEGRA124_CLK_PLL_U_480M, 120000000, 0}, + {TEGRA124_CLK_XUSB_FS_SRC, TEGRA124_CLK_PLL_U_48M, 48000000, 0}, + {TEGRA124_CLK_XUSB_HS_SRC, TEGRA124_CLK_PLL_U_60M, 60000000, 0}, + {TEGRA124_CLK_XUSB_FALCON_SRC, TEGRA124_CLK_PLL_RE_OUT, 224000000, 0}, + {TEGRA124_CLK_XUSB_HOST_SRC, TEGRA124_CLK_PLL_RE_OUT, 112000000, 0}, /* This MUST be the last entry. */ {TEGRA124_CLK_CLK_MAX, TEGRA124_CLK_CLK_MAX, 0, 0}, }; -- cgit v1.2.3 From 1923ca92a618b6107d3a44b5c9f9360b104dcddb Mon Sep 17 00:00:00 2001 From: Ulrich Hecht Date: Fri, 23 May 2014 15:27:15 +0200 Subject: clk: shmobile: Add R8A7740-specific clock support Driver for the R8A7740's clocks that are too specific to be supported by a generic driver. Signed-off-by: Ulrich Hecht Acked-by: Geert Uytterhoeven Acked-by: Laurent Pinchart Signed-off-by: Mike Turquette --- .../bindings/clock/renesas,r8a7740-cpg-clocks.txt | 41 +++++ drivers/clk/shmobile/Makefile | 1 + drivers/clk/shmobile/clk-r8a7740.c | 199 +++++++++++++++++++++ 3 files changed, 241 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/renesas,r8a7740-cpg-clocks.txt create mode 100644 drivers/clk/shmobile/clk-r8a7740.c (limited to 'drivers/clk') diff --git a/Documentation/devicetree/bindings/clock/renesas,r8a7740-cpg-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,r8a7740-cpg-clocks.txt new file mode 100644 index 000000000000..2c03302f86ed --- /dev/null +++ b/Documentation/devicetree/bindings/clock/renesas,r8a7740-cpg-clocks.txt @@ -0,0 +1,41 @@ +These bindings should be considered EXPERIMENTAL for now. + +* Renesas R8A7740 Clock Pulse Generator (CPG) + +The CPG generates core clocks for the R8A7740 SoC. It includes three PLLs +and several fixed ratio and variable ratio dividers. + +Required Properties: + + - compatible: Must be "renesas,r8a7740-cpg-clocks" + + - reg: Base address and length of the memory resource used by the CPG + + - clocks: Reference to the three parent clocks + - #clock-cells: Must be 1 + - clock-output-names: The names of the clocks. Supported clocks are + "system", "pllc0", "pllc1", "pllc2", "r", "usb24s", "i", "zg", "b", + "m1", "hp", "hpp", "usbp", "s", "zb", "m3", and "cp". + + - renesas,mode: board-specific settings of the MD_CK* bits + + +Example +------- + +cpg_clocks: cpg_clocks@e6150000 { + compatible = "renesas,r8a7740-cpg-clocks"; + reg = <0xe6150000 0x10000>; + clocks = <&extal1_clk>, <&extal2_clk>, <&extalr_clk>; + #clock-cells = <1>; + clock-output-names = "system", "pllc0", "pllc1", + "pllc2", "r", + "usb24s", + "i", "zg", "b", "m1", "hp", + "hpp", "usbp", "s", "zb", "m3", + "cp"; +}; + +&cpg_clocks { + renesas,mode = <0x05>; +}; diff --git a/drivers/clk/shmobile/Makefile b/drivers/clk/shmobile/Makefile index bdf342daefa5..e0029237827a 100644 --- a/drivers/clk/shmobile/Makefile +++ b/drivers/clk/shmobile/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_ARCH_EMEV2) += clk-emev2.o obj-$(CONFIG_ARCH_R7S72100) += clk-rz.o +obj-$(CONFIG_ARCH_R8A7740) += clk-r8a7740.o obj-$(CONFIG_ARCH_R8A7779) += clk-r8a7779.o obj-$(CONFIG_ARCH_R8A7790) += clk-rcar-gen2.o obj-$(CONFIG_ARCH_R8A7791) += clk-rcar-gen2.o diff --git a/drivers/clk/shmobile/clk-r8a7740.c b/drivers/clk/shmobile/clk-r8a7740.c new file mode 100644 index 000000000000..1e2eaae21e01 --- /dev/null +++ b/drivers/clk/shmobile/clk-r8a7740.c @@ -0,0 +1,199 @@ +/* + * r8a7740 Core CPG Clocks + * + * Copyright (C) 2014 Ulrich Hecht + * + * 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 +#include +#include + +struct r8a7740_cpg { + struct clk_onecell_data data; + spinlock_t lock; + void __iomem *reg; +}; + +#define CPG_FRQCRA 0x00 +#define CPG_FRQCRB 0x04 +#define CPG_PLLC2CR 0x2c +#define CPG_USBCKCR 0x8c +#define CPG_FRQCRC 0xe0 + +#define CLK_ENABLE_ON_INIT BIT(0) + +struct div4_clk { + const char *name; + unsigned int reg; + unsigned int shift; + int flags; +}; + +static struct div4_clk div4_clks[] = { + { "i", CPG_FRQCRA, 20, CLK_ENABLE_ON_INIT }, + { "zg", CPG_FRQCRA, 16, CLK_ENABLE_ON_INIT }, + { "b", CPG_FRQCRA, 8, CLK_ENABLE_ON_INIT }, + { "m1", CPG_FRQCRA, 4, CLK_ENABLE_ON_INIT }, + { "hp", CPG_FRQCRB, 4, 0 }, + { "hpp", CPG_FRQCRC, 20, 0 }, + { "usbp", CPG_FRQCRC, 16, 0 }, + { "s", CPG_FRQCRC, 12, 0 }, + { "zb", CPG_FRQCRC, 8, 0 }, + { "m3", CPG_FRQCRC, 4, 0 }, + { "cp", CPG_FRQCRC, 0, 0 }, + { NULL, 0, 0, 0 }, +}; + +static const struct clk_div_table div4_div_table[] = { + { 0, 2 }, { 1, 3 }, { 2, 4 }, { 3, 6 }, { 4, 8 }, { 5, 12 }, + { 6, 16 }, { 7, 18 }, { 8, 24 }, { 9, 32 }, { 10, 36 }, { 11, 48 }, + { 13, 72 }, { 14, 96 }, { 0, 0 } +}; + +static u32 cpg_mode __initdata; + +static struct clk * __init +r8a7740_cpg_register_clock(struct device_node *np, struct r8a7740_cpg *cpg, + const char *name) +{ + const struct clk_div_table *table = NULL; + const char *parent_name; + unsigned int shift, reg; + unsigned int mult = 1; + unsigned int div = 1; + + if (!strcmp(name, "r")) { + switch (cpg_mode & (BIT(2) | BIT(1))) { + case BIT(1) | BIT(2): + /* extal1 */ + parent_name = of_clk_get_parent_name(np, 0); + div = 2048; + break; + case BIT(2): + /* extal1 */ + parent_name = of_clk_get_parent_name(np, 0); + div = 1024; + break; + default: + /* extalr */ + parent_name = of_clk_get_parent_name(np, 2); + break; + } + } else if (!strcmp(name, "system")) { + parent_name = of_clk_get_parent_name(np, 0); + if (cpg_mode & BIT(1)) + div = 2; + } else if (!strcmp(name, "pllc0")) { + /* PLLC0/1 are configurable multiplier clocks. Register them as + * fixed factor clocks for now as there's no generic multiplier + * clock implementation and we currently have no need to change + * the multiplier value. + */ + u32 value = clk_readl(cpg->reg + CPG_FRQCRC); + parent_name = "system"; + mult = ((value >> 24) & 0x7f) + 1; + } else if (!strcmp(name, "pllc1")) { + u32 value = clk_readl(cpg->reg + CPG_FRQCRA); + parent_name = "system"; + mult = ((value >> 24) & 0x7f) + 1; + div = 2; + } else if (!strcmp(name, "pllc2")) { + u32 value = clk_readl(cpg->reg + CPG_PLLC2CR); + parent_name = "system"; + mult = ((value >> 24) & 0x3f) + 1; + } else if (!strcmp(name, "usb24s")) { + u32 value = clk_readl(cpg->reg + CPG_USBCKCR); + if (value & BIT(7)) + /* extal2 */ + parent_name = of_clk_get_parent_name(np, 1); + else + parent_name = "system"; + if (!(value & BIT(6))) + div = 2; + } else { + struct div4_clk *c; + for (c = div4_clks; c->name; c++) { + if (!strcmp(name, c->name)) { + parent_name = "pllc1"; + table = div4_div_table; + reg = c->reg; + shift = c->shift; + break; + } + } + if (!c->name) + return ERR_PTR(-EINVAL); + } + + if (!table) { + return clk_register_fixed_factor(NULL, name, parent_name, 0, + mult, div); + } else { + return clk_register_divider_table(NULL, name, parent_name, 0, + cpg->reg + reg, shift, 4, 0, + table, &cpg->lock); + } +} + +static void __init r8a7740_cpg_clocks_init(struct device_node *np) +{ + struct r8a7740_cpg *cpg; + struct clk **clks; + unsigned int i; + int num_clks; + + if (of_property_read_u32(np, "renesas,mode", &cpg_mode)) + pr_warn("%s: missing renesas,mode property\n", __func__); + + num_clks = of_property_count_strings(np, "clock-output-names"); + if (num_clks < 0) { + pr_err("%s: failed to count clocks\n", __func__); + return; + } + + cpg = kzalloc(sizeof(*cpg), GFP_KERNEL); + clks = kzalloc(num_clks * sizeof(*clks), GFP_KERNEL); + if (cpg == NULL || clks == NULL) { + /* We're leaking memory on purpose, there's no point in cleaning + * up as the system won't boot anyway. + */ + return; + } + + spin_lock_init(&cpg->lock); + + cpg->data.clks = clks; + cpg->data.clk_num = num_clks; + + cpg->reg = of_iomap(np, 0); + if (WARN_ON(cpg->reg == NULL)) + return; + + 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 = r8a7740_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(r8a7740_cpg_clks, "renesas,r8a7740-cpg-clocks", + r8a7740_cpg_clocks_init); -- cgit v1.2.3 From 79c6ab509558f9871175c7e4411f857de12cf33b Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Fri, 23 May 2014 18:32:15 +0530 Subject: clk: divider: add CLK_DIVIDER_READ_ONLY flag From: Heiko Stuebner Similar to muxes which already have a read-only flag there sometimes exist dividers which should not be changed by the clock framework but whose value still should be readable. Therefore add a READ_ONLY flag similar to the mux-one to clk-divider Signed-off-by: Heiko Stuebner [changed flag bit to BIT(5) as suggested by Tomasz Figa] Signed-off-by: Thomas Abraham Acked-by: Tomasz Figa Acked-by: Max Schwarz Tested-by: Max Schwarz Signed-off-by: Mike Turquette --- drivers/clk/clk-divider.c | 10 +++++++++- include/linux/clk-provider.h | 4 ++++ 2 files changed, 13 insertions(+), 1 deletion(-) (limited to 'drivers/clk') diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index b3c83966be18..c9343f5d9918 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -361,6 +361,11 @@ const struct clk_ops clk_divider_ops = { }; EXPORT_SYMBOL_GPL(clk_divider_ops); +const struct clk_ops clk_divider_ro_ops = { + .recalc_rate = clk_divider_recalc_rate, +}; +EXPORT_SYMBOL_GPL(clk_divider_ro_ops); + static struct clk *_register_divider(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 shift, u8 width, @@ -386,7 +391,10 @@ static struct clk *_register_divider(struct device *dev, const char *name, } init.name = name; - init.ops = &clk_divider_ops; + if (clk_divider_flags & CLK_DIVIDER_READ_ONLY) + init.ops = &clk_divider_ro_ops; + else + init.ops = &clk_divider_ops; init.flags = flags | CLK_IS_BASIC; init.parent_names = (parent_name ? &parent_name: NULL); init.num_parents = (parent_name ? 1 : 0); diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 40809431641e..c7135dbbcd65 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -320,6 +320,8 @@ struct clk_div_table { * updated to indicate changing divider bits. * CLK_DIVIDER_ROUND_CLOSEST - Makes the best calculated divider to be rounded * to the closest integer instead of the up one. + * CLK_DIVIDER_READ_ONLY - The divider settings are preconfigured and should + * not be changed by the clock framework. */ struct clk_divider { struct clk_hw hw; @@ -336,8 +338,10 @@ struct clk_divider { #define CLK_DIVIDER_ALLOW_ZERO BIT(2) #define CLK_DIVIDER_HIWORD_MASK BIT(3) #define CLK_DIVIDER_ROUND_CLOSEST BIT(4) +#define CLK_DIVIDER_READ_ONLY BIT(5) extern const struct clk_ops clk_divider_ops; +extern const struct clk_ops clk_divider_ro_ops; struct clk *clk_register_divider(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 shift, u8 width, -- cgit v1.2.3 From fb8abb7aefe8454e3b478ef11da78ea25b891ad4 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 25 Mar 2014 12:16:24 +0100 Subject: clk: Neaten clk_summary output - Limit ruler to 80 characters (was: 81), - Widen rate column by 1 for nicer spacing, - Right-align numbers and their column headers, - Move a newline to reduce the number of seq_printf() calls, - Use set_puts() for fixed strings. Before: clock enable_cnt prepare_cnt rate accuracy --------------------------------------------------------------------------------- extal 2 2 20000000 0 thermal 1 1 20000000 0 cp 0 0 10000000 0 tpu0 0 0 10000000 0 tmu0 0 0 10000000 0 main 1 1 20000000 0 pll3 0 0 1600000000 0 ddr 0 0 200000000 0 zb3d2 0 0 200000000 0 zb3 0 0 400000000 0 pll1 4 4 1560000000 0 oscclk 0 0 126953 0 rclk 1 1 31738 0 cmt1 0 0 31738 0 cmt0 1 1 31738 0 imp 0 0 390000000 0 After: clock enable_cnt prepare_cnt rate accuracy -------------------------------------------------------------------------------- extal 2 2 20000000 0 thermal 1 1 20000000 0 cp 0 0 10000000 0 tpu0 0 0 10000000 0 tmu0 0 0 10000000 0 main 1 1 20000000 0 pll3 0 0 1600000000 0 ddr 0 0 200000000 0 zb3d2 0 0 200000000 0 zb3 0 0 400000000 0 pll1 4 4 1560000000 0 oscclk 0 0 126953 0 rclk 1 1 31738 0 cmt1 0 0 31738 0 cmt0 1 1 31738 0 imp 0 0 390000000 0 Signed-off-by: Geert Uytterhoeven Signed-off-by: Mike Turquette --- drivers/clk/clk.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 4d562206d62f..2df2b26297ce 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -106,12 +106,11 @@ static void clk_summary_show_one(struct seq_file *s, struct clk *c, int level) if (!c) return; - seq_printf(s, "%*s%-*s %-11d %-12d %-10lu %-11lu", + seq_printf(s, "%*s%-*s %11d %12d %11lu %10lu\n", level * 3 + 1, "", 30 - level * 3, c->name, c->enable_count, c->prepare_count, clk_get_rate(c), clk_get_accuracy(c)); - seq_printf(s, "\n"); } static void clk_summary_show_subtree(struct seq_file *s, struct clk *c, @@ -132,8 +131,8 @@ static int clk_summary_show(struct seq_file *s, void *data) { struct clk *c; - seq_printf(s, " clock enable_cnt prepare_cnt rate accuracy\n"); - seq_printf(s, "---------------------------------------------------------------------------------\n"); + seq_puts(s, " clock enable_cnt prepare_cnt rate accuracy\n"); + seq_puts(s, "--------------------------------------------------------------------------------\n"); clk_prepare_lock(); -- cgit v1.2.3