Commit | Line | Data |
---|---|---|
1e832e51 TF |
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 | ||
6f1ed07a | 12 | #include <linux/slab.h> |
1e832e51 | 13 | #include <linux/clk.h> |
1e832e51 TF |
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); | |
abec147f IS |
145 | CLK_OF_DECLARE(exynos3250_clkout, "samsung,exynos3250-pmu", |
146 | exynos4_clkout_init); | |
1e832e51 TF |
147 | |
148 | static void __init exynos5_clkout_init(struct device_node *node) | |
149 | { | |
150 | exynos_clkout_init(node, EXYNOS5_CLKOUT_MUX_MASK); | |
151 | } | |
152 | CLK_OF_DECLARE(exynos5250_clkout, "samsung,exynos5250-pmu", | |
153 | exynos5_clkout_init); | |
d8137e03 KK |
154 | CLK_OF_DECLARE(exynos5410_clkout, "samsung,exynos5410-pmu", |
155 | exynos5_clkout_init); | |
1e832e51 TF |
156 | CLK_OF_DECLARE(exynos5420_clkout, "samsung,exynos5420-pmu", |
157 | exynos5_clkout_init); | |
6166c01c IS |
158 | CLK_OF_DECLARE(exynos5433_clkout, "samsung,exynos5433-pmu", |
159 | exynos5_clkout_init); |