diff mbox

[RFC,3/4] clk: samsung: Add driver to control CLKOUT line on Exynos SoCs

Message ID 1400175151-27779-4-git-send-email-t.figa@samsung.com (mailing list archive)
State New, archived
Headers show

Commit Message

Tomasz Figa May 15, 2014, 5:32 p.m. UTC
This patch introduces a driver that handles configuration of CLKOUT pin
of Exynos SoCs that can be used to output certain clocks from inside of
the SoC to a dedicated output pin.

Signed-off-by: Tomasz Figa <t.figa@samsung.com>
---
 .../devicetree/bindings/arm/samsung/pmu.txt        |  18 ++++
 drivers/clk/samsung/Makefile                       |   1 +
 drivers/clk/samsung/exynos-clkout.c                | 107 +++++++++++++++++++++
 3 files changed, 126 insertions(+)
 create mode 100644 drivers/clk/samsung/exynos-clkout.c

Comments

Rahul Sharma May 16, 2014, 10:39 a.m. UTC | #1
[snip]
> +       gate->lock = &clkout_lock;
> +
> +       mux->reg = reg + EXYNOS_PMU_DEBUG_REG;
> +       mux->mask = EXYNOS_CLKOUT_MUX_MASK;
> +       mux->shift = EXYNOS_CLKOUT_MUX_SHIFT;
> +       mux->lock = &clkout_lock;
> +
> +       clk = clk_register_composite(NULL, "clkout", parent_names,
> +                                       parent_count, &mux->hw,
> +                                       &clk_mux_ops, NULL, NULL, &gate->hw,
> +                                       &clk_gate_ops, 0);
> +       if (IS_ERR(clk))
> +               goto err_unmap;
> +

Hi Tomasz,

Do we really need a composite clock here? How about registering
a mux and a gate separately?

Regards,
Rahul Sharma.

> +       of_clk_add_provider(node, of_clk_src_simple_get, clk);
> +
> +       return;
> +
> +err_unmap:
> +       iounmap(reg);
> +clks_put:
> +       for (i = 0; i < EXYNOS_CLKOUT_PARENTS; ++i)
> +               if (!IS_ERR(parents[i]))
> +                       clk_put(parents[i]);
> +free_gate:
> +       kfree(gate);
> +free_mux:
> +       kfree(mux);
> +
> +       pr_err("%s: failed to register clkout clock\n", __func__);
> +}
> +CLK_OF_DECLARE(exynos4210_clkout, "samsung,exynos4210-pmu", exynos_clkout_init);
> +CLK_OF_DECLARE(exynos4412_clkout, "samsung,exynos4x12-pmu", exynos_clkout_init);
> +CLK_OF_DECLARE(exynos5250_clkout, "samsung,exynos5250-pmu", exynos_clkout_init);
> +CLK_OF_DECLARE(exynos5420_clkout, "samsung,exynos5420-pmu", exynos_clkout_init);
> --
> 1.9.2
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Tomasz Figa May 16, 2014, 10:52 a.m. UTC | #2
Hi Rahul,

On 16.05.2014 12:39, Rahul Sharma wrote:
> [snip]
>> +       gate->lock = &clkout_lock;
>> +
>> +       mux->reg = reg + EXYNOS_PMU_DEBUG_REG;
>> +       mux->mask = EXYNOS_CLKOUT_MUX_MASK;
>> +       mux->shift = EXYNOS_CLKOUT_MUX_SHIFT;
>> +       mux->lock = &clkout_lock;
>> +
>> +       clk = clk_register_composite(NULL, "clkout", parent_names,
>> +                                       parent_count, &mux->hw,
>> +                                       &clk_mux_ops, NULL, NULL, &gate->hw,
>> +                                       &clk_gate_ops, 0);
>> +       if (IS_ERR(clk))
>> +               goto err_unmap;
>> +
> 
> Hi Tomasz,
> 
> Do we really need a composite clock here? How about registering
> a mux and a gate separately?

What's wrong with a composite clock? It simplifies the code as just a
single clock needs to be registered. I don't see any drawbacks compared
to registering two clocks separately.

