diff options
| author | Tomasz Figa <t.figa@samsung.com> | 2014-06-24 12:08:26 -0400 |
|---|---|---|
| committer | Tomasz Figa <t.figa@samsung.com> | 2014-07-25 20:47:10 -0400 |
| commit | 1e832e51018e960ecfc6f04abb1cbdd1ed82b8cb (patch) | |
| tree | 1323f19d7d3d3053056d6eba949e07f79b8bfed6 | |
| parent | 01f7ec260ab35291f23bf42b1a43367649392646 (diff) | |
clk: samsung: Add driver to control CLKOUT line on Exynos SoCs
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>
| -rw-r--r-- | Documentation/devicetree/bindings/arm/samsung/pmu.txt | 30 | ||||
| -rw-r--r-- | drivers/clk/samsung/Makefile | 1 | ||||
| -rw-r--r-- | drivers/clk/samsung/clk-exynos-clkout.c | 153 |
3 files changed, 184 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/arm/samsung/pmu.txt b/Documentation/devicetree/bindings/arm/samsung/pmu.txt index 2a4ab046a8a1..f9865e77e0b0 100644 --- a/Documentation/devicetree/bindings/arm/samsung/pmu.txt +++ b/Documentation/devicetree/bindings/arm/samsung/pmu.txt | |||
| @@ -12,8 +12,38 @@ Properties: | |||
| 12 | 12 | ||
| 13 | - reg : offset and length of the register set. | 13 | - reg : offset and length of the register set. |
| 14 | 14 | ||
| 15 | - #clock-cells : must be <1>, since PMU requires once cell as clock specifier. | ||
| 16 | The single specifier cell is used as index to list of clocks | ||
| 17 | provided by PMU, which is currently: | ||
| 18 | 0 : SoC clock output (CLKOUT pin) | ||
| 19 | |||
| 20 | - clock-names : list of clock names for particular CLKOUT mux inputs in | ||
| 21 | following format: | ||
| 22 | "clkoutN", where N is a decimal number corresponding to | ||
| 23 | CLKOUT mux control bits value for given input, e.g. | ||
| 24 | "clkout0", "clkout7", "clkout15". | ||
| 25 | |||
| 26 | - clocks : list of phandles and specifiers to all input clocks listed in | ||
| 27 | clock-names property. | ||
| 28 | |||
| 15 | Example : | 29 | Example : |
| 16 | pmu_system_controller: system-controller@10040000 { | 30 | pmu_system_controller: system-controller@10040000 { |
| 17 | compatible = "samsung,exynos5250-pmu", "syscon"; | 31 | compatible = "samsung,exynos5250-pmu", "syscon"; |
| 18 | reg = <0x10040000 0x5000>; | 32 | reg = <0x10040000 0x5000>; |
| 33 | #clock-cells = <1>; | ||
| 34 | clock-names = "clkout0", "clkout1", "clkout2", "clkout3", | ||
| 35 | "clkout4", "clkout8", "clkout9"; | ||
| 36 | clocks = <&clock CLK_OUT_DMC>, <&clock CLK_OUT_TOP>, | ||
| 37 | <&clock CLK_OUT_LEFTBUS>, <&clock CLK_OUT_RIGHTBUS>, | ||
| 38 | <&clock CLK_OUT_CPU>, <&clock CLK_XXTI>, | ||
| 39 | <&clock CLK_XUSBXTI>; | ||
| 40 | }; | ||
| 41 | |||
| 42 | Example of clock consumer : | ||
| 43 | |||
| 44 | usb3503: usb3503@08 { | ||
| 45 | /* ... */ | ||
| 46 | clock-names = "refclk"; | ||
| 47 | clocks = <&pmu_system_controller 0>; | ||
| 48 | /* ... */ | ||
| 19 | }; | 49 | }; |
diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index 69e81773164e..2949a556af8f 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile | |||
| @@ -11,6 +11,7 @@ obj-$(CONFIG_SOC_EXYNOS5410) += clk-exynos5410.o | |||
| 11 | obj-$(CONFIG_SOC_EXYNOS5420) += clk-exynos5420.o | 11 | obj-$(CONFIG_SOC_EXYNOS5420) += clk-exynos5420.o |
| 12 | obj-$(CONFIG_SOC_EXYNOS5440) += clk-exynos5440.o | 12 | obj-$(CONFIG_SOC_EXYNOS5440) += clk-exynos5440.o |
| 13 | obj-$(CONFIG_ARCH_EXYNOS) += clk-exynos-audss.o | 13 | obj-$(CONFIG_ARCH_EXYNOS) += clk-exynos-audss.o |
| 14 | obj-$(CONFIG_ARCH_EXYNOS) += clk-exynos-clkout.o | ||
| 14 | obj-$(CONFIG_S3C2410_COMMON_CLK)+= clk-s3c2410.o | 15 | obj-$(CONFIG_S3C2410_COMMON_CLK)+= clk-s3c2410.o |
| 15 | obj-$(CONFIG_S3C2410_COMMON_DCLK)+= clk-s3c2410-dclk.o | 16 | obj-$(CONFIG_S3C2410_COMMON_DCLK)+= clk-s3c2410-dclk.o |
| 16 | obj-$(CONFIG_S3C2412_COMMON_CLK)+= clk-s3c2412.o | 17 | obj-$(CONFIG_S3C2412_COMMON_CLK)+= clk-s3c2412.o |
diff --git a/drivers/clk/samsung/clk-exynos-clkout.c b/drivers/clk/samsung/clk-exynos-clkout.c new file mode 100644 index 000000000000..3a7cb2506731 --- /dev/null +++ b/drivers/clk/samsung/clk-exynos-clkout.c | |||
| @@ -0,0 +1,153 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2014 Samsung Electronics Co., Ltd. | ||
| 3 | * Author: Tomasz Figa <t.figa@samsung.com> | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License version 2 as | ||
| 7 | * published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * Clock driver for Exynos clock output | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/clk.h> | ||
| 13 | #include <linux/clkdev.h> | ||
| 14 | #include <linux/clk-provider.h> | ||
| 15 | #include <linux/of.h> | ||
| 16 | #include <linux/of_address.h> | ||
| 17 | #include <linux/syscore_ops.h> | ||
| 18 | |||
| 19 | #define EXYNOS_CLKOUT_NR_CLKS 1 | ||
| 20 | #define EXYNOS_CLKOUT_PARENTS 32 | ||
| 21 | |||
| 22 | #define EXYNOS_PMU_DEBUG_REG 0xa00 | ||
| 23 | #define EXYNOS_CLKOUT_DISABLE_SHIFT 0 | ||
| 24 | #define EXYNOS_CLKOUT_MUX_SHIFT 8 | ||
| 25 | #define EXYNOS4_CLKOUT_MUX_MASK 0xf | ||
| 26 | #define EXYNOS5_CLKOUT_MUX_MASK 0x1f | ||
| 27 | |||
| 28 | struct exynos_clkout { | ||
| 29 | struct clk_gate gate; | ||
| 30 | struct clk_mux mux; | ||
| 31 | spinlock_t slock; | ||
| 32 | struct clk_onecell_data data; | ||
| 33 | struct clk *clk_table[EXYNOS_CLKOUT_NR_CLKS]; | ||
| 34 | void __iomem *reg; | ||
| 35 | u32 pmu_debug_save; | ||
| 36 | }; | ||
| 37 | |||
| 38 | static struct exynos_clkout *clkout; | ||
| 39 | |||
| 40 | static int exynos_clkout_suspend(void) | ||
| 41 | { | ||
| 42 | clkout->pmu_debug_save = readl(clkout->reg + EXYNOS_PMU_DEBUG_REG); | ||
| 43 | |||
| 44 | return 0; | ||
| 45 | } | ||
| 46 | |||
| 47 | static void exynos_clkout_resume(void) | ||
| 48 | { | ||
| 49 | writel(clkout->pmu_debug_save, clkout->reg + EXYNOS_PMU_DEBUG_REG); | ||
| 50 | } | ||
| 51 | |||
| 52 | static struct syscore_ops exynos_clkout_syscore_ops = { | ||
| 53 | .suspend = exynos_clkout_suspend, | ||
| 54 | .resume = exynos_clkout_resume, | ||
| 55 | }; | ||
| 56 | |||
| 57 | static void __init exynos_clkout_init(struct device_node *node, u32 mux_mask) | ||
| 58 | { | ||
| 59 | const char *parent_names[EXYNOS_CLKOUT_PARENTS]; | ||
| 60 | struct clk *parents[EXYNOS_CLKOUT_PARENTS]; | ||
| 61 | int parent_count; | ||
| 62 | int ret; | ||
| 63 | int i; | ||
| 64 | |||
| 65 | clkout = kzalloc(sizeof(*clkout), GFP_KERNEL); | ||
| 66 | if (!clkout) | ||
| 67 | return; | ||
| 68 | |||
| 69 | spin_lock_init(&clkout->slock); | ||
| 70 | |||
| 71 | parent_count = 0; | ||
| 72 | for (i = 0; i < EXYNOS_CLKOUT_PARENTS; ++i) { | ||
| 73 | char name[] = "clkoutXX"; | ||
| 74 | |||
| 75 | snprintf(name, sizeof(name), "clkout%d", i); | ||
| 76 | parents[i] = of_clk_get_by_name(node, name); | ||
| 77 | if (IS_ERR(parents[i])) { | ||
| 78 | parent_names[i] = "none"; | ||
| 79 | continue; | ||
| 80 | } | ||
| 81 | |||
| 82 | parent_names[i] = __clk_get_name(parents[i]); | ||
| 83 | parent_count = i + 1; | ||
| 84 | } | ||
| 85 | |||
| 86 | if (!parent_count) | ||
| 87 | goto free_clkout; | ||
| 88 | |||
| 89 | clkout->reg = of_iomap(node, 0); | ||
| 90 | if (!clkout->reg) | ||
| 91 | goto clks_put; | ||
| 92 | |||
| 93 | clkout->gate.reg = clkout->reg + EXYNOS_PMU_DEBUG_REG; | ||
| 94 | clkout->gate.bit_idx = EXYNOS_CLKOUT_DISABLE_SHIFT; | ||
| 95 | clkout->gate.flags = CLK_GATE_SET_TO_DISABLE; | ||
| 96 | clkout->gate.lock = &clkout->slock; | ||
| 97 | |||
| 98 | clkout->mux.reg = clkout->reg + EXYNOS_PMU_DEBUG_REG; | ||
| 99 | clkout->mux.mask = mux_mask; | ||
| 100 | clkout->mux.shift = EXYNOS_CLKOUT_MUX_SHIFT; | ||
| 101 | clkout->mux.lock = &clkout->slock; | ||
| 102 | |||
| 103 | clkout->clk_table[0] = clk_register_composite(NULL, "clkout", | ||
| 104 | parent_names, parent_count, &clkout->mux.hw, | ||
| 105 | &clk_mux_ops, NULL, NULL, &clkout->gate.hw, | ||
| 106 | &clk_gate_ops, CLK_SET_RATE_PARENT | ||
| 107 | | CLK_SET_RATE_NO_REPARENT); | ||
| 108 | if (IS_ERR(clkout->clk_table[0])) | ||
| 109 | goto err_unmap; | ||
| 110 | |||
| 111 | clkout->data.clks = clkout->clk_table; | ||
| 112 | clkout->data.clk_num = EXYNOS_CLKOUT_NR_CLKS; | ||
| 113 | ret = of_clk_add_provider(node, of_clk_src_onecell_get, &clkout->data); | ||
| 114 | if (ret) | ||
| 115 | goto err_clk_unreg; | ||
| 116 | |||
| 117 | register_syscore_ops(&exynos_clkout_syscore_ops); | ||
| 118 | |||
| 119 | return; | ||
| 120 | |||
| 121 | err_clk_unreg: | ||
| 122 | clk_unregister(clkout->clk_table[0]); | ||
| 123 | err_unmap: | ||
| 124 | iounmap(clkout->reg); | ||
| 125 | clks_put: | ||
| 126 | for (i = 0; i < EXYNOS_CLKOUT_PARENTS; ++i) | ||
| 127 | if (!IS_ERR(parents[i])) | ||
| 128 | clk_put(parents[i]); | ||
| 129 | free_clkout: | ||
| 130 | kfree(clkout); | ||
| 131 | |||
| 132 | pr_err("%s: failed to register clkout clock\n", __func__); | ||
| 133 | } | ||
| 134 | |||
| 135 | static void __init exynos4_clkout_init(struct device_node *node) | ||
| 136 | { | ||
| 137 | exynos_clkout_init(node, EXYNOS4_CLKOUT_MUX_MASK); | ||
| 138 | } | ||
| 139 | CLK_OF_DECLARE(exynos4210_clkout, "samsung,exynos4210-pmu", | ||
| 140 | exynos4_clkout_init); | ||
| 141 | CLK_OF_DECLARE(exynos4212_clkout, "samsung,exynos4212-pmu", | ||
| 142 | exynos4_clkout_init); | ||
| 143 | CLK_OF_DECLARE(exynos4412_clkout, "samsung,exynos4412-pmu", | ||
| 144 | exynos4_clkout_init); | ||
| 145 | |||
| 146 | static void __init exynos5_clkout_init(struct device_node *node) | ||
| 147 | { | ||
| 148 | exynos_clkout_init(node, EXYNOS5_CLKOUT_MUX_MASK); | ||
| 149 | } | ||
| 150 | CLK_OF_DECLARE(exynos5250_clkout, "samsung,exynos5250-pmu", | ||
| 151 | exynos5_clkout_init); | ||
| 152 | CLK_OF_DECLARE(exynos5420_clkout, "samsung,exynos5420-pmu", | ||
| 153 | exynos5_clkout_init); | ||
