diff options
author | Soren Brinkmann <soren.brinkmann@xilinx.com> | 2013-05-13 13:46:36 -0400 |
---|---|---|
committer | Michal Simek <michal.simek@xilinx.com> | 2013-05-21 10:21:35 -0400 |
commit | 3682af46d55f2c97898b9cc1c8c80afad81f62be (patch) | |
tree | dbf312bceb367cfd6dd0b3ad3c201879afe128f9 /drivers/clk | |
parent | c7788792a5e7b0d5d7f96d0766b4cb6112d47d75 (diff) |
clk: zynq: Factor out PLL driver
Refactor the PLL driver so it works with the clock controller driver.
Signed-off-by: Soren Brinkmann <soren.brinkmann@xilinx.com>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
Acked-by: Mike Turquette <mturquette@linaro.org>
Diffstat (limited to 'drivers/clk')
-rw-r--r-- | drivers/clk/zynq/pll.c | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/drivers/clk/zynq/pll.c b/drivers/clk/zynq/pll.c new file mode 100644 index 000000000000..47e307c25a7b --- /dev/null +++ b/drivers/clk/zynq/pll.c | |||
@@ -0,0 +1,235 @@ | |||
1 | /* | ||
2 | * Zynq PLL driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Xilinx | ||
5 | * | ||
6 | * Sören Brinkmann <soren.brinkmann@xilinx.com> | ||
7 | * | ||
8 | * This program is free software: you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License v2 as published by | ||
10 | * the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | * | ||
20 | */ | ||
21 | #include <linux/clk/zynq.h> | ||
22 | #include <linux/clk-provider.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/io.h> | ||
25 | |||
26 | /** | ||
27 | * struct zynq_pll | ||
28 | * @hw: Handle between common and hardware-specific interfaces | ||
29 | * @pll_ctrl: PLL control register | ||
30 | * @pll_status: PLL status register | ||
31 | * @lock: Register lock | ||
32 | * @lockbit: Indicates the associated PLL_LOCKED bit in the PLL status | ||
33 | * register. | ||
34 | */ | ||
35 | struct zynq_pll { | ||
36 | struct clk_hw hw; | ||
37 | void __iomem *pll_ctrl; | ||
38 | void __iomem *pll_status; | ||
39 | spinlock_t *lock; | ||
40 | u8 lockbit; | ||
41 | }; | ||
42 | #define to_zynq_pll(_hw) container_of(_hw, struct zynq_pll, hw) | ||
43 | |||
44 | /* Register bitfield defines */ | ||
45 | #define PLLCTRL_FBDIV_MASK 0x7f000 | ||
46 | #define PLLCTRL_FBDIV_SHIFT 12 | ||
47 | #define PLLCTRL_BPQUAL_MASK (1 << 3) | ||
48 | #define PLLCTRL_PWRDWN_MASK 2 | ||
49 | #define PLLCTRL_PWRDWN_SHIFT 1 | ||
50 | #define PLLCTRL_RESET_MASK 1 | ||
51 | #define PLLCTRL_RESET_SHIFT 0 | ||
52 | |||
53 | /** | ||
54 | * zynq_pll_round_rate() - Round a clock frequency | ||
55 | * @hw: Handle between common and hardware-specific interfaces | ||
56 | * @rate: Desired clock frequency | ||
57 | * @prate: Clock frequency of parent clock | ||
58 | * Returns frequency closest to @rate the hardware can generate. | ||
59 | */ | ||
60 | static long zynq_pll_round_rate(struct clk_hw *hw, unsigned long rate, | ||
61 | unsigned long *prate) | ||
62 | { | ||
63 | u32 fbdiv; | ||
64 | |||
65 | fbdiv = DIV_ROUND_CLOSEST(rate, *prate); | ||
66 | if (fbdiv < 13) | ||
67 | fbdiv = 13; | ||
68 | else if (fbdiv > 66) | ||
69 | fbdiv = 66; | ||
70 | |||
71 | return *prate * fbdiv; | ||
72 | } | ||
73 | |||
74 | /** | ||
75 | * zynq_pll_recalc_rate() - Recalculate clock frequency | ||
76 | * @hw: Handle between common and hardware-specific interfaces | ||
77 | * @parent_rate: Clock frequency of parent clock | ||
78 | * Returns current clock frequency. | ||
79 | */ | ||
80 | static unsigned long zynq_pll_recalc_rate(struct clk_hw *hw, | ||
81 | unsigned long parent_rate) | ||
82 | { | ||
83 | struct zynq_pll *clk = to_zynq_pll(hw); | ||
84 | u32 fbdiv; | ||
85 | |||
86 | /* | ||
87 | * makes probably sense to redundantly save fbdiv in the struct | ||
88 | * zynq_pll to save the IO access. | ||
89 | */ | ||
90 | fbdiv = (readl(clk->pll_ctrl) & PLLCTRL_FBDIV_MASK) >> | ||
91 | PLLCTRL_FBDIV_SHIFT; | ||
92 | |||
93 | return parent_rate * fbdiv; | ||
94 | } | ||
95 | |||
96 | /** | ||
97 | * zynq_pll_is_enabled - Check if a clock is enabled | ||
98 | * @hw: Handle between common and hardware-specific interfaces | ||
99 | * Returns 1 if the clock is enabled, 0 otherwise. | ||
100 | * | ||
101 | * Not sure this is a good idea, but since disabled means bypassed for | ||
102 | * this clock implementation we say we are always enabled. | ||
103 | */ | ||
104 | static int zynq_pll_is_enabled(struct clk_hw *hw) | ||
105 | { | ||
106 | unsigned long flags = 0; | ||
107 | u32 reg; | ||
108 | struct zynq_pll *clk = to_zynq_pll(hw); | ||
109 | |||
110 | spin_lock_irqsave(clk->lock, flags); | ||
111 | |||
112 | reg = readl(clk->pll_ctrl); | ||
113 | |||
114 | spin_unlock_irqrestore(clk->lock, flags); | ||
115 | |||
116 | return !(reg & (PLLCTRL_RESET_MASK | PLLCTRL_PWRDWN_MASK)); | ||
117 | } | ||
118 | |||
119 | /** | ||
120 | * zynq_pll_enable - Enable clock | ||
121 | * @hw: Handle between common and hardware-specific interfaces | ||
122 | * Returns 0 on success | ||
123 | */ | ||
124 | static int zynq_pll_enable(struct clk_hw *hw) | ||
125 | { | ||
126 | unsigned long flags = 0; | ||
127 | u32 reg; | ||
128 | struct zynq_pll *clk = to_zynq_pll(hw); | ||
129 | |||
130 | if (zynq_pll_is_enabled(hw)) | ||
131 | return 0; | ||
132 | |||
133 | pr_info("PLL: enable\n"); | ||
134 | |||
135 | /* Power up PLL and wait for lock */ | ||
136 | spin_lock_irqsave(clk->lock, flags); | ||
137 | |||
138 | reg = readl(clk->pll_ctrl); | ||
139 | reg &= ~(PLLCTRL_RESET_MASK | PLLCTRL_PWRDWN_MASK); | ||
140 | writel(reg, clk->pll_ctrl); | ||
141 | while (!(readl(clk->pll_status) & (1 << clk->lockbit))) | ||
142 | ; | ||
143 | |||
144 | spin_unlock_irqrestore(clk->lock, flags); | ||
145 | |||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | /** | ||
150 | * zynq_pll_disable - Disable clock | ||
151 | * @hw: Handle between common and hardware-specific interfaces | ||
152 | * Returns 0 on success | ||
153 | */ | ||
154 | static void zynq_pll_disable(struct clk_hw *hw) | ||
155 | { | ||
156 | unsigned long flags = 0; | ||
157 | u32 reg; | ||
158 | struct zynq_pll *clk = to_zynq_pll(hw); | ||
159 | |||
160 | if (!zynq_pll_is_enabled(hw)) | ||
161 | return; | ||
162 | |||
163 | pr_info("PLL: shutdown\n"); | ||
164 | |||
165 | /* shut down PLL */ | ||
166 | spin_lock_irqsave(clk->lock, flags); | ||
167 | |||
168 | reg = readl(clk->pll_ctrl); | ||
169 | reg |= PLLCTRL_RESET_MASK | PLLCTRL_PWRDWN_MASK; | ||
170 | writel(reg, clk->pll_ctrl); | ||
171 | |||
172 | spin_unlock_irqrestore(clk->lock, flags); | ||
173 | } | ||
174 | |||
175 | static const struct clk_ops zynq_pll_ops = { | ||
176 | .enable = zynq_pll_enable, | ||
177 | .disable = zynq_pll_disable, | ||
178 | .is_enabled = zynq_pll_is_enabled, | ||
179 | .round_rate = zynq_pll_round_rate, | ||
180 | .recalc_rate = zynq_pll_recalc_rate | ||
181 | }; | ||
182 | |||
183 | /** | ||
184 | * clk_register_zynq_pll() - Register PLL with the clock framework | ||
185 | * @np Pointer to the DT device node | ||
186 | */ | ||
187 | struct clk *clk_register_zynq_pll(const char *name, const char *parent, | ||
188 | void __iomem *pll_ctrl, void __iomem *pll_status, u8 lock_index, | ||
189 | spinlock_t *lock) | ||
190 | { | ||
191 | struct zynq_pll *pll; | ||
192 | struct clk *clk; | ||
193 | u32 reg; | ||
194 | const char *parent_arr[1] = {parent}; | ||
195 | unsigned long flags = 0; | ||
196 | struct clk_init_data initd = { | ||
197 | .name = name, | ||
198 | .parent_names = parent_arr, | ||
199 | .ops = &zynq_pll_ops, | ||
200 | .num_parents = 1, | ||
201 | .flags = 0 | ||
202 | }; | ||
203 | |||
204 | pll = kmalloc(sizeof(*pll), GFP_KERNEL); | ||
205 | if (!pll) { | ||
206 | pr_err("%s: Could not allocate Zynq PLL clk.\n", __func__); | ||
207 | return ERR_PTR(-ENOMEM); | ||
208 | } | ||
209 | |||
210 | /* Populate the struct */ | ||
211 | pll->hw.init = &initd; | ||
212 | pll->pll_ctrl = pll_ctrl; | ||
213 | pll->pll_status = pll_status; | ||
214 | pll->lockbit = lock_index; | ||
215 | pll->lock = lock; | ||
216 | |||
217 | spin_lock_irqsave(pll->lock, flags); | ||
218 | |||
219 | reg = readl(pll->pll_ctrl); | ||
220 | reg &= ~PLLCTRL_BPQUAL_MASK; | ||
221 | writel(reg, pll->pll_ctrl); | ||
222 | |||
223 | spin_unlock_irqrestore(pll->lock, flags); | ||
224 | |||
225 | clk = clk_register(NULL, &pll->hw); | ||
226 | if (WARN_ON(IS_ERR(clk))) | ||
227 | goto free_pll; | ||
228 | |||
229 | return clk; | ||
230 | |||
231 | free_pll: | ||
232 | kfree(pll); | ||
233 | |||
234 | return clk; | ||
235 | } | ||