Best regards,
Tomasz
Rahul Sharma May 16, 2014, 2:35 p.m. UTC | #3
On 16 May 2014 16:22, Tomasz Figa <t.figa@samsung.com> wrote:
> Hi Rahul,
>
> On 16.05.2014 12:39, Rahul Sharma wrote:
>> [snip]
>>> +       gate->lock = &clkout_lock;
>>> +
>>> +       mux->reg = reg + EXYNOS_PMU_DEBUG_REG;
>>> +       mux->mask = EXYNOS_CLKOUT_MUX_MASK;
>>> +       mux->shift = EXYNOS_CLKOUT_MUX_SHIFT;
>>> +       mux->lock = &clkout_lock;
>>> +
>>> +       clk = clk_register_composite(NULL, "clkout", parent_names,
>>> +                                       parent_count, &mux->hw,
>>> +                                       &clk_mux_ops, NULL, NULL, &gate->hw,
>>> +                                       &clk_gate_ops, 0);
>>> +       if (IS_ERR(clk))
>>> +               goto err_unmap;
>>> +
>>
>> Hi Tomasz,
>>
>> Do we really need a composite clock here? How about registering
>> a mux and a gate separately?
>
> What's wrong with a composite clock? It simplifies the code as just a
> single clock needs to be registered. I don't see any drawbacks compared
> to registering two clocks separately.
>

I always took it as a thumb rule to not to use composite clocks if you
can easily represent the block using basic clocks structures.

There can be a problem when drivers using such clocks assume that such
clock continue to offer composite functionality for all futures SoCs and
write code around it. This is what we faced when fixing drivers during
CCF migration.

> Best regards,
> Tomasz
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Tomasz Figa May 16, 2014, 2:58 p.m. UTC | #4
On 16.05.2014 16:35, Rahul Sharma wrote:
> On 16 May 2014 16:22, Tomasz Figa <t.figa@samsung.com> wrote:
>> Hi Rahul,
>>
>> On 16.05.2014 12:39, Rahul Sharma wrote:
>>> [snip]
>>>> +       gate->lock = &clkout_lock;
>>>> +
>>>> +       mux->reg = reg + EXYNOS_PMU_DEBUG_REG;
>>>> +       mux->mask = EXYNOS_CLKOUT_MUX_MASK;
>>>> +       mux->shift = EXYNOS_CLKOUT_MUX_SHIFT;
>>>> +       mux->lock = &clkout_lock;
>>>> +
>>>> +       clk = clk_register_composite(NULL, "clkout", parent_names,
>>>> +                                       parent_count, &mux->hw,
>>>> +                                       &clk_mux_ops, NULL, NULL, &gate->hw,
>>>> +                                       &clk_gate_ops, 0);
>>>> +       if (IS_ERR(clk))
>>>> +               goto err_unmap;
>>>> +
>>>
>>> Hi Tomasz,
>>>
>>> Do we really need a composite clock here? How about registering
>>> a mux and a gate separately?
>>
>> What's wrong with a composite clock? It simplifies the code as just a
>> single clock needs to be registered. I don't see any drawbacks compared
>> to registering two clocks separately.
>>
> 
> I always took it as a thumb rule to not to use composite clocks if you
> can easily represent the block using basic clocks structures.
> 
> There can be a problem when drivers using such clocks assume that such
> clock continue to offer composite functionality for all futures SoCs and
> write code around it. This is what we faced when fixing drivers during
> CCF migration.

The drivers using CLKOUT need to be designed this way, because they are
not Exynos-specific, such as drivers for HSIC hubs or audio codecs. They
can't have any Exynos-specific knowledge about clock hierarchy.

So regardless of whether this is implemented using the composite clock
or not, consumer device drivers need to be able to use just a single
clock to do whatever they need, e.g. gating or rate configuration.

However I can see one problem here with my implementation - it is
missing the CLK_SET_RATE_PARENT flag, so dividers of particular CLK_OUTs
from CMU blocks could be reconfigured.

Also the driver is missing save and restore of PMU_DEBUG register.

I will fix both in next version.

Best regards,
Tomasz
Mike Turquette May 16, 2014, 11:04 p.m. UTC | #5
Quoting Tomasz Figa (2014-05-15 10:32:30)
> This patch introduces a driver that handles configuration of CLKOUT pin
> of Exynos SoCs that can be used to output certain clocks from inside of
> the SoC to a dedicated output pin.
> 
> Signed-off-by: Tomasz Figa <t.figa@samsung.com>

