diff options
Diffstat (limited to 'drivers/clk/clk-axi-clkgen.c')
-rw-r--r-- | drivers/clk/clk-axi-clkgen.c | 170 |
1 files changed, 39 insertions, 131 deletions
diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c index 3bcd42f..3294db3 100644 --- a/drivers/clk/clk-axi-clkgen.c +++ b/drivers/clk/clk-axi-clkgen.c @@ -16,19 +16,8 @@ #include <linux/module.h> #include <linux/err.h> -#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_CLKSEL 0x44 #define AXI_CLKGEN_V2_REG_DRP_CNTRL 0x70 #define AXI_CLKGEN_V2_REG_DRP_STATUS 0x74 @@ -51,40 +40,11 @@ #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) { @@ -207,70 +167,6 @@ 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; @@ -286,7 +182,7 @@ static int axi_clkgen_wait_non_busy(struct axi_clkgen *axi_clkgen) return val & 0xffff; } -static int axi_clkgen_v2_mmcm_read(struct axi_clkgen *axi_clkgen, +static int axi_clkgen_mmcm_read(struct axi_clkgen *axi_clkgen, unsigned int reg, unsigned int *val) { unsigned int reg_val; @@ -310,7 +206,7 @@ static int axi_clkgen_v2_mmcm_read(struct axi_clkgen *axi_clkgen, return 0; } -static int axi_clkgen_v2_mmcm_write(struct axi_clkgen *axi_clkgen, +static int axi_clkgen_mmcm_write(struct axi_clkgen *axi_clkgen, unsigned int reg, unsigned int val, unsigned int mask) { unsigned int reg_val = 0; @@ -321,7 +217,7 @@ static int axi_clkgen_v2_mmcm_write(struct axi_clkgen *axi_clkgen, return ret; if (mask != 0xffff) { - axi_clkgen_v2_mmcm_read(axi_clkgen, reg, ®_val); + axi_clkgen_mmcm_read(axi_clkgen, reg, ®_val); reg_val &= ~mask; } @@ -332,7 +228,7 @@ static int axi_clkgen_v2_mmcm_write(struct axi_clkgen *axi_clkgen, return 0; } -static void axi_clkgen_v2_mmcm_enable(struct axi_clkgen *axi_clkgen, +static void axi_clkgen_mmcm_enable(struct axi_clkgen *axi_clkgen, bool enable) { unsigned int val = AXI_CLKGEN_V2_RESET_ENABLE; @@ -343,12 +239,6 @@ static void axi_clkgen_v2_mmcm_enable(struct axi_clkgen *axi_clkgen, 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); @@ -438,10 +328,7 @@ static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw, tmp = (unsigned long long)(parent_rate / d) * m; do_div(tmp, dout); - if (tmp > ULONG_MAX) - return ULONG_MAX; - - return tmp; + return min_t(unsigned long long, tmp, ULONG_MAX); } static int axi_clkgen_enable(struct clk_hw *clk_hw) @@ -460,21 +347,38 @@ static void axi_clkgen_disable(struct clk_hw *clk_hw) axi_clkgen_mmcm_enable(axi_clkgen, false); } +static int axi_clkgen_set_parent(struct clk_hw *clk_hw, u8 index) +{ + struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw); + + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_V2_REG_CLKSEL, index); + + return 0; +} + +static u8 axi_clkgen_get_parent(struct clk_hw *clk_hw) +{ + struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw); + unsigned int parent; + + axi_clkgen_read(axi_clkgen, AXI_CLKGEN_V2_REG_CLKSEL, &parent); + + return parent; +} + 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, + .set_parent = axi_clkgen_set_parent, + .get_parent = axi_clkgen_get_parent, }; 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, }, { }, }; @@ -485,10 +389,11 @@ 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; + const char *parent_names[2]; const char *clk_name; struct resource *mem; struct clk *clk; + unsigned int i; if (!pdev->dev.of_node) return -ENODEV; @@ -501,26 +406,29 @@ static int axi_clkgen_probe(struct platform_device *pdev) 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)) return PTR_ERR(axi_clkgen->base); - parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0); - if (!parent_name) + init.num_parents = of_clk_get_parent_count(pdev->dev.of_node); + if (init.num_parents < 1 || init.num_parents > 2) return -EINVAL; + for (i = 0; i < init.num_parents; i++) { + parent_names[i] = of_clk_get_parent_name(pdev->dev.of_node, i); + if (!parent_names[i]) + return -EINVAL; + } + clk_name = pdev->dev.of_node->name; of_property_read_string(pdev->dev.of_node, "clock-output-names", &clk_name); init.name = clk_name; init.ops = &axi_clkgen_ops; - init.flags = CLK_SET_RATE_GATE; - init.parent_names = &parent_name; - init.num_parents = 1; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; + init.parent_names = parent_names; axi_clkgen_mmcm_enable(axi_clkgen, false); |