]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/clk/sunxi-ng/ccu_mult.c
Merge remote-tracking branch 'asoc/fix/dapm' into asoc-linus
[karo-tx-linux.git] / drivers / clk / sunxi-ng / ccu_mult.c
1 /*
2  * Copyright (C) 2016 Maxime Ripard
3  * Maxime Ripard <maxime.ripard@free-electrons.com>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of
8  * the License, or (at your option) any later version.
9  */
10
11 #include <linux/clk-provider.h>
12
13 #include "ccu_gate.h"
14 #include "ccu_mult.h"
15
16 struct _ccu_mult {
17         unsigned long   mult, min, max;
18 };
19
20 static void ccu_mult_find_best(unsigned long parent, unsigned long rate,
21                                struct _ccu_mult *mult)
22 {
23         int _mult;
24
25         _mult = rate / parent;
26         if (_mult < mult->min)
27                 _mult = mult->min;
28
29         if (_mult > mult->max)
30                 _mult = mult->max;
31
32         mult->mult = _mult;
33 }
34
35 static unsigned long ccu_mult_round_rate(struct ccu_mux_internal *mux,
36                                         unsigned long parent_rate,
37                                         unsigned long rate,
38                                         void *data)
39 {
40         struct ccu_mult *cm = data;
41         struct _ccu_mult _cm;
42
43         _cm.min = 1;
44         _cm.max = 1 << cm->mult.width;
45         ccu_mult_find_best(parent_rate, rate, &_cm);
46
47         return parent_rate * _cm.mult;
48 }
49
50 static void ccu_mult_disable(struct clk_hw *hw)
51 {
52         struct ccu_mult *cm = hw_to_ccu_mult(hw);
53
54         return ccu_gate_helper_disable(&cm->common, cm->enable);
55 }
56
57 static int ccu_mult_enable(struct clk_hw *hw)
58 {
59         struct ccu_mult *cm = hw_to_ccu_mult(hw);
60
61         return ccu_gate_helper_enable(&cm->common, cm->enable);
62 }
63
64 static int ccu_mult_is_enabled(struct clk_hw *hw)
65 {
66         struct ccu_mult *cm = hw_to_ccu_mult(hw);
67
68         return ccu_gate_helper_is_enabled(&cm->common, cm->enable);
69 }
70
71 static unsigned long ccu_mult_recalc_rate(struct clk_hw *hw,
72                                         unsigned long parent_rate)
73 {
74         struct ccu_mult *cm = hw_to_ccu_mult(hw);
75         unsigned long val;
76         u32 reg;
77
78         reg = readl(cm->common.base + cm->common.reg);
79         val = reg >> cm->mult.shift;
80         val &= (1 << cm->mult.width) - 1;
81
82         ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1,
83                                                 &parent_rate);
84
85         return parent_rate * (val + 1);
86 }
87
88 static int ccu_mult_determine_rate(struct clk_hw *hw,
89                                 struct clk_rate_request *req)
90 {
91         struct ccu_mult *cm = hw_to_ccu_mult(hw);
92
93         return ccu_mux_helper_determine_rate(&cm->common, &cm->mux,
94                                              req, ccu_mult_round_rate, cm);
95 }
96
97 static int ccu_mult_set_rate(struct clk_hw *hw, unsigned long rate,
98                            unsigned long parent_rate)
99 {
100         struct ccu_mult *cm = hw_to_ccu_mult(hw);
101         struct _ccu_mult _cm;
102         unsigned long flags;
103         u32 reg;
104
105         ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1,
106                                                 &parent_rate);
107
108         _cm.min = cm->mult.min;
109         _cm.max = 1 << cm->mult.width;
110         ccu_mult_find_best(parent_rate, rate, &_cm);
111
112         spin_lock_irqsave(cm->common.lock, flags);
113
114         reg = readl(cm->common.base + cm->common.reg);
115         reg &= ~GENMASK(cm->mult.width + cm->mult.shift - 1, cm->mult.shift);
116
117         writel(reg | ((_cm.mult - 1) << cm->mult.shift),
118                cm->common.base + cm->common.reg);
119
120         spin_unlock_irqrestore(cm->common.lock, flags);
121
122         return 0;
123 }
124
125 static u8 ccu_mult_get_parent(struct clk_hw *hw)
126 {
127         struct ccu_mult *cm = hw_to_ccu_mult(hw);
128
129         return ccu_mux_helper_get_parent(&cm->common, &cm->mux);
130 }
131
132 static int ccu_mult_set_parent(struct clk_hw *hw, u8 index)
133 {
134         struct ccu_mult *cm = hw_to_ccu_mult(hw);
135
136         return ccu_mux_helper_set_parent(&cm->common, &cm->mux, index);
137 }
138
139 const struct clk_ops ccu_mult_ops = {
140         .disable        = ccu_mult_disable,
141         .enable         = ccu_mult_enable,
142         .is_enabled     = ccu_mult_is_enabled,
143
144         .get_parent     = ccu_mult_get_parent,
145         .set_parent     = ccu_mult_set_parent,
146
147         .determine_rate = ccu_mult_determine_rate,
148         .recalc_rate    = ccu_mult_recalc_rate,
149         .set_rate       = ccu_mult_set_rate,
150 };