Overall implementation looks good to me. Nice solution for exposing
these pins to external ICs.

Regards,
Mike

> ---
>  .../devicetree/bindings/arm/samsung/pmu.txt        |  18 ++++
>  drivers/clk/samsung/Makefile                       |   1 +
>  drivers/clk/samsung/exynos-clkout.c                | 107 +++++++++++++++++++++
>  3 files changed, 126 insertions(+)
>  create mode 100644 drivers/clk/samsung/exynos-clkout.c
> 
> diff --git a/Documentation/devicetree/bindings/arm/samsung/pmu.txt b/Documentation/devicetree/bindings/arm/samsung/pmu.txt
> index b562634..67c7272 100644
> --- a/Documentation/devicetree/bindings/arm/samsung/pmu.txt
> +++ b/Documentation/devicetree/bindings/arm/samsung/pmu.txt
> @@ -11,8 +11,26 @@ Properties:
>  
>   - reg : offset and length of the register set.
>  
> + - #clock-cells : must be zero.
> +
> + - clock-names : list of clock names for particular CLKOUT mux inputs in
> +               following format:
> +                       "clkoutN", where N is a decimal number corresponding to
> +                       CLKOUT mux control bits value for given input, e.g.
> +                               "clkout0", "clkout7", "clkout15".
> +
> + - clocks : list of phandles and specifiers to all input clocks listed in
> +               clock-names property.
> +
>  Example :
>  pmu_system_controller: system-controller@10040000 {
>         compatible = "samsung,exynos5250-pmu", "syscon";
>         reg = <0x10040000 0x5000>;
> +       #clock-cells = <0>;
> +       clock-names = "clkout0", "clkout1", "clkout2", "clkout3",
> +                       "clkout4", "clkout8", "clkout9";
> +       clocks = <&clock CLK_OUT_DMC>, <&clock CLK_OUT_TOP>,
> +               <&clock CLK_OUT_LEFTBUS>, <&clock CLK_OUT_RIGHTBUS>,
> +               <&clock CLK_OUT_CPU>, <&clock CLK_XXTI>,
> +               <&clock CLK_XUSBXTI>;
>  };
> diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile
> index 2cb62f8..3c40362 100644
> --- a/drivers/clk/samsung/Makefile
> +++ b/drivers/clk/samsung/Makefile
> @@ -8,6 +8,7 @@ obj-$(CONFIG_SOC_EXYNOS5250)    += clk-exynos5250.o
>  obj-$(CONFIG_SOC_EXYNOS5420)   += clk-exynos5420.o
>  obj-$(CONFIG_SOC_EXYNOS5440)   += clk-exynos5440.o
>  obj-$(CONFIG_ARCH_EXYNOS)      += clk-exynos-audss.o
> +obj-$(CONFIG_ARCH_EXYNOS)      += exynos-clkout.o
>  obj-$(CONFIG_S3C2410_COMMON_CLK)+= clk-s3c2410.o
>  obj-$(CONFIG_S3C2410_COMMON_DCLK)+= clk-s3c2410-dclk.o
>  obj-$(CONFIG_S3C2412_COMMON_CLK)+= clk-s3c2412.o
> diff --git a/drivers/clk/samsung/exynos-clkout.c b/drivers/clk/samsung/exynos-clkout.c
> new file mode 100644
> index 0000000..461f1c3
> --- /dev/null
> +++ b/drivers/clk/samsung/exynos-clkout.c
> @@ -0,0 +1,107 @@
> +/*
> + * Copyright (c) 2014 Samsung Electronics Co., Ltd.
> + * Author: Tomasz Figa <t.figa@samsung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * Clock driver for Exynos clock output
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clkdev.h>
> +#include <linux/clk-provider.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/syscore_ops.h>
> +
> +#define EXYNOS_CLKOUT_PARENTS          32
> +
> +#define EXYNOS_PMU_DEBUG_REG           0xa00
> +#define EXYNOS_CLKOUT_DISABLE_SHIFT    0
> +#define EXYNOS_CLKOUT_MUX_SHIFT                8
> +#define EXYNOS_CLKOUT_MUX_MASK         0x1f
> +
> +static DEFINE_SPINLOCK(clkout_lock);
> +
> +static void __init exynos_clkout_init(struct device_node *node)
> +{
> +       const char *parent_names[EXYNOS_CLKOUT_PARENTS];
> +       struct clk *parents[EXYNOS_CLKOUT_PARENTS];
> +       struct clk_gate *gate;
> +       struct clk_mux *mux;
> +       int parent_count;
> +       struct clk *clk;
> +       void *reg;
> +       int i;
> +
> +       /* 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;
> +
> +       parent_count = 0;
> +       for (i = 0; i < EXYNOS_CLKOUT_PARENTS; ++i) {
> +               char name[] = "clkoutXX";
> +
> +               snprintf(name, sizeof(name), "clkout%d", i);
> +               parents[i] = of_clk_get_by_name(node, name);
> +               if (IS_ERR(parents[i])) {
> +                       parent_names[i] = "none";
> +                       continue;
> +               }
> +
> +               parent_names[i] = __clk_get_name(parents[i]);
> +               parent_count = i + 1;
> +       }
> +
> +       if (!parent_count)
> +               goto free_gate;
> +
> +       reg = of_iomap(node, 0);
> +       if (!reg)
> +               goto clks_put;
> +
> +       gate->reg = reg + EXYNOS_PMU_DEBUG_REG;
> +       gate->bit_idx = EXYNOS_CLKOUT_DISABLE_SHIFT;
> +       gate->flags = CLK_GATE_SET_TO_DISABLE;
> +       gate->lock = &clkout_lock;
> +
> +       mux->reg = reg + EXYNOS_PMU_DEBUG_REG;
> +       mux->mask = EXYNOS_CLKOUT_MUX_MASK;
> +       mux->shift = EXYNOS_CLKOUT_MUX_SHIFT;
> +       mux->lock = &clkout_lock;
> +
> +       clk = clk_register_composite(NULL, "clkout", parent_names,
> +                                       parent_count, &mux->hw,
> +                                       &clk_mux_ops, NULL, NULL, &gate->hw,
> +                                       &clk_gate_ops, 0);
> +       if (IS_ERR(clk))
> +               goto err_unmap;
> +
> +       of_clk_add_provider(node, of_clk_src_simple_get, clk);
> +
> +       return;
> +
> +err_unmap:
> +       iounmap(reg);
> +clks_put:
> +       for (i = 0; i < EXYNOS_CLKOUT_PARENTS; ++i)
> +               if (!IS_ERR(parents[i]))
> +                       clk_put(parents[i]);
> +free_gate:
> +       kfree(gate);
> +free_mux:
> +       kfree(mux);
> +
> +       pr_err("%s: failed to register clkout clock\n", __func__);
> +}
> +CLK_OF_DECLARE(exynos4210_clkout, "samsung,exynos4210-pmu", exynos_clkout_init);
> +CLK_OF_DECLARE(exynos4412_clkout, "samsung,exynos4x12-pmu", exynos_clkout_init);
> +CLK_OF_DECLARE(exynos5250_clkout, "samsung,exynos5250-pmu", exynos_clkout_init);
> +CLK_OF_DECLARE(exynos5420_clkout, "samsung,exynos5420-pmu", exynos_clkout_init);
> -- 
> 1.9.2
>
Tushar Behera May 19, 2014, 7:16 a.m. UTC | #6
On 05/15/2014 11:02 PM, Tomasz Figa wrote:
> This patch introduces a driver that handles configuration of CLKOUT pin
> of Exynos SoCs that can be used to output certain clocks from inside of
> the SoC to a dedicated output pin.
> 
> Signed-off-by: Tomasz Figa <t.figa@samsung.com>
> ---

Tested the series on Exynos5420-based peach-pit board for audio playback
(with an internal patch to set CLK_FIN_PLL as the parent of clkout).

Tested-by: Tushar Behera <tushar.behera@linaro.org>

>  Example :
>  pmu_system_controller: system-controller@10040000 {
>  	compatible = "samsung,exynos5250-pmu", "syscon";
>  	reg = <0x10040000 0x5000>;
> +	#clock-cells = <0>;
> +	clock-names = "clkout0", "clkout1", "clkout2", "clkout3",
> +			"clkout4", "clkout8", "clkout9";
> +	clocks = <&clock CLK_OUT_DMC>, <&clock CLK_OUT_TOP>,
> +		<&clock CLK_OUT_LEFTBUS>, <&clock CLK_OUT_RIGHTBUS>,
> +		<&clock CLK_OUT_CPU>, <&clock CLK_XXTI>,
> +		<&clock CLK_XUSBXTI>;
>  };

Adding an usage example in the documentation would be helpful.
Tomasz Figa May 19, 2014, 10:25 a.m. UTC | #7
Hi Tushar,

On 19.05.2014 09:16, Tushar Behera wrote:
> On 05/15/2014 11:02 PM, Tomasz Figa wrote:
>> This patch introduces a driver that handles configuration of CLKOUT pin
>> of Exynos SoCs that can be used to output certain clocks from inside of
>> the SoC to a dedicated output pin.
>>
>> Signed-off-by: Tomasz Figa <t.figa@samsung.com>
>> ---
> 
> Tested the series on Exynos5420-based peach-pit board for audio playback
> (with an internal patch to set CLK_FIN_PLL as the parent of clkout).
> 
> Tested-by: Tushar Behera <tushar.behera@linaro.org>
> 

Thank you for testing.

>>  Example :
>>  pmu_system_controller: system-controller@10040000 {
>>  	compatible = "samsung,exynos5250-pmu", "syscon";
>>  	reg = <0x10040000 0x5000>;
>> +	#clock-cells = <0>;
>> +	clock-names = "clkout0", "clkout1", "clkout2", "clkout3",
>> +			"clkout4", "clkout8", "clkout9";
>> +	clocks = <&clock CLK_OUT_DMC>, <&clock CLK_OUT_TOP>,
>> +		<&clock CLK_OUT_LEFTBUS>, <&clock CLK_OUT_RIGHTBUS>,
>> +		<&clock CLK_OUT_CPU>, <&clock CLK_XXTI>,
>> +		<&clock CLK_XUSBXTI>;
>>  };
> 
> Adding an usage example in the documentation would be helpful.

Right, that's usually a good idea, I don't know why I haven't added one.
Thanks.

Best regards,
Tomasz
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/arm/samsung/pmu.txt b/Documentation/devicetree/bindings/arm/samsung/pmu.txt
index b562634..67c7272 100644
--- a/Documentation/devicetree/bindings/arm/samsung/pmu.txt
+++ b/Documentation/devicetree/bindings/arm/samsung/pmu.txt
@@ -11,8 +11,26 @@  Properties:
 
  - reg : offset and length of the register set.
 
+ - #clock-cells : must be zero.
+
+ - clock-names : list of clock names for particular CLKOUT mux inputs in
+		following format:
+			"clkoutN", where N is a decimal number corresponding to
+			CLKOUT mux control bits value for given input, e.g.
+				"clkout0", "clkout7", "clkout15".
+
+ - clocks : list of phandles and specifiers to all input clocks listed in
+		clock-names property.
+
 Example :
 pmu_system_controller: system-controller@10040000 {
 	compatible = "samsung,exynos5250-pmu", "syscon";
 	reg = <0x10040000 0x5000>;
+	#clock-cells = <0>;
+	clock-names = "clkout0", "clkout1", "clkout2", "clkout3",
+			"clkout4", "clkout8", "clkout9";
+	clocks = <&clock CLK_OUT_DMC>, <&clock CLK_OUT_TOP>,
+		<&clock CLK_OUT_LEFTBUS>, <&clock CLK_OUT_RIGHTBUS>,
+		<&clock CLK_OUT_CPU>, <&clock CLK_XXTI>,
+		<&clock CLK_XUSBXTI>;
 };
diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile
index 2cb62f8..3c40362 100644
--- a/drivers/clk/samsung/Makefile
+++ b/drivers/clk/samsung/Makefile
@@ -8,6 +8,7 @@  obj-$(CONFIG_SOC_EXYNOS5250)	+= clk-exynos5250.o
 obj-$(CONFIG_SOC_EXYNOS5420)	+= clk-exynos5420.o
 obj-$(CONFIG_SOC_EXYNOS5440)	+= clk-exynos5440.o
 obj-$(CONFIG_ARCH_EXYNOS)	+= clk-exynos-audss.o
+obj-$(CONFIG_ARCH_EXYNOS)	+= exynos-clkout.o
 obj-$(CONFIG_S3C2410_COMMON_CLK)+= clk-s3c2410.o
 obj-$(CONFIG_S3C2410_COMMON_DCLK)+= clk-s3c2410-dclk.o
 obj-$(CONFIG_S3C2412_COMMON_CLK)+= clk-s3c2412.o
diff --git a/drivers/clk/samsung/exynos-clkout.c b/drivers/clk/samsung/exynos-clkout.c
new file mode 100644
index 0000000..461f1c3
--- /dev/null
+++ b/drivers/clk/samsung/exynos-clkout.c
@@ -0,0 +1,107 @@ 
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * Author: Tomasz Figa <t.figa@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Clock driver for Exynos clock output
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/syscore_ops.h>
+
+#define EXYNOS_CLKOUT_PARENTS		32
+
+#define EXYNOS_PMU_DEBUG_REG		0xa00
+#define EXYNOS_CLKOUT_DISABLE_SHIFT	0
+#define EXYNOS_CLKOUT_MUX_SHIFT		8
+#define EXYNOS_CLKOUT_MUX_MASK		0x1f
+
+static DEFINE_SPINLOCK(clkout_lock);
+
+static void __init exynos_clkout_init(struct device_node *node)
+{
+	const char *parent_names[EXYNOS_CLKOUT_PARENTS];
+	struct clk *parents[EXYNOS_CLKOUT_PARENTS];
+	struct clk_gate *gate;
+	struct clk_mux *mux;
+	int parent_count;
+	struct clk *clk;
+	void *reg;
+	int i;
+
+	/* 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;
+
+	parent_count = 0;
+	for (i = 0; i < EXYNOS_CLKOUT_PARENTS; ++i) {
+		char name[] = "clkoutXX";
+
+		snprintf(name, sizeof(name), "clkout%d", i);
+		parents[i] = of_clk_get_by_name(node, name);
+		if (IS_ERR(parents[i])) {
+			parent_names[i] = "none";
+			continue;
+		}
+
+		parent_names[i] = __clk_get_name(parents[i]);
+		parent_count = i + 1;
+	}
+
+	if (!parent_count)
+		goto free_gate;
+
+	reg = of_iomap(node, 0);
+	if (!reg)
+		goto clks_put;
+
+	gate->reg = reg + EXYNOS_PMU_DEBUG_REG;
+	gate->bit_idx = EXYNOS_CLKOUT_DISABLE_SHIFT;
+	gate->flags = CLK_GATE_SET_TO_DISABLE;
+	gate->lock = &clkout_lock;
+
+	mux->reg = reg + EXYNOS_PMU_DEBUG_REG;
+	mux->mask = EXYNOS_CLKOUT_MUX_MASK;
+	mux->shift = EXYNOS_CLKOUT_MUX_SHIFT;
+	mux->lock = &clkout_lock;
+
+	clk = clk_register_composite(NULL, "clkout", parent_names,
+					parent_count, &mux->hw,
+					&clk_mux_ops, NULL, NULL, &gate->hw,
+					&clk_gate_ops, 0);
+	if (IS_ERR(clk))
+		goto err_unmap;
+
+	of_clk_add_provider(node, of_clk_src_simple_get, clk);
+
+	return;
+
+err_unmap:
+	iounmap(reg);
+clks_put:
+	for (i = 0; i < EXYNOS_CLKOUT_PARENTS; ++i)
+		if (!IS_ERR(parents[i]))
+			clk_put(parents[i]);
+free_gate:
+	kfree(gate);
+free_mux:
+	kfree(mux);
+
+	pr_err("%s: failed to register clkout clock\n", __func__);
+}
+CLK_OF_DECLARE(exynos4210_clkout, "samsung,exynos4210-pmu", exynos_clkout_init);
+CLK_OF_DECLARE(exynos4412_clkout, "samsung,exynos4x12-pmu", exynos_clkout_init);
+CLK_OF_DECLARE(exynos5250_clkout, "samsung,exynos5250-pmu", exynos_clkout_init);
+CLK_OF_DECLARE(exynos5420_clkout, "samsung,exynos5420-pmu", exynos_clkout_init);