From 29e6beb5a55fe79c94e0f746d0de7bd851cd3618 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Fri, 7 Apr 2017 15:47:08 +0200 Subject: [PATCH 1/4] clk: mvebu: cp110: make failure labels more meaningful In preparation to the addition of a new clock, rename the goto labels used to handle the failure cases using a name related to the failure cause. This will allow to insert additional failing cases without renaming all the labels. Reviewed-by: Thomas Petazzoni Signed-off-by: Gregory CLEMENT --- drivers/clk/mvebu/cp110-system-controller.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/clk/mvebu/cp110-system-controller.c b/drivers/clk/mvebu/cp110-system-controller.c index 6b11d7b3e0e0..3b27a6056c73 100644 --- a/drivers/clk/mvebu/cp110-system-controller.c +++ b/drivers/clk/mvebu/cp110-system-controller.c @@ -221,7 +221,7 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) 1000 * 1000 * 1000); if (IS_ERR(hw)) { ret = PTR_ERR(hw); - goto fail0; + goto fail_apll; } cp110_clks[CP110_CORE_APLL] = hw; @@ -232,7 +232,7 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) hw = clk_hw_register_fixed_factor(NULL, ppv2_name, apll_name, 0, 1, 3); if (IS_ERR(hw)) { ret = PTR_ERR(hw); - goto fail1; + goto fail_ppv2; } cp110_clks[CP110_CORE_PPV2] = hw; @@ -243,7 +243,7 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) hw = clk_hw_register_fixed_factor(NULL, eip_name, apll_name, 0, 1, 2); if (IS_ERR(hw)) { ret = PTR_ERR(hw); - goto fail2; + goto fail_eip; } cp110_clks[CP110_CORE_EIP] = hw; @@ -254,7 +254,7 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) hw = clk_hw_register_fixed_factor(NULL, core_name, eip_name, 0, 1, 2); if (IS_ERR(hw)) { ret = PTR_ERR(hw); - goto fail3; + goto fail_core; } cp110_clks[CP110_CORE_CORE] = hw; @@ -270,7 +270,7 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) core_name, 0, 1, 1); if (IS_ERR(hw)) { ret = PTR_ERR(hw); - goto fail4; + goto fail_nand; } cp110_clks[CP110_CORE_NAND] = hw; @@ -365,15 +365,15 @@ fail_gate: } clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_NAND]); -fail4: +fail_nand: clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_CORE]); -fail3: +fail_core: clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_EIP]); -fail2: +fail_eip: clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_PPV2]); -fail1: +fail_ppv2: clk_hw_unregister_fixed_rate(cp110_clks[CP110_CORE_APLL]); -fail0: +fail_apll: return ret; } From f5667274ba9e73a740210ed996cc0f63c8d2f601 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Wed, 31 May 2017 15:11:09 +0200 Subject: [PATCH 2/4] clk: mvebu: cp110: do not depend anymore of the *-clock-output-names Using the *-clock-output-names property was a convenient way to have a unique name for each clock even when there are multiple cp110 blocks as we can find on Armada 8K. However it has some drawbacks: the main one being a stronger link than necessary between the driver and the device tree. For example the clock name can't be changed, removed or moved. It is still the early stage of introduction of the Armada 7K/8K and the hardware is still not totally documented, especially for the clock part. By removing the use of *-clock-output-names it will be easier to add new clocks without breaking the compatibility. The name of each clock is now created by using its physical address as a prefix (as it was done for the platform device names). Thanks to this we have an automatic way to compute a unique name. Acked-by: Rob Herring Signed-off-by: Gregory CLEMENT --- drivers/clk/mvebu/cp110-system-controller.c | 103 ++++++++++++-------- 1 file changed, 64 insertions(+), 39 deletions(-) diff --git a/drivers/clk/mvebu/cp110-system-controller.c b/drivers/clk/mvebu/cp110-system-controller.c index 3b27a6056c73..081f089d2656 100644 --- a/drivers/clk/mvebu/cp110-system-controller.c +++ b/drivers/clk/mvebu/cp110-system-controller.c @@ -84,6 +84,33 @@ enum { #define CP110_GATE_EIP150 25 #define CP110_GATE_EIP197 26 +const char *gate_base_names[] = { + [CP110_GATE_AUDIO] = "audio", + [CP110_GATE_COMM_UNIT] = "communit", + [CP110_GATE_NAND] = "nand", + [CP110_GATE_PPV2] = "ppv2", + [CP110_GATE_SDIO] = "sdio", + [CP110_GATE_MG] = "mg-domain", + [CP110_GATE_MG_CORE] = "mg-core", + [CP110_GATE_XOR1] = "xor1", + [CP110_GATE_XOR0] = "xor0", + [CP110_GATE_GOP_DP] = "gop-dp", + [CP110_GATE_PCIE_X1_0] = "pcie_x10", + [CP110_GATE_PCIE_X1_1] = "pcie_x11", + [CP110_GATE_PCIE_X4] = "pcie_x4", + [CP110_GATE_PCIE_XOR] = "pcie-xor", + [CP110_GATE_SATA] = "sata", + [CP110_GATE_SATA_USB] = "sata-usb", + [CP110_GATE_MAIN] = "main", + [CP110_GATE_SDMMC_GOP] = "sd-mmc-gop", + [CP110_GATE_SLOW_IO] = "slow-io", + [CP110_GATE_USB3H0] = "usb3h0", + [CP110_GATE_USB3H1] = "usb3h1", + [CP110_GATE_USB3DEV] = "usb3dev", + [CP110_GATE_EIP150] = "eip150", + [CP110_GATE_EIP197] = "eip197" +}; + struct cp110_gate_clk { struct clk_hw hw; struct regmap *regmap; @@ -186,15 +213,33 @@ static struct clk_hw *cp110_of_clk_get(struct of_phandle_args *clkspec, return ERR_PTR(-EINVAL); } +static char *cp110_unique_name(struct device *dev, const char *name) +{ + struct device_node *np = dev->of_node; + const __be32 *reg; + u64 addr; + + /* Do not create a name if there is no clock */ + if (!name) + return NULL; + + reg = of_get_property(np, "reg", NULL); + addr = of_translate_address(np, reg); + return devm_kasprintf(dev, GFP_KERNEL, "%llx-%s", + (unsigned long long)addr, name); +} + static int cp110_syscon_clk_probe(struct platform_device *pdev) { struct regmap *regmap; - struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; const char *ppv2_name, *apll_name, *core_name, *eip_name, *nand_name; struct clk_hw_onecell_data *cp110_clk_data; struct clk_hw *hw, **cp110_clks; u32 nand_clk_ctrl; int i, ret; + char *gate_name[ARRAY_SIZE(gate_base_names)]; regmap = syscon_node_to_regmap(np); if (IS_ERR(regmap)) @@ -205,7 +250,7 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) if (ret) return ret; - cp110_clk_data = devm_kzalloc(&pdev->dev, sizeof(*cp110_clk_data) + + cp110_clk_data = devm_kzalloc(dev, sizeof(*cp110_clk_data) + sizeof(struct clk_hw *) * CP110_CLK_NUM, GFP_KERNEL); if (!cp110_clk_data) @@ -215,8 +260,7 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) cp110_clk_data->num = CP110_CLK_NUM; /* Register the APLL which is the root of the hw tree */ - of_property_read_string_index(np, "core-clock-output-names", - CP110_CORE_APLL, &apll_name); + apll_name = cp110_unique_name(dev, "apll"); hw = clk_hw_register_fixed_rate(NULL, apll_name, NULL, 0, 1000 * 1000 * 1000); if (IS_ERR(hw)) { @@ -227,8 +271,7 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) cp110_clks[CP110_CORE_APLL] = hw; /* PPv2 is APLL/3 */ - of_property_read_string_index(np, "core-clock-output-names", - CP110_CORE_PPV2, &ppv2_name); + ppv2_name = cp110_unique_name(dev, "ppv2-core"); hw = clk_hw_register_fixed_factor(NULL, ppv2_name, apll_name, 0, 1, 3); if (IS_ERR(hw)) { ret = PTR_ERR(hw); @@ -238,8 +281,7 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) cp110_clks[CP110_CORE_PPV2] = hw; /* EIP clock is APLL/2 */ - of_property_read_string_index(np, "core-clock-output-names", - CP110_CORE_EIP, &eip_name); + eip_name = cp110_unique_name(dev, "eip"); hw = clk_hw_register_fixed_factor(NULL, eip_name, apll_name, 0, 1, 2); if (IS_ERR(hw)) { ret = PTR_ERR(hw); @@ -249,8 +291,7 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) cp110_clks[CP110_CORE_EIP] = hw; /* Core clock is EIP/2 */ - of_property_read_string_index(np, "core-clock-output-names", - CP110_CORE_CORE, &core_name); + core_name = cp110_unique_name(dev, "core"); hw = clk_hw_register_fixed_factor(NULL, core_name, eip_name, 0, 1, 2); if (IS_ERR(hw)) { ret = PTR_ERR(hw); @@ -258,10 +299,8 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) } cp110_clks[CP110_CORE_CORE] = hw; - /* NAND can be either APLL/2.5 or core clock */ - of_property_read_string_index(np, "core-clock-output-names", - CP110_CORE_NAND, &nand_name); + nand_name = cp110_unique_name(dev, "nand-core"); if (nand_clk_ctrl & NF_CLOCK_SEL_400_MASK) hw = clk_hw_register_fixed_factor(NULL, nand_name, apll_name, 0, 2, 5); @@ -275,18 +314,14 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) cp110_clks[CP110_CORE_NAND] = hw; - for (i = 0; i < CP110_MAX_GATABLE_CLOCKS; i++) { - const char *parent, *name; - int ret; + /* create the unique name for all the gate clocks */ + for (i = 0; i < ARRAY_SIZE(gate_base_names); i++) + gate_name[i] = cp110_unique_name(dev, gate_base_names[i]); - ret = of_property_read_string_index(np, - "gate-clock-output-names", - i, &name); - /* Reached the end of the list? */ - if (ret < 0) - break; + for (i = 0; i < ARRAY_SIZE(gate_base_names); i++) { + const char *parent; - if (!strcmp(name, "none")) + if (gate_name[i] == NULL) continue; switch (i) { @@ -295,14 +330,10 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) case CP110_GATE_EIP150: case CP110_GATE_EIP197: case CP110_GATE_SLOW_IO: - of_property_read_string_index(np, - "gate-clock-output-names", - CP110_GATE_MAIN, &parent); + parent = gate_name[CP110_GATE_MAIN]; break; case CP110_GATE_MG: - of_property_read_string_index(np, - "gate-clock-output-names", - CP110_GATE_MG_CORE, &parent); + parent = gate_name[CP110_GATE_MG_CORE]; break; case CP110_GATE_NAND: parent = nand_name; @@ -312,33 +343,27 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) break; case CP110_GATE_SDIO: case CP110_GATE_GOP_DP: - of_property_read_string_index(np, - "gate-clock-output-names", - CP110_GATE_SDMMC_GOP, &parent); + parent = gate_name[CP110_GATE_SDMMC_GOP]; break; case CP110_GATE_XOR1: case CP110_GATE_XOR0: case CP110_GATE_PCIE_X1_0: case CP110_GATE_PCIE_X1_1: case CP110_GATE_PCIE_X4: - of_property_read_string_index(np, - "gate-clock-output-names", - CP110_GATE_PCIE_XOR, &parent); + parent = gate_name[CP110_GATE_PCIE_XOR]; break; case CP110_GATE_SATA: case CP110_GATE_USB3H0: case CP110_GATE_USB3H1: case CP110_GATE_USB3DEV: - of_property_read_string_index(np, - "gate-clock-output-names", - CP110_GATE_SATA_USB, &parent); + parent = gate_name[CP110_GATE_SATA_USB]; break; default: parent = core_name; break; } + hw = cp110_register_gate(gate_name[i], parent, regmap, i); - hw = cp110_register_gate(name, parent, regmap, i); if (IS_ERR(hw)) { ret = PTR_ERR(hw); goto fail_gate; From 5ffeb5f5a77adc925699bbd9da198e5b8a4eec5a Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Tue, 30 May 2017 17:46:06 +0200 Subject: [PATCH 3/4] clk: mvebu: cp110: introduce a new binding The initial intent when the binding of the cp110 system controller was to have one flat node. The idea being that what is currently a clock-only driver in drivers would become a MFD driver, exposing the clock, GPIO and pinctrl functionality. However, after taking a step back, this would lead to a messy binding. Indeed, a single node would be a GPIO controller, clock controller, pinmux controller, and more. This patch adopts a more classical solution of a top-level syscon node with sub-nodes for the individual devices. The main benefit will be to have each functional block associated to its own sub-node where we can put its own properties. The introduction of the Armada 7K/8K is still in the early stage so the plan is to remove the old binding. However, we don't want to break the device tree compatibility for the few devices already in the field. For this we still keep the support of the legacy compatible string with a big warning in the kernel about updating the device tree. Reviewed-by: Thomas Petazzoni Acked-by: Rob Herring Signed-off-by: Gregory CLEMENT --- drivers/clk/mvebu/cp110-system-controller.c | 63 ++++++++++++++++----- 1 file changed, 48 insertions(+), 15 deletions(-) diff --git a/drivers/clk/mvebu/cp110-system-controller.c b/drivers/clk/mvebu/cp110-system-controller.c index 081f089d2656..ae3d81263304 100644 --- a/drivers/clk/mvebu/cp110-system-controller.c +++ b/drivers/clk/mvebu/cp110-system-controller.c @@ -213,9 +213,9 @@ static struct clk_hw *cp110_of_clk_get(struct of_phandle_args *clkspec, return ERR_PTR(-EINVAL); } -static char *cp110_unique_name(struct device *dev, const char *name) +static char *cp110_unique_name(struct device *dev, struct device_node *np, + const char *name) { - struct device_node *np = dev->of_node; const __be32 *reg; u64 addr; @@ -229,7 +229,8 @@ static char *cp110_unique_name(struct device *dev, const char *name) (unsigned long long)addr, name); } -static int cp110_syscon_clk_probe(struct platform_device *pdev) +static int cp110_syscon_common_probe(struct platform_device *pdev, + struct device_node *syscon_node) { struct regmap *regmap; struct device *dev = &pdev->dev; @@ -241,7 +242,7 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) int i, ret; char *gate_name[ARRAY_SIZE(gate_base_names)]; - regmap = syscon_node_to_regmap(np); + regmap = syscon_node_to_regmap(syscon_node); if (IS_ERR(regmap)) return PTR_ERR(regmap); @@ -260,7 +261,7 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) cp110_clk_data->num = CP110_CLK_NUM; /* Register the APLL which is the root of the hw tree */ - apll_name = cp110_unique_name(dev, "apll"); + apll_name = cp110_unique_name(dev, syscon_node, "apll"); hw = clk_hw_register_fixed_rate(NULL, apll_name, NULL, 0, 1000 * 1000 * 1000); if (IS_ERR(hw)) { @@ -271,7 +272,7 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) cp110_clks[CP110_CORE_APLL] = hw; /* PPv2 is APLL/3 */ - ppv2_name = cp110_unique_name(dev, "ppv2-core"); + ppv2_name = cp110_unique_name(dev, syscon_node, "ppv2-core"); hw = clk_hw_register_fixed_factor(NULL, ppv2_name, apll_name, 0, 1, 3); if (IS_ERR(hw)) { ret = PTR_ERR(hw); @@ -281,7 +282,7 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) cp110_clks[CP110_CORE_PPV2] = hw; /* EIP clock is APLL/2 */ - eip_name = cp110_unique_name(dev, "eip"); + eip_name = cp110_unique_name(dev, syscon_node, "eip"); hw = clk_hw_register_fixed_factor(NULL, eip_name, apll_name, 0, 1, 2); if (IS_ERR(hw)) { ret = PTR_ERR(hw); @@ -291,7 +292,7 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) cp110_clks[CP110_CORE_EIP] = hw; /* Core clock is EIP/2 */ - core_name = cp110_unique_name(dev, "core"); + core_name = cp110_unique_name(dev, syscon_node, "core"); hw = clk_hw_register_fixed_factor(NULL, core_name, eip_name, 0, 1, 2); if (IS_ERR(hw)) { ret = PTR_ERR(hw); @@ -300,7 +301,7 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) cp110_clks[CP110_CORE_CORE] = hw; /* NAND can be either APLL/2.5 or core clock */ - nand_name = cp110_unique_name(dev, "nand-core"); + nand_name = cp110_unique_name(dev, syscon_node, "nand-core"); if (nand_clk_ctrl & NF_CLOCK_SEL_400_MASK) hw = clk_hw_register_fixed_factor(NULL, nand_name, apll_name, 0, 2, 5); @@ -316,7 +317,8 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev) /* create the unique name for all the gate clocks */ for (i = 0; i < ARRAY_SIZE(gate_base_names); i++) - gate_name[i] = cp110_unique_name(dev, gate_base_names[i]); + gate_name[i] = cp110_unique_name(dev, syscon_node, + gate_base_names[i]); for (i = 0; i < ARRAY_SIZE(gate_base_names); i++) { const char *parent; @@ -402,17 +404,48 @@ fail_apll: return ret; } -static const struct of_device_id cp110_syscon_of_match[] = { +static int cp110_syscon_legacy_clk_probe(struct platform_device *pdev) +{ + dev_warn(&pdev->dev, FW_WARN "Using legacy device tree binding\n"); + dev_warn(&pdev->dev, FW_WARN "Update your device tree:\n"); + dev_warn(&pdev->dev, FW_WARN + "This binding won't be supported in future kernels\n"); + + return cp110_syscon_common_probe(pdev, pdev->dev.of_node); +} + +static int cp110_clk_probe(struct platform_device *pdev) +{ + return cp110_syscon_common_probe(pdev, pdev->dev.of_node->parent); +} + + +static const struct of_device_id cp110_syscon_legacy_of_match[] = { { .compatible = "marvell,cp110-system-controller0", }, { } }; -static struct platform_driver cp110_syscon_driver = { - .probe = cp110_syscon_clk_probe, +static struct platform_driver cp110_syscon_legacy_driver = { + .probe = *cp110_syscon_legacy_clk_probe, .driver = { .name = "marvell-cp110-system-controller0", - .of_match_table = cp110_syscon_of_match, + .of_match_table = cp110_syscon_legacy_of_match, .suppress_bind_attrs = true, }, }; -builtin_platform_driver(cp110_syscon_driver); +builtin_platform_driver(cp110_syscon_legacy_driver); + +static const struct of_device_id cp110_clock_of_match[] = { + { .compatible = "marvell,cp110-clock", }, + { } +}; + +static struct platform_driver cp110_clock_driver = { + .probe = cp110_clk_probe, + .driver = { + .name = "marvell-cp110-clock", + .of_match_table = cp110_clock_of_match, + .suppress_bind_attrs = true, + }, +}; +builtin_platform_driver(cp110_clock_driver); From a45af6d3a98b4d8a2746de13a8db52fb4123bb56 Mon Sep 17 00:00:00 2001 From: Konstantin Porotchkin Date: Wed, 31 May 2017 15:19:15 +0200 Subject: [PATCH 4/4] clk: mvebu: cp110: add sdio clock to cp-110 system controller This commit updates the CP110 system controller driver to add the definition for a missing clock. The SDIO clock is dedicated driving the SDHCI interface and its frequency is 400MHz (2/5 of PLL source clock). The SDIO interface should be bound to this clock and not the core clock as in the older code. Using the wrong clock lead to a maximum SDHCI frequency of 250 Mhz, while the HW really supports up to 400 Mhz. This patch also fixes the NAND clock relationship documentation. Signed-off-by: Konstantin Porotchkin [gregory.clement@free-electrons.com: - use sdio instead of emmc to name the clock] Reviewed-by: Thomas Petazzoni Acked-by: Rob Herring Signed-off-by: Gregory CLEMENT --- drivers/clk/mvebu/cp110-system-controller.c | 28 +++++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/drivers/clk/mvebu/cp110-system-controller.c b/drivers/clk/mvebu/cp110-system-controller.c index ae3d81263304..b034b79345ec 100644 --- a/drivers/clk/mvebu/cp110-system-controller.c +++ b/drivers/clk/mvebu/cp110-system-controller.c @@ -11,15 +11,16 @@ */ /* - * CP110 has 5 core clocks: + * CP110 has 6 core clocks: * * - APLL (1 Ghz) * - PPv2 core (1/3 APLL) * - EIP (1/2 APLL) - * - Core (1/2 EIP) + * - Core (1/2 EIP) + * - SDIO (2/5 APLL) * * - NAND clock, which is either: - * - Equal to the core clock + * - Equal to SDIO clock * - 2/5 APLL * * CP110 has 32 gatable clocks, for the various peripherals in the @@ -46,7 +47,7 @@ enum { CP110_CLK_TYPE_GATABLE, }; -#define CP110_MAX_CORE_CLOCKS 5 +#define CP110_MAX_CORE_CLOCKS 6 #define CP110_MAX_GATABLE_CLOCKS 32 #define CP110_CLK_NUM \ @@ -57,6 +58,7 @@ enum { #define CP110_CORE_EIP 2 #define CP110_CORE_CORE 3 #define CP110_CORE_NAND 4 +#define CP110_CORE_SDIO 5 /* A number of gatable clocks need special handling */ #define CP110_GATE_AUDIO 0 @@ -235,7 +237,8 @@ static int cp110_syscon_common_probe(struct platform_device *pdev, struct regmap *regmap; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; - const char *ppv2_name, *apll_name, *core_name, *eip_name, *nand_name; + const char *ppv2_name, *apll_name, *core_name, *eip_name, *nand_name, + *sdio_name; struct clk_hw_onecell_data *cp110_clk_data; struct clk_hw *hw, **cp110_clks; u32 nand_clk_ctrl; @@ -315,6 +318,17 @@ static int cp110_syscon_common_probe(struct platform_device *pdev, cp110_clks[CP110_CORE_NAND] = hw; + /* SDIO clock is APLL/2.5 */ + sdio_name = cp110_unique_name(dev, syscon_node, "sdio-core"); + hw = clk_hw_register_fixed_factor(NULL, sdio_name, + apll_name, 0, 2, 5); + if (IS_ERR(hw)) { + ret = PTR_ERR(hw); + goto fail_sdio; + } + + cp110_clks[CP110_CORE_SDIO] = hw; + /* create the unique name for all the gate clocks */ for (i = 0; i < ARRAY_SIZE(gate_base_names); i++) gate_name[i] = cp110_unique_name(dev, syscon_node, @@ -344,6 +358,8 @@ static int cp110_syscon_common_probe(struct platform_device *pdev, parent = ppv2_name; break; case CP110_GATE_SDIO: + parent = sdio_name; + break; case CP110_GATE_GOP_DP: parent = gate_name[CP110_GATE_SDMMC_GOP]; break; @@ -391,6 +407,8 @@ fail_gate: cp110_unregister_gate(hw); } + clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_SDIO]); +fail_sdio: clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_NAND]); fail_nand: clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_CORE]);