diff options
Diffstat (limited to 'drivers/clk')
43 files changed, 2150 insertions, 552 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 7641965d208..da1b416aa57 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -65,10 +65,12 @@ config COMMON_CLK_SI570 clock generators. config COMMON_CLK_S2MPS11 - tristate "Clock driver for S2MPS11 MFD" + tristate "Clock driver for S2MPS11/S5M8767 MFD" depends on MFD_SEC_CORE ---help--- - This driver supports S2MPS11 crystal oscillator clock. + This driver supports S2MPS11/S5M8767 crystal oscillator clock. These + multi-function devices have 3 fixed-rate oscillators, clocked at + 32KHz each. config CLK_TWL6040 tristate "External McPDM functional clock from twl6040" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 5134a79be32..b5221b71238 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o +obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o obj-$(CONFIG_CLK_PPC_CORENET) += clk-ppc-corenet.o diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c index bd313f7816a..c1af80bcdf2 100644 --- a/drivers/clk/at91/clk-master.c +++ b/drivers/clk/at91/clk-master.c @@ -242,7 +242,7 @@ of_at91_clk_master_setup(struct device_node *np, struct at91_pmc *pmc, irq = irq_of_parse_and_map(np, 0); if (!irq) - return; + goto out_free_characteristics; clk = at91_clk_register_master(pmc, irq, name, num_parents, parent_names, layout, diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c index 8137327847c..1127ee46b80 100644 --- a/drivers/clk/clk-axi-clkgen.c +++ b/drivers/clk/clk-axi-clkgen.c @@ -17,23 +17,75 @@ #include <linux/module.h> #include <linux/err.h> -#define AXI_CLKGEN_REG_UPDATE_ENABLE 0x04 -#define AXI_CLKGEN_REG_CLK_OUT1 0x08 -#define AXI_CLKGEN_REG_CLK_OUT2 0x0c -#define AXI_CLKGEN_REG_CLK_DIV 0x10 -#define AXI_CLKGEN_REG_CLK_FB1 0x14 -#define AXI_CLKGEN_REG_CLK_FB2 0x18 -#define AXI_CLKGEN_REG_LOCK1 0x1c -#define AXI_CLKGEN_REG_LOCK2 0x20 -#define AXI_CLKGEN_REG_LOCK3 0x24 -#define AXI_CLKGEN_REG_FILTER1 0x28 -#define AXI_CLKGEN_REG_FILTER2 0x2c +#define AXI_CLKGEN_V1_REG_UPDATE_ENABLE 0x04 +#define AXI_CLKGEN_V1_REG_CLK_OUT1 0x08 +#define AXI_CLKGEN_V1_REG_CLK_OUT2 0x0c +#define AXI_CLKGEN_V1_REG_CLK_DIV 0x10 +#define AXI_CLKGEN_V1_REG_CLK_FB1 0x14 +#define AXI_CLKGEN_V1_REG_CLK_FB2 0x18 +#define AXI_CLKGEN_V1_REG_LOCK1 0x1c +#define AXI_CLKGEN_V1_REG_LOCK2 0x20 +#define AXI_CLKGEN_V1_REG_LOCK3 0x24 +#define AXI_CLKGEN_V1_REG_FILTER1 0x28 +#define AXI_CLKGEN_V1_REG_FILTER2 0x2c + +#define AXI_CLKGEN_V2_REG_RESET 0x40 +#define AXI_CLKGEN_V2_REG_DRP_CNTRL 0x70 +#define AXI_CLKGEN_V2_REG_DRP_STATUS 0x74 + +#define AXI_CLKGEN_V2_RESET_MMCM_ENABLE BIT(1) +#define AXI_CLKGEN_V2_RESET_ENABLE BIT(0) + +#define AXI_CLKGEN_V2_DRP_CNTRL_SEL BIT(29) +#define AXI_CLKGEN_V2_DRP_CNTRL_READ BIT(28) + +#define AXI_CLKGEN_V2_DRP_STATUS_BUSY BIT(16) + +#define MMCM_REG_CLKOUT0_1 0x08 +#define MMCM_REG_CLKOUT0_2 0x09 +#define MMCM_REG_CLK_FB1 0x14 +#define MMCM_REG_CLK_FB2 0x15 +#define MMCM_REG_CLK_DIV 0x16 +#define MMCM_REG_LOCK1 0x18 +#define MMCM_REG_LOCK2 0x19 +#define MMCM_REG_LOCK3 0x1a +#define MMCM_REG_FILTER1 0x4e +#define MMCM_REG_FILTER2 0x4f + +struct axi_clkgen; + +struct axi_clkgen_mmcm_ops { + void (*enable)(struct axi_clkgen *axi_clkgen, bool enable); + int (*write)(struct axi_clkgen *axi_clkgen, unsigned int reg, + unsigned int val, unsigned int mask); + int (*read)(struct axi_clkgen *axi_clkgen, unsigned int reg, + unsigned int *val); +}; struct axi_clkgen { void __iomem *base; + const struct axi_clkgen_mmcm_ops *mmcm_ops; struct clk_hw clk_hw; }; +static void axi_clkgen_mmcm_enable(struct axi_clkgen *axi_clkgen, + bool enable) +{ + axi_clkgen->mmcm_ops->enable(axi_clkgen, enable); +} + +static int axi_clkgen_mmcm_write(struct axi_clkgen *axi_clkgen, + unsigned int reg, unsigned int val, unsigned int mask) +{ + return axi_clkgen->mmcm_ops->write(axi_clkgen, reg, val, mask); +} + +static int axi_clkgen_mmcm_read(struct axi_clkgen *axi_clkgen, + unsigned int reg, unsigned int *val) +{ + return axi_clkgen->mmcm_ops->read(axi_clkgen, reg, val); +} + static uint32_t axi_clkgen_lookup_filter(unsigned int m) { switch (m) { @@ -156,6 +208,148 @@ static void axi_clkgen_read(struct axi_clkgen *axi_clkgen, *val = readl(axi_clkgen->base + reg); } +static unsigned int axi_clkgen_v1_map_mmcm_reg(unsigned int reg) +{ + switch (reg) { + case MMCM_REG_CLKOUT0_1: + return AXI_CLKGEN_V1_REG_CLK_OUT1; + case MMCM_REG_CLKOUT0_2: + return AXI_CLKGEN_V1_REG_CLK_OUT2; + case MMCM_REG_CLK_FB1: + return AXI_CLKGEN_V1_REG_CLK_FB1; + case MMCM_REG_CLK_FB2: + return AXI_CLKGEN_V1_REG_CLK_FB2; + case MMCM_REG_CLK_DIV: + return AXI_CLKGEN_V1_REG_CLK_DIV; + case MMCM_REG_LOCK1: + return AXI_CLKGEN_V1_REG_LOCK1; + case MMCM_REG_LOCK2: + return AXI_CLKGEN_V1_REG_LOCK2; + case MMCM_REG_LOCK3: + return AXI_CLKGEN_V1_REG_LOCK3; + case MMCM_REG_FILTER1: + return AXI_CLKGEN_V1_REG_FILTER1; + case MMCM_REG_FILTER2: + return AXI_CLKGEN_V1_REG_FILTER2; + default: + return 0; + } +} + +static int axi_clkgen_v1_mmcm_write(struct axi_clkgen *axi_clkgen, + unsigned int reg, unsigned int val, unsigned int mask) +{ + reg = axi_clkgen_v1_map_mmcm_reg(reg); + if (reg == 0) + return -EINVAL; + + axi_clkgen_write(axi_clkgen, reg, val); + + return 0; +} + +static int axi_clkgen_v1_mmcm_read(struct axi_clkgen *axi_clkgen, + unsigned int reg, unsigned int *val) +{ + reg = axi_clkgen_v1_map_mmcm_reg(reg); + if (reg == 0) + return -EINVAL; + + axi_clkgen_read(axi_clkgen, reg, val); + + return 0; +} + +static void axi_clkgen_v1_mmcm_enable(struct axi_clkgen *axi_clkgen, + bool enable) +{ + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V1_REG_UPDATE_ENABLE, enable); +} + +static const struct axi_clkgen_mmcm_ops axi_clkgen_v1_mmcm_ops = { + .write = axi_clkgen_v1_mmcm_write, + .read = axi_clkgen_v1_mmcm_read, + .enable = axi_clkgen_v1_mmcm_enable, +}; + +static int axi_clkgen_wait_non_busy(struct axi_clkgen *axi_clkgen) +{ + unsigned int timeout = 10000; + unsigned int val; + + do { + axi_clkgen_read(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_STATUS, &val); + } while ((val & AXI_CLKGEN_V2_DRP_STATUS_BUSY) && --timeout); + + if (val & AXI_CLKGEN_V2_DRP_STATUS_BUSY) + return -EIO; + + return val & 0xffff; +} + +static int axi_clkgen_v2_mmcm_read(struct axi_clkgen *axi_clkgen, + unsigned int reg, unsigned int *val) +{ + unsigned int reg_val; + int ret; + + ret = axi_clkgen_wait_non_busy(axi_clkgen); + if (ret < 0) + return ret; + + reg_val = AXI_CLKGEN_V2_DRP_CNTRL_SEL | AXI_CLKGEN_V2_DRP_CNTRL_READ; + reg_val |= (reg << 16); + + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_CNTRL, reg_val); + + ret = axi_clkgen_wait_non_busy(axi_clkgen); + if (ret < 0) + return ret; + + *val = ret; + + return 0; +} + +static int axi_clkgen_v2_mmcm_write(struct axi_clkgen *axi_clkgen, + unsigned int reg, unsigned int val, unsigned int mask) +{ + unsigned int reg_val = 0; + int ret; + + ret = axi_clkgen_wait_non_busy(axi_clkgen); + if (ret < 0) + return ret; + + if (mask != 0xffff) { + axi_clkgen_v2_mmcm_read(axi_clkgen, reg, ®_val); + reg_val &= ~mask; + } + + reg_val |= AXI_CLKGEN_V2_DRP_CNTRL_SEL | (reg << 16) | (val & mask); + + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_DRP_CNTRL, reg_val); + + return 0; +} + +static void axi_clkgen_v2_mmcm_enable(struct axi_clkgen *axi_clkgen, + bool enable) +{ + unsigned int val = AXI_CLKGEN_V2_RESET_ENABLE; + + if (enable) + val |= AXI_CLKGEN_V2_RESET_MMCM_ENABLE; + + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_RESET, val); +} + +static const struct axi_clkgen_mmcm_ops axi_clkgen_v2_mmcm_ops = { + .write = axi_clkgen_v2_mmcm_write, + .read = axi_clkgen_v2_mmcm_read, + .enable = axi_clkgen_v2_mmcm_enable, +}; + static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw) { return container_of(clk_hw, struct axi_clkgen, clk_hw); @@ -184,33 +378,29 @@ static int axi_clkgen_set_rate(struct clk_hw *clk_hw, filter = axi_clkgen_lookup_filter(m - 1); lock = axi_clkgen_lookup_lock(m - 1); - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 0); - axi_clkgen_calc_clk_params(dout, &low, &high, &edge, &nocount); - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, - (high << 6) | low); - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT2, - (edge << 7) | (nocount << 6)); + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_1, + (high << 6) | low, 0xefff); + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLKOUT0_2, + (edge << 7) | (nocount << 6), 0x03ff); axi_clkgen_calc_clk_params(d, &low, &high, &edge, &nocount); - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, - (edge << 13) | (nocount << 12) | (high << 6) | low); + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_DIV, + (edge << 13) | (nocount << 12) | (high << 6) | low, 0x3fff); axi_clkgen_calc_clk_params(m, &low, &high, &edge, &nocount); - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, - (high << 6) | low); - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB2, - (edge << 7) | (nocount << 6)); - - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK1, lock & 0x3ff); - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK2, - (((lock >> 16) & 0x1f) << 10) | 0x1); - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK3, - (((lock >> 24) & 0x1f) << 10) | 0x3e9); - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER1, filter >> 16); - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER2, filter); - - axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 1); + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB1, + (high << 6) | low, 0xefff); + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_FB2, + (edge << 7) | (nocount << 6), 0x03ff); + + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK1, lock & 0x3ff, 0x3ff); + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK2, + (((lock >> 16) & 0x1f) << 10) | 0x1, 0x7fff); + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK3, + (((lock >> 24) & 0x1f) << 10) | 0x3e9, 0x7fff); + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER1, filter >> 16, 0x9900); + axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER2, filter, 0x9900); return 0; } @@ -236,11 +426,11 @@ static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw, unsigned int reg; unsigned long long tmp; - axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, ®); + axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_1, ®); dout = (reg & 0x3f) + ((reg >> 6) & 0x3f); - axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, ®); + axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_DIV, ®); d = (reg & 0x3f) + ((reg >> 6) & 0x3f); - axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, ®); + axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB1, ®); m = (reg & 0x3f) + ((reg >> 6) & 0x3f); if (d == 0 || dout == 0) @@ -255,14 +445,45 @@ static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw, return tmp; } +static int axi_clkgen_enable(struct clk_hw *clk_hw) +{ + struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw); + + axi_clkgen_mmcm_enable(axi_clkgen, true); + + return 0; +} + +static void axi_clkgen_disable(struct clk_hw *clk_hw) +{ + struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw); + + axi_clkgen_mmcm_enable(axi_clkgen, false); +} + static const struct clk_ops axi_clkgen_ops = { .recalc_rate = axi_clkgen_recalc_rate, .round_rate = axi_clkgen_round_rate, .set_rate = axi_clkgen_set_rate, + .enable = axi_clkgen_enable, + .disable = axi_clkgen_disable, }; +static const struct of_device_id axi_clkgen_ids[] = { + { + .compatible = "adi,axi-clkgen-1.00.a", + .data = &axi_clkgen_v1_mmcm_ops + }, { + .compatible = "adi,axi-clkgen-2.00.a", + .data = &axi_clkgen_v2_mmcm_ops, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, axi_clkgen_ids); + static int axi_clkgen_probe(struct platform_device *pdev) { + const struct of_device_id *id; struct axi_clkgen *axi_clkgen; struct clk_init_data init; const char *parent_name; @@ -270,10 +491,19 @@ static int axi_clkgen_probe(struct platform_device *pdev) struct resource *mem; struct clk *clk; + if (!pdev->dev.of_node) + return -ENODEV; + + id = of_match_node(axi_clkgen_ids, pdev->dev.of_node); + if (!id) + return -ENODEV; + axi_clkgen = devm_kzalloc(&pdev->dev, sizeof(*axi_clkgen), GFP_KERNEL); if (!axi_clkgen) return -ENOMEM; + axi_clkgen->mmcm_ops = id->data; + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); axi_clkgen->base = devm_ioremap_resource(&pdev->dev, mem); if (IS_ERR(axi_clkgen->base)) @@ -289,10 +519,12 @@ static int axi_clkgen_probe(struct platform_device *pdev) init.name = clk_name; init.ops = &axi_clkgen_ops; - init.flags = 0; + init.flags = CLK_SET_RATE_GATE; init.parent_names = &parent_name; init.num_parents = 1; + axi_clkgen_mmcm_enable(axi_clkgen, false); + axi_clkgen->clk_hw.init = &init; clk = devm_clk_register(&pdev->dev, &axi_clkgen->clk_hw); if (IS_ERR(clk)) @@ -309,12 +541,6 @@ static int axi_clkgen_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id axi_clkgen_ids[] = { - { .compatible = "adi,axi-clkgen-1.00.a" }, - { }, -}; -MODULE_DEVICE_TABLE(of, axi_clkgen_ids); - static struct platform_driver axi_clkgen_driver = { .driver = { .name = "adi-axi-clkgen", diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 5543b7df8e1..ec22112e569 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -24,7 +24,7 @@ * Traits of this clock: * prepare - clk_prepare only ensures that parents are prepared * enable - clk_enable only ensures that parents are enabled - * rate - rate is adjustable. clk->rate = parent->rate / divisor + * rate - rate is adjustable. clk->rate = DIV_ROUND_UP(parent->rate / divisor) * parent - fixed parent. No clk_set_parent support */ @@ -115,7 +115,7 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw, return parent_rate; } - return parent_rate / div; + return DIV_ROUND_UP(parent_rate, div); } /* @@ -185,7 +185,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, } parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), MULT_ROUND_UP(rate, i)); - now = parent_rate / i; + now = DIV_ROUND_UP(parent_rate, i); if (now <= rate && now > best) { bestdiv = i; best = now; @@ -207,7 +207,7 @@ static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate, int div; div = clk_divider_bestdiv(hw, rate, prate); - return *prate / div; + return DIV_ROUND_UP(*prate, div); } static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, @@ -218,7 +218,7 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long flags = 0; u32 val; - div = parent_rate / rate; + div = DIV_ROUND_UP(parent_rate, rate); value = _get_val(divider, div); if (value > div_mask(divider)) diff --git a/drivers/clk/clk-moxart.c b/drivers/clk/clk-moxart.c new file mode 100644 index 00000000000..30a3b6999e1 --- /dev/null +++ b/drivers/clk/clk-moxart.c @@ -0,0 +1,97 @@ +/* + * MOXA ART SoCs clock driver. + * + * Copyright (C) 2013 Jonas Jensen + * + * Jonas Jensen <jonas.jensen@gmail.com> + * + * 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 <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/of_address.h> +#include <linux/clkdev.h> + +void __init moxart_of_pll_clk_init(struct device_node *node) +{ + static void __iomem *base; + struct clk *clk, *ref_clk; + unsigned int mul; + const char *name = node->name; + const char *parent_name; + + of_property_read_string(node, "clock-output-names", &name); + parent_name = of_clk_get_parent_name(node, 0); + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s: of_iomap failed\n", node->full_name); + return; + } + + mul = readl(base + 0x30) >> 3 & 0x3f; + iounmap(base); + + ref_clk = of_clk_get(node, 0); + if (IS_ERR(ref_clk)) { + pr_err("%s: of_clk_get failed\n", node->full_name); + return; + } + + clk = clk_register_fixed_factor(NULL, name, parent_name, 0, mul, 1); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock\n", node->full_name); + return; + } + + clk_register_clkdev(clk, NULL, name); + of_clk_add_provider(node, of_clk_src_simple_get, clk); +} +CLK_OF_DECLARE(moxart_pll_clock, "moxa,moxart-pll-clock", + moxart_of_pll_clk_init); + +void __init moxart_of_apb_clk_init(struct device_node *node) +{ + static void __iomem *base; + struct clk *clk, *pll_clk; + unsigned int div, val; + unsigned int div_idx[] = { 2, 3, 4, 6, 8}; + const char *name = node->name; + const char *parent_name; + + of_property_read_string(node, "clock-output-names", &name); + parent_name = of_clk_get_parent_name(node, 0); + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s: of_iomap failed\n", node->full_name); + return; + } + + val = readl(base + 0xc) >> 4 & 0x7; + iounmap(base); + + if (val > 4) + val = 0; + div = div_idx[val] * 2; + + pll_clk = of_clk_get(node, 0); + if (IS_ERR(pll_clk)) { + pr_err("%s: of_clk_get failed\n", node->full_name); + return; + } + + clk = clk_register_fixed_factor(NULL, name, parent_name, 0, 1, div); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock\n", node->full_name); + return; + } + + clk_register_clkdev(clk, NULL, name); + of_clk_add_provider(node, of_clk_src_simple_get, clk); +} +CLK_OF_DECLARE(moxart_apb_clock, "moxa,moxart-apb-clock", + moxart_of_apb_clk_init); diff --git a/drivers/clk/clk-nomadik.c b/drivers/clk/clk-nomadik.c index 6a934a5296b..05e04ce0f14 100644 --- a/drivers/clk/clk-nomadik.c +++ b/drivers/clk/clk-nomadik.c @@ -494,6 +494,9 @@ static const struct file_operations nomadik_src_clk_debugfs_ops = { static int __init nomadik_src_clk_init_debugfs(void) { + /* Vital for multiplatform */ + if (!src_base) + return -ENODEV; src_pcksr0_boot = readl(src_base + SRC_PCKSR0); src_pcksr1_boot = readl(src_base + SRC_PCKSR1); debugfs_create_file("nomadik-src-clk", S_IFREG | S_IRUGO, diff --git a/drivers/clk/clk-s2mps11.c b/drivers/clk/clk-s2mps11.c index 00a3abe103a..f4c1f0881b6 100644 --- a/drivers/clk/clk-s2mps11.c +++ b/drivers/clk/clk-s2mps11.c @@ -27,6 +27,7 @@ #include <linux/clk-provider.h> #include <linux/platform_device.h> #include <linux/mfd/samsung/s2mps11.h> +#include <linux/mfd/samsung/s5m8767.h> #include <linux/mfd/samsung/core.h> #define s2mps11_name(a) (a->hw.init->name) @@ -48,6 +49,7 @@ struct s2mps11_clk { struct clk_lookup *lookup; u32 mask; bool enabled; + unsigned int reg; }; static struct s2mps11_clk *to_s2mps11_clk(struct clk_hw *hw) @@ -61,7 +63,7 @@ static int s2mps11_clk_prepare(struct clk_hw *hw) int ret; ret = regmap_update_bits(s2mps11->iodev->regmap_pmic, - S2MPS11_REG_RTC_CTRL, + s2mps11->reg, s2mps11->mask, s2mps11->mask); if (!ret) s2mps11->enabled = true; @@ -74,7 +76,7 @@ static void s2mps11_clk_unprepare(struct clk_hw *hw) struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw); int ret; - ret = regmap_update_bits(s2mps11->iodev->regmap_pmic, S2MPS11_REG_RTC_CTRL, + ret = regmap_update_bits(s2mps11->iodev->regmap_pmic, s2mps11->reg, s2mps11->mask, ~s2mps11->mask); if (!ret) @@ -155,6 +157,7 @@ static int s2mps11_clk_probe(struct platform_device *pdev) struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent); struct s2mps11_clk *s2mps11_clks, *s2mps11_clk; struct device_node *clk_np = NULL; + unsigned int s2mps11_reg; int i, ret = 0; u32 val; @@ -169,13 +172,26 @@ static int s2mps11_clk_probe(struct platform_device *pdev) if (IS_ERR(clk_np)) return PTR_ERR(clk_np); + switch(platform_get_device_id(pdev)->driver_data) { + case S2MPS11X: + s2mps11_reg = S2MPS11_REG_RTC_CTRL; + break; + case S5M8767X: + s2mps11_reg = S5M8767_REG_CTRL1; + break; + default: + dev_err(&pdev->dev, "Invalid device type\n"); + return -EINVAL; + }; + for (i = 0; i < S2MPS11_CLKS_NUM; i++, s2mps11_clk++) { s2mps11_clk->iodev = iodev; s2mps11_clk->hw.init = &s2mps11_clks_init[i]; s2mps11_clk->mask = 1 << i; + s2mps11_clk->reg = s2mps11_reg; ret = regmap_read(s2mps11_clk->iodev->regmap_pmic, - S2MPS11_REG_RTC_CTRL, &val); + s2mps11_clk->reg, &val); if (ret < 0) goto err_reg; @@ -241,7 +257,8 @@ static int s2mps11_clk_remove(struct platform_device *pdev) } static const struct platform_device_id s2mps11_clk_id[] = { - { "s2mps11-clk", 0}, + { "s2mps11-clk", S2MPS11X}, + { "s5m8767-clk", S5M8767X}, { }, }; MODULE_DEVICE_TABLE(platform, s2mps11_clk_id); diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 5517944495d..895b3d204e2 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1339,8 +1339,11 @@ static int __clk_speculate_rates(struct clk *clk, unsigned long parent_rate) if (clk->notifier_count) ret = __clk_notify(clk, PRE_RATE_CHANGE, clk->rate, new_rate); - if (ret & NOTIFY_STOP_MASK) + if (ret & NOTIFY_STOP_MASK) { + pr_debug("%s: clk notifier callback for clock %s aborted with error %d\n", + __func__, clk->name, ret); goto out; + } hlist_for_each_entry(child, &clk->children, child_node) { ret = __clk_speculate_rates(child, new_rate); @@ -2226,24 +2229,25 @@ EXPORT_SYMBOL_GPL(devm_clk_unregister); */ int __clk_get(struct clk *clk) { - if (clk && !try_module_get(clk->owner)) - return 0; + if (clk) { + if (!try_module_get(clk->owner)) + return 0; - kref_get(&clk->ref); + kref_get(&clk->ref); + } return 1; } void __clk_put(struct clk *clk) { - if (WARN_ON_ONCE(IS_ERR(clk))) + if (!clk || WARN_ON_ONCE(IS_ERR(clk))) return; clk_prepare_lock(); kref_put(&clk->ref, __clk_release); clk_prepare_unlock(); - if (clk) - module_put(clk->owner); + module_put(clk->owner); } /*** clk rate change notifiers ***/ @@ -2259,20 +2263,11 @@ void __clk_put(struct clk *clk) * re-enter into the clk framework by calling any top-level clk APIs; * this will cause a nested prepare_lock mutex. * - * Pre-change notifier callbacks will be passed the current, pre-change - * rate of the clk via struct clk_notifier_data.old_rate. The new, - * post-change rate of the clk is passed via struct + * In all notification cases cases (pre, post and abort rate change) the + * original clock rate is passed to the callback via struct + * clk_notifier_data.old_rate and the new frequency is passed via struct * clk_notifier_data.new_rate. * - * Post-change notifiers will pass the now-current, post-change rate of - * the clk in both struct clk_notifier_data.old_rate and struct - * clk_notifier_data.new_rate. - * - * Abort-change notifiers are effectively the opposite of pre-change - * notifiers: the original pre-change clk rate is passed in via struct - * clk_notifier_data.new_rate and the failed post-change rate is passed - * in via struct clk_notifier_data.old_rate. - * * clk_notifier_register() must be called from non-atomic context. * Returns -EINVAL if called with null arguments, -ENOMEM upon * allocation failure; otherwise, passes along the return value of @@ -2472,7 +2467,7 @@ EXPORT_SYMBOL_GPL(of_clk_del_provider); struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec) { struct of_clk_provider *provider; - struct clk *clk = ERR_PTR(-ENOENT); + struct clk *clk = ERR_PTR(-EPROBE_DEFER); /* Check if we have such a provider in our array */ list_for_each_entry(provider, &of_clk_providers, link) { @@ -2505,8 +2500,12 @@ EXPORT_SYMBOL_GPL(of_clk_get_parent_count); const char *of_clk_get_parent_name(struct device_node *np, int index) { struct of_phandle_args clkspec; + struct property *prop; const char *clk_name; + const __be32 *vp; + u32 pv; int rc; + int count; if (index < 0) return NULL; @@ -2516,8 +2515,22 @@ const char *of_clk_get_parent_name(struct device_node *np, int index) if (rc) return NULL; + index = clkspec.args_count ? clkspec.args[0] : 0; + count = 0; + + /* if there is an indices property, use it to transfer the index + * specified into an array offset for the clock-output-names property. + */ + of_property_for_each_u32(clkspec.np, "clock-indices", prop, vp, pv) { + if (index == pv) { + index = count; + break; + } + count++; + } + if (of_property_read_string_index(clkspec.np, "clock-output-names", - clkspec.args_count ? clkspec.args[0] : 0, + index, &clk_name) < 0) clk_name = clkspec.np->name; diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c index 48f67218247..a360b2eca5c 100644 --- a/drivers/clk/clkdev.c +++ b/drivers/clk/clkdev.c @@ -167,6 +167,8 @@ struct clk *clk_get(struct device *dev, const char *con_id) clk = of_clk_get_by_name(dev->of_node, con_id); if (!IS_ERR(clk)) return clk; + if (PTR_ERR(clk) == -EPROBE_DEFER) + return clk; } return clk_get_sys(dev_id, con_id); diff --git a/drivers/clk/hisilicon/clk-hi3620.c b/drivers/clk/hisilicon/clk-hi3620.c index cc6dff19d6e..233eba22187 100644 --- a/drivers/clk/hisilicon/clk-hi3620.c +++ b/drivers/clk/hisilicon/clk-hi3620.c @@ -231,3 +231,277 @@ static void __init hi3620_clk_init(struct device_node *np) clk_data); } CLK_OF_DECLARE(hi3620_clk, "hisilicon,hi3620-clock", hi3620_clk_init); + +struct hisi_mmc_clock { + unsigned int id; + const char *name; + const char *parent_name; + unsigned long flags; + u32 clken_reg; + u32 clken_bit; + u32 div_reg; + u32 div_off; + u32 div_bits; + u32 drv_reg; + u32 drv_off; + u32 drv_bits; + u32 sam_reg; + u32 sam_off; + u32 sam_bits; +}; + +struct clk_mmc { + struct clk_hw hw; + u32 id; + void __iomem *clken_reg; + u32 clken_bit; + void __iomem *div_reg; + u32 div_off; + u32 div_bits; + void __iomem *drv_reg; + u32 drv_off; + u32 drv_bits; + void __iomem *sam_reg; + u32 sam_off; + u32 sam_bits; +}; + +#define to_mmc(_hw) container_of(_hw, struct clk_mmc, hw) + +static struct hisi_mmc_clock hi3620_mmc_clks[] __initdata = { + { HI3620_SD_CIUCLK, "sd_bclk1", "sd_clk", CLK_SET_RATE_PARENT, 0x1f8, 0, 0x1f8, 1, 3, 0x1f8, 4, 4, 0x1f8, 8, 4}, + { HI3620_MMC_CIUCLK1, "mmc_bclk1", "mmc_clk1", CLK_SET_RATE_PARENT, 0x1f8, 12, 0x1f8, 13, 3, 0x1f8, 16, 4, 0x1f8, 20, 4}, + { HI3620_MMC_CIUCLK2, "mmc_bclk2", "mmc_clk2", CLK_SET_RATE_PARENT, 0x1f8, 24, 0x1f8, 25, 3, 0x1f8, 28, 4, 0x1fc, 0, 4}, + { HI3620_MMC_CIUCLK3, "mmc_bclk3", "mmc_clk3", CLK_SET_RATE_PARENT, 0x1fc, 4, 0x1fc, 5, 3, 0x1fc, 8, 4, 0x1fc, 12, 4}, +}; + +static unsigned long mmc_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + switch (parent_rate) { + case 26000000: + return 13000000; + case 180000000: + return 25000000; + case 360000000: + return 50000000; + case 720000000: + return 100000000; + case 1440000000: + return 180000000; + default: + return parent_rate; + } +} + +static long mmc_clk_determine_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *best_parent_rate, + struct clk **best_parent_p) +{ + struct clk_mmc *mclk = to_mmc(hw); + unsigned long best = 0; + + if ((rate <= 13000000) && (mclk->id == HI3620_MMC_CIUCLK1)) { + rate = 13000000; + best = 26000000; + } else if (rate <= 26000000) { + rate = 25000000; + best = 180000000; + } else if (rate <= 52000000) { + rate = 50000000; + best = 360000000; + } else if (rate <= 100000000) { + rate = 100000000; + best = 720000000; + } else { + /* max is 180M */ + rate = 180000000; + best = 1440000000; + } + *best_parent_rate = best; + return rate; +} + +static u32 mmc_clk_delay(u32 val, u32 para, u32 off, u32 len) +{ + u32 i; + + if (para >= 0) { + for (i = 0; i < len; i++) { + if (para % 2) + val |= 1 << (off + i); + else + val &= ~(1 << (off + i)); + para = para >> 1; + } + } + return val; +} + +static int mmc_clk_set_timing(struct clk_hw *hw, unsigned long rate) +{ + struct clk_mmc *mclk = to_mmc(hw); + unsigned long flags; + u32 sam, drv, div, val; + static DEFINE_SPINLOCK(mmc_clk_lock); + + switch (rate) { + case 13000000: + sam = 3; + drv = 1; + div = 1; + break; + case 25000000: + sam = 13; + drv = 6; + div = 6; + break; + case 50000000: + sam = 3; + drv = 6; + div = 6; + break; + case 100000000: + sam = 6; + drv = 4; + div = 6; + break; + case 180000000: + sam = 6; + drv = 4; + div = 7; + break; + default: + return -EINVAL; + } + + spin_lock_irqsave(&mmc_clk_lock, flags); + + val = readl_relaxed(mclk->clken_reg); + val &= ~(1 << mclk->clken_bit); + writel_relaxed(val, mclk->clken_reg); + + val = readl_relaxed(mclk->sam_reg); + val = mmc_clk_delay(val, sam, mclk->sam_off, mclk->sam_bits); + writel_relaxed(val, mclk->sam_reg); + + val = readl_relaxed(mclk->drv_reg); + val = mmc_clk_delay(val, drv, mclk->drv_off, mclk->drv_bits); + writel_relaxed(val, mclk->drv_reg); + + val = readl_relaxed(mclk->div_reg); + val = mmc_clk_delay(val, div, mclk->div_off, mclk->div_bits); + writel_relaxed(val, mclk->div_reg); + + val = readl_relaxed(mclk->clken_reg); + val |= 1 << mclk->clken_bit; + writel_relaxed(val, mclk->clken_reg); + + spin_unlock_irqrestore(&mmc_clk_lock, flags); + + return 0; +} + +static int mmc_clk_prepare(struct clk_hw *hw) +{ + struct clk_mmc *mclk = to_mmc(hw); + unsigned long rate; + + if (mclk->id == HI3620_MMC_CIUCLK1) + rate = 13000000; + else + rate = 25000000; + + return mmc_clk_set_timing(hw, rate); +} + +static int mmc_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + return mmc_clk_set_timing(hw, rate); +} + +static struct clk_ops clk_mmc_ops = { + .prepare = mmc_clk_prepare, + .determine_rate = mmc_clk_determine_rate, + .set_rate = mmc_clk_set_rate, + .recalc_rate = mmc_clk_recalc_rate, +}; + +static struct clk *hisi_register_clk_mmc(struct hisi_mmc_clock *mmc_clk, + void __iomem *base, struct device_node *np) +{ + struct clk_mmc *mclk; + struct clk *clk; + struct clk_init_data init; + + mclk = kzalloc(sizeof(*mclk), GFP_KERNEL); + if (!mclk) { + pr_err("%s: fail to allocate mmc clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + init.name = mmc_clk->name; + init.ops = &clk_mmc_ops; + init.flags = mmc_clk->flags | CLK_IS_BASIC; + init.parent_names = (mmc_clk->parent_name ? &mmc_clk->parent_name : NULL); + init.num_parents = (mmc_clk->parent_name ? 1 : 0); + mclk->hw.init = &init; + + mclk->id = mmc_clk->id; + mclk->clken_reg = base + mmc_clk->clken_reg; + mclk->clken_bit = mmc_clk->clken_bit; + mclk->div_reg = base + mmc_clk->div_reg; + mclk->div_off = mmc_clk->div_off; + mclk->div_bits = mmc_clk->div_bits; + mclk->drv_reg = base + mmc_clk->drv_reg; + mclk->drv_off = mmc_clk->drv_off; + mclk->drv_bits = mmc_clk->drv_bits; + mclk->sam_reg = base + mmc_clk->sam_reg; + mclk->sam_off = mmc_clk->sam_off; + mclk->sam_bits = mmc_clk->sam_bits; + + clk = clk_register(NULL, &mclk->hw); + if (WARN_ON(IS_ERR(clk))) + kfree(mclk); + return clk; +} + +static void __init hi3620_mmc_clk_init(struct device_node *node) +{ + void __iomem *base; + int i, num = ARRAY_SIZE(hi3620_mmc_clks); + struct clk_onecell_data *clk_data; + + if (!node) { + pr_err("failed to find pctrl node in DTS\n"); + return; + } + + base = of_iomap(node, 0); + if (!base) { + pr_err("failed to map pctrl\n"); + return; + } + + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); + if (WARN_ON(!clk_data)) + return; + + clk_data->clks = kzalloc(sizeof(struct clk *) * num, GFP_KERNEL); + if (!clk_data->clks) { + pr_err("%s: fail to allocate mmc clk\n", __func__); + return; + } + + for (i = 0; i < num; i++) { + struct hisi_mmc_clock *mmc_clk = &hi3620_mmc_clks[i]; + clk_data->clks[mmc_clk->id] = + hisi_register_clk_mmc(mmc_clk, base, node); + } + + clk_data->clk_num = num; + of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); +} + +CLK_OF_DECLARE(hi3620_mmc_clk, "hisilicon,hi3620-mmc-clock", hi3620_mmc_clk_init); diff --git a/drivers/clk/keystone/gate.c b/drivers/clk/keystone/gate.c index 17a598398a5..86f1e362eaf 100644 --- a/drivers/clk/keystone/gate.c +++ b/drivers/clk/keystone/gate.c @@ -179,6 +179,7 @@ static struct clk *clk_register_psc(struct device *dev, init.name = name; init.ops = &clk_psc_ops; + init.flags = 0; init.parent_names = (parent_name ? &parent_name : NULL); init.num_parents = (parent_name ? 1 : 0); diff --git a/drivers/clk/mvebu/Kconfig b/drivers/clk/mvebu/Kconfig index c339b829d3e..693f7be129f 100644 --- a/drivers/clk/mvebu/Kconfig +++ b/drivers/clk/mvebu/Kconfig @@ -13,6 +13,14 @@ config ARMADA_370_CLK select MVEBU_CLK_CPU select MVEBU_CLK_COREDIV +config ARMADA_375_CLK + bool + select MVEBU_CLK_COMMON + +config ARMADA_38X_CLK + bool + select MVEBU_CLK_COMMON + config ARMADA_XP_CLK bool select MVEBU_CLK_COMMON diff --git a/drivers/clk/mvebu/Makefile b/drivers/clk/mvebu/Makefile index 21bbfb4a9f4..4c66162fb0b 100644 --- a/drivers/clk/mvebu/Makefile +++ b/drivers/clk/mvebu/Makefile @@ -3,6 +3,8 @@ obj-$(CONFIG_MVEBU_CLK_CPU) += clk-cpu.o obj-$(CONFIG_MVEBU_CLK_COREDIV) += clk-corediv.o obj-$(CONFIG_ARMADA_370_CLK) += armada-370.o +obj-$(CONFIG_ARMADA_375_CLK) += armada-375.o +obj-$(CONFIG_ARMADA_38X_CLK) += armada-38x.o obj-$(CONFIG_ARMADA_XP_CLK) += armada-xp.o obj-$(CONFIG_DOVE_CLK) += dove.o obj-$(CONFIG_KIRKWOOD_CLK) += kirkwood.o diff --git a/drivers/clk/mvebu/armada-370.c b/drivers/clk/mvebu/armada-370.c index 81a202d12a7..bef198a8386 100644 --- a/drivers/clk/mvebu/armada-370.c +++ b/drivers/clk/mvebu/armada-370.c @@ -141,13 +141,6 @@ static const struct coreclk_soc_desc a370_coreclks = { .num_ratios = ARRAY_SIZE(a370_coreclk_ratios), }; -static void __init a370_coreclk_init(struct device_node *np) -{ - mvebu_coreclk_setup(np, &a370_coreclks); -} -CLK_OF_DECLARE(a370_core_clk, "marvell,armada-370-core-clock", - a370_coreclk_init); - /* * Clock Gating Control */ @@ -168,9 +161,15 @@ static const struct clk_gating_soc_desc a370_gating_desc[] __initconst = { { } }; -static void __init a370_clk_gating_init(struct device_node *np) +static void __init a370_clk_init(struct device_node *np) { - mvebu_clk_gating_setup(np, a370_gating_desc); + struct device_node *cgnp = + of_find_compatible_node(NULL, NULL, "marvell,armada-370-gating-clock"); + + mvebu_coreclk_setup(np, &a370_coreclks); + + if (cgnp) + mvebu_clk_gating_setup(cgnp, a370_gating_desc); } -CLK_OF_DECLARE(a370_clk_gating, "marvell,armada-370-gating-clock", - a370_clk_gating_init); +CLK_OF_DECLARE(a370_clk, "marvell,armada-370-core-clock", a370_clk_init); + diff --git a/drivers/clk/mvebu/armada-375.c b/drivers/clk/mvebu/armada-375.c new file mode 100644 index 00000000000..c991a4d95e1 --- /dev/null +++ b/drivers/clk/mvebu/armada-375.c @@ -0,0 +1,184 @@ +/* + * Marvell Armada 375 SoC clocks + * + * Copyright (C) 2014 Marvell + * + * Gregory CLEMENT <gregory.clement@free-electrons.com> + * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> + * Andrew Lunn <andrew@lunn.ch> + * + * 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 <linux/kernel.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/of.h> +#include "common.h" + +/* + * Core Clocks + */ + +/* + * For the Armada 375 SoCs, the CPU, DDR and L2 clocks frequencies are + * all modified at the same time, and not separately as for the Armada + * 370 or the Armada XP SoCs. + * + * SAR0[21:17] : CPU frequency DDR frequency L2 frequency + * 6 = 400 MHz 400 MHz 200 MHz + * 15 = 600 MHz 600 MHz 300 MHz + * 21 = 800 MHz 534 MHz 400 MHz + * 25 = 1000 MHz 500 MHz 500 MHz + * others reserved. + * + * SAR0[22] : TCLK frequency + * 0 = 166 MHz + * 1 = 200 MHz + */ + +#define SAR1_A375_TCLK_FREQ_OPT 22 +#define SAR1_A375_TCLK_FREQ_OPT_MASK 0x1 +#define SAR1_A375_CPU_DDR_L2_FREQ_OPT 17 +#define SAR1_A375_CPU_DDR_L2_FREQ_OPT_MASK 0x1F + +static const u32 armada_375_tclk_frequencies[] __initconst = { + 166000000, + 200000000, +}; + +static u32 __init armada_375_get_tclk_freq(void __iomem *sar) +{ + u8 tclk_freq_select; + + tclk_freq_select = ((readl(sar) >> SAR1_A375_TCLK_FREQ_OPT) & + SAR1_A375_TCLK_FREQ_OPT_MASK); + return armada_375_tclk_frequencies[tclk_freq_select]; +} + + +static const u32 armada_375_cpu_frequencies[] __initconst = { + 0, 0, 0, 0, 0, 0, + 400000000, + 0, 0, 0, 0, 0, 0, 0, 0, + 600000000, + 0, 0, 0, 0, 0, + 800000000, + 0, 0, 0, + 1000000000, +}; + +static u32 __init armada_375_get_cpu_freq(void __iomem *sar) +{ + u8 cpu_freq_select; + + cpu_freq_select = ((readl(sar) >> SAR1_A375_CPU_DDR_L2_FREQ_OPT) & + SAR1_A375_CPU_DDR_L2_FREQ_OPT_MASK); + if (cpu_freq_select >= ARRAY_SIZE(armada_375_cpu_frequencies)) { + pr_err("Selected CPU frequency (%d) unsupported\n", + cpu_freq_select); + return 0; + } else + return armada_375_cpu_frequencies[cpu_freq_select]; +} + +enum { A375_CPU_TO_DDR, A375_CPU_TO_L2 }; + +static const struct coreclk_ratio armada_375_coreclk_ratios[] __initconst = { + { .id = A375_CPU_TO_L2, .name = "l2clk" }, + { .id = A375_CPU_TO_DDR, .name = "ddrclk" }, +}; + +static const int armada_375_cpu_l2_ratios[32][2] __initconst = { + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {1, 2}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {1, 2}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {1, 2}, {0, 1}, {0, 1}, + {0, 1}, {1, 2}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, +}; + +static const int armada_375_cpu_ddr_ratios[32][2] __initconst = { + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {1, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {2, 3}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {2, 3}, {0, 1}, {0, 1}, + {0, 1}, {1, 2}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, +}; + +static void __init armada_375_get_clk_ratio( + void __iomem *sar, int id, int *mult, int *div) +{ + u32 opt = ((readl(sar) >> SAR1_A375_CPU_DDR_L2_FREQ_OPT) & + SAR1_A375_CPU_DDR_L2_FREQ_OPT_MASK); + + switch (id) { + case A375_CPU_TO_L2: + *mult = armada_375_cpu_l2_ratios[opt][0]; + *div = armada_375_cpu_l2_ratios[opt][1]; + break; + case A375_CPU_TO_DDR: + *mult = armada_375_cpu_ddr_ratios[opt][0]; + *div = armada_375_cpu_ddr_ratios[opt][1]; + break; + } +} + +static const struct coreclk_soc_desc armada_375_coreclks = { + .get_tclk_freq = armada_375_get_tclk_freq, + .get_cpu_freq = armada_375_get_cpu_freq, + .get_clk_ratio = armada_375_get_clk_ratio, + .ratios = armada_375_coreclk_ratios, + .num_ratios = ARRAY_SIZE(armada_375_coreclk_ratios), +}; + +static void __init armada_375_coreclk_init(struct device_node *np) +{ + mvebu_coreclk_setup(np, &armada_375_coreclks); +} +CLK_OF_DECLARE(armada_375_core_clk, "marvell,armada-375-core-clock", + armada_375_coreclk_init); + +/* + * Clock Gating Control + */ +static const struct clk_gating_soc_desc armada_375_gating_desc[] __initconst = { + { "mu", NULL, 2 }, + { "pp", NULL, 3 }, + { "ptp", NULL, 4 }, + { "pex0", NULL, 5 }, + { "pex1", NULL, 6 }, + { "audio", NULL, 8 }, + { "nd_clk", "nand", 11 }, + { "sata0_link", "sata0_core", 14 }, + { "sata0_core", NULL, 15 }, + { "usb3", NULL, 16 }, + { "sdio", NULL, 17 }, + { "usb", NULL, 18 }, + { "gop", NULL, 19 }, + { "sata1_link", "sata1_core", 20 }, + { "sata1_core", NULL, 21 }, + { "xor0", NULL, 22 }, + { "xor1", NULL, 23 }, + { "copro", NULL, 24 }, + { "tdm", NULL, 25 }, + { "crypto0_enc", NULL, 28 }, + { "crypto0_core", NULL, 29 }, + { "crypto1_enc", NULL, 30 }, + { "crypto1_core", NULL, 31 }, + { } +}; + +static void __init armada_375_clk_gating_init(struct device_node *np) +{ + mvebu_clk_gating_setup(np, armada_375_gating_desc); +} +CLK_OF_DECLARE(armada_375_clk_gating, "marvell,armada-375-gating-clock", + armada_375_clk_gating_init); diff --git a/drivers/clk/mvebu/armada-38x.c b/drivers/clk/mvebu/armada-38x.c new file mode 100644 index 00000000000..8bccf4ecdab --- /dev/null +++ b/drivers/clk/mvebu/armada-38x.c @@ -0,0 +1,167 @@ +/* + * Marvell Armada 380/385 SoC clocks + * + * Copyright (C) 2014 Marvell + * + * Gregory CLEMENT <gregory.clement@free-electrons.com> + * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> + * Andrew Lunn <andrew@lunn.ch> + * + * 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 <linux/kernel.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/of.h> +#include "common.h" + +/* + * SAR[14:10] : Ratios between PCLK0, NBCLK, HCLK and DRAM clocks + * + * SAR[15] : TCLK frequency + * 0 = 250 MHz + * 1 = 200 MHz + */ + +#define SAR_A380_TCLK_FREQ_OPT 15 +#define SAR_A380_TCLK_FREQ_OPT_MASK 0x1 +#define SAR_A380_CPU_DDR_L2_FREQ_OPT 10 +#define SAR_A380_CPU_DDR_L2_FREQ_OPT_MASK 0x1F + +static const u32 armada_38x_tclk_frequencies[] __initconst = { + 250000000, + 200000000, +}; + +static u32 __init armada_38x_get_tclk_freq(void __iomem *sar) +{ + u8 tclk_freq_select; + + tclk_freq_select = ((readl(sar) >> SAR_A380_TCLK_FREQ_OPT) & + SAR_A380_TCLK_FREQ_OPT_MASK); + return armada_38x_tclk_frequencies[tclk_freq_select]; +} + +static const u32 armada_38x_cpu_frequencies[] __initconst = { + 0, 0, 0, 0, + 1066 * 1000 * 1000, 0, 0, 0, + 1332 * 1000 * 1000, 0, 0, 0, + 1600 * 1000 * 1000, +}; + +static u32 __init armada_38x_get_cpu_freq(void __iomem *sar) +{ + u8 cpu_freq_select; + + cpu_freq_select = ((readl(sar) >> SAR_A380_CPU_DDR_L2_FREQ_OPT) & + SAR_A380_CPU_DDR_L2_FREQ_OPT_MASK); + if (cpu_freq_select >= ARRAY_SIZE(armada_38x_cpu_frequencies)) { + pr_err("Selected CPU frequency (%d) unsupported\n", + cpu_freq_select); + return 0; + } + + return armada_38x_cpu_frequencies[cpu_freq_select]; +} + +enum { A380_CPU_TO_DDR, A380_CPU_TO_L2 }; + +static const struct coreclk_ratio armada_38x_coreclk_ratios[] __initconst = { + { .id = A380_CPU_TO_L2, .name = "l2clk" }, + { .id = A380_CPU_TO_DDR, .name = "ddrclk" }, +}; + +static const int armada_38x_cpu_l2_ratios[32][2] __initconst = { + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {1, 2}, {0, 1}, {0, 1}, {0, 1}, + {1, 2}, {0, 1}, {0, 1}, {0, 1}, + {1, 2}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, +}; + +static const int armada_38x_cpu_ddr_ratios[32][2] __initconst = { + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {1, 2}, {0, 1}, {0, 1}, {0, 1}, + {1, 2}, {0, 1}, {0, 1}, {0, 1}, + {1, 2}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, + {0, 1}, {0, 1}, {0, 1}, {0, 1}, +}; + +static void __init armada_38x_get_clk_ratio( + void __iomem *sar, int id, int *mult, int *div) +{ + u32 opt = ((readl(sar) >> SAR_A380_CPU_DDR_L2_FREQ_OPT) & + SAR_A380_CPU_DDR_L2_FREQ_OPT_MASK); + + switch (id) { + case A380_CPU_TO_L2: + *mult = armada_38x_cpu_l2_ratios[opt][0]; + *div = armada_38x_cpu_l2_ratios[opt][1]; + break; + case A380_CPU_TO_DDR: + *mult = armada_38x_cpu_ddr_ratios[opt][0]; + *div = armada_38x_cpu_ddr_ratios[opt][1]; + break; + } +} + +static const struct coreclk_soc_desc armada_38x_coreclks = { + .get_tclk_freq = armada_38x_get_tclk_freq, + .get_cpu_freq = armada_38x_get_cpu_freq, + .get_clk_ratio = armada_38x_get_clk_ratio, + .ratios = armada_38x_coreclk_ratios, + .num_ratios = ARRAY_SIZE(armada_38x_coreclk_ratios), +}; + +static void __init armada_38x_coreclk_init(struct device_node *np) +{ + mvebu_coreclk_setup(np, &armada_38x_coreclks); +} +CLK_OF_DECLARE(armada_38x_core_clk, "marvell,armada-380-core-clock", + armada_38x_coreclk_init); + +/* + * Clock Gating Control + */ +static const struct clk_gating_soc_desc armada_38x_gating_desc[] __initconst = { + { "audio", NULL, 0 }, + { "ge2", NULL, 2 }, + { "ge1", NULL, 3 }, + { "ge0", NULL, 4 }, + { "pex1", NULL, 5 }, + { "pex2", NULL, 6 }, + { "pex3", NULL, 7 }, + { "pex0", NULL, 8 }, + { "usb3h0", NULL, 9 }, + { "usb3h1", NULL, 10 }, + { "usb3d", NULL, 11 }, + { "bm", NULL, 13 }, + { "crypto0z", NULL, 14 }, + { "sata0", NULL, 15 }, + { "crypto1z", NULL, 16 }, + { "sdio", NULL, 17 }, + { "usb2", NULL, 18 }, + { "crypto1", NULL, 21 }, + { "xor0", NULL, 22 }, + { "crypto0", NULL, 23 }, + { "tdm", NULL, 25 }, + { "xor1", NULL, 28 }, + { "sata1", NULL, 30 }, + { } +}; + +static void __init armada_38x_clk_gating_init(struct device_node *np) +{ + mvebu_clk_gating_setup(np, armada_38x_gating_desc); +} +CLK_OF_DECLARE(armada_38x_clk_gating, "marvell,armada-380-gating-clock", + armada_38x_clk_gating_init); diff --git a/drivers/clk/mvebu/armada-xp.c b/drivers/clk/mvebu/armada-xp.c index 9922c4475aa..b3094315a3c 100644 --- a/drivers/clk/mvebu/armada-xp.c +++ b/drivers/clk/mvebu/armada-xp.c @@ -158,13 +158,6 @@ static const struct coreclk_soc_desc axp_coreclks = { .num_ratios = ARRAY_SIZE(axp_coreclk_ratios), }; -static void __init axp_coreclk_init(struct device_node *np) -{ - mvebu_coreclk_setup(np, &axp_coreclks); -} -CLK_OF_DECLARE(axp_core_clk, "marvell,armada-xp-core-clock", - axp_coreclk_init); - /* * Clock Gating Control */ @@ -202,9 +195,14 @@ static const struct clk_gating_soc_desc axp_gating_desc[] __initconst = { { } }; -static void __init axp_clk_gating_init(struct device_node *np) +static void __init axp_clk_init(struct device_node *np) { - mvebu_clk_gating_setup(np, axp_gating_desc); + struct device_node *cgnp = + of_find_compatible_node(NULL, NULL, "marvell,armada-xp-gating-clock"); + + mvebu_coreclk_setup(np, &axp_coreclks); + + if (cgnp) + mvebu_clk_gating_setup(cgnp, axp_gating_desc); } -CLK_OF_DECLARE(axp_clk_gating, "marvell,armada-xp-gating-clock", - axp_clk_gating_init); +CLK_OF_DECLARE(axp_clk, "marvell,armada-xp-core-clock", axp_clk_init); diff --git a/drivers/clk/mvebu/clk-corediv.c b/drivers/clk/mvebu/clk-corediv.c index 7162615bcdc..d1e5863d337 100644 --- a/drivers/clk/mvebu/clk-corediv.c +++ b/drivers/clk/mvebu/clk-corediv.c @@ -18,26 +18,56 @@ #include "common.h" #define CORE_CLK_DIV_RATIO_MASK 0xff -#define CORE_CLK_DIV_RATIO_RELOAD BIT(8) -#define CORE_CLK_DIV_ENABLE_OFFSET 24 -#define CORE_CLK_DIV_RATIO_OFFSET 0x8 +/* + * This structure describes the hardware details (bit offset and mask) + * to configure one particular core divider clock. Those hardware + * details may differ from one SoC to another. This structure is + * therefore typically instantiated statically to describe the + * hardware details. + */ struct clk_corediv_desc { unsigned int mask; unsigned int offset; unsigned int fieldbit; }; +/* + * This structure describes the hardware details to configure the core + * divider clocks on a given SoC. Amongst others, it points to the + * array of core divider clock descriptors for this SoC, as well as + * the corresponding operations to manipulate them. + */ +struct clk_corediv_soc_desc { + const struct clk_corediv_desc *descs; + unsigned int ndescs; + const struct clk_ops ops; + u32 ratio_reload; + u32 enable_bit_offset; + u32 ratio_offset; +}; + +/* + * This structure represents one core divider clock for the clock + * framework, and is dynamically allocated for each core divider clock + * existing in the current SoC. + */ struct clk_corediv { struct clk_hw hw; void __iomem *reg; - struct clk_corediv_desc desc; + const struct clk_corediv_desc *desc; + const struct clk_corediv_soc_desc *soc_desc; spinlock_t lock; }; static struct clk_onecell_data clk_data; -static const struct clk_corediv_desc mvebu_corediv_desc[] __initconst = { +/* + * Description of the core divider clocks available. For now, we + * support only NAND, and it is available at the same register + * locations regardless of the SoC. + */ +static const struct clk_corediv_desc mvebu_corediv_desc[] = { { .mask = 0x3f, .offset = 8, .fieldbit = 1 }, /* NAND clock */ }; @@ -46,8 +76,9 @@ static const struct clk_corediv_desc mvebu_corediv_desc[] __initconst = { static int clk_corediv_is_enabled(struct clk_hw *hwclk) { struct clk_corediv *corediv = to_corediv_clk(hwclk); - struct clk_corediv_desc *desc = &corediv->desc; - u32 enable_mask = BIT(desc->fieldbit) << CORE_CLK_DIV_ENABLE_OFFSET; + const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc; + const struct clk_corediv_desc *desc = corediv->desc; + u32 enable_mask = BIT(desc->fieldbit) << soc_desc->enable_bit_offset; return !!(readl(corediv->reg) & enable_mask); } @@ -55,14 +86,15 @@ static int clk_corediv_is_enabled(struct clk_hw *hwclk) static int clk_corediv_enable(struct clk_hw *hwclk) { struct clk_corediv *corediv = to_corediv_clk(hwclk); - struct clk_corediv_desc *desc = &corediv->desc; + const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc; + const struct clk_corediv_desc *desc = corediv->desc; unsigned long flags = 0; u32 reg; spin_lock_irqsave(&corediv->lock, flags); reg = readl(corediv->reg); - reg |= (BIT(desc->fieldbit) << CORE_CLK_DIV_ENABLE_OFFSET); + reg |= (BIT(desc->fieldbit) << soc_desc->enable_bit_offset); writel(reg, corediv->reg); spin_unlock_irqrestore(&corediv->lock, flags); @@ -73,14 +105,15 @@ static int clk_corediv_enable(struct clk_hw *hwclk) static void clk_corediv_disable(struct clk_hw *hwclk) { struct clk_corediv *corediv = to_corediv_clk(hwclk); - struct clk_corediv_desc *desc = &corediv->desc; + const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc; + const struct clk_corediv_desc *desc = corediv->desc; unsigned long flags = 0; u32 reg; spin_lock_irqsave(&corediv->lock, flags); reg = readl(corediv->reg); - reg &= ~(BIT(desc->fieldbit) << CORE_CLK_DIV_ENABLE_OFFSET); + reg &= ~(BIT(desc->fieldbit) << soc_desc->enable_bit_offset); writel(reg, corediv->reg); spin_unlock_irqrestore(&corediv->lock, flags); @@ -90,10 +123,11 @@ static unsigned long clk_corediv_recalc_rate(struct clk_hw *hwclk, unsigned long parent_rate) { struct clk_corediv *corediv = to_corediv_clk(hwclk); - struct clk_corediv_desc *desc = &corediv->desc; + const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc; + const struct clk_corediv_desc *desc = corediv->desc; u32 reg, div; - reg = readl(corediv->reg + CORE_CLK_DIV_RATIO_OFFSET); + reg = readl(corediv->reg + soc_desc->ratio_offset); div = (reg >> desc->offset) & desc->mask; return parent_rate / div; } @@ -117,7 +151,8 @@ static int clk_corediv_set_rate(struct clk_hw *hwclk, unsigned long rate, unsigned long parent_rate) { struct clk_corediv *corediv = to_corediv_clk(hwclk); - struct clk_corediv_desc *desc = &corediv->desc; + const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc; + const struct clk_corediv_desc *desc = corediv->desc; unsigned long flags = 0; u32 reg, div; @@ -126,17 +161,17 @@ static int clk_corediv_set_rate(struct clk_hw *hwclk, unsigned long rate, spin_lock_irqsave(&corediv->lock, flags); /* Write new divider to the divider ratio register */ - reg = readl(corediv->reg + CORE_CLK_DIV_RATIO_OFFSET); + reg = readl(corediv->reg + soc_desc->ratio_offset); reg &= ~(desc->mask << desc->offset); reg |= (div & desc->mask) << desc->offset; - writel(reg, corediv->reg + CORE_CLK_DIV_RATIO_OFFSET); + writel(reg, corediv->reg + soc_desc->ratio_offset); /* Set reload-force for this clock */ reg = readl(corediv->reg) | BIT(desc->fieldbit); writel(reg, corediv->reg); /* Now trigger the clock update */ - reg = readl(corediv->reg) | CORE_CLK_DIV_RATIO_RELOAD; + reg = readl(corediv->reg) | soc_desc->ratio_reload; writel(reg, corediv->reg); /* @@ -144,7 +179,7 @@ static int clk_corediv_set_rate(struct clk_hw *hwclk, unsigned long rate, * ratios request and the reload request. */ udelay(1000); - reg &= ~(CORE_CLK_DIV_RATIO_MASK | CORE_CLK_DIV_RATIO_RELOAD); + reg &= ~(CORE_CLK_DIV_RATIO_MASK | soc_desc->ratio_reload); writel(reg, corediv->reg); udelay(1000); @@ -153,16 +188,53 @@ static int clk_corediv_set_rate(struct clk_hw *hwclk, unsigned long rate, return 0; } -static const struct clk_ops corediv_ops = { - .enable = clk_corediv_enable, - .disable = clk_corediv_disable, - .is_enabled = clk_corediv_is_enabled, - .recalc_rate = clk_corediv_recalc_rate, - .round_rate = clk_corediv_round_rate, - .set_rate = clk_corediv_set_rate, +static const struct clk_corediv_soc_desc armada370_corediv_soc = { + .descs = mvebu_corediv_desc, + .ndescs = ARRAY_SIZE(mvebu_corediv_desc), + .ops = { + .enable = clk_corediv_enable, + .disable = clk_corediv_disable, + .is_enabled = clk_corediv_is_enabled, + .recalc_rate = clk_corediv_recalc_rate, + .round_rate = clk_corediv_round_rate, + .set_rate = clk_corediv_set_rate, + }, + .ratio_reload = BIT(8), + .enable_bit_offset = 24, + .ratio_offset = 0x8, +}; + +static const struct clk_corediv_soc_desc armada380_corediv_soc = { + .descs = mvebu_corediv_desc, + .ndescs = ARRAY_SIZE(mvebu_corediv_desc), + .ops = { + .enable = clk_corediv_enable, + .disable = clk_corediv_disable, + .is_enabled = clk_corediv_is_enabled, + .recalc_rate = clk_corediv_recalc_rate, + .round_rate = clk_corediv_round_rate, + .set_rate = clk_corediv_set_rate, + }, + .ratio_reload = BIT(8), + .enable_bit_offset = 16, + .ratio_offset = 0x4, }; -static void __init mvebu_corediv_clk_init(struct device_node *node) +static const struct clk_corediv_soc_desc armada375_corediv_soc = { + .descs = mvebu_corediv_desc, + .ndescs = ARRAY_SIZE(mvebu_corediv_desc), + .ops = { + .recalc_rate = clk_corediv_recalc_rate, + .round_rate = clk_corediv_round_rate, + .set_rate = clk_corediv_set_rate, + }, + .ratio_reload = BIT(8), + .ratio_offset = 0x4, +}; + +static void __init +mvebu_corediv_clk_init(struct device_node *node, + const struct clk_corediv_soc_desc *soc_desc) { struct clk_init_data init; struct clk_corediv *corediv; @@ -178,7 +250,7 @@ static void __init mvebu_corediv_clk_init(struct device_node *node) parent_name = of_clk_get_parent_name(node, 0); - clk_data.clk_num = ARRAY_SIZE(mvebu_corediv_desc); + clk_data.clk_num = soc_desc->ndescs; /* clks holds the clock array */ clks = kcalloc(clk_data.clk_num, sizeof(struct clk *), @@ -199,10 +271,11 @@ static void __init mvebu_corediv_clk_init(struct device_node *node) init.num_parents = 1; init.parent_names = &parent_name; init.name = clk_name; - init.ops = &corediv_ops; + init.ops = &soc_desc->ops; init.flags = 0; - corediv[i].desc = mvebu_corediv_desc[i]; + corediv[i].soc_desc = soc_desc; + corediv[i].desc = soc_desc->descs + i; corediv[i].reg = base; corediv[i].hw.init = &init; @@ -219,5 +292,24 @@ err_free_clks: err_unmap: iounmap(base); } -CLK_OF_DECLARE(mvebu_corediv_clk, "marvell,armada-370-corediv-clock", - mvebu_corediv_clk_init); + +static void __init armada370_corediv_clk_init(struct device_node *node) +{ + return mvebu_corediv_clk_init(node, &armada370_corediv_soc); +} +CLK_OF_DECLARE(armada370_corediv_clk, "marvell,armada-370-corediv-clock", + armada370_corediv_clk_init); + +static void __init armada375_corediv_clk_init(struct device_node *node) +{ + return mvebu_corediv_clk_init(node, &armada375_corediv_soc); +} +CLK_OF_DECLARE(armada375_corediv_clk, "marvell,armada-375-corediv-clock", + armada375_corediv_clk_init); + +static void __init armada380_corediv_clk_init(struct device_node *node) +{ + return mvebu_corediv_clk_init(node, &armada380_corediv_soc); +} +CLK_OF_DECLARE(armada380_corediv_clk, "marvell,armada-380-corediv-clock", + armada380_corediv_clk_init); diff --git a/drivers/clk/mvebu/dove.c b/drivers/clk/mvebu/dove.c index 38aee1e3f24..b8c2424ac92 100644 --- a/drivers/clk/mvebu/dove.c +++ b/drivers/clk/mvebu/dove.c @@ -154,12 +154,6 @@ static const struct coreclk_soc_desc dove_coreclks = { .num_ratios = ARRAY_SIZE(dove_coreclk_ratios), }; -static void __init dove_coreclk_init(struct device_node *np) -{ - mvebu_coreclk_setup(np, &dove_coreclks); -} -CLK_OF_DECLARE(dove_core_clk, "marvell,dove-core-clock", dove_coreclk_init); - /* * Clock Gating Control */ @@ -186,9 +180,14 @@ static const struct clk_gating_soc_desc dove_gating_desc[] __initconst = { { } }; -static void __init dove_clk_gating_init(struct device_node *np) +static void __init dove_clk_init(struct device_node *np) { - mvebu_clk_gating_setup(np, dove_gating_desc); + struct device_node *cgnp = + of_find_compatible_node(NULL, NULL, "marvell,dove-gating-clock"); + + mvebu_coreclk_setup(np, &dove_coreclks); + + if (cgnp) + mvebu_clk_gating_setup(cgnp, dove_gating_desc); } -CLK_OF_DECLARE(dove_clk_gating, "marvell,dove-gating-clock", - dove_clk_gating_init); +CLK_OF_DECLARE(dove_clk, "marvell,dove-core-clock", dove_clk_init); diff --git a/drivers/clk/mvebu/kirkwood.c b/drivers/clk/mvebu/kirkwood.c index 2636a55f29f..ddb666a8650 100644 --- a/drivers/clk/mvebu/kirkwood.c +++ b/drivers/clk/mvebu/kirkwood.c @@ -193,13 +193,6 @@ static const struct coreclk_soc_desc kirkwood_coreclks = { .num_ratios = ARRAY_SIZE(kirkwood_coreclk_ratios), }; -static void __init kirkwood_coreclk_init(struct device_node *np) -{ - mvebu_coreclk_setup(np, &kirkwood_coreclks); -} -CLK_OF_DECLARE(kirkwood_core_clk, "marvell,kirkwood-core-clock", - kirkwood_coreclk_init); - static const struct coreclk_soc_desc mv88f6180_coreclks = { .get_tclk_freq = kirkwood_get_tclk_freq, .get_cpu_freq = mv88f6180_get_cpu_freq, @@ -208,13 +201,6 @@ static const struct coreclk_soc_desc mv88f6180_coreclks = { .num_ratios = ARRAY_SIZE(kirkwood_coreclk_ratios), }; -static void __init mv88f6180_coreclk_init(struct device_node *np) -{ - mvebu_coreclk_setup(np, &mv88f6180_coreclks); -} -CLK_OF_DECLARE(mv88f6180_core_clk, "marvell,mv88f6180-core-clock", - mv88f6180_coreclk_init); - /* * Clock Gating Control */ @@ -239,9 +225,21 @@ static const struct clk_gating_soc_desc kirkwood_gating_desc[] __initconst = { { } }; -static void __init kirkwood_clk_gating_init(struct device_node *np) +static void __init kirkwood_clk_init(struct device_node *np) { - mvebu_clk_gating_setup(np, kirkwood_gating_desc); + struct device_node *cgnp = + of_find_compatible_node(NULL, NULL, "marvell,kirkwood-gating-clock"); + + + if (of_device_is_compatible(np, "marvell,mv88f6180-core-clock")) + mvebu_coreclk_setup(np, &mv88f6180_coreclks); + else + mvebu_coreclk_setup(np, &kirkwood_coreclks); + + if (cgnp) + mvebu_clk_gating_setup(cgnp, kirkwood_gating_desc); } -CLK_OF_DECLARE(kirkwood_clk_gating, "marvell,kirkwood-gating-clock", - kirkwood_clk_gating_init); +CLK_OF_DECLARE(kirkwood_clk, "marvell,kirkwood-core-clock", + kirkwood_clk_init); +CLK_OF_DECLARE(mv88f6180_clk, "marvell,mv88f6180-core-clock", + kirkwood_clk_init); diff --git a/drivers/clk/shmobile/clk-div6.c b/drivers/clk/shmobile/clk-div6.c index aac4756ec52..f065f694cb6 100644 --- a/drivers/clk/shmobile/clk-div6.c +++ b/drivers/clk/shmobile/clk-div6.c @@ -23,7 +23,7 @@ #define CPG_DIV6_DIV_MASK 0x3f /** - * struct div6_clock - MSTP gating clock + * struct div6_clock - CPG 6 bit divider clock * @hw: handle between common and hardware-specific interfaces * @reg: IO-remapped register * @div: divisor value (1-64) diff --git a/drivers/clk/shmobile/clk-rcar-gen2.c b/drivers/clk/shmobile/clk-rcar-gen2.c index a59ec217a12..dd272a0d144 100644 --- a/drivers/clk/shmobile/clk-rcar-gen2.c +++ b/drivers/clk/shmobile/clk-rcar-gen2.c @@ -186,7 +186,7 @@ rcar_gen2_cpg_register_clock(struct device_node *np, struct rcar_gen2_cpg *cpg, const char *name) { const struct clk_div_table *table = NULL; - const char *parent_name = "main"; + const char *parent_name; unsigned int shift; unsigned int mult = 1; unsigned int div = 1; @@ -201,23 +201,31 @@ rcar_gen2_cpg_register_clock(struct device_node *np, struct rcar_gen2_cpg *cpg, * the multiplier value. */ u32 value = clk_readl(cpg->reg + CPG_PLL0CR); + parent_name = "main"; mult = ((value >> 24) & ((1 << 7) - 1)) + 1; } else if (!strcmp(name, "pll1")) { + parent_name = "main"; mult = config->pll1_mult / 2; } else if (!strcmp(name, "pll3")) { + parent_name = "main"; mult = config->pll3_mult; } else if (!strcmp(name, "lb")) { + parent_name = "pll1_div2"; div = cpg_mode & BIT(18) ? 36 : 24; } else if (!strcmp(name, "qspi")) { + parent_name = "pll1_div2"; div = (cpg_mode & (BIT(3) | BIT(2) | BIT(1))) == BIT(2) - ? 16 : 20; + ? 8 : 10; } else if (!strcmp(name, "sdh")) { + parent_name = "pll1_div2"; table = cpg_sdh_div_table; shift = 8; } else if (!strcmp(name, "sd0")) { + parent_name = "pll1_div2"; table = cpg_sd01_div_table; shift = 4; } else if (!strcmp(name, "sd1")) { + parent_name = "pll1_div2"; table = cpg_sd01_div_table; shift = 0; } else if (!strcmp(name, "z")) { diff --git a/drivers/clk/socfpga/Makefile b/drivers/clk/socfpga/Makefile index 0303c0b99cd..7e2d15a0c7b 100644 --- a/drivers/clk/socfpga/Makefile +++ b/drivers/clk/socfpga/Makefile @@ -1 +1,4 @@ obj-y += clk.o +obj-y += clk-gate.o +obj-y += clk-pll.o +obj-y += clk-periph.o diff --git a/drivers/clk/socfpga/clk-gate.c b/drivers/clk/socfpga/clk-gate.c new file mode 100644 index 00000000000..501d513bf89 --- /dev/null +++ b/drivers/clk/socfpga/clk-gate.c @@ -0,0 +1,263 @@ +/* + * Copyright 2011-2012 Calxeda, Inc. + * Copyright (C) 2012-2013 Altera Corporation <www.altera.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Based from clk-highbank.c + * + */ +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/mfd/syscon.h> +#include <linux/of.h> +#include <linux/regmap.h> + +#include "clk.h" + +#define SOCFPGA_L4_MP_CLK "l4_mp_clk" +#define SOCFPGA_L4_SP_CLK "l4_sp_clk" +#define SOCFPGA_NAND_CLK "nand_clk" +#define SOCFPGA_NAND_X_CLK "nand_x_clk" +#define SOCFPGA_MMC_CLK "sdmmc_clk" +#define SOCFPGA_GPIO_DB_CLK_OFFSET 0xA8 + +#define div_mask(width) ((1 << (width)) - 1) +#define streq(a, b) (strcmp((a), (b)) == 0) + +#define to_socfpga_gate_clk(p) container_of(p, struct socfpga_gate_clk, hw.hw) + +/* SDMMC Group for System Manager defines */ +#define SYSMGR_SDMMCGRP_CTRL_OFFSET 0x108 +#define SYSMGR_SDMMC_CTRL_SET(smplsel, drvsel) \ + ((((smplsel) & 0x7) << 3) | (((drvsel) & 0x7) << 0)) + +static u8 socfpga_clk_get_parent(struct clk_hw *hwclk) +{ + u32 l4_src; + u32 perpll_src; + + if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) { + l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC); + return l4_src &= 0x1; + } + if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) { + l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC); + return !!(l4_src & 2); + } + + perpll_src = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC); + if (streq(hwclk->init->name, SOCFPGA_MMC_CLK)) + return perpll_src &= 0x3; + if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) || + streq(hwclk->init->name, SOCFPGA_NAND_X_CLK)) + return (perpll_src >> 2) & 3; + + /* QSPI clock */ + return (perpll_src >> 4) & 3; + +} + +static int socfpga_clk_set_parent(struct clk_hw *hwclk, u8 parent) +{ + u32 src_reg; + + if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) { + src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC); + src_reg &= ~0x1; + src_reg |= parent; + writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC); + } else if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) { + src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC); + src_reg &= ~0x2; + src_reg |= (parent << 1); + writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC); + } else { + src_reg = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC); + if (streq(hwclk->init->name, SOCFPGA_MMC_CLK)) { + src_reg &= ~0x3; + src_reg |= parent; + } else if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) || + streq(hwclk->init->name, SOCFPGA_NAND_X_CLK)) { + src_reg &= ~0xC; + src_reg |= (parent << 2); + } else {/* QSPI clock */ + src_reg &= ~0x30; + src_reg |= (parent << 4); + } + writel(src_reg, clk_mgr_base_addr + CLKMGR_PERPLL_SRC); + } + + return 0; +} + +static unsigned long socfpga_clk_recalc_rate(struct clk_hw *hwclk, + unsigned long parent_rate) +{ + struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk); + u32 div = 1, val; + + if (socfpgaclk->fixed_div) + div = socfpgaclk->fixed_div; + else if (socfpgaclk->div_reg) { + val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift; + val &= div_mask(socfpgaclk->width); + /* Check for GPIO_DB_CLK by its offset */ + if ((int) socfpgaclk->div_reg & SOCFPGA_GPIO_DB_CLK_OFFSET) + div = val + 1; + else + div = (1 << val); + } + + return parent_rate / div; +} + +static int socfpga_clk_prepare(struct clk_hw *hwclk) +{ + struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk); + struct regmap *sys_mgr_base_addr; + int i; + u32 hs_timing; + u32 clk_phase[2]; + + if (socfpgaclk->clk_phase[0] || socfpgaclk->clk_phase[1]) { + sys_mgr_base_addr = syscon_regmap_lookup_by_compatible("altr,sys-mgr"); + if (IS_ERR(sys_mgr_base_addr)) { + pr_err("%s: failed to find altr,sys-mgr regmap!\n", __func__); + return -EINVAL; + } + + for (i = 0; i < 2; i++) { + switch (socfpgaclk->clk_phase[i]) { + case 0: + clk_phase[i] = 0; + break; + case 45: + clk_phase[i] = 1; + break; + case 90: + clk_phase[i] = 2; + break; + case 135: + clk_phase[i] = 3; + break; + case 180: + clk_phase[i] = 4; + break; + case 225: + clk_phase[i] = 5; + break; + case 270: + clk_phase[i] = 6; + break; + case 315: + clk_phase[i] = 7; + break; + default: + clk_phase[i] = 0; + break; + } + } + hs_timing = SYSMGR_SDMMC_CTRL_SET(clk_phase[0], clk_phase[1]); + regmap_write(sys_mgr_base_addr, SYSMGR_SDMMCGRP_CTRL_OFFSET, + hs_timing); + } + return 0; +} + +static struct clk_ops gateclk_ops = { + .prepare = socfpga_clk_prepare, + .recalc_rate = socfpga_clk_recalc_rate, + .get_parent = socfpga_clk_get_parent, + .set_parent = socfpga_clk_set_parent, +}; + +static void __init __socfpga_gate_init(struct device_node *node, + const struct clk_ops *ops) +{ + u32 clk_gate[2]; + u32 div_reg[3]; + u32 clk_phase[2]; + u32 fixed_div; + struct clk *clk; + struct socfpga_gate_clk *socfpga_clk; + const char *clk_name = node->name; + const char *parent_name[SOCFPGA_MAX_PARENTS]; + struct clk_init_data init; + int rc; + int i = 0; + + socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL); + if (WARN_ON(!socfpga_clk)) + return; + + rc = of_property_read_u32_array(node, "clk-gate", clk_gate, 2); + if (rc) + clk_gate[0] = 0; + + if (clk_gate[0]) { + socfpga_clk->hw.reg = clk_mgr_base_addr + clk_gate[0]; + socfpga_clk->hw.bit_idx = clk_gate[1]; + + gateclk_ops.enable = clk_gate_ops.enable; + gateclk_ops.disable = clk_gate_ops.disable; + } + + rc = of_property_read_u32(node, "fixed-divider", &fixed_div); + if (rc) + socfpga_clk->fixed_div = 0; + else + socfpga_clk->fixed_div = fixed_div; + + rc = of_property_read_u32_array(node, "div-reg", div_reg, 3); + if (!rc) { + socfpga_clk->div_reg = clk_mgr_base_addr + div_reg[0]; + socfpga_clk->shift = div_reg[1]; + socfpga_clk->width = div_reg[2]; + } else { + socfpga_clk->div_reg = 0; + } + + rc = of_property_read_u32_array(node, "clk-phase", clk_phase, 2); + if (!rc) { + socfpga_clk->clk_phase[0] = clk_phase[0]; + socfpga_clk->clk_phase[1] = clk_phase[1]; + } + + of_property_read_string(node, "clock-output-names", &clk_name); + + init.name = clk_name; + init.ops = ops; + init.flags = 0; + while (i < SOCFPGA_MAX_PARENTS && (parent_name[i] = + of_clk_get_parent_name(node, i)) != NULL) + i++; + + init.parent_names = parent_name; + init.num_parents = i; + socfpga_clk->hw.hw.init = &init; + + clk = clk_register(NULL, &socfpga_clk->hw.hw); + if (WARN_ON(IS_ERR(clk))) { + kfree(socfpga_clk); + return; + } + rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); + if (WARN_ON(rc)) + return; +} + +void __init socfpga_gate_init(struct device_node *node) +{ + __socfpga_gate_init(node, &gateclk_ops); +} diff --git a/drivers/clk/socfpga/clk-periph.c b/drivers/clk/socfpga/clk-periph.c new file mode 100644 index 00000000000..81623a3736f --- /dev/null +++ b/drivers/clk/socfpga/clk-periph.c @@ -0,0 +1,94 @@ +/* + * Copyright 2011-2012 Calxeda, Inc. + * Copyright (C) 2012-2013 Altera Corporation <www.altera.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Based from clk-highbank.c + * + */ +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/of.h> + +#include "clk.h" + +#define to_socfpga_periph_clk(p) container_of(p, struct socfpga_periph_clk, hw.hw) + +static unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk, + unsigned long parent_rate) +{ + struct socfpga_periph_clk *socfpgaclk = to_socfpga_periph_clk(hwclk); + u32 div; + + if (socfpgaclk->fixed_div) + div = socfpgaclk->fixed_div; + else + div = ((readl(socfpgaclk->hw.reg) & 0x1ff) + 1); + + return parent_rate / div; +} + +static const struct clk_ops periclk_ops = { + .recalc_rate = clk_periclk_recalc_rate, +}; + +static __init void __socfpga_periph_init(struct device_node *node, + const struct clk_ops *ops) +{ + u32 reg; + struct clk *clk; + struct socfpga_periph_clk *periph_clk; + const char *clk_name = node->name; + const char *parent_name; + struct clk_init_data init; + int rc; + u32 fixed_div; + + of_property_read_u32(node, "reg", ®); + + periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL); + if (WARN_ON(!periph_clk)) + return; + + periph_clk->hw.reg = clk_mgr_base_addr + reg; + + rc = of_property_read_u32(node, "fixed-divider", &fixed_div); + if (rc) + periph_clk->fixed_div = 0; + else + periph_clk->fixed_div = fixed_div; + + of_property_read_string(node, "clock-output-names", &clk_name); + + init.name = clk_name; + init.ops = ops; + init.flags = 0; + parent_name = of_clk_get_parent_name(node, 0); + init.parent_names = &parent_name; + init.num_parents = 1; + + periph_clk->hw.hw.init = &init; + + clk = clk_register(NULL, &periph_clk->hw.hw); + if (WARN_ON(IS_ERR(clk))) { + kfree(periph_clk); + return; + } + rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); +} + +void __init socfpga_periph_init(struct device_node *node) +{ + __socfpga_periph_init(node, &periclk_ops); +} diff --git a/drivers/clk/socfpga/clk-pll.c b/drivers/clk/socfpga/clk-pll.c new file mode 100644 index 00000000000..88dafb5e962 --- /dev/null +++ b/drivers/clk/socfpga/clk-pll.c @@ -0,0 +1,131 @@ +/* + * Copyright 2011-2012 Calxeda, Inc. + * Copyright (C) 2012-2013 Altera Corporation <www.altera.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Based from clk-highbank.c + * + */ +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/of.h> + +#include "clk.h" + +/* Clock bypass bits */ +#define MAINPLL_BYPASS (1<<0) +#define SDRAMPLL_BYPASS (1<<1) +#define SDRAMPLL_SRC_BYPASS (1<<2) +#define PERPLL_BYPASS (1<<3) +#define PERPLL_SRC_BYPASS (1<<4) + +#define SOCFPGA_PLL_BG_PWRDWN 0 +#define SOCFPGA_PLL_EXT_ENA 1 +#define SOCFPGA_PLL_PWR_DOWN 2 +#define SOCFPGA_PLL_DIVF_MASK 0x0000FFF8 +#define SOCFPGA_PLL_DIVF_SHIFT 3 +#define SOCFPGA_PLL_DIVQ_MASK 0x003F0000 +#define SOCFPGA_PLL_DIVQ_SHIFT 16 + +#define CLK_MGR_PLL_CLK_SRC_SHIFT 22 +#define CLK_MGR_PLL_CLK_SRC_MASK 0x3 + +#define to_socfpga_clk(p) container_of(p, struct socfpga_pll, hw.hw) + +static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk, + unsigned long parent_rate) +{ + struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk); + unsigned long divf, divq, reg; + unsigned long long vco_freq; + unsigned long bypass; + + reg = readl(socfpgaclk->hw.reg); + bypass = readl(clk_mgr_base_addr + CLKMGR_BYPASS); + if (bypass & MAINPLL_BYPASS) + return parent_rate; + + divf = (reg & SOCFPGA_PLL_DIVF_MASK) >> SOCFPGA_PLL_DIVF_SHIFT; + divq = (reg & SOCFPGA_PLL_DIVQ_MASK) >> SOCFPGA_PLL_DIVQ_SHIFT; + vco_freq = (unsigned long long)parent_rate * (divf + 1); + do_div(vco_freq, (1 + divq)); + return (unsigned long)vco_freq; +} + +static u8 clk_pll_get_parent(struct clk_hw *hwclk) +{ + u32 pll_src; + struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk); + + pll_src = readl(socfpgaclk->hw.reg); + return (pll_src >> CLK_MGR_PLL_CLK_SRC_SHIFT) & + CLK_MGR_PLL_CLK_SRC_MASK; +} + +static struct clk_ops clk_pll_ops = { + .recalc_rate = clk_pll_recalc_rate, + .get_parent = clk_pll_get_parent, +}; + +static __init struct clk *__socfpga_pll_init(struct device_node *node, + const struct clk_ops *ops) +{ + u32 reg; + struct clk *clk; + struct socfpga_pll *pll_clk; + const char *clk_name = node->name; + const char *parent_name[SOCFPGA_MAX_PARENTS]; + struct clk_init_data init; + int rc; + int i = 0; + + of_property_read_u32(node, "reg", ®); + + pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL); + if (WARN_ON(!pll_clk)) + return NULL; + + pll_clk->hw.reg = clk_mgr_base_addr + reg; + + of_property_read_string(node, "clock-output-names", &clk_name); + + init.name = clk_name; + init.ops = ops; + init.flags = 0; + + while (i < SOCFPGA_MAX_PARENTS && (parent_name[i] = + of_clk_get_parent_name(node, i)) != NULL) + i++; + + init.num_parents = i; + init.parent_names = parent_name; + pll_clk->hw.hw.init = &init; + + pll_clk->hw.bit_idx = SOCFPGA_PLL_EXT_ENA; + clk_pll_ops.enable = clk_gate_ops.enable; + clk_pll_ops.disable = clk_gate_ops.disable; + + clk = clk_register(NULL, &pll_clk->hw.hw); + if (WARN_ON(IS_ERR(clk))) { + kfree(pll_clk); + return NULL; + } + rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); + return clk; +} + +void __init socfpga_pll_init(struct device_node *node) +{ + __socfpga_pll_init(node, &clk_pll_ops); +} diff --git a/drivers/clk/socfpga/clk.c b/drivers/clk/socfpga/clk.c index 5983a26a8c5..35a960a993f 100644 --- a/drivers/clk/socfpga/clk.c +++ b/drivers/clk/socfpga/clk.c @@ -22,325 +22,23 @@ #include <linux/clk-provider.h> #include <linux/io.h> #include <linux/of.h> +#include <linux/of_address.h> -/* Clock Manager offsets */ -#define CLKMGR_CTRL 0x0 -#define CLKMGR_BYPASS 0x4 -#define CLKMGR_L4SRC 0x70 -#define CLKMGR_PERPLL_SRC 0xAC +#include "clk.h" -/* Clock bypass bits */ -#define MAINPLL_BYPASS (1<<0) -#define SDRAMPLL_BYPASS (1<<1) -#define SDRAMPLL_SRC_BYPASS (1<<2) -#define PERPLL_BYPASS (1<<3) -#define PERPLL_SRC_BYPASS (1<<4) +void __iomem *clk_mgr_base_addr; -#define SOCFPGA_PLL_BG_PWRDWN 0 -#define SOCFPGA_PLL_EXT_ENA 1 -#define SOCFPGA_PLL_PWR_DOWN 2 -#define SOCFPGA_PLL_DIVF_MASK 0x0000FFF8 -#define SOCFPGA_PLL_DIVF_SHIFT 3 -#define SOCFPGA_PLL_DIVQ_MASK 0x003F0000 -#define SOCFPGA_PLL_DIVQ_SHIFT 16 -#define SOCFGPA_MAX_PARENTS 3 - -#define SOCFPGA_L4_MP_CLK "l4_mp_clk" -#define SOCFPGA_L4_SP_CLK "l4_sp_clk" -#define SOCFPGA_NAND_CLK "nand_clk" -#define SOCFPGA_NAND_X_CLK "nand_x_clk" -#define SOCFPGA_MMC_CLK "sdmmc_clk" -#define SOCFPGA_DB_CLK "gpio_db_clk" - -#define div_mask(width) ((1 << (width)) - 1) -#define streq(a, b) (strcmp((a), (b)) == 0) - -extern void __iomem *clk_mgr_base_addr; - -struct socfpga_clk { - struct clk_gate hw; - char *parent_name; - char *clk_name; - u32 fixed_div; - void __iomem *div_reg; - u32 width; /* only valid if div_reg != 0 */ - u32 shift; /* only valid if div_reg != 0 */ -}; -#define to_socfpga_clk(p) container_of(p, struct socfpga_clk, hw.hw) - -static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk, - unsigned long parent_rate) -{ - struct socfpga_clk *socfpgaclk = to_socfpga_clk(hwclk); - unsigned long divf, divq, vco_freq, reg; - unsigned long bypass; - - reg = readl(socfpgaclk->hw.reg); - bypass = readl(clk_mgr_base_addr + CLKMGR_BYPASS); - if (bypass & MAINPLL_BYPASS) - return parent_rate; - - divf = (reg & SOCFPGA_PLL_DIVF_MASK) >> SOCFPGA_PLL_DIVF_SHIFT; - divq = (reg & SOCFPGA_PLL_DIVQ_MASK) >> SOCFPGA_PLL_DIVQ_SHIFT; - vco_freq = parent_rate * (divf + 1); - return vco_freq / (1 + divq); -} - - -static struct clk_ops clk_pll_ops = { - .recalc_rate = clk_pll_recalc_rate, -}; - -static unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk, - unsigned long parent_rate) -{ - struct socfpga_clk *socfpgaclk = to_socfpga_clk(hwclk); - u32 div; - - if (socfpgaclk->fixed_div) - div = socfpgaclk->fixed_div; - else - div = ((readl(socfpgaclk->hw.reg) & 0x1ff) + 1); - - return parent_rate / div; -} - -static const struct clk_ops periclk_ops = { - .recalc_rate = clk_periclk_recalc_rate, -}; - -static __init struct clk *socfpga_clk_init(struct device_node *node, - const struct clk_ops *ops) -{ - u32 reg; - struct clk *clk; - struct socfpga_clk *socfpga_clk; - const char *clk_name = node->name; - const char *parent_name; - struct clk_init_data init; - int rc; - u32 fixed_div; - - of_property_read_u32(node, "reg", ®); - - socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL); - if (WARN_ON(!socfpga_clk)) - return NULL; - - socfpga_clk->hw.reg = clk_mgr_base_addr + reg; - - rc = of_property_read_u32(node, "fixed-divider", &fixed_div); - if (rc) - socfpga_clk->fixed_div = 0; - else - socfpga_clk->fixed_div = fixed_div; - - of_property_read_string(node, "clock-output-names", &clk_name); - - init.name = clk_name; - init.ops = ops; - init.flags = 0; - parent_name = of_clk_get_parent_name(node, 0); - init.parent_names = &parent_name; - init.num_parents = 1; - - socfpga_clk->hw.hw.init = &init; - - if (streq(clk_name, "main_pll") || - streq(clk_name, "periph_pll") || - streq(clk_name, "sdram_pll")) { - socfpga_clk->hw.bit_idx = SOCFPGA_PLL_EXT_ENA; - clk_pll_ops.enable = clk_gate_ops.enable; - clk_pll_ops.disable = clk_gate_ops.disable; - } - - clk = clk_register(NULL, &socfpga_clk->hw.hw); - if (WARN_ON(IS_ERR(clk))) { - kfree(socfpga_clk); - return NULL; - } - rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); - return clk; -} - -static u8 socfpga_clk_get_parent(struct clk_hw *hwclk) -{ - u32 l4_src; - u32 perpll_src; - - if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) { - l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC); - return l4_src &= 0x1; - } - if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) { - l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC); - return !!(l4_src & 2); - } - - perpll_src = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC); - if (streq(hwclk->init->name, SOCFPGA_MMC_CLK)) - return perpll_src &= 0x3; - if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) || - streq(hwclk->init->name, SOCFPGA_NAND_X_CLK)) - return (perpll_src >> 2) & 3; - - /* QSPI clock */ - return (perpll_src >> 4) & 3; - -} - -static int socfpga_clk_set_parent(struct clk_hw *hwclk, u8 parent) -{ - u32 src_reg; - - if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) { - src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC); - src_reg &= ~0x1; - src_reg |= parent; - writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC); - } else if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) { - src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC); - src_reg &= ~0x2; - src_reg |= (parent << 1); - writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC); - } else { - src_reg = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC); - if (streq(hwclk->init->name, SOCFPGA_MMC_CLK)) { - src_reg &= ~0x3; - src_reg |= parent; - } else if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) || - streq(hwclk->init->name, SOCFPGA_NAND_X_CLK)) { - src_reg &= ~0xC; - src_reg |= (parent << 2); - } else {/* QSPI clock */ - src_reg &= ~0x30; - src_reg |= (parent << 4); - } - writel(src_reg, clk_mgr_base_addr + CLKMGR_PERPLL_SRC); - } - - return 0; -} - -static unsigned long socfpga_clk_recalc_rate(struct clk_hw *hwclk, - unsigned long parent_rate) -{ - struct socfpga_clk *socfpgaclk = to_socfpga_clk(hwclk); - u32 div = 1, val; - - if (socfpgaclk->fixed_div) - div = socfpgaclk->fixed_div; - else if (socfpgaclk->div_reg) { - val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift; - val &= div_mask(socfpgaclk->width); - if (streq(hwclk->init->name, SOCFPGA_DB_CLK)) - div = val + 1; - else - div = (1 << val); - } - - return parent_rate / div; -} - -static struct clk_ops gateclk_ops = { - .recalc_rate = socfpga_clk_recalc_rate, - .get_parent = socfpga_clk_get_parent, - .set_parent = socfpga_clk_set_parent, +static const struct of_device_id socfpga_child_clocks[] __initconst = { + { .compatible = "altr,socfpga-pll-clock", socfpga_pll_init, }, + { .compatible = "altr,socfpga-perip-clk", socfpga_periph_init, }, + { .compatible = "altr,socfpga-gate-clk", socfpga_gate_init, }, + {}, }; -static void __init socfpga_gate_clk_init(struct device_node *node, - const struct clk_ops *ops) -{ - u32 clk_gate[2]; - u32 div_reg[3]; - u32 fixed_div; - struct clk *clk; - struct socfpga_clk *socfpga_clk; - const char *clk_name = node->name; - const char *parent_name[SOCFGPA_MAX_PARENTS]; - struct clk_init_data init; - int rc; - int i = 0; - - socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL); - if (WARN_ON(!socfpga_clk)) - return; - - rc = of_property_read_u32_array(node, "clk-gate", clk_gate, 2); - if (rc) - clk_gate[0] = 0; - - if (clk_gate[0]) { - socfpga_clk->hw.reg = clk_mgr_base_addr + clk_gate[0]; - socfpga_clk->hw.bit_idx = clk_gate[1]; - - gateclk_ops.enable = clk_gate_ops.enable; - gateclk_ops.disable = clk_gate_ops.disable; - } - - rc = of_property_read_u32(node, "fixed-divider", &fixed_div); - if (rc) - socfpga_clk->fixed_div = 0; - else - socfpga_clk->fixed_div = fixed_div; - - rc = of_property_read_u32_array(node, "div-reg", div_reg, 3); - if (!rc) { - socfpga_clk->div_reg = clk_mgr_base_addr + div_reg[0]; - socfpga_clk->shift = div_reg[1]; - socfpga_clk->width = div_reg[2]; - } else { - socfpga_clk->div_reg = NULL; - } - - of_property_read_string(node, "clock-output-names", &clk_name); - - init.name = clk_name; - init.ops = ops; - init.flags = 0; - while (i < SOCFGPA_MAX_PARENTS && (parent_name[i] = - of_clk_get_parent_name(node, i)) != NULL) - i++; - - init.parent_names = parent_name; - init.num_parents = i; - socfpga_clk->hw.hw.init = &init; - - clk = clk_register(NULL, &socfpga_clk->hw.hw); - if (WARN_ON(IS_ERR(clk))) { - kfree(socfpga_clk); - return; - } - rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); - if (WARN_ON(rc)) - return; -} - -static void __init socfpga_pll_init(struct device_node *node) +static void __init socfpga_clkmgr_init(struct device_node *node) { - socfpga_clk_init(node, &clk_pll_ops); + clk_mgr_base_addr = of_iomap(node, 0); + of_clk_init(socfpga_child_clocks); } -CLK_OF_DECLARE(socfpga_pll, "altr,socfpga-pll-clock", socfpga_pll_init); +CLK_OF_DECLARE(socfpga_mgr, "altr,clk-mgr", socfpga_clkmgr_init); -static void __init socfpga_periph_init(struct device_node *node) -{ - socfpga_clk_init(node, &periclk_ops); -} -CLK_OF_DECLARE(socfpga_periph, "altr,socfpga-perip-clk", socfpga_periph_init); - -static void __init socfpga_gate_init(struct device_node *node) -{ - socfpga_gate_clk_init(node, &gateclk_ops); -} -CLK_OF_DECLARE(socfpga_gate, "altr,socfpga-gate-clk", socfpga_gate_init); - -void __init socfpga_init_clocks(void) -{ - struct clk *clk; - int ret; - - clk = clk_register_fixed_factor(NULL, "smp_twd", "mpuclk", 0, 1, 4); - ret = clk_register_clkdev(clk, NULL, "smp_twd"); - if (ret) - pr_err("smp_twd alias not registered\n"); -} diff --git a/drivers/clk/socfpga/clk.h b/drivers/clk/socfpga/clk.h new file mode 100644 index 00000000000..d2e54019c94 --- /dev/null +++ b/drivers/clk/socfpga/clk.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2013, Steffen Trumtrar <s.trumtrar@pengutronix.de> + * + * based on drivers/clk/tegra/clk.h + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#ifndef __SOCFPGA_CLK_H +#define __SOCFPGA_CLK_H + +#include <linux/clk-provider.h> +#include <linux/clkdev.h> + +/* Clock Manager offsets */ +#define CLKMGR_CTRL 0x0 +#define CLKMGR_BYPASS 0x4 +#define CLKMGR_L4SRC 0x70 +#define CLKMGR_PERPLL_SRC 0xAC + +#define SOCFPGA_MAX_PARENTS 3 + +extern void __iomem *clk_mgr_base_addr; + +void __init socfpga_pll_init(struct device_node *node); +void __init socfpga_periph_init(struct device_node *node); +void __init socfpga_gate_init(struct device_node *node); + +struct socfpga_pll { + struct clk_gate hw; +}; + +struct socfpga_gate_clk { + struct clk_gate hw; + char *parent_name; + u32 fixed_div; + void __iomem *div_reg; + u32 width; /* only valid if div_reg != 0 */ + u32 shift; /* only valid if div_reg != 0 */ + u32 clk_phase[2]; +}; + +struct socfpga_periph_clk { + struct clk_gate hw; + char *parent_name; + u32 fixed_div; +}; + +#endif /* SOCFPGA_CLK_H */ diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index abb6c5ac8a1..bd7dc733c1c 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -18,6 +18,7 @@ #include <linux/clkdev.h> #include <linux/of.h> #include <linux/of_address.h> +#include <linux/reset-controller.h> #include "clk-factors.h" @@ -51,6 +52,8 @@ static void __init sun4i_osc_clk_setup(struct device_node *node) if (!gate) goto err_free_fixed; + of_property_read_string(node, "clock-output-names", &clk_name); + /* set up gate and fixed rate properties */ gate->reg = of_iomap(node, 0); gate->bit_idx = SUNXI_OSC24M_GATE; @@ -77,7 +80,7 @@ err_free_gate: err_free_fixed: kfree(fixed); } -CLK_OF_DECLARE(sun4i_osc, "allwinner,sun4i-osc-clk", sun4i_osc_clk_setup); +CLK_OF_DECLARE(sun4i_osc, "allwinner,sun4i-a10-osc-clk", sun4i_osc_clk_setup); @@ -249,7 +252,38 @@ static void sun4i_get_pll5_factors(u32 *freq, u32 parent_rate, *n = DIV_ROUND_UP(div, (*k+1)); } +/** + * sun6i_a31_get_pll6_factors() - calculates n, k factors for A31 PLL6 + * PLL6 rate is calculated as follows + * rate = parent_rate * n * (k + 1) / 2 + * parent_rate is always 24Mhz + */ + +static void sun6i_a31_get_pll6_factors(u32 *freq, u32 parent_rate, + u8 *n, u8 *k, u8 *m, u8 *p) +{ + u8 div; + + /* + * We always have 24MHz / 2, so we can just say that our + * parent clock is 12MHz. + */ + parent_rate = parent_rate / 2; + /* Normalize value to a parent_rate multiple (24M / 2) */ + div = *freq / parent_rate; + *freq = parent_rate * div; + + /* we were called to round the frequency, we can now return */ + if (n == NULL) + return; + + *k = div / 32; + if (*k > 3) + *k = 3; + + *n = DIV_ROUND_UP(div, (*k+1)); +} /** * sun4i_get_apb1_factors() - calculates m, p factors for APB1 @@ -265,7 +299,7 @@ static void sun4i_get_apb1_factors(u32 *freq, u32 parent_rate, if (parent_rate < *freq) *freq = parent_rate; - parent_rate = (parent_rate + (*freq - 1)) / *freq; + parent_rate = DIV_ROUND_UP(parent_rate, *freq); /* Invalid rate! */ if (parent_rate > 32) @@ -296,7 +330,7 @@ static void sun4i_get_apb1_factors(u32 *freq, u32 parent_rate, /** * sun4i_get_mod0_factors() - calculates m, n factors for MOD0-style clocks - * MMC rate is calculated as follows + * MOD0 rate is calculated as follows * rate = (parent_rate >> p) / (m + 1); */ @@ -310,7 +344,7 @@ static void sun4i_get_mod0_factors(u32 *freq, u32 parent_rate, if (*freq > parent_rate) *freq = parent_rate; - div = parent_rate / *freq; + div = DIV_ROUND_UP(parent_rate, *freq); if (div < 16) calcp = 0; @@ -351,7 +385,7 @@ static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate, if (*freq > parent_rate) *freq = parent_rate; - div = parent_rate / *freq; + div = DIV_ROUND_UP(parent_rate, *freq); if (div < 32) calcp = 0; @@ -377,6 +411,102 @@ static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate, /** + * sun7i_a20_gmac_clk_setup - Setup function for A20/A31 GMAC clock module + * + * This clock looks something like this + * ________________________ + * MII TX clock from PHY >-----|___________ _________|----> to GMAC core + * GMAC Int. RGMII TX clk >----|___________\__/__gate---|----> to PHY + * Ext. 125MHz RGMII TX clk >--|__divider__/ | + * |________________________| + * + * The external 125 MHz reference is optional, i.e. GMAC can use its + * internal TX clock just fine. The A31 GMAC clock module does not have + * the divider controls for the external reference. + * + * To keep it simple, let the GMAC use either the MII TX clock for MII mode, + * and its internal TX clock for GMII and RGMII modes. The GMAC driver should + * select the appropriate source and gate/ungate the output to the PHY. + * + * Only the GMAC should use this clock. Altering the clock so that it doesn't + * match the GMAC's operation parameters will result in the GMAC not being + * able to send traffic out. The GMAC driver should set the clock rate and + * enable/disable this clock to configure the required state. The clock + * driver then responds by auto-reparenting the clock. + */ + +#define SUN7I_A20_GMAC_GPIT 2 +#define SUN7I_A20_GMAC_MASK 0x3 +#define SUN7I_A20_GMAC_PARENTS 2 + +static void __init sun7i_a20_gmac_clk_setup(struct device_node *node) +{ + struct clk *clk; + struct clk_mux *mux; + struct clk_gate *gate; + const char *clk_name = node->name; + const char *parents[SUN7I_A20_GMAC_PARENTS]; + void *reg; + + if (of_property_read_string(node, "clock-output-names", &clk_name)) + return; + + /* allocate mux and gate clock structs */ + mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL); + if (!mux) + return; + + gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL); + if (!gate) + goto free_mux; + + /* gmac clock requires exactly 2 parents */ + parents[0] = of_clk_get_parent_name(node, 0); + parents[1] = of_clk_get_parent_name(node, 1); + if (!parents[0] || !parents[1]) + goto free_gate; + + reg = of_iomap(node, 0); + if (!reg) + goto free_gate; + + /* set up gate and fixed rate properties */ + gate->reg = reg; + gate->bit_idx = SUN7I_A20_GMAC_GPIT; + gate->lock = &clk_lock; + mux->reg = reg; + mux->mask = SUN7I_A20_GMAC_MASK; + mux->flags = CLK_MUX_INDEX_BIT; + mux->lock = &clk_lock; + + clk = clk_register_composite(NULL, clk_name, + parents, SUN7I_A20_GMAC_PARENTS, + &mux->hw, &clk_mux_ops, + NULL, NULL, + &gate->hw, &clk_gate_ops, + 0); + + if (IS_ERR(clk)) + goto iounmap_reg; + + of_clk_add_provider(node, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + + return; + +iounmap_reg: + iounmap(reg); +free_gate: + kfree(gate); +free_mux: + kfree(mux); +} +CLK_OF_DECLARE(sun7i_a20_gmac, "allwinner,sun7i-a20-gmac-clk", + sun7i_a20_gmac_clk_setup); + + + +/** * sunxi_factors_clk_setup() - Setup function for factor clocks */ @@ -387,6 +517,7 @@ struct factors_data { int mux; struct clk_factors_config *table; void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p); + const char *name; }; static struct clk_factors_config sun4i_pll1_config = { @@ -416,6 +547,13 @@ static struct clk_factors_config sun4i_pll5_config = { .kwidth = 2, }; +static struct clk_factors_config sun6i_a31_pll6_config = { + .nshift = 8, + .nwidth = 5, + .kshift = 4, + .kwidth = 2, +}; + static struct clk_factors_config sun4i_apb1_config = { .mshift = 0, .mwidth = 5, @@ -451,10 +589,30 @@ static const struct factors_data sun6i_a31_pll1_data __initconst = { .getter = sun6i_a31_get_pll1_factors, }; +static const struct factors_data sun7i_a20_pll4_data __initconst = { + .enable = 31, + .table = &sun4i_pll5_config, + .getter = sun4i_get_pll5_factors, +}; + static const struct factors_data sun4i_pll5_data __initconst = { .enable = 31, .table = &sun4i_pll5_config, .getter = sun4i_get_pll5_factors, + .name = "pll5", +}; + +static const struct factors_data sun4i_pll6_data __initconst = { + .enable = 31, + .table = &sun4i_pll5_config, + .getter = sun4i_get_pll5_factors, + .name = "pll6", +}; + +static const struct factors_data sun6i_a31_pll6_data __initconst = { + .enable = 31, + .table = &sun6i_a31_pll6_config, + .getter = sun6i_a31_get_pll6_factors, }; static const struct factors_data sun4i_apb1_data __initconst = { @@ -497,14 +655,14 @@ static struct clk * __init sunxi_factors_clk_setup(struct device_node *node, (parents[i] = of_clk_get_parent_name(node, i)) != NULL) i++; - /* Nodes should be providing the name via clock-output-names - * but originally our dts didn't, and so we used node->name. - * The new, better nodes look like clk@deadbeef, so we pull the - * name just in this case */ - if (!strcmp("clk", clk_name)) { - of_property_read_string_index(node, "clock-output-names", - 0, &clk_name); - } + /* + * some factor clocks, such as pll5 and pll6, may have multiple + * outputs, and have their name designated in factors_data + */ + if (data->name) + clk_name = data->name; + else + of_property_read_string(node, "clock-output-names", &clk_name); factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL); if (!factors) @@ -601,6 +759,8 @@ static void __init sunxi_mux_clk_setup(struct device_node *node, (parents[i] = of_clk_get_parent_name(node, i)) != NULL) i++; + of_property_read_string(node, "clock-output-names", &clk_name); + clk = clk_register_mux(NULL, clk_name, parents, i, CLK_SET_RATE_NO_REPARENT, reg, data->shift, SUNXI_MUX_GATE_WIDTH, @@ -660,6 +820,8 @@ static void __init sunxi_divider_clk_setup(struct device_node *node, clk_parent = of_clk_get_parent_name(node, 0); + of_property_read_string(node, "clock-output-names", &clk_name); + clk = clk_register_divider(NULL, clk_name, clk_parent, 0, reg, data->shift, data->width, data->pow ? CLK_DIVIDER_POWER_OF_TWO : 0, @@ -673,6 +835,59 @@ static void __init sunxi_divider_clk_setup(struct device_node *node, /** + * sunxi_gates_reset... - reset bits in leaf gate clk registers handling + */ + +struct gates_reset_data { + void __iomem *reg; + spinlock_t *lock; + struct reset_controller_dev rcdev; +}; + +static int sunxi_gates_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct gates_reset_data *data = container_of(rcdev, + struct gates_reset_data, + rcdev); + unsigned long flags; + u32 reg; + + spin_lock_irqsave(data->lock, flags); + + reg = readl(data->reg); + writel(reg & ~BIT(id), data->reg); + + spin_unlock_irqrestore(data->lock, flags); + + return 0; +} + +static int sunxi_gates_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct gates_reset_data *data = container_of(rcdev, + struct gates_reset_data, + rcdev); + unsigned long flags; + u32 reg; + + spin_lock_irqsave(data->lock, flags); + + reg = readl(data->reg); + writel(reg | BIT(id), data->reg); + + spin_unlock_irqrestore(data->lock, flags); + + return 0; +} + +static struct reset_control_ops sunxi_gates_reset_ops = { + .assert = sunxi_gates_reset_assert, + .deassert = sunxi_gates_reset_deassert, +}; + +/** * sunxi_gates_clk_setup() - Setup function for leaf gates on clocks */ @@ -680,6 +895,7 @@ static void __init sunxi_divider_clk_setup(struct device_node *node, struct gates_data { DECLARE_BITMAP(mask, SUNXI_GATES_MAX_SIZE); + u32 reset_mask; }; static const struct gates_data sun4i_axi_gates_data __initconst = { @@ -746,10 +962,21 @@ static const struct gates_data sun7i_a20_apb1_gates_data __initconst = { .mask = { 0xff80ff }, }; +static const struct gates_data sun4i_a10_usb_gates_data __initconst = { + .mask = {0x1C0}, + .reset_mask = 0x07, +}; + +static const struct gates_data sun5i_a13_usb_gates_data __initconst = { + .mask = {0x140}, + .reset_mask = 0x03, +}; + static void __init sunxi_gates_clk_setup(struct device_node *node, struct gates_data *data) { struct clk_onecell_data *clk_data; + struct gates_reset_data *reset_data; const char *clk_parent; const char *clk_name; void *reg; @@ -793,6 +1020,21 @@ static void __init sunxi_gates_clk_setup(struct device_node *node, clk_data->clk_num = i; of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + /* Register a reset controler for gates with reset bits */ + if (data->reset_mask == 0) + return; + + reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL); + if (!reset_data) + return; + + reset_data->reg = reg; + reset_data->lock = &clk_lock; + reset_data->rcdev.nr_resets = __fls(data->reset_mask) + 1; + reset_data->rcdev.ops = &sunxi_gates_reset_ops; + reset_data->rcdev.of_node = node; + reset_controller_register(&reset_data->rcdev); } @@ -832,7 +1074,7 @@ static const struct divs_data pll5_divs_data __initconst = { }; static const struct divs_data pll6_divs_data __initconst = { - .factors = &sun4i_pll5_data, + .factors = &sun4i_pll6_data, .div = { { .shift = 0, .table = pll6_sata_tbl, .gate = 14 }, /* M, SATA */ { .fixed = 2 }, /* P, other */ @@ -854,7 +1096,7 @@ static void __init sunxi_divs_clk_setup(struct device_node *node, struct divs_data *data) { struct clk_onecell_data *clk_data; - const char *parent = node->name; + const char *parent; const char *clk_name; struct clk **clks, *pclk; struct clk_hw *gate_hw, *rate_hw; @@ -868,6 +1110,7 @@ static void __init sunxi_divs_clk_setup(struct device_node *node, /* Set up factor clock that we will be dividing */ pclk = sunxi_factors_clk_setup(node, data->factors); + parent = __clk_get_name(pclk); reg = of_iomap(node, 0); @@ -970,56 +1213,60 @@ free_clkdata: /* Matches for factors clocks */ static const struct of_device_id clk_factors_match[] __initconst = { - {.compatible = "allwinner,sun4i-pll1-clk", .data = &sun4i_pll1_data,}, + {.compatible = "allwinner,sun4i-a10-pll1-clk", .data = &sun4i_pll1_data,}, {.compatible = "allwinner,sun6i-a31-pll1-clk", .data = &sun6i_a31_pll1_data,}, - {.compatible = "allwinner,sun4i-apb1-clk", .data = &sun4i_apb1_data,}, - {.compatible = "allwinner,sun4i-mod0-clk", .data = &sun4i_mod0_data,}, + {.compatible = "allwinner,sun7i-a20-pll4-clk", .data = &sun7i_a20_pll4_data,}, + {.compatible = "allwinner,sun6i-a31-pll6-clk", .data = &sun6i_a31_pll6_data,}, + {.compatible = "allwinner,sun4i-a10-apb1-clk", .data = &sun4i_apb1_data,}, + {.compatible = "allwinner,sun4i-a10-mod0-clk", .data = &sun4i_mod0_data,}, {.compatible = "allwinner,sun7i-a20-out-clk", .data = &sun7i_a20_out_data,}, {} }; /* Matches for divider clocks */ static const struct of_device_id clk_div_match[] __initconst = { - {.compatible = "allwinner,sun4i-axi-clk", .data = &sun4i_axi_data,}, - {.compatible = "allwinner,sun4i-ahb-clk", .data = &sun4i_ahb_data,}, - {.compatible = "allwinner,sun4i-apb0-clk", .data = &sun4i_apb0_data,}, + {.compatible = "allwinner,sun4i-a10-axi-clk", .data = &sun4i_axi_data,}, + {.compatible = "allwinner,sun4i-a10-ahb-clk", .data = &sun4i_ahb_data,}, + {.compatible = "allwinner,sun4i-a10-apb0-clk", .data = &sun4i_apb0_data,}, {.compatible = "allwinner,sun6i-a31-apb2-div-clk", .data = &sun6i_a31_apb2_div_data,}, {} }; /* Matches for divided outputs */ static const struct of_device_id clk_divs_match[] __initconst = { - {.compatible = "allwinner,sun4i-pll5-clk", .data = &pll5_divs_data,}, - {.compatible = "allwinner,sun4i-pll6-clk", .data = &pll6_divs_data,}, + {.compatible = "allwinner,sun4i-a10-pll5-clk", .data = &pll5_divs_data,}, + {.compatible = "allwinner,sun4i-a10-pll6-clk", .data = &pll6_divs_data,}, {} }; /* Matches for mux clocks */ static const struct of_device_id clk_mux_match[] __initconst = { - {.compatible = "allwinner,sun4i-cpu-clk", .data = &sun4i_cpu_mux_data,}, - {.compatible = "allwinner,sun4i-apb1-mux-clk", .data = &sun4i_apb1_mux_data,}, + {.compatible = "allwinner,sun4i-a10-cpu-clk", .data = &sun4i_cpu_mux_data,}, + {.compatible = "allwinner,sun4i-a10-apb1-mux-clk", .data = &sun4i_apb1_mux_data,}, {.compatible = "allwinner,sun6i-a31-ahb1-mux-clk", .data = &sun6i_a31_ahb1_mux_data,}, {} }; /* Matches for gate clocks */ static const struct of_device_id clk_gates_match[] __initconst = { - {.compatible = "allwinner,sun4i-axi-gates-clk", .data = &sun4i_axi_gates_data,}, - {.compatible = "allwinner,sun4i-ahb-gates-clk", .data = &sun4i_ahb_gates_data,}, + {.compatible = "allwinner,sun4i-a10-axi-gates-clk", .data = &sun4i_axi_gates_data,}, + {.compatible = "allwinner,sun4i-a10-ahb-gates-clk", .data = &sun4i_ahb_gates_data,}, {.compatible = "allwinner,sun5i-a10s-ahb-gates-clk", .data = &sun5i_a10s_ahb_gates_data,}, {.compatible = "allwinner,sun5i-a13-ahb-gates-clk", .data = &sun5i_a13_ahb_gates_data,}, {.compatible = "allwinner,sun6i-a31-ahb1-gates-clk", .data = &sun6i_a31_ahb1_gates_data,}, {.compatible = "allwinner,sun7i-a20-ahb-gates-clk", .data = &sun7i_a20_ahb_gates_data,}, - {.compatible = "allwinner,sun4i-apb0-gates-clk", .data = &sun4i_apb0_gates_data,}, + {.compatible = "allwinner,sun4i-a10-apb0-gates-clk", .data = &sun4i_apb0_gates_data,}, {.compatible = "allwinner,sun5i-a10s-apb0-gates-clk", .data = &sun5i_a10s_apb0_gates_data,}, {.compatible = "allwinner,sun5i-a13-apb0-gates-clk", .data = &sun5i_a13_apb0_gates_data,}, {.compatible = "allwinner,sun7i-a20-apb0-gates-clk", .data = &sun7i_a20_apb0_gates_data,}, - {.compatible = "allwinner,sun4i-apb1-gates-clk", .data = &sun4i_apb1_gates_data,}, + {.compatible = "allwinner,sun4i-a10-apb1-gates-clk", .data = &sun4i_apb1_gates_data,}, {.compatible = "allwinner,sun5i-a10s-apb1-gates-clk", .data = &sun5i_a10s_apb1_gates_data,}, {.compatible = "allwinner,sun5i-a13-apb1-gates-clk", .data = &sun5i_a13_apb1_gates_data,}, {.compatible = "allwinner,sun6i-a31-apb1-gates-clk", .data = &sun6i_a31_apb1_gates_data,}, {.compatible = "allwinner,sun7i-a20-apb1-gates-clk", .data = &sun7i_a20_apb1_gates_data,}, {.compatible = "allwinner,sun6i-a31-apb2-gates-clk", .data = &sun6i_a31_apb2_gates_data,}, + {.compatible = "allwinner,sun4i-a10-usb-clk", .data = &sun4i_a10_usb_gates_data,}, + {.compatible = "allwinner,sun5i-a13-usb-clk", .data = &sun5i_a13_usb_gates_data,}, {} }; diff --git a/drivers/clk/tegra/clk-divider.c b/drivers/clk/tegra/clk-divider.c index 4d75b1f37e3..290f9c1a374 100644 --- a/drivers/clk/tegra/clk-divider.c +++ b/drivers/clk/tegra/clk-divider.c @@ -59,7 +59,7 @@ static int get_div(struct tegra_clk_frac_div *divider, unsigned long rate, return 0; if (divider_ux1 > get_max_div(divider)) - return -EINVAL; + return get_max_div(divider); return divider_ux1; } diff --git a/drivers/clk/tegra/clk-id.h b/drivers/clk/tegra/clk-id.h index cf0c323f2c3..c39613c519a 100644 --- a/drivers/clk/tegra/clk-id.h +++ b/drivers/clk/tegra/clk-id.h @@ -180,9 +180,13 @@ enum clk_id { tegra_clk_sbc6_8, tegra_clk_sclk, tegra_clk_sdmmc1, + tegra_clk_sdmmc1_8, tegra_clk_sdmmc2, + tegra_clk_sdmmc2_8, tegra_clk_sdmmc3, + tegra_clk_sdmmc3_8, tegra_clk_sdmmc4, + tegra_clk_sdmmc4_8, tegra_clk_se, tegra_clk_soc_therm, tegra_clk_sor0, diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c index 356e9b80442..9e899c18af8 100644 --- a/drivers/clk/tegra/clk-periph.c +++ b/drivers/clk/tegra/clk-periph.c @@ -130,7 +130,7 @@ static const struct clk_ops tegra_clk_periph_nodiv_ops = { .disable = clk_periph_disable, }; -const struct clk_ops tegra_clk_periph_no_gate_ops = { +static const struct clk_ops tegra_clk_periph_no_gate_ops = { .get_parent = clk_periph_get_parent, .set_parent = clk_periph_set_parent, .recalc_rate = clk_periph_recalc_rate, diff --git a/drivers/clk/tegra/clk-tegra-periph.c b/drivers/clk/tegra/clk-tegra-periph.c index 5c35885f4a7..1fa5c3f33b2 100644 --- a/drivers/clk/tegra/clk-tegra-periph.c +++ b/drivers/clk/tegra/clk-tegra-periph.c @@ -371,9 +371,7 @@ static const char *mux_pllp3_pllc_clkm[] = { static const char *mux_pllm_pllc_pllp_plla_pllc2_c3_clkm[] = { "pll_m", "pll_c", "pll_p", "pll_a", "pll_c2", "pll_c3", "clk_m" }; -static u32 mux_pllm_pllc_pllp_plla_pllc2_c3_clkm_idx[] = { - [0] = 0, [1] = 1, [2] = 2, [3] = 3, [4] = 4, [5] = 6, -}; +#define mux_pllm_pllc_pllp_plla_pllc2_c3_clkm_idx NULL static const char *mux_pllm_pllc2_c_c3_pllp_plla_pllc4[] = { "pll_m", "pll_c2", "pll_c", "pll_c3", "pll_p", "pll_a_out0", "pll_c4", @@ -465,6 +463,10 @@ static struct tegra_periph_init_data periph_clks[] = { MUX("adx1", mux_plla_pllc_pllp_clkm, CLK_SOURCE_ADX1, 180, TEGRA_PERIPH_ON_APB, tegra_clk_adx1), MUX("amx1", mux_plla_pllc_pllp_clkm, CLK_SOURCE_AMX1, 185, TEGRA_PERIPH_ON_APB, tegra_clk_amx1), MUX("vi_sensor2", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_VI_SENSOR2, 20, TEGRA_PERIPH_NO_RESET, tegra_clk_vi_sensor2), + MUX8("sdmmc1", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SDMMC1, 14, 0, tegra_clk_sdmmc1_8), + MUX8("sdmmc2", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SDMMC2, 9, 0, tegra_clk_sdmmc2_8), + MUX8("sdmmc3", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SDMMC3, 69, 0, tegra_clk_sdmmc3_8), + MUX8("sdmmc4", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SDMMC4, 15, 0, tegra_clk_sdmmc4_8), MUX8("sbc1", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SBC1, 41, TEGRA_PERIPH_ON_APB, tegra_clk_sbc1_8), MUX8("sbc2", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SBC2, 44, TEGRA_PERIPH_ON_APB, tegra_clk_sbc2_8), MUX8("sbc3", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SBC3, 46, TEGRA_PERIPH_ON_APB, tegra_clk_sbc3_8), @@ -492,7 +494,7 @@ static struct tegra_periph_init_data periph_clks[] = { UART("uartb", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTB, 7, tegra_clk_uartb), UART("uartc", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTC, 55, tegra_clk_uartc), UART("uartd", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTD, 65, tegra_clk_uartd), - UART("uarte", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTE, 65, tegra_clk_uarte), + UART("uarte", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTE, 66, tegra_clk_uarte), XUSB("xusb_host_src", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_HOST_SRC, 143, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_host_src), 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), diff --git a/drivers/clk/tegra/clk-tegra-super-gen4.c b/drivers/clk/tegra/clk-tegra-super-gen4.c index 05dce4aa2c1..feb3201c85c 100644 --- a/drivers/clk/tegra/clk-tegra-super-gen4.c +++ b/drivers/clk/tegra/clk-tegra-super-gen4.c @@ -120,7 +120,7 @@ void __init tegra_super_clk_gen4_init(void __iomem *clk_base, ARRAY_SIZE(cclk_lp_parents), CLK_SET_RATE_PARENT, clk_base + CCLKLP_BURST_POLICY, - 0, 4, 8, 9, NULL); + TEGRA_DIVIDER_2, 4, 8, 9, NULL); *dt_clk = clk; } diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c index 90d9d25f222..80431f0fb26 100644 --- a/drivers/clk/tegra/clk-tegra114.c +++ b/drivers/clk/tegra/clk-tegra114.c @@ -682,12 +682,12 @@ static struct tegra_clk tegra114_clks[tegra_clk_max] __initdata = { [tegra_clk_timer] = { .dt_id = TEGRA114_CLK_TIMER, .present = true }, [tegra_clk_uarta] = { .dt_id = TEGRA114_CLK_UARTA, .present = true }, [tegra_clk_uartd] = { .dt_id = TEGRA114_CLK_UARTD, .present = true }, - [tegra_clk_sdmmc2] = { .dt_id = TEGRA114_CLK_SDMMC2, .present = true }, + [tegra_clk_sdmmc2_8] = { .dt_id = TEGRA114_CLK_SDMMC2, .present = true }, [tegra_clk_i2s1] = { .dt_id = TEGRA114_CLK_I2S1, .present = true }, [tegra_clk_i2c1] = { .dt_id = TEGRA114_CLK_I2C1, .present = true }, [tegra_clk_ndflash] = { .dt_id = TEGRA114_CLK_NDFLASH, .present = true }, - [tegra_clk_sdmmc1] = { .dt_id = TEGRA114_CLK_SDMMC1, .present = true }, - [tegra_clk_sdmmc4] = { .dt_id = TEGRA114_CLK_SDMMC4, .present = true }, + [tegra_clk_sdmmc1_8] = { .dt_id = TEGRA114_CLK_SDMMC1, .present = true }, + [tegra_clk_sdmmc4_8] = { .dt_id = TEGRA114_CLK_SDMMC4, .present = true }, [tegra_clk_pwm] = { .dt_id = TEGRA114_CLK_PWM, .present = true }, [tegra_clk_i2s0] = { .dt_id = TEGRA114_CLK_I2S0, .present = true }, [tegra_clk_i2s2] = { .dt_id = TEGRA114_CLK_I2S2, .present = true }, @@ -723,7 +723,7 @@ static struct tegra_clk tegra114_clks[tegra_clk_max] __initdata = { [tegra_clk_bsev] = { .dt_id = TEGRA114_CLK_BSEV, .present = true }, [tegra_clk_i2c3] = { .dt_id = TEGRA114_CLK_I2C3, .present = true }, [tegra_clk_sbc4_8] = { .dt_id = TEGRA114_CLK_SBC4, .present = true }, - [tegra_clk_sdmmc3] = { .dt_id = TEGRA114_CLK_SDMMC3, .present = true }, + [tegra_clk_sdmmc3_8] = { .dt_id = TEGRA114_CLK_SDMMC3, .present = true }, [tegra_clk_owr] = { .dt_id = TEGRA114_CLK_OWR, .present = true }, [tegra_clk_csite] = { .dt_id = TEGRA114_CLK_CSITE, .present = true }, [tegra_clk_la] = { .dt_id = TEGRA114_CLK_LA, .present = true }, diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c index aff86b5bc74..166e02f16c8 100644 --- a/drivers/clk/tegra/clk-tegra124.c +++ b/drivers/clk/tegra/clk-tegra124.c @@ -516,11 +516,11 @@ static struct div_nmp pllp_nmp = { }; static struct tegra_clk_pll_freq_table pll_p_freq_table[] = { - {12000000, 216000000, 432, 12, 1, 8}, - {13000000, 216000000, 432, 13, 1, 8}, - {16800000, 216000000, 360, 14, 1, 8}, - {19200000, 216000000, 360, 16, 1, 8}, - {26000000, 216000000, 432, 26, 1, 8}, + {12000000, 408000000, 408, 12, 0, 8}, + {13000000, 408000000, 408, 13, 0, 8}, + {16800000, 408000000, 340, 14, 0, 8}, + {19200000, 408000000, 340, 16, 0, 8}, + {26000000, 408000000, 408, 26, 0, 8}, {0, 0, 0, 0, 0, 0}, }; @@ -570,6 +570,15 @@ static struct tegra_clk_pll_params pll_a_params = { .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_USE_LOCK, }; +static struct div_nmp plld_nmp = { + .divm_shift = 0, + .divm_width = 5, + .divn_shift = 8, + .divn_width = 11, + .divp_shift = 20, + .divp_width = 3, +}; + static struct tegra_clk_pll_freq_table pll_d_freq_table[] = { {12000000, 216000000, 864, 12, 4, 12}, {13000000, 216000000, 864, 13, 4, 12}, @@ -603,19 +612,18 @@ static struct tegra_clk_pll_params pll_d_params = { .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, - .div_nmp = &pllp_nmp, + .div_nmp = &plld_nmp, .freq_table = pll_d_freq_table, .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON | TEGRA_PLL_USE_LOCK, }; static struct tegra_clk_pll_freq_table tegra124_pll_d2_freq_table[] = { - { 12000000, 148500000, 99, 1, 8}, - { 12000000, 594000000, 99, 1, 1}, - { 13000000, 594000000, 91, 1, 1}, /* actual: 591.5 MHz */ - { 16800000, 594000000, 71, 1, 1}, /* actual: 596.4 MHz */ - { 19200000, 594000000, 62, 1, 1}, /* actual: 595.2 MHz */ - { 26000000, 594000000, 91, 2, 1}, /* actual: 591.5 MHz */ + { 12000000, 594000000, 99, 1, 2}, + { 13000000, 594000000, 91, 1, 2}, /* actual: 591.5 MHz */ + { 16800000, 594000000, 71, 1, 2}, /* actual: 596.4 MHz */ + { 19200000, 594000000, 62, 1, 2}, /* actual: 595.2 MHz */ + { 26000000, 594000000, 91, 2, 2}, /* actual: 591.5 MHz */ { 0, 0, 0, 0, 0, 0 }, }; @@ -753,21 +761,19 @@ static struct tegra_clk tegra124_clks[tegra_clk_max] __initdata = { [tegra_clk_rtc] = { .dt_id = TEGRA124_CLK_RTC, .present = true }, [tegra_clk_timer] = { .dt_id = TEGRA124_CLK_TIMER, .present = true }, [tegra_clk_uarta] = { .dt_id = TEGRA124_CLK_UARTA, .present = true }, - [tegra_clk_sdmmc2] = { .dt_id = TEGRA124_CLK_SDMMC2, .present = true }, + [tegra_clk_sdmmc2_8] = { .dt_id = TEGRA124_CLK_SDMMC2, .present = true }, [tegra_clk_i2s1] = { .dt_id = TEGRA124_CLK_I2S1, .present = true }, [tegra_clk_i2c1] = { .dt_id = TEGRA124_CLK_I2C1, .present = true }, [tegra_clk_ndflash] = { .dt_id = TEGRA124_CLK_NDFLASH, .present = true }, - [tegra_clk_sdmmc1] = { .dt_id = TEGRA124_CLK_SDMMC1, .present = true }, - [tegra_clk_sdmmc4] = { .dt_id = TEGRA124_CLK_SDMMC4, .present = true }, + [tegra_clk_sdmmc1_8] = { .dt_id = TEGRA124_CLK_SDMMC1, .present = true }, + [tegra_clk_sdmmc4_8] = { .dt_id = TEGRA124_CLK_SDMMC4, .present = true }, [tegra_clk_pwm] = { .dt_id = TEGRA124_CLK_PWM, .present = true }, [tegra_clk_i2s2] = { .dt_id = TEGRA124_CLK_I2S2, .present = true }, - [tegra_clk_gr2d] = { .dt_id = TEGRA124_CLK_GR_2D, .present = true }, [tegra_clk_usbd] = { .dt_id = TEGRA124_CLK_USBD, .present = true }, [tegra_clk_isp_8] = { .dt_id = TEGRA124_CLK_ISP, .present = true }, - [tegra_clk_gr3d] = { .dt_id = TEGRA124_CLK_GR_3D, .present = true }, [tegra_clk_disp2] = { .dt_id = TEGRA124_CLK_DISP2, .present = true }, [tegra_clk_disp1] = { .dt_id = TEGRA124_CLK_DISP1, .present = true }, - [tegra_clk_host1x] = { .dt_id = TEGRA124_CLK_HOST1X, .present = true }, + [tegra_clk_host1x_8] = { .dt_id = TEGRA124_CLK_HOST1X, .present = true }, [tegra_clk_vcp] = { .dt_id = TEGRA124_CLK_VCP, .present = true }, [tegra_clk_i2s0] = { .dt_id = TEGRA124_CLK_I2S0, .present = true }, [tegra_clk_apbdma] = { .dt_id = TEGRA124_CLK_APBDMA, .present = true }, @@ -794,7 +800,7 @@ static struct tegra_clk tegra124_clks[tegra_clk_max] __initdata = { [tegra_clk_uartd] = { .dt_id = TEGRA124_CLK_UARTD, .present = true }, [tegra_clk_i2c3] = { .dt_id = TEGRA124_CLK_I2C3, .present = true }, [tegra_clk_sbc4] = { .dt_id = TEGRA124_CLK_SBC4, .present = true }, - [tegra_clk_sdmmc3] = { .dt_id = TEGRA124_CLK_SDMMC3, .present = true }, + [tegra_clk_sdmmc3_8] = { .dt_id = TEGRA124_CLK_SDMMC3, .present = true }, [tegra_clk_pcie] = { .dt_id = TEGRA124_CLK_PCIE, .present = true }, [tegra_clk_owr] = { .dt_id = TEGRA124_CLK_OWR, .present = true }, [tegra_clk_afi] = { .dt_id = TEGRA124_CLK_AFI, .present = true }, @@ -1286,9 +1292,9 @@ static void __init tegra124_pll_init(void __iomem *clk_base, clk_register_clkdev(clk, "pll_d2", NULL); clks[TEGRA124_CLK_PLL_D2] = clk; - /* PLLD2_OUT0 ?? */ + /* PLLD2_OUT0 */ clk = clk_register_fixed_factor(NULL, "pll_d2_out0", "pll_d2", - CLK_SET_RATE_PARENT, 1, 2); + CLK_SET_RATE_PARENT, 1, 1); clk_register_clkdev(clk, "pll_d2_out0", NULL); clks[TEGRA124_CLK_PLL_D2_OUT0] = clk; diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index dbace152b2f..dace2b1b5ae 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -574,6 +574,8 @@ static struct tegra_clk tegra20_clks[tegra_clk_max] __initdata = { [tegra_clk_tvdac] = { .dt_id = TEGRA20_CLK_TVDAC, .present = true }, [tegra_clk_vi_sensor] = { .dt_id = TEGRA20_CLK_VI_SENSOR, .present = true }, [tegra_clk_afi] = { .dt_id = TEGRA20_CLK_AFI, .present = true }, + [tegra_clk_fuse] = { .dt_id = TEGRA20_CLK_FUSE, .present = true }, + [tegra_clk_kfuse] = { .dt_id = TEGRA20_CLK_KFUSE, .present = true }, }; static unsigned long tegra20_clk_measure_input_freq(void) diff --git a/drivers/clk/ti/clk-33xx.c b/drivers/clk/ti/clk-33xx.c index 776ee4594bd..028b33783d3 100644 --- a/drivers/clk/ti/clk-33xx.c +++ b/drivers/clk/ti/clk-33xx.c @@ -34,7 +34,6 @@ static struct ti_dt_clk am33xx_clks[] = { DT_CLK(NULL, "dpll_core_m5_ck", "dpll_core_m5_ck"), DT_CLK(NULL, "dpll_core_m6_ck", "dpll_core_m6_ck"), DT_CLK(NULL, "dpll_mpu_ck", "dpll_mpu_ck"), - DT_CLK("cpu0", NULL, "dpll_mpu_ck"), DT_CLK(NULL, "dpll_mpu_m2_ck", "dpll_mpu_m2_ck"), DT_CLK(NULL, "dpll_ddr_ck", "dpll_ddr_ck"), DT_CLK(NULL, "dpll_ddr_m2_ck", "dpll_ddr_m2_ck"), diff --git a/drivers/clk/ti/divider.c b/drivers/clk/ti/divider.c index a15e445570b..e6aa10db7bb 100644 --- a/drivers/clk/ti/divider.c +++ b/drivers/clk/ti/divider.c @@ -112,7 +112,7 @@ static unsigned long ti_clk_divider_recalc_rate(struct clk_hw *hw, return parent_rate; } - return parent_rate / div; + return DIV_ROUND_UP(parent_rate, div); } /* @@ -182,7 +182,7 @@ static int ti_clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, } parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), MULT_ROUND_UP(rate, i)); - now = parent_rate / i; + now = DIV_ROUND_UP(parent_rate, i); if (now <= rate && now > best) { bestdiv = i; best = now; @@ -205,7 +205,7 @@ static long ti_clk_divider_round_rate(struct clk_hw *hw, unsigned long rate, int div; div = ti_clk_divider_bestdiv(hw, rate, prate); - return *prate / div; + return DIV_ROUND_UP(*prate, div); } static int ti_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, @@ -216,7 +216,7 @@ static int ti_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long flags = 0; u32 val; - div = parent_rate / rate; + div = DIV_ROUND_UP(parent_rate, rate); value = _get_val(divider, div); if (value > div_mask(divider)) diff --git a/drivers/clk/ux500/u8500_of_clk.c b/drivers/clk/ux500/u8500_of_clk.c index cdeff299de2..7b55ef89baa 100644 --- a/drivers/clk/ux500/u8500_of_clk.c +++ b/drivers/clk/ux500/u8500_of_clk.c @@ -29,7 +29,8 @@ static struct clk *prcc_kclk[(PRCC_NUM_PERIPH_CLUSTERS + 1) * PRCC_PERIPHS_PER_C #define PRCC_KCLK_STORE(clk, base, bit) \ prcc_kclk[(base * PRCC_PERIPHS_PER_CLUSTER) + bit] = clk -struct clk *ux500_twocell_get(struct of_phandle_args *clkspec, void *data) +static struct clk *ux500_twocell_get(struct of_phandle_args *clkspec, + void *data) { struct clk **clk_data = data; unsigned int base, bit; diff --git a/drivers/clk/zynq/clkc.c b/drivers/clk/zynq/clkc.c index 09dd0173ea0..e726c1b1121 100644 --- a/drivers/clk/zynq/clkc.c +++ b/drivers/clk/zynq/clkc.c @@ -148,7 +148,7 @@ static void __init zynq_clk_register_fclk(enum zynq_clk fclk, clks[fclk] = clk_register_gate(NULL, clk_name, div1_name, CLK_SET_RATE_PARENT, fclk_gate_reg, 0, CLK_GATE_SET_TO_DISABLE, fclk_gate_lock); - enable_reg = readl(fclk_gate_reg) & 1; + enable_reg = clk_readl(fclk_gate_reg) & 1; if (enable && !enable_reg) { if (clk_prepare_enable(clks[fclk])) pr_warn("%s: FCLK%u enable failed\n", __func__, @@ -277,7 +277,7 @@ static void __init zynq_clk_setup(struct device_node *np) SLCR_IOPLL_CTRL, 4, 1, 0, &iopll_lock); /* CPU clocks */ - tmp = readl(SLCR_621_TRUE) & 1; + tmp = clk_readl(SLCR_621_TRUE) & 1; clk = clk_register_mux(NULL, "cpu_mux", cpu_parents, 4, CLK_SET_RATE_NO_REPARENT, SLCR_ARM_CLK_CTRL, 4, 2, 0, &armclk_lock); diff --git a/drivers/clk/zynq/pll.c b/drivers/clk/zynq/pll.c index 3226f54fa59..cec97596fe6 100644 --- a/drivers/clk/zynq/pll.c +++ b/drivers/clk/zynq/pll.c @@ -90,7 +90,7 @@ static unsigned long zynq_pll_recalc_rate(struct clk_hw *hw, * makes probably sense to redundantly save fbdiv in the struct * zynq_pll to save the IO access. */ - fbdiv = (readl(clk->pll_ctrl) & PLLCTRL_FBDIV_MASK) >> + fbdiv = (clk_readl(clk->pll_ctrl) & PLLCTRL_FBDIV_MASK) >> PLLCTRL_FBDIV_SHIFT; return parent_rate * fbdiv; @@ -112,7 +112,7 @@ static int zynq_pll_is_enabled(struct clk_hw *hw) spin_lock_irqsave(clk->lock, flags); - reg = readl(clk->pll_ctrl); + reg = clk_readl(clk->pll_ctrl); spin_unlock_irqrestore(clk->lock, flags); @@ -138,10 +138,10 @@ static int zynq_pll_enable(struct clk_hw *hw) /* Power up PLL and wait for lock */ spin_lock_irqsave(clk->lock, flags); - reg = readl(clk->pll_ctrl); + reg = clk_readl(clk->pll_ctrl); reg &= ~(PLLCTRL_RESET_MASK | PLLCTRL_PWRDWN_MASK); - writel(reg, clk->pll_ctrl); - while (!(readl(clk->pll_status) & (1 << clk->lockbit))) + clk_writel(reg, clk->pll_ctrl); + while (!(clk_readl(clk->pll_status) & (1 << clk->lockbit))) ; spin_unlock_irqrestore(clk->lock, flags); @@ -168,9 +168,9 @@ static void zynq_pll_disable(struct clk_hw *hw) /* shut down PLL */ spin_lock_irqsave(clk->lock, flags); - reg = readl(clk->pll_ctrl); + reg = clk_readl(clk->pll_ctrl); reg |= PLLCTRL_RESET_MASK | PLLCTRL_PWRDWN_MASK; - writel(reg, clk->pll_ctrl); + clk_writel(reg, clk->pll_ctrl); spin_unlock_irqrestore(clk->lock, flags); } @@ -225,9 +225,9 @@ struct clk *clk_register_zynq_pll(const char *name, const char *parent, spin_lock_irqsave(pll->lock, flags); - reg = readl(pll->pll_ctrl); + reg = clk_readl(pll->pll_ctrl); reg &= ~PLLCTRL_BPQUAL_MASK; - writel(reg, pll->pll_ctrl); + clk_writel(reg, pll->pll_ctrl); spin_unlock_irqrestore(pll->lock, flags); |