Commit 62d1e782 authored by Lars-Peter Clausen's avatar Lars-Peter Clausen Committed by Stephen Boyd

clk: axi-clkgen: Add multi-parent support

The clock generator has two clock inputs that can be used as the reference
clock. Add support for switching between them at runtime.
Signed-off-by: default avatarLars-Peter Clausen <lars@metafoo.de>
Signed-off-by: default avatarStephen Boyd <sboyd@codeaurora.org>
parent d95b599c
...@@ -8,7 +8,10 @@ Required properties: ...@@ -8,7 +8,10 @@ Required properties:
- compatible : shall be "adi,axi-clkgen-1.00.a" or "adi,axi-clkgen-2.00.a". - compatible : shall be "adi,axi-clkgen-1.00.a" or "adi,axi-clkgen-2.00.a".
- #clock-cells : from common clock binding; Should always be set to 0. - #clock-cells : from common clock binding; Should always be set to 0.
- reg : Address and length of the axi-clkgen register set. - reg : Address and length of the axi-clkgen register set.
- clocks : Phandle and clock specifier for the parent clock. - clocks : Phandle and clock specifier for the parent clock(s). This must
either reference one clock if only the first clock input is connected or two
if both clock inputs are connected. For the later case the clock connected
to the first input must be specified first.
Optional properties: Optional properties:
- clock-output-names : From common clock binding. - clock-output-names : From common clock binding.
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/err.h> #include <linux/err.h>
#define AXI_CLKGEN_V2_REG_RESET 0x40 #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_CNTRL 0x70
#define AXI_CLKGEN_V2_REG_DRP_STATUS 0x74 #define AXI_CLKGEN_V2_REG_DRP_STATUS 0x74
...@@ -349,12 +350,33 @@ static void axi_clkgen_disable(struct clk_hw *clk_hw) ...@@ -349,12 +350,33 @@ static void axi_clkgen_disable(struct clk_hw *clk_hw)
axi_clkgen_mmcm_enable(axi_clkgen, false); 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 = { static const struct clk_ops axi_clkgen_ops = {
.recalc_rate = axi_clkgen_recalc_rate, .recalc_rate = axi_clkgen_recalc_rate,
.round_rate = axi_clkgen_round_rate, .round_rate = axi_clkgen_round_rate,
.set_rate = axi_clkgen_set_rate, .set_rate = axi_clkgen_set_rate,
.enable = axi_clkgen_enable, .enable = axi_clkgen_enable,
.disable = axi_clkgen_disable, .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[] = { static const struct of_device_id axi_clkgen_ids[] = {
...@@ -370,10 +392,11 @@ static int axi_clkgen_probe(struct platform_device *pdev) ...@@ -370,10 +392,11 @@ static int axi_clkgen_probe(struct platform_device *pdev)
const struct of_device_id *id; const struct of_device_id *id;
struct axi_clkgen *axi_clkgen; struct axi_clkgen *axi_clkgen;
struct clk_init_data init; struct clk_init_data init;
const char *parent_name; const char *parent_names[2];
const char *clk_name; const char *clk_name;
struct resource *mem; struct resource *mem;
struct clk *clk; struct clk *clk;
unsigned int i;
if (!pdev->dev.of_node) if (!pdev->dev.of_node)
return -ENODEV; return -ENODEV;
...@@ -391,19 +414,24 @@ static int axi_clkgen_probe(struct platform_device *pdev) ...@@ -391,19 +414,24 @@ static int axi_clkgen_probe(struct platform_device *pdev)
if (IS_ERR(axi_clkgen->base)) if (IS_ERR(axi_clkgen->base))
return PTR_ERR(axi_clkgen->base); return PTR_ERR(axi_clkgen->base);
parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0); init.num_parents = of_clk_get_parent_count(pdev->dev.of_node);
if (!parent_name) if (init.num_parents < 1 || init.num_parents > 2)
return -EINVAL; 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; clk_name = pdev->dev.of_node->name;
of_property_read_string(pdev->dev.of_node, "clock-output-names", of_property_read_string(pdev->dev.of_node, "clock-output-names",
&clk_name); &clk_name);
init.name = clk_name; init.name = clk_name;
init.ops = &axi_clkgen_ops; init.ops = &axi_clkgen_ops;
init.flags = CLK_SET_RATE_GATE; init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
init.parent_names = &parent_name; init.parent_names = parent_names;
init.num_parents = 1;
axi_clkgen_mmcm_enable(axi_clkgen, false); axi_clkgen_mmcm_enable(axi_clkgen, false);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment