diff options
Diffstat (limited to 'drivers/clk/ux500')
-rw-r--r-- | drivers/clk/ux500/clk-prcmu.c | 134 |
1 files changed, 80 insertions, 54 deletions
diff --git a/drivers/clk/ux500/clk-prcmu.c b/drivers/clk/ux500/clk-prcmu.c index 74faa7e3cf59..9d832567c6be 100644 --- a/drivers/clk/ux500/clk-prcmu.c +++ b/drivers/clk/ux500/clk-prcmu.c | |||
@@ -20,15 +20,23 @@ | |||
20 | struct clk_prcmu { | 20 | struct clk_prcmu { |
21 | struct clk_hw hw; | 21 | struct clk_hw hw; |
22 | u8 cg_sel; | 22 | u8 cg_sel; |
23 | int is_prepared; | ||
23 | int is_enabled; | 24 | int is_enabled; |
25 | int opp_requested; | ||
24 | }; | 26 | }; |
25 | 27 | ||
26 | /* PRCMU clock operations. */ | 28 | /* PRCMU clock operations. */ |
27 | 29 | ||
28 | static int clk_prcmu_prepare(struct clk_hw *hw) | 30 | static int clk_prcmu_prepare(struct clk_hw *hw) |
29 | { | 31 | { |
32 | int ret; | ||
30 | struct clk_prcmu *clk = to_clk_prcmu(hw); | 33 | struct clk_prcmu *clk = to_clk_prcmu(hw); |
31 | return prcmu_request_clock(clk->cg_sel, true); | 34 | |
35 | ret = prcmu_request_clock(clk->cg_sel, true); | ||
36 | if (!ret) | ||
37 | clk->is_prepared = 1; | ||
38 | |||
39 | return ret;; | ||
32 | } | 40 | } |
33 | 41 | ||
34 | static void clk_prcmu_unprepare(struct clk_hw *hw) | 42 | static void clk_prcmu_unprepare(struct clk_hw *hw) |
@@ -37,6 +45,14 @@ static void clk_prcmu_unprepare(struct clk_hw *hw) | |||
37 | if (prcmu_request_clock(clk->cg_sel, false)) | 45 | if (prcmu_request_clock(clk->cg_sel, false)) |
38 | pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, | 46 | pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, |
39 | hw->init->name); | 47 | hw->init->name); |
48 | else | ||
49 | clk->is_prepared = 0; | ||
50 | } | ||
51 | |||
52 | static int clk_prcmu_is_prepared(struct clk_hw *hw) | ||
53 | { | ||
54 | struct clk_prcmu *clk = to_clk_prcmu(hw); | ||
55 | return clk->is_prepared; | ||
40 | } | 56 | } |
41 | 57 | ||
42 | static int clk_prcmu_enable(struct clk_hw *hw) | 58 | static int clk_prcmu_enable(struct clk_hw *hw) |
@@ -79,58 +95,52 @@ static int clk_prcmu_set_rate(struct clk_hw *hw, unsigned long rate, | |||
79 | return prcmu_set_clock_rate(clk->cg_sel, rate); | 95 | return prcmu_set_clock_rate(clk->cg_sel, rate); |
80 | } | 96 | } |
81 | 97 | ||
82 | static int request_ape_opp100(bool enable) | ||
83 | { | ||
84 | static int reqs; | ||
85 | int err = 0; | ||
86 | |||
87 | if (enable) { | ||
88 | if (!reqs) | ||
89 | err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, | ||
90 | "clock", 100); | ||
91 | if (!err) | ||
92 | reqs++; | ||
93 | } else { | ||
94 | reqs--; | ||
95 | if (!reqs) | ||
96 | prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, | ||
97 | "clock"); | ||
98 | } | ||
99 | return err; | ||
100 | } | ||
101 | |||
102 | static int clk_prcmu_opp_prepare(struct clk_hw *hw) | 98 | static int clk_prcmu_opp_prepare(struct clk_hw *hw) |
103 | { | 99 | { |
104 | int err; | 100 | int err; |
105 | struct clk_prcmu *clk = to_clk_prcmu(hw); | 101 | struct clk_prcmu *clk = to_clk_prcmu(hw); |
106 | 102 | ||
107 | err = request_ape_opp100(true); | 103 | if (!clk->opp_requested) { |
108 | if (err) { | 104 | err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, |
109 | pr_err("clk_prcmu: %s failed to request APE OPP100 for %s.\n", | 105 | (char *)__clk_get_name(hw->clk), |
110 | __func__, hw->init->name); | 106 | 100); |
111 | return err; | 107 | if (err) { |
108 | pr_err("clk_prcmu: %s fail req APE OPP for %s.\n", | ||
109 | __func__, hw->init->name); | ||
110 | return err; | ||
111 | } | ||
112 | clk->opp_requested = 1; | ||
112 | } | 113 | } |
113 | 114 | ||
114 | err = prcmu_request_clock(clk->cg_sel, true); | 115 | err = prcmu_request_clock(clk->cg_sel, true); |
115 | if (err) | 116 | if (err) { |
116 | request_ape_opp100(false); | 117 | prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, |
118 | (char *)__clk_get_name(hw->clk)); | ||
119 | clk->opp_requested = 0; | ||
120 | return err; | ||
121 | } | ||
117 | 122 | ||
118 | return err; | 123 | clk->is_prepared = 1; |
124 | return 0; | ||
119 | } | 125 | } |
120 | 126 | ||
121 | static void clk_prcmu_opp_unprepare(struct clk_hw *hw) | 127 | static void clk_prcmu_opp_unprepare(struct clk_hw *hw) |
122 | { | 128 | { |
123 | struct clk_prcmu *clk = to_clk_prcmu(hw); | 129 | struct clk_prcmu *clk = to_clk_prcmu(hw); |
124 | 130 | ||
125 | if (prcmu_request_clock(clk->cg_sel, false)) | 131 | if (prcmu_request_clock(clk->cg_sel, false)) { |
126 | goto out_error; | 132 | pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, |
127 | if (request_ape_opp100(false)) | 133 | hw->init->name); |
128 | goto out_error; | 134 | return; |
129 | return; | 135 | } |
130 | 136 | ||
131 | out_error: | 137 | if (clk->opp_requested) { |
132 | pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, | 138 | prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, |
133 | hw->init->name); | 139 | (char *)__clk_get_name(hw->clk)); |
140 | clk->opp_requested = 0; | ||
141 | } | ||
142 | |||
143 | clk->is_prepared = 0; | ||
134 | } | 144 | } |
135 | 145 | ||
136 | static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw) | 146 | static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw) |
@@ -138,38 +148,49 @@ static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw) | |||
138 | int err; | 148 | int err; |
139 | struct clk_prcmu *clk = to_clk_prcmu(hw); | 149 | struct clk_prcmu *clk = to_clk_prcmu(hw); |
140 | 150 | ||
141 | err = prcmu_request_ape_opp_100_voltage(true); | 151 | if (!clk->opp_requested) { |
142 | if (err) { | 152 | err = prcmu_request_ape_opp_100_voltage(true); |
143 | pr_err("clk_prcmu: %s failed to request APE OPP VOLT for %s.\n", | 153 | if (err) { |
144 | __func__, hw->init->name); | 154 | pr_err("clk_prcmu: %s fail req APE OPP VOLT for %s.\n", |
145 | return err; | 155 | __func__, hw->init->name); |
156 | return err; | ||
157 | } | ||
158 | clk->opp_requested = 1; | ||
146 | } | 159 | } |
147 | 160 | ||
148 | err = prcmu_request_clock(clk->cg_sel, true); | 161 | err = prcmu_request_clock(clk->cg_sel, true); |
149 | if (err) | 162 | if (err) { |
150 | prcmu_request_ape_opp_100_voltage(false); | 163 | prcmu_request_ape_opp_100_voltage(false); |
164 | clk->opp_requested = 0; | ||
165 | return err; | ||
166 | } | ||
151 | 167 | ||
152 | return err; | 168 | clk->is_prepared = 1; |
169 | return 0; | ||
153 | } | 170 | } |
154 | 171 | ||
155 | static void clk_prcmu_opp_volt_unprepare(struct clk_hw *hw) | 172 | static void clk_prcmu_opp_volt_unprepare(struct clk_hw *hw) |
156 | { | 173 | { |
157 | struct clk_prcmu *clk = to_clk_prcmu(hw); | 174 | struct clk_prcmu *clk = to_clk_prcmu(hw); |
158 | 175 | ||
159 | if (prcmu_request_clock(clk->cg_sel, false)) | 176 | if (prcmu_request_clock(clk->cg_sel, false)) { |
160 | goto out_error; | 177 | pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, |
161 | if (prcmu_request_ape_opp_100_voltage(false)) | 178 | hw->init->name); |
162 | goto out_error; | 179 | return; |
163 | return; | 180 | } |
164 | 181 | ||
165 | out_error: | 182 | if (clk->opp_requested) { |
166 | pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, | 183 | prcmu_request_ape_opp_100_voltage(false); |
167 | hw->init->name); | 184 | clk->opp_requested = 0; |
185 | } | ||
186 | |||
187 | clk->is_prepared = 0; | ||
168 | } | 188 | } |
169 | 189 | ||
170 | static struct clk_ops clk_prcmu_scalable_ops = { | 190 | static struct clk_ops clk_prcmu_scalable_ops = { |
171 | .prepare = clk_prcmu_prepare, | 191 | .prepare = clk_prcmu_prepare, |
172 | .unprepare = clk_prcmu_unprepare, | 192 | .unprepare = clk_prcmu_unprepare, |
193 | .is_prepared = clk_prcmu_is_prepared, | ||
173 | .enable = clk_prcmu_enable, | 194 | .enable = clk_prcmu_enable, |
174 | .disable = clk_prcmu_disable, | 195 | .disable = clk_prcmu_disable, |
175 | .is_enabled = clk_prcmu_is_enabled, | 196 | .is_enabled = clk_prcmu_is_enabled, |
@@ -181,6 +202,7 @@ static struct clk_ops clk_prcmu_scalable_ops = { | |||
181 | static struct clk_ops clk_prcmu_gate_ops = { | 202 | static struct clk_ops clk_prcmu_gate_ops = { |
182 | .prepare = clk_prcmu_prepare, | 203 | .prepare = clk_prcmu_prepare, |
183 | .unprepare = clk_prcmu_unprepare, | 204 | .unprepare = clk_prcmu_unprepare, |
205 | .is_prepared = clk_prcmu_is_prepared, | ||
184 | .enable = clk_prcmu_enable, | 206 | .enable = clk_prcmu_enable, |
185 | .disable = clk_prcmu_disable, | 207 | .disable = clk_prcmu_disable, |
186 | .is_enabled = clk_prcmu_is_enabled, | 208 | .is_enabled = clk_prcmu_is_enabled, |
@@ -202,6 +224,7 @@ static struct clk_ops clk_prcmu_rate_ops = { | |||
202 | static struct clk_ops clk_prcmu_opp_gate_ops = { | 224 | static struct clk_ops clk_prcmu_opp_gate_ops = { |
203 | .prepare = clk_prcmu_opp_prepare, | 225 | .prepare = clk_prcmu_opp_prepare, |
204 | .unprepare = clk_prcmu_opp_unprepare, | 226 | .unprepare = clk_prcmu_opp_unprepare, |
227 | .is_prepared = clk_prcmu_is_prepared, | ||
205 | .enable = clk_prcmu_enable, | 228 | .enable = clk_prcmu_enable, |
206 | .disable = clk_prcmu_disable, | 229 | .disable = clk_prcmu_disable, |
207 | .is_enabled = clk_prcmu_is_enabled, | 230 | .is_enabled = clk_prcmu_is_enabled, |
@@ -211,6 +234,7 @@ static struct clk_ops clk_prcmu_opp_gate_ops = { | |||
211 | static struct clk_ops clk_prcmu_opp_volt_scalable_ops = { | 234 | static struct clk_ops clk_prcmu_opp_volt_scalable_ops = { |
212 | .prepare = clk_prcmu_opp_volt_prepare, | 235 | .prepare = clk_prcmu_opp_volt_prepare, |
213 | .unprepare = clk_prcmu_opp_volt_unprepare, | 236 | .unprepare = clk_prcmu_opp_volt_unprepare, |
237 | .is_prepared = clk_prcmu_is_prepared, | ||
214 | .enable = clk_prcmu_enable, | 238 | .enable = clk_prcmu_enable, |
215 | .disable = clk_prcmu_disable, | 239 | .disable = clk_prcmu_disable, |
216 | .is_enabled = clk_prcmu_is_enabled, | 240 | .is_enabled = clk_prcmu_is_enabled, |
@@ -242,7 +266,9 @@ static struct clk *clk_reg_prcmu(const char *name, | |||
242 | } | 266 | } |
243 | 267 | ||
244 | clk->cg_sel = cg_sel; | 268 | clk->cg_sel = cg_sel; |
269 | clk->is_prepared = 1; | ||
245 | clk->is_enabled = 1; | 270 | clk->is_enabled = 1; |
271 | clk->opp_requested = 0; | ||
246 | /* "rate" can be used for changing the initial frequency */ | 272 | /* "rate" can be used for changing the initial frequency */ |
247 | if (rate) | 273 | if (rate) |
248 | prcmu_set_clock_rate(cg_sel, rate); | 274 | prcmu_set_clock_rate(cg_sel, rate); |