aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHoan Tran <hotran@apm.com>2016-09-12 14:23:24 -0400
committerStephen Boyd <sboyd@codeaurora.org>2016-09-14 16:54:35 -0400
commit1a85b50bef13b44016c206272785a746adae7833 (patch)
tree7495fc987a795c2132b11d88592e402a874cb634
parent405f27be6182362e343c25ae6ec2b2933919ef30 (diff)
clk: xgene: Add PMD clock
Add X-Gene PMD clock support. PMD clock is implemented for a single register field. Output rate = parent_rate * (denominator - scale) / denominator with - denominator = bitmask of register field + 1 - scale = values of register field For example, for bitmask is 0x7, denominator will be 8 and scale will be computed and programmed accordingly. Signed-off-by: Hoan Tran <hotran@apm.com> Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
-rw-r--r--drivers/clk/clk-xgene.c221
1 files changed, 221 insertions, 0 deletions
diff --git a/drivers/clk/clk-xgene.c b/drivers/clk/clk-xgene.c
index 343313250c58..5daddf5ecc4b 100644
--- a/drivers/clk/clk-xgene.c
+++ b/drivers/clk/clk-xgene.c
@@ -217,6 +217,226 @@ static void xgene_pcppllclk_init(struct device_node *np)
217 xgene_pllclk_init(np, PLL_TYPE_PCP); 217 xgene_pllclk_init(np, PLL_TYPE_PCP);
218} 218}
219 219
220/**
221 * struct xgene_clk_pmd - PMD clock
222 *
223 * @hw: handle between common and hardware-specific interfaces
224 * @reg: register containing the fractional scale multiplier (scaler)
225 * @shift: shift to the unit bit field
226 * @denom: 1/denominator unit
227 * @lock: register lock
228 * Flags:
229 * XGENE_CLK_PMD_SCALE_INVERTED - By default the scaler is the value read
230 * from the register plus one. For example,
231 * 0 for (0 + 1) / denom,
232 * 1 for (1 + 1) / denom and etc.
233 * If this flag is set, it is
234 * 0 for (denom - 0) / denom,
235 * 1 for (denom - 1) / denom and etc.
236 *
237 */
238struct xgene_clk_pmd {
239 struct clk_hw hw;
240 void __iomem *reg;
241 u8 shift;
242 u32 mask;
243 u64 denom;
244 u32 flags;
245 spinlock_t *lock;
246};
247
248#define to_xgene_clk_pmd(_hw) container_of(_hw, struct xgene_clk_pmd, hw)
249
250#define XGENE_CLK_PMD_SCALE_INVERTED BIT(0)
251#define XGENE_CLK_PMD_SHIFT 8
252#define XGENE_CLK_PMD_WIDTH 3
253
254static unsigned long xgene_clk_pmd_recalc_rate(struct clk_hw *hw,
255 unsigned long parent_rate)
256{
257 struct xgene_clk_pmd *fd = to_xgene_clk_pmd(hw);
258 unsigned long flags = 0;
259 u64 ret, scale;
260 u32 val;
261
262 if (fd->lock)
263 spin_lock_irqsave(fd->lock, flags);
264 else
265 __acquire(fd->lock);
266
267 val = clk_readl(fd->reg);
268
269 if (fd->lock)
270 spin_unlock_irqrestore(fd->lock, flags);
271 else
272 __release(fd->lock);
273
274 ret = (u64)parent_rate;
275
276 scale = (val & fd->mask) >> fd->shift;
277 if (fd->flags & XGENE_CLK_PMD_SCALE_INVERTED)
278 scale = fd->denom - scale;
279 else
280 scale++;
281
282 /* freq = parent_rate * scaler / denom */
283 do_div(ret, fd->denom);
284 ret *= scale;
285 if (ret == 0)
286 ret = (u64)parent_rate;
287
288 return ret;
289}
290
291static long xgene_clk_pmd_round_rate(struct clk_hw *hw, unsigned long rate,
292 unsigned long *parent_rate)
293{
294 struct xgene_clk_pmd *fd = to_xgene_clk_pmd(hw);
295 u64 ret, scale;
296
297 if (!rate || rate >= *parent_rate)
298 return *parent_rate;
299
300 /* freq = parent_rate * scaler / denom */
301 ret = rate * fd->denom;
302 scale = DIV_ROUND_UP_ULL(ret, *parent_rate);
303
304 ret = (u64)*parent_rate * scale;
305 do_div(ret, fd->denom);
306
307 return ret;
308}
309
310static int xgene_clk_pmd_set_rate(struct clk_hw *hw, unsigned long rate,
311 unsigned long parent_rate)
312{
313 struct xgene_clk_pmd *fd = to_xgene_clk_pmd(hw);
314 unsigned long flags = 0;
315 u64 scale, ret;
316 u32 val;
317
318 /*
319 * Compute the scaler:
320 *
321 * freq = parent_rate * scaler / denom, or
322 * scaler = freq * denom / parent_rate
323 */
324 ret = rate * fd->denom;
325 scale = DIV_ROUND_UP_ULL(ret, (u64)parent_rate);
326
327 /* Check if inverted */
328 if (fd->flags & XGENE_CLK_PMD_SCALE_INVERTED)
329 scale = fd->denom - scale;
330 else
331 scale--;
332
333 if (fd->lock)
334 spin_lock_irqsave(fd->lock, flags);
335 else
336 __acquire(fd->lock);
337
338 val = clk_readl(fd->reg);
339 val &= ~fd->mask;
340 val |= (scale << fd->shift);
341 clk_writel(val, fd->reg);
342
343 if (fd->lock)
344 spin_unlock_irqrestore(fd->lock, flags);
345 else
346 __release(fd->lock);
347
348 return 0;
349}
350
351static const struct clk_ops xgene_clk_pmd_ops = {
352 .recalc_rate = xgene_clk_pmd_recalc_rate,
353 .round_rate = xgene_clk_pmd_round_rate,
354 .set_rate = xgene_clk_pmd_set_rate,
355};
356
357static struct clk *
358xgene_register_clk_pmd(struct device *dev,
359 const char *name, const char *parent_name,
360 unsigned long flags, void __iomem *reg, u8 shift,
361 u8 width, u64 denom, u32 clk_flags, spinlock_t *lock)
362{
363 struct xgene_clk_pmd *fd;
364 struct clk_init_data init;
365 struct clk *clk;
366
367 fd = kzalloc(sizeof(*fd), GFP_KERNEL);
368 if (!fd)
369 return ERR_PTR(-ENOMEM);
370
371 init.name = name;
372 init.ops = &xgene_clk_pmd_ops;
373 init.flags = flags;
374 init.parent_names = parent_name ? &parent_name : NULL;
375 init.num_parents = parent_name ? 1 : 0;
376
377 fd->reg = reg;
378 fd->shift = shift;
379 fd->mask = (BIT(width) - 1) << shift;
380 fd->denom = denom;
381 fd->flags = clk_flags;
382 fd->lock = lock;
383 fd->hw.init = &init;
384
385 clk = clk_register(dev, &fd->hw);
386 if (IS_ERR(clk)) {
387 pr_err("%s: could not register clk %s\n", __func__, name);
388 kfree(fd);
389 return NULL;
390 }
391
392 return clk;
393}
394
395static void xgene_pmdclk_init(struct device_node *np)
396{
397 const char *clk_name = np->full_name;
398 void __iomem *csr_reg;
399 struct resource res;
400 struct clk *clk;
401 u64 denom;
402 u32 flags = 0;
403 int rc;
404
405 /* Check if the entry is disabled */
406 if (!of_device_is_available(np))
407 return;
408
409 /* Parse the DTS register for resource */
410 rc = of_address_to_resource(np, 0, &res);
411 if (rc != 0) {
412 pr_err("no DTS register for %s\n", np->full_name);
413 return;
414 }
415 csr_reg = of_iomap(np, 0);
416 if (!csr_reg) {
417 pr_err("Unable to map resource for %s\n", np->full_name);
418 return;
419 }
420 of_property_read_string(np, "clock-output-names", &clk_name);
421
422 denom = BIT(XGENE_CLK_PMD_WIDTH);
423 flags |= XGENE_CLK_PMD_SCALE_INVERTED;
424
425 clk = xgene_register_clk_pmd(NULL, clk_name,
426 of_clk_get_parent_name(np, 0), 0,
427 csr_reg, XGENE_CLK_PMD_SHIFT,
428 XGENE_CLK_PMD_WIDTH, denom,
429 flags, &clk_lock);
430 if (!IS_ERR(clk)) {
431 of_clk_add_provider(np, of_clk_src_simple_get, clk);
432 clk_register_clkdev(clk, clk_name, NULL);
433 pr_debug("Add %s clock\n", clk_name);
434 } else {
435 if (csr_reg)
436 iounmap(csr_reg);
437 }
438}
439
220/* IP Clock */ 440/* IP Clock */
221struct xgene_dev_parameters { 441struct xgene_dev_parameters {
222 void __iomem *csr_reg; /* CSR for IP clock */ 442 void __iomem *csr_reg; /* CSR for IP clock */
@@ -543,6 +763,7 @@ err:
543 763
544CLK_OF_DECLARE(xgene_socpll_clock, "apm,xgene-socpll-clock", xgene_socpllclk_init); 764CLK_OF_DECLARE(xgene_socpll_clock, "apm,xgene-socpll-clock", xgene_socpllclk_init);
545CLK_OF_DECLARE(xgene_pcppll_clock, "apm,xgene-pcppll-clock", xgene_pcppllclk_init); 765CLK_OF_DECLARE(xgene_pcppll_clock, "apm,xgene-pcppll-clock", xgene_pcppllclk_init);
766CLK_OF_DECLARE(xgene_pmd_clock, "apm,xgene-pmd-clock", xgene_pmdclk_init);
546CLK_OF_DECLARE(xgene_socpll_v2_clock, "apm,xgene-socpll-v2-clock", 767CLK_OF_DECLARE(xgene_socpll_v2_clock, "apm,xgene-socpll-v2-clock",
547 xgene_socpllclk_init); 768 xgene_socpllclk_init);
548CLK_OF_DECLARE(xgene_pcppll_v2_clock, "apm,xgene-pcppll-v2-clock", 769CLK_OF_DECLARE(xgene_pcppll_v2_clock, "apm,xgene-pcppll-v2-clock",