linux/drivers/clk/spear/clk-gpt-synth.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2012 ST Microelectronics
   3 * Viresh Kumar <vireshk@kernel.org>
   4 *
   5 * This file is licensed under the terms of the GNU General Public
   6 * License version 2. This program is licensed "as is" without any
   7 * warranty of any kind, whether express or implied.
   8 *
   9 * General Purpose Timer Synthesizer clock implementation
  10 */
  11
  12#define pr_fmt(fmt) "clk-gpt-synth: " fmt
  13
  14#include <linux/clk-provider.h>
  15#include <linux/slab.h>
  16#include <linux/io.h>
  17#include <linux/err.h>
  18#include "clk.h"
  19
  20#define GPT_MSCALE_MASK         0xFFF
  21#define GPT_NSCALE_SHIFT        12
  22#define GPT_NSCALE_MASK         0xF
  23
  24/*
  25 * DOC: General Purpose Timer Synthesizer clock
  26 *
  27 * Calculates gpt synth clk rate for different values of mscale and nscale
  28 *
  29 * Fout= Fin/((2 ^ (N+1)) * (M+1))
  30 */
  31
  32#define to_clk_gpt(_hw) container_of(_hw, struct clk_gpt, hw)
  33
  34static unsigned long gpt_calc_rate(struct clk_hw *hw, unsigned long prate,
  35                int index)
  36{
  37        struct clk_gpt *gpt = to_clk_gpt(hw);
  38        struct gpt_rate_tbl *rtbl = gpt->rtbl;
  39
  40        prate /= ((1 << (rtbl[index].nscale + 1)) * (rtbl[index].mscale + 1));
  41
  42        return prate;
  43}
  44
  45static long clk_gpt_round_rate(struct clk_hw *hw, unsigned long drate,
  46                unsigned long *prate)
  47{
  48        struct clk_gpt *gpt = to_clk_gpt(hw);
  49        int unused;
  50
  51        return clk_round_rate_index(hw, drate, *prate, gpt_calc_rate,
  52                        gpt->rtbl_cnt, &unused);
  53}
  54
  55static unsigned long clk_gpt_recalc_rate(struct clk_hw *hw,
  56                unsigned long parent_rate)
  57{
  58        struct clk_gpt *gpt = to_clk_gpt(hw);
  59        unsigned long flags = 0;
  60        unsigned int div = 1, val;
  61
  62        if (gpt->lock)
  63                spin_lock_irqsave(gpt->lock, flags);
  64
  65        val = readl_relaxed(gpt->reg);
  66
  67        if (gpt->lock)
  68                spin_unlock_irqrestore(gpt->lock, flags);
  69
  70        div += val & GPT_MSCALE_MASK;
  71        div *= 1 << (((val >> GPT_NSCALE_SHIFT) & GPT_NSCALE_MASK) + 1);
  72
  73        if (!div)
  74                return 0;
  75
  76        return parent_rate / div;
  77}
  78
  79/* Configures new clock rate of gpt */
  80static int clk_gpt_set_rate(struct clk_hw *hw, unsigned long drate,
  81                                unsigned long prate)
  82{
  83        struct clk_gpt *gpt = to_clk_gpt(hw);
  84        struct gpt_rate_tbl *rtbl = gpt->rtbl;
  85        unsigned long flags = 0, val;
  86        int i;
  87
  88        clk_round_rate_index(hw, drate, prate, gpt_calc_rate, gpt->rtbl_cnt,
  89                        &i);
  90
  91        if (gpt->lock)
  92                spin_lock_irqsave(gpt->lock, flags);
  93
  94        val = readl(gpt->reg) & ~GPT_MSCALE_MASK;
  95        val &= ~(GPT_NSCALE_MASK << GPT_NSCALE_SHIFT);
  96
  97        val |= rtbl[i].mscale & GPT_MSCALE_MASK;
  98        val |= (rtbl[i].nscale & GPT_NSCALE_MASK) << GPT_NSCALE_SHIFT;
  99
 100        writel_relaxed(val, gpt->reg);
 101
 102        if (gpt->lock)
 103                spin_unlock_irqrestore(gpt->lock, flags);
 104
 105        return 0;
 106}
 107
 108static const struct clk_ops clk_gpt_ops = {
 109        .recalc_rate = clk_gpt_recalc_rate,
 110        .round_rate = clk_gpt_round_rate,
 111        .set_rate = clk_gpt_set_rate,
 112};
 113
 114struct clk *clk_register_gpt(const char *name, const char *parent_name, unsigned
 115                long flags, void __iomem *reg, struct gpt_rate_tbl *rtbl, u8
 116                rtbl_cnt, spinlock_t *lock)
 117{
 118        struct clk_init_data init;
 119        struct clk_gpt *gpt;
 120        struct clk *clk;
 121
 122        if (!name || !parent_name || !reg || !rtbl || !rtbl_cnt) {
 123                pr_err("Invalid arguments passed\n");
 124                return ERR_PTR(-EINVAL);
 125        }
 126
 127        gpt = kzalloc(sizeof(*gpt), GFP_KERNEL);
 128        if (!gpt)
 129                return ERR_PTR(-ENOMEM);
 130
 131        /* struct clk_gpt assignments */
 132        gpt->reg = reg;
 133        gpt->rtbl = rtbl;
 134        gpt->rtbl_cnt = rtbl_cnt;
 135        gpt->lock = lock;
 136        gpt->hw.init = &init;
 137
 138        init.name = name;
 139        init.ops = &clk_gpt_ops;
 140        init.flags = flags;
 141        init.parent_names = &parent_name;
 142        init.num_parents = 1;
 143
 144        clk = clk_register(NULL, &gpt->hw);
 145        if (!IS_ERR_OR_NULL(clk))
 146                return clk;
 147
 148        pr_err("clk register failed\n");
 149        kfree(gpt);
 150
 151        return NULL;
 152}
 153