Merge tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux
[linux-2.6-microblaze.git] / drivers / clk / at91 / clk-master.c
1 /*
2  *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  */
10
11 #include <linux/clk-provider.h>
12 #include <linux/clkdev.h>
13 #include <linux/clk/at91_pmc.h>
14 #include <linux/of.h>
15 #include <linux/mfd/syscon.h>
16 #include <linux/regmap.h>
17
18 #include "pmc.h"
19
20 #define MASTER_PRES_MASK        0x7
21 #define MASTER_PRES_MAX         MASTER_PRES_MASK
22 #define MASTER_DIV_SHIFT        8
23 #define MASTER_DIV_MASK         0x3
24
25 #define to_clk_master(hw) container_of(hw, struct clk_master, hw)
26
27 struct clk_master {
28         struct clk_hw hw;
29         struct regmap *regmap;
30         const struct clk_master_layout *layout;
31         const struct clk_master_characteristics *characteristics;
32         u32 mckr;
33 };
34
35 static inline bool clk_master_ready(struct regmap *regmap)
36 {
37         unsigned int status;
38
39         regmap_read(regmap, AT91_PMC_SR, &status);
40
41         return status & AT91_PMC_MCKRDY ? 1 : 0;
42 }
43
44 static int clk_master_prepare(struct clk_hw *hw)
45 {
46         struct clk_master *master = to_clk_master(hw);
47
48         while (!clk_master_ready(master->regmap))
49                 cpu_relax();
50
51         return 0;
52 }
53
54 static int clk_master_is_prepared(struct clk_hw *hw)
55 {
56         struct clk_master *master = to_clk_master(hw);
57
58         return clk_master_ready(master->regmap);
59 }
60
61 static unsigned long clk_master_recalc_rate(struct clk_hw *hw,
62                                             unsigned long parent_rate)
63 {
64         u8 pres;
65         u8 div;
66         unsigned long rate = parent_rate;
67         struct clk_master *master = to_clk_master(hw);
68         const struct clk_master_layout *layout = master->layout;
69         const struct clk_master_characteristics *characteristics =
70                                                 master->characteristics;
71         unsigned int mckr;
72
73         regmap_read(master->regmap, master->layout->offset, &mckr);
74         mckr &= layout->mask;
75
76         pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK;
77         div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
78
79         if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX)
80                 rate /= 3;
81         else
82                 rate >>= pres;
83
84         rate /= characteristics->divisors[div];
85
86         if (rate < characteristics->output.min)
87                 pr_warn("master clk is underclocked");
88         else if (rate > characteristics->output.max)
89                 pr_warn("master clk is overclocked");
90
91         return rate;
92 }
93
94 static u8 clk_master_get_parent(struct clk_hw *hw)
95 {
96         struct clk_master *master = to_clk_master(hw);
97         unsigned int mckr;
98
99         regmap_read(master->regmap, master->layout->offset, &mckr);
100
101         return mckr & AT91_PMC_CSS;
102 }
103
104 static const struct clk_ops master_ops = {
105         .prepare = clk_master_prepare,
106         .is_prepared = clk_master_is_prepared,
107         .recalc_rate = clk_master_recalc_rate,
108         .get_parent = clk_master_get_parent,
109 };
110
111 struct clk_hw * __init
112 at91_clk_register_master(struct regmap *regmap,
113                 const char *name, int num_parents,
114                 const char **parent_names,
115                 const struct clk_master_layout *layout,
116                 const struct clk_master_characteristics *characteristics)
117 {
118         struct clk_master *master;
119         struct clk_init_data init;
120         struct clk_hw *hw;
121         int ret;
122
123         if (!name || !num_parents || !parent_names)
124                 return ERR_PTR(-EINVAL);
125
126         master = kzalloc(sizeof(*master), GFP_KERNEL);
127         if (!master)
128                 return ERR_PTR(-ENOMEM);
129
130         init.name = name;
131         init.ops = &master_ops;
132         init.parent_names = parent_names;
133         init.num_parents = num_parents;
134         init.flags = 0;
135
136         master->hw.init = &init;
137         master->layout = layout;
138         master->characteristics = characteristics;
139         master->regmap = regmap;
140
141         hw = &master->hw;
142         ret = clk_hw_register(NULL, &master->hw);
143         if (ret) {
144                 kfree(master);
145                 hw = ERR_PTR(ret);
146         }
147
148         return hw;
149 }
150
151 const struct clk_master_layout at91rm9200_master_layout = {
152         .mask = 0x31F,
153         .pres_shift = 2,
154         .offset = AT91_PMC_MCKR,
155 };
156
157 const struct clk_master_layout at91sam9x5_master_layout = {
158         .mask = 0x373,
159         .pres_shift = 4,
160         .offset = AT91_PMC_MCKR,
161 };