Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
clk-frac.c
Go to the documentation of this file.
1 /*
2  * Copyright 2012 Freescale Semiconductor, Inc.
3  *
4  * The code contained herein is licensed under the GNU General Public
5  * License. You may obtain a copy of the GNU General Public License
6  * Version 2 or later at the following locations:
7  *
8  * http://www.opensource.org/licenses/gpl-license.html
9  * http://www.gnu.org/copyleft/gpl.html
10  */
11 
12 #include <linux/clk.h>
13 #include <linux/clk-provider.h>
14 #include <linux/err.h>
15 #include <linux/io.h>
16 #include <linux/slab.h>
17 #include "clk.h"
18 
30 struct clk_frac {
31  struct clk_hw hw;
32  void __iomem *reg;
36 };
37 
38 #define to_clk_frac(_hw) container_of(_hw, struct clk_frac, hw)
39 
40 static unsigned long clk_frac_recalc_rate(struct clk_hw *hw,
41  unsigned long parent_rate)
42 {
43  struct clk_frac *frac = to_clk_frac(hw);
44  u32 div;
45 
46  div = readl_relaxed(frac->reg) >> frac->shift;
47  div &= (1 << frac->width) - 1;
48 
49  return (parent_rate >> frac->width) * div;
50 }
51 
52 static long clk_frac_round_rate(struct clk_hw *hw, unsigned long rate,
53  unsigned long *prate)
54 {
55  struct clk_frac *frac = to_clk_frac(hw);
56  unsigned long parent_rate = *prate;
57  u32 div;
58  u64 tmp;
59 
60  if (rate > parent_rate)
61  return -EINVAL;
62 
63  tmp = rate;
64  tmp <<= frac->width;
65  do_div(tmp, parent_rate);
66  div = tmp;
67 
68  if (!div)
69  return -EINVAL;
70 
71  return (parent_rate >> frac->width) * div;
72 }
73 
74 static int clk_frac_set_rate(struct clk_hw *hw, unsigned long rate,
75  unsigned long parent_rate)
76 {
77  struct clk_frac *frac = to_clk_frac(hw);
78  unsigned long flags;
79  u32 div, val;
80  u64 tmp;
81 
82  if (rate > parent_rate)
83  return -EINVAL;
84 
85  tmp = rate;
86  tmp <<= frac->width;
87  do_div(tmp, parent_rate);
88  div = tmp;
89 
90  if (!div)
91  return -EINVAL;
92 
93  spin_lock_irqsave(&mxs_lock, flags);
94 
95  val = readl_relaxed(frac->reg);
96  val &= ~(((1 << frac->width) - 1) << frac->shift);
97  val |= div << frac->shift;
98  writel_relaxed(val, frac->reg);
99 
100  spin_unlock_irqrestore(&mxs_lock, flags);
101 
102  return mxs_clk_wait(frac->reg, frac->busy);
103 }
104 
105 static struct clk_ops clk_frac_ops = {
106  .recalc_rate = clk_frac_recalc_rate,
107  .round_rate = clk_frac_round_rate,
108  .set_rate = clk_frac_set_rate,
109 };
110 
111 struct clk *mxs_clk_frac(const char *name, const char *parent_name,
112  void __iomem *reg, u8 shift, u8 width, u8 busy)
113 {
114  struct clk_frac *frac;
115  struct clk *clk;
116  struct clk_init_data init;
117 
118  frac = kzalloc(sizeof(*frac), GFP_KERNEL);
119  if (!frac)
120  return ERR_PTR(-ENOMEM);
121 
122  init.name = name;
123  init.ops = &clk_frac_ops;
124  init.flags = CLK_SET_RATE_PARENT;
125  init.parent_names = (parent_name ? &parent_name: NULL);
126  init.num_parents = (parent_name ? 1 : 0);
127 
128  frac->reg = reg;
129  frac->shift = shift;
130  frac->width = width;
131  frac->busy = busy;
132  frac->hw.init = &init;
133 
134  clk = clk_register(NULL, &frac->hw);
135  if (IS_ERR(clk))
136  kfree(frac);
137 
138  return clk;
139 }