|
@@ -17,6 +17,7 @@
|
|
|
#include <linux/err.h>
|
|
|
|
|
|
#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
|
|
|
|
|
@@ -349,12 +350,33 @@ 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[] = {
|
|
@@ -370,10 +392,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;
|
|
@@ -391,19 +414,24 @@ static int axi_clkgen_probe(struct platform_device *pdev)
|
|
|
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);
|
|
|
|