Merge tag 'csky-for-linus-4.20-fixup-dtb' of https://github.com/c-sky/csky-linux
[linux-block.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 };
33
34 static inline bool clk_master_ready(struct regmap *regmap)
35 {
36         unsigned int status;
37
38         regmap_read(regmap, AT91_PMC_SR, &status);
39
40         return status & AT91_PMC_MCKRDY ? 1 : 0;
41 }
42
43 static int clk_master_prepare(struct clk_hw *hw)
44 {
45         struct clk_master *master = to_clk_master(hw);
46
47         while (!clk_master_ready(master->regmap))
48                 cpu_relax();
49
50         return 0;
51 }
52
53 static int clk_master_is_prepared(struct clk_hw *hw)
54 {
55         struct clk_master *master = to_clk_master(hw);
56
57         return clk_master_ready(master->regmap);
58 }
59
60 static unsigned long clk_master_recalc_rate(struct clk_hw *hw,
61                                             unsigned long parent_rate)
62 {
63         u8 pres;
64         u8 div;
65         unsigned long rate = parent_rate;
66         struct clk_master *master = to_clk_master(hw);
67         const struct clk_master_layout *layout = master->layout;
68         const struct clk_master_characteristics *characteristics =
69                                                 master->characteristics;
70         unsigned int mckr;
71
72         regmap_read(master->regmap, AT91_PMC_MCKR, &mckr);
73         mckr &= layout->mask;
74
75         pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK;
76         div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
77
78         if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX)
79                 rate /= 3;
80         else
81                 rate >>= pres;
82
83         rate /= characteristics->divisors[div];
84
85         if (rate < characteristics->output.min)
86                 pr_warn("master clk is underclocked");
87         else if (rate > characteristics->output.max)
88                 pr_warn("master clk is overclocked");
89
90         return rate;
91 }
92
93 static u8 clk_master_get_parent(struct clk_hw *hw)
94 {
95         struct clk_master *master = to_clk_master(hw);
96         unsigned int mckr;
97
98         regmap_read(master->regmap, AT91_PMC_MCKR, &mckr);
99
100         return mckr & AT91_PMC_CSS;
101 }
102
103 static const struct clk_ops master_ops = {
104         .prepare = clk_master_prepare,
105         .is_prepared = clk_master_is_prepared,
106         .recalc_rate = clk_master_recalc_rate,
107         .get_parent = clk_master_get_parent,
108 };
109
110 struct clk_hw * __init
111 at91_clk_register_master(struct regmap *regmap,
112                 const char *name, int num_parents,
113                 const char **parent_names,
114                 const struct clk_master_layout *layout,
115                 const struct clk_master_characteristics *characteristics)
116 {
117         struct clk_master *master;
118         struct clk_init_data init;
119         struct clk_hw *hw;
120         int ret;
121
122         if (!name || !num_parents || !parent_names)
123                 return ERR_PTR(-EINVAL);
124
125         master = kzalloc(sizeof(*master), GFP_KERNEL);
126         if (!master)
127                 return ERR_PTR(-ENOMEM);
128
129         init.name = name;
130         init.ops = &master_ops;
131         init.parent_names = parent_names;
132         init.num_parents = num_parents;
133         init.flags = 0;
134
135         master->hw.init = &init;
136         master->layout = layout;
137         master->characteristics = characteristics;
138         master->regmap = regmap;
139
140         hw = &master->hw;
141         ret = clk_hw_register(NULL, &master->hw);
142         if (ret) {
143                 kfree(master);
144                 hw = ERR_PTR(ret);
145         }
146
147         return hw;
148 }
149
150
151 const struct clk_master_layout at91rm9200_master_layout = {
152         .mask = 0x31F,
153         .pres_shift = 2,
154 };
155
156 const struct clk_master_layout at91sam9x5_master_layout = {
157         .mask = 0x373,
158         .pres_shift = 4,
159 };