These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / clk / tegra / clk-periph.c
1 /*
2  * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms and conditions of the GNU General Public License,
6  * version 2, as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
11  * more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15  */
16
17 #include <linux/clk-provider.h>
18 #include <linux/export.h>
19 #include <linux/slab.h>
20 #include <linux/err.h>
21
22 #include "clk.h"
23
24 static u8 clk_periph_get_parent(struct clk_hw *hw)
25 {
26         struct tegra_clk_periph *periph = to_clk_periph(hw);
27         const struct clk_ops *mux_ops = periph->mux_ops;
28         struct clk_hw *mux_hw = &periph->mux.hw;
29
30         __clk_hw_set_clk(mux_hw, hw);
31
32         return mux_ops->get_parent(mux_hw);
33 }
34
35 static int clk_periph_set_parent(struct clk_hw *hw, u8 index)
36 {
37         struct tegra_clk_periph *periph = to_clk_periph(hw);
38         const struct clk_ops *mux_ops = periph->mux_ops;
39         struct clk_hw *mux_hw = &periph->mux.hw;
40
41         __clk_hw_set_clk(mux_hw, hw);
42
43         return mux_ops->set_parent(mux_hw, index);
44 }
45
46 static unsigned long clk_periph_recalc_rate(struct clk_hw *hw,
47                                             unsigned long parent_rate)
48 {
49         struct tegra_clk_periph *periph = to_clk_periph(hw);
50         const struct clk_ops *div_ops = periph->div_ops;
51         struct clk_hw *div_hw = &periph->divider.hw;
52
53         __clk_hw_set_clk(div_hw, hw);
54
55         return div_ops->recalc_rate(div_hw, parent_rate);
56 }
57
58 static long clk_periph_round_rate(struct clk_hw *hw, unsigned long rate,
59                                   unsigned long *prate)
60 {
61         struct tegra_clk_periph *periph = to_clk_periph(hw);
62         const struct clk_ops *div_ops = periph->div_ops;
63         struct clk_hw *div_hw = &periph->divider.hw;
64
65         __clk_hw_set_clk(div_hw, hw);
66
67         return div_ops->round_rate(div_hw, rate, prate);
68 }
69
70 static int clk_periph_set_rate(struct clk_hw *hw, unsigned long rate,
71                                unsigned long parent_rate)
72 {
73         struct tegra_clk_periph *periph = to_clk_periph(hw);
74         const struct clk_ops *div_ops = periph->div_ops;
75         struct clk_hw *div_hw = &periph->divider.hw;
76
77         __clk_hw_set_clk(div_hw, hw);
78
79         return div_ops->set_rate(div_hw, rate, parent_rate);
80 }
81
82 static int clk_periph_is_enabled(struct clk_hw *hw)
83 {
84         struct tegra_clk_periph *periph = to_clk_periph(hw);
85         const struct clk_ops *gate_ops = periph->gate_ops;
86         struct clk_hw *gate_hw = &periph->gate.hw;
87
88         __clk_hw_set_clk(gate_hw, hw);
89
90         return gate_ops->is_enabled(gate_hw);
91 }
92
93 static int clk_periph_enable(struct clk_hw *hw)
94 {
95         struct tegra_clk_periph *periph = to_clk_periph(hw);
96         const struct clk_ops *gate_ops = periph->gate_ops;
97         struct clk_hw *gate_hw = &periph->gate.hw;
98
99         __clk_hw_set_clk(gate_hw, hw);
100
101         return gate_ops->enable(gate_hw);
102 }
103
104 static void clk_periph_disable(struct clk_hw *hw)
105 {
106         struct tegra_clk_periph *periph = to_clk_periph(hw);
107         const struct clk_ops *gate_ops = periph->gate_ops;
108         struct clk_hw *gate_hw = &periph->gate.hw;
109
110         gate_ops->disable(gate_hw);
111 }
112
113 const struct clk_ops tegra_clk_periph_ops = {
114         .get_parent = clk_periph_get_parent,
115         .set_parent = clk_periph_set_parent,
116         .recalc_rate = clk_periph_recalc_rate,
117         .round_rate = clk_periph_round_rate,
118         .set_rate = clk_periph_set_rate,
119         .is_enabled = clk_periph_is_enabled,
120         .enable = clk_periph_enable,
121         .disable = clk_periph_disable,
122 };
123
124 static const struct clk_ops tegra_clk_periph_nodiv_ops = {
125         .get_parent = clk_periph_get_parent,
126         .set_parent = clk_periph_set_parent,
127         .is_enabled = clk_periph_is_enabled,
128         .enable = clk_periph_enable,
129         .disable = clk_periph_disable,
130 };
131
132 static const struct clk_ops tegra_clk_periph_no_gate_ops = {
133         .get_parent = clk_periph_get_parent,
134         .set_parent = clk_periph_set_parent,
135         .recalc_rate = clk_periph_recalc_rate,
136         .round_rate = clk_periph_round_rate,
137         .set_rate = clk_periph_set_rate,
138 };
139
140 static struct clk *_tegra_clk_register_periph(const char *name,
141                         const char **parent_names, int num_parents,
142                         struct tegra_clk_periph *periph,
143                         void __iomem *clk_base, u32 offset,
144                         unsigned long flags)
145 {
146         struct clk *clk;
147         struct clk_init_data init;
148         struct tegra_clk_periph_regs *bank;
149         bool div = !(periph->gate.flags & TEGRA_PERIPH_NO_DIV);
150
151         if (periph->gate.flags & TEGRA_PERIPH_NO_DIV) {
152                 flags |= CLK_SET_RATE_PARENT;
153                 init.ops = &tegra_clk_periph_nodiv_ops;
154         } else if (periph->gate.flags & TEGRA_PERIPH_NO_GATE)
155                 init.ops = &tegra_clk_periph_no_gate_ops;
156         else
157                 init.ops = &tegra_clk_periph_ops;
158
159         init.name = name;
160         init.flags = flags;
161         init.parent_names = parent_names;
162         init.num_parents = num_parents;
163
164         bank = get_reg_bank(periph->gate.clk_num);
165         if (!bank)
166                 return ERR_PTR(-EINVAL);
167
168         /* Data in .init is copied by clk_register(), so stack variable OK */
169         periph->hw.init = &init;
170         periph->magic = TEGRA_CLK_PERIPH_MAGIC;
171         periph->mux.reg = clk_base + offset;
172         periph->divider.reg = div ? (clk_base + offset) : NULL;
173         periph->gate.clk_base = clk_base;
174         periph->gate.regs = bank;
175         periph->gate.enable_refcnt = periph_clk_enb_refcnt;
176
177         clk = clk_register(NULL, &periph->hw);
178         if (IS_ERR(clk))
179                 return clk;
180
181         periph->mux.hw.clk = clk;
182         periph->divider.hw.clk = div ? clk : NULL;
183         periph->gate.hw.clk = clk;
184
185         return clk;
186 }
187
188 struct clk *tegra_clk_register_periph(const char *name,
189                 const char **parent_names, int num_parents,
190                 struct tegra_clk_periph *periph, void __iomem *clk_base,
191                 u32 offset, unsigned long flags)
192 {
193         return _tegra_clk_register_periph(name, parent_names, num_parents,
194                         periph, clk_base, offset, flags);
195 }
196
197 struct clk *tegra_clk_register_periph_nodiv(const char *name,
198                 const char **parent_names, int num_parents,
199                 struct tegra_clk_periph *periph, void __iomem *clk_base,
200                 u32 offset)
201 {
202         periph->gate.flags |= TEGRA_PERIPH_NO_DIV;
203         return _tegra_clk_register_periph(name, parent_names, num_parents,
204                         periph, clk_base, offset, CLK_SET_RATE_PARENT);
205 }