.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * Freescale FlexTimer Module (FTM) PWM Driver |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright 2012-2013 Freescale Semiconductor, Inc. |
---|
5 | | - * |
---|
6 | | - * This program is free software; you can redistribute it and/or modify |
---|
7 | | - * it under the terms of the GNU General Public License as published by |
---|
8 | | - * the Free Software Foundation; either version 2 of the License, or |
---|
9 | | - * (at your option) any later version. |
---|
10 | 6 | */ |
---|
11 | 7 | |
---|
12 | 8 | #include <linux/clk.h> |
---|
.. | .. |
---|
22 | 18 | #include <linux/pwm.h> |
---|
23 | 19 | #include <linux/regmap.h> |
---|
24 | 20 | #include <linux/slab.h> |
---|
| 21 | +#include <linux/fsl/ftm.h> |
---|
25 | 22 | |
---|
26 | | -#define FTM_SC 0x00 |
---|
27 | | -#define FTM_SC_CLK_MASK_SHIFT 3 |
---|
28 | | -#define FTM_SC_CLK_MASK (3 << FTM_SC_CLK_MASK_SHIFT) |
---|
29 | 23 | #define FTM_SC_CLK(c) (((c) + 1) << FTM_SC_CLK_MASK_SHIFT) |
---|
30 | | -#define FTM_SC_PS_MASK 0x7 |
---|
31 | | - |
---|
32 | | -#define FTM_CNT 0x04 |
---|
33 | | -#define FTM_MOD 0x08 |
---|
34 | | - |
---|
35 | | -#define FTM_CSC_BASE 0x0C |
---|
36 | | -#define FTM_CSC_MSB BIT(5) |
---|
37 | | -#define FTM_CSC_MSA BIT(4) |
---|
38 | | -#define FTM_CSC_ELSB BIT(3) |
---|
39 | | -#define FTM_CSC_ELSA BIT(2) |
---|
40 | | -#define FTM_CSC(_channel) (FTM_CSC_BASE + ((_channel) * 8)) |
---|
41 | | - |
---|
42 | | -#define FTM_CV_BASE 0x10 |
---|
43 | | -#define FTM_CV(_channel) (FTM_CV_BASE + ((_channel) * 8)) |
---|
44 | | - |
---|
45 | | -#define FTM_CNTIN 0x4C |
---|
46 | | -#define FTM_STATUS 0x50 |
---|
47 | | - |
---|
48 | | -#define FTM_MODE 0x54 |
---|
49 | | -#define FTM_MODE_FTMEN BIT(0) |
---|
50 | | -#define FTM_MODE_INIT BIT(2) |
---|
51 | | -#define FTM_MODE_PWMSYNC BIT(3) |
---|
52 | | - |
---|
53 | | -#define FTM_SYNC 0x58 |
---|
54 | | -#define FTM_OUTINIT 0x5C |
---|
55 | | -#define FTM_OUTMASK 0x60 |
---|
56 | | -#define FTM_COMBINE 0x64 |
---|
57 | | -#define FTM_DEADTIME 0x68 |
---|
58 | | -#define FTM_EXTTRIG 0x6C |
---|
59 | | -#define FTM_POL 0x70 |
---|
60 | | -#define FTM_FMS 0x74 |
---|
61 | | -#define FTM_FILTER 0x78 |
---|
62 | | -#define FTM_FLTCTRL 0x7C |
---|
63 | | -#define FTM_QDCTRL 0x80 |
---|
64 | | -#define FTM_CONF 0x84 |
---|
65 | | -#define FTM_FLTPOL 0x88 |
---|
66 | | -#define FTM_SYNCONF 0x8C |
---|
67 | | -#define FTM_INVCTRL 0x90 |
---|
68 | | -#define FTM_SWOCTRL 0x94 |
---|
69 | | -#define FTM_PWMLOAD 0x98 |
---|
70 | 24 | |
---|
71 | 25 | enum fsl_pwm_clk { |
---|
72 | 26 | FSL_PWM_CLK_SYS, |
---|
.. | .. |
---|
80 | 34 | bool has_enable_bits; |
---|
81 | 35 | }; |
---|
82 | 36 | |
---|
| 37 | +struct fsl_pwm_periodcfg { |
---|
| 38 | + enum fsl_pwm_clk clk_select; |
---|
| 39 | + unsigned int clk_ps; |
---|
| 40 | + unsigned int mod_period; |
---|
| 41 | +}; |
---|
| 42 | + |
---|
83 | 43 | struct fsl_pwm_chip { |
---|
84 | 44 | struct pwm_chip chip; |
---|
85 | | - |
---|
86 | 45 | struct mutex lock; |
---|
87 | | - |
---|
88 | | - unsigned int cnt_select; |
---|
89 | | - unsigned int clk_ps; |
---|
90 | | - |
---|
91 | 46 | struct regmap *regmap; |
---|
92 | 47 | |
---|
93 | | - int period_ns; |
---|
| 48 | + /* This value is valid iff a pwm is running */ |
---|
| 49 | + struct fsl_pwm_periodcfg period; |
---|
94 | 50 | |
---|
95 | 51 | struct clk *ipg_clk; |
---|
96 | 52 | struct clk *clk[FSL_PWM_CLK_MAX]; |
---|
.. | .. |
---|
101 | 57 | static inline struct fsl_pwm_chip *to_fsl_chip(struct pwm_chip *chip) |
---|
102 | 58 | { |
---|
103 | 59 | return container_of(chip, struct fsl_pwm_chip, chip); |
---|
| 60 | +} |
---|
| 61 | + |
---|
| 62 | +static void ftm_clear_write_protection(struct fsl_pwm_chip *fpc) |
---|
| 63 | +{ |
---|
| 64 | + u32 val; |
---|
| 65 | + |
---|
| 66 | + regmap_read(fpc->regmap, FTM_FMS, &val); |
---|
| 67 | + if (val & FTM_FMS_WPEN) |
---|
| 68 | + regmap_update_bits(fpc->regmap, FTM_MODE, FTM_MODE_WPDIS, |
---|
| 69 | + FTM_MODE_WPDIS); |
---|
| 70 | +} |
---|
| 71 | + |
---|
| 72 | +static void ftm_set_write_protection(struct fsl_pwm_chip *fpc) |
---|
| 73 | +{ |
---|
| 74 | + regmap_update_bits(fpc->regmap, FTM_FMS, FTM_FMS_WPEN, FTM_FMS_WPEN); |
---|
| 75 | +} |
---|
| 76 | + |
---|
| 77 | +static bool fsl_pwm_periodcfg_are_equal(const struct fsl_pwm_periodcfg *a, |
---|
| 78 | + const struct fsl_pwm_periodcfg *b) |
---|
| 79 | +{ |
---|
| 80 | + if (a->clk_select != b->clk_select) |
---|
| 81 | + return false; |
---|
| 82 | + if (a->clk_ps != b->clk_ps) |
---|
| 83 | + return false; |
---|
| 84 | + if (a->mod_period != b->mod_period) |
---|
| 85 | + return false; |
---|
| 86 | + return true; |
---|
104 | 87 | } |
---|
105 | 88 | |
---|
106 | 89 | static int fsl_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) |
---|
.. | .. |
---|
133 | 116 | clk_disable_unprepare(fpc->ipg_clk); |
---|
134 | 117 | } |
---|
135 | 118 | |
---|
136 | | -static int fsl_pwm_calculate_default_ps(struct fsl_pwm_chip *fpc, |
---|
137 | | - enum fsl_pwm_clk index) |
---|
| 119 | +static unsigned int fsl_pwm_ticks_to_ns(struct fsl_pwm_chip *fpc, |
---|
| 120 | + unsigned int ticks) |
---|
138 | 121 | { |
---|
139 | | - unsigned long sys_rate, cnt_rate; |
---|
140 | | - unsigned long long ratio; |
---|
| 122 | + unsigned long rate; |
---|
| 123 | + unsigned long long exval; |
---|
141 | 124 | |
---|
142 | | - sys_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_SYS]); |
---|
143 | | - if (!sys_rate) |
---|
144 | | - return -EINVAL; |
---|
145 | | - |
---|
146 | | - cnt_rate = clk_get_rate(fpc->clk[fpc->cnt_select]); |
---|
147 | | - if (!cnt_rate) |
---|
148 | | - return -EINVAL; |
---|
149 | | - |
---|
150 | | - switch (index) { |
---|
151 | | - case FSL_PWM_CLK_SYS: |
---|
152 | | - fpc->clk_ps = 1; |
---|
153 | | - break; |
---|
154 | | - case FSL_PWM_CLK_FIX: |
---|
155 | | - ratio = 2 * cnt_rate - 1; |
---|
156 | | - do_div(ratio, sys_rate); |
---|
157 | | - fpc->clk_ps = ratio; |
---|
158 | | - break; |
---|
159 | | - case FSL_PWM_CLK_EXT: |
---|
160 | | - ratio = 4 * cnt_rate - 1; |
---|
161 | | - do_div(ratio, sys_rate); |
---|
162 | | - fpc->clk_ps = ratio; |
---|
163 | | - break; |
---|
164 | | - default: |
---|
165 | | - return -EINVAL; |
---|
166 | | - } |
---|
167 | | - |
---|
168 | | - return 0; |
---|
| 125 | + rate = clk_get_rate(fpc->clk[fpc->period.clk_select]); |
---|
| 126 | + exval = ticks; |
---|
| 127 | + exval *= 1000000000UL; |
---|
| 128 | + do_div(exval, rate >> fpc->period.clk_ps); |
---|
| 129 | + return exval; |
---|
169 | 130 | } |
---|
170 | 131 | |
---|
171 | | -static unsigned long fsl_pwm_calculate_cycles(struct fsl_pwm_chip *fpc, |
---|
172 | | - unsigned long period_ns) |
---|
| 132 | +static bool fsl_pwm_calculate_period_clk(struct fsl_pwm_chip *fpc, |
---|
| 133 | + unsigned int period_ns, |
---|
| 134 | + enum fsl_pwm_clk index, |
---|
| 135 | + struct fsl_pwm_periodcfg *periodcfg |
---|
| 136 | + ) |
---|
173 | 137 | { |
---|
174 | | - unsigned long long c, c0; |
---|
| 138 | + unsigned long long c; |
---|
| 139 | + unsigned int ps; |
---|
175 | 140 | |
---|
176 | | - c = clk_get_rate(fpc->clk[fpc->cnt_select]); |
---|
| 141 | + c = clk_get_rate(fpc->clk[index]); |
---|
177 | 142 | c = c * period_ns; |
---|
178 | 143 | do_div(c, 1000000000UL); |
---|
179 | 144 | |
---|
180 | | - do { |
---|
181 | | - c0 = c; |
---|
182 | | - do_div(c0, (1 << fpc->clk_ps)); |
---|
183 | | - if (c0 <= 0xFFFF) |
---|
184 | | - return (unsigned long)c0; |
---|
185 | | - } while (++fpc->clk_ps < 8); |
---|
| 145 | + if (c == 0) |
---|
| 146 | + return false; |
---|
186 | 147 | |
---|
187 | | - return 0; |
---|
188 | | -} |
---|
189 | | - |
---|
190 | | -static unsigned long fsl_pwm_calculate_period_cycles(struct fsl_pwm_chip *fpc, |
---|
191 | | - unsigned long period_ns, |
---|
192 | | - enum fsl_pwm_clk index) |
---|
193 | | -{ |
---|
194 | | - int ret; |
---|
195 | | - |
---|
196 | | - ret = fsl_pwm_calculate_default_ps(fpc, index); |
---|
197 | | - if (ret) { |
---|
198 | | - dev_err(fpc->chip.dev, |
---|
199 | | - "failed to calculate default prescaler: %d\n", |
---|
200 | | - ret); |
---|
201 | | - return 0; |
---|
| 148 | + for (ps = 0; ps < 8 ; ++ps, c >>= 1) { |
---|
| 149 | + if (c <= 0x10000) { |
---|
| 150 | + periodcfg->clk_select = index; |
---|
| 151 | + periodcfg->clk_ps = ps; |
---|
| 152 | + periodcfg->mod_period = c - 1; |
---|
| 153 | + return true; |
---|
| 154 | + } |
---|
202 | 155 | } |
---|
203 | | - |
---|
204 | | - return fsl_pwm_calculate_cycles(fpc, period_ns); |
---|
| 156 | + return false; |
---|
205 | 157 | } |
---|
206 | 158 | |
---|
207 | | -static unsigned long fsl_pwm_calculate_period(struct fsl_pwm_chip *fpc, |
---|
208 | | - unsigned long period_ns) |
---|
| 159 | +static bool fsl_pwm_calculate_period(struct fsl_pwm_chip *fpc, |
---|
| 160 | + unsigned int period_ns, |
---|
| 161 | + struct fsl_pwm_periodcfg *periodcfg) |
---|
209 | 162 | { |
---|
210 | 163 | enum fsl_pwm_clk m0, m1; |
---|
211 | | - unsigned long fix_rate, ext_rate, cycles; |
---|
| 164 | + unsigned long fix_rate, ext_rate; |
---|
| 165 | + bool ret; |
---|
212 | 166 | |
---|
213 | | - cycles = fsl_pwm_calculate_period_cycles(fpc, period_ns, |
---|
214 | | - FSL_PWM_CLK_SYS); |
---|
215 | | - if (cycles) { |
---|
216 | | - fpc->cnt_select = FSL_PWM_CLK_SYS; |
---|
217 | | - return cycles; |
---|
218 | | - } |
---|
| 167 | + ret = fsl_pwm_calculate_period_clk(fpc, period_ns, FSL_PWM_CLK_SYS, |
---|
| 168 | + periodcfg); |
---|
| 169 | + if (ret) |
---|
| 170 | + return true; |
---|
219 | 171 | |
---|
220 | 172 | fix_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_FIX]); |
---|
221 | 173 | ext_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_EXT]); |
---|
.. | .. |
---|
228 | 180 | m1 = FSL_PWM_CLK_FIX; |
---|
229 | 181 | } |
---|
230 | 182 | |
---|
231 | | - cycles = fsl_pwm_calculate_period_cycles(fpc, period_ns, m0); |
---|
232 | | - if (cycles) { |
---|
233 | | - fpc->cnt_select = m0; |
---|
234 | | - return cycles; |
---|
235 | | - } |
---|
| 183 | + ret = fsl_pwm_calculate_period_clk(fpc, period_ns, m0, periodcfg); |
---|
| 184 | + if (ret) |
---|
| 185 | + return true; |
---|
236 | 186 | |
---|
237 | | - fpc->cnt_select = m1; |
---|
238 | | - |
---|
239 | | - return fsl_pwm_calculate_period_cycles(fpc, period_ns, m1); |
---|
| 187 | + return fsl_pwm_calculate_period_clk(fpc, period_ns, m1, periodcfg); |
---|
240 | 188 | } |
---|
241 | 189 | |
---|
242 | | -static unsigned long fsl_pwm_calculate_duty(struct fsl_pwm_chip *fpc, |
---|
243 | | - unsigned long period_ns, |
---|
244 | | - unsigned long duty_ns) |
---|
| 190 | +static unsigned int fsl_pwm_calculate_duty(struct fsl_pwm_chip *fpc, |
---|
| 191 | + unsigned int duty_ns) |
---|
245 | 192 | { |
---|
246 | 193 | unsigned long long duty; |
---|
247 | | - u32 val; |
---|
248 | 194 | |
---|
249 | | - regmap_read(fpc->regmap, FTM_MOD, &val); |
---|
250 | | - duty = (unsigned long long)duty_ns * (val + 1); |
---|
| 195 | + unsigned int period = fpc->period.mod_period + 1; |
---|
| 196 | + unsigned int period_ns = fsl_pwm_ticks_to_ns(fpc, period); |
---|
| 197 | + |
---|
| 198 | + duty = (unsigned long long)duty_ns * period; |
---|
251 | 199 | do_div(duty, period_ns); |
---|
252 | 200 | |
---|
253 | | - return (unsigned long)duty; |
---|
| 201 | + return (unsigned int)duty; |
---|
254 | 202 | } |
---|
255 | 203 | |
---|
256 | | -static int fsl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, |
---|
257 | | - int duty_ns, int period_ns) |
---|
| 204 | +static bool fsl_pwm_is_any_pwm_enabled(struct fsl_pwm_chip *fpc, |
---|
| 205 | + struct pwm_device *pwm) |
---|
258 | 206 | { |
---|
259 | | - struct fsl_pwm_chip *fpc = to_fsl_chip(chip); |
---|
260 | | - u32 period, duty; |
---|
| 207 | + u32 val; |
---|
261 | 208 | |
---|
262 | | - mutex_lock(&fpc->lock); |
---|
| 209 | + regmap_read(fpc->regmap, FTM_OUTMASK, &val); |
---|
| 210 | + if (~val & 0xFF) |
---|
| 211 | + return true; |
---|
| 212 | + else |
---|
| 213 | + return false; |
---|
| 214 | +} |
---|
263 | 215 | |
---|
| 216 | +static bool fsl_pwm_is_other_pwm_enabled(struct fsl_pwm_chip *fpc, |
---|
| 217 | + struct pwm_device *pwm) |
---|
| 218 | +{ |
---|
| 219 | + u32 val; |
---|
| 220 | + |
---|
| 221 | + regmap_read(fpc->regmap, FTM_OUTMASK, &val); |
---|
| 222 | + if (~(val | BIT(pwm->hwpwm)) & 0xFF) |
---|
| 223 | + return true; |
---|
| 224 | + else |
---|
| 225 | + return false; |
---|
| 226 | +} |
---|
| 227 | + |
---|
| 228 | +static int fsl_pwm_apply_config(struct fsl_pwm_chip *fpc, |
---|
| 229 | + struct pwm_device *pwm, |
---|
| 230 | + const struct pwm_state *newstate) |
---|
| 231 | +{ |
---|
| 232 | + unsigned int duty; |
---|
| 233 | + u32 reg_polarity; |
---|
| 234 | + |
---|
| 235 | + struct fsl_pwm_periodcfg periodcfg; |
---|
| 236 | + bool do_write_period = false; |
---|
| 237 | + |
---|
| 238 | + if (!fsl_pwm_calculate_period(fpc, newstate->period, &periodcfg)) { |
---|
| 239 | + dev_err(fpc->chip.dev, "failed to calculate new period\n"); |
---|
| 240 | + return -EINVAL; |
---|
| 241 | + } |
---|
| 242 | + |
---|
| 243 | + if (!fsl_pwm_is_any_pwm_enabled(fpc, pwm)) |
---|
| 244 | + do_write_period = true; |
---|
264 | 245 | /* |
---|
265 | 246 | * The Freescale FTM controller supports only a single period for |
---|
266 | | - * all PWM channels, therefore incompatible changes need to be |
---|
267 | | - * refused. |
---|
| 247 | + * all PWM channels, therefore verify if the newly computed period |
---|
| 248 | + * is different than the current period being used. In such case |
---|
| 249 | + * we allow to change the period only if no other pwm is running. |
---|
268 | 250 | */ |
---|
269 | | - if (fpc->period_ns && fpc->period_ns != period_ns) { |
---|
270 | | - dev_err(fpc->chip.dev, |
---|
271 | | - "conflicting period requested for PWM %u\n", |
---|
272 | | - pwm->hwpwm); |
---|
273 | | - mutex_unlock(&fpc->lock); |
---|
274 | | - return -EBUSY; |
---|
275 | | - } |
---|
276 | | - |
---|
277 | | - if (!fpc->period_ns && duty_ns) { |
---|
278 | | - period = fsl_pwm_calculate_period(fpc, period_ns); |
---|
279 | | - if (!period) { |
---|
280 | | - dev_err(fpc->chip.dev, "failed to calculate period\n"); |
---|
281 | | - mutex_unlock(&fpc->lock); |
---|
282 | | - return -EINVAL; |
---|
| 251 | + else if (!fsl_pwm_periodcfg_are_equal(&fpc->period, &periodcfg)) { |
---|
| 252 | + if (fsl_pwm_is_other_pwm_enabled(fpc, pwm)) { |
---|
| 253 | + dev_err(fpc->chip.dev, |
---|
| 254 | + "Cannot change period for PWM %u, disable other PWMs first\n", |
---|
| 255 | + pwm->hwpwm); |
---|
| 256 | + return -EBUSY; |
---|
283 | 257 | } |
---|
| 258 | + if (fpc->period.clk_select != periodcfg.clk_select) { |
---|
| 259 | + int ret; |
---|
| 260 | + enum fsl_pwm_clk oldclk = fpc->period.clk_select; |
---|
| 261 | + enum fsl_pwm_clk newclk = periodcfg.clk_select; |
---|
284 | 262 | |
---|
285 | | - regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_PS_MASK, |
---|
286 | | - fpc->clk_ps); |
---|
287 | | - regmap_write(fpc->regmap, FTM_MOD, period - 1); |
---|
288 | | - |
---|
289 | | - fpc->period_ns = period_ns; |
---|
| 263 | + ret = clk_prepare_enable(fpc->clk[newclk]); |
---|
| 264 | + if (ret) |
---|
| 265 | + return ret; |
---|
| 266 | + clk_disable_unprepare(fpc->clk[oldclk]); |
---|
| 267 | + } |
---|
| 268 | + do_write_period = true; |
---|
290 | 269 | } |
---|
291 | 270 | |
---|
292 | | - mutex_unlock(&fpc->lock); |
---|
| 271 | + ftm_clear_write_protection(fpc); |
---|
293 | 272 | |
---|
294 | | - duty = fsl_pwm_calculate_duty(fpc, period_ns, duty_ns); |
---|
| 273 | + if (do_write_period) { |
---|
| 274 | + regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_CLK_MASK, |
---|
| 275 | + FTM_SC_CLK(periodcfg.clk_select)); |
---|
| 276 | + regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_PS_MASK, |
---|
| 277 | + periodcfg.clk_ps); |
---|
| 278 | + regmap_write(fpc->regmap, FTM_MOD, periodcfg.mod_period); |
---|
| 279 | + |
---|
| 280 | + fpc->period = periodcfg; |
---|
| 281 | + } |
---|
| 282 | + |
---|
| 283 | + duty = fsl_pwm_calculate_duty(fpc, newstate->duty_cycle); |
---|
295 | 284 | |
---|
296 | 285 | regmap_write(fpc->regmap, FTM_CSC(pwm->hwpwm), |
---|
297 | 286 | FTM_CSC_MSB | FTM_CSC_ELSB); |
---|
298 | 287 | regmap_write(fpc->regmap, FTM_CV(pwm->hwpwm), duty); |
---|
299 | 288 | |
---|
| 289 | + reg_polarity = 0; |
---|
| 290 | + if (newstate->polarity == PWM_POLARITY_INVERSED) |
---|
| 291 | + reg_polarity = BIT(pwm->hwpwm); |
---|
| 292 | + |
---|
| 293 | + regmap_update_bits(fpc->regmap, FTM_POL, BIT(pwm->hwpwm), reg_polarity); |
---|
| 294 | + |
---|
| 295 | + ftm_set_write_protection(fpc); |
---|
| 296 | + |
---|
300 | 297 | return 0; |
---|
301 | 298 | } |
---|
302 | 299 | |
---|
303 | | -static int fsl_pwm_set_polarity(struct pwm_chip *chip, |
---|
304 | | - struct pwm_device *pwm, |
---|
305 | | - enum pwm_polarity polarity) |
---|
| 300 | +static int fsl_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, |
---|
| 301 | + const struct pwm_state *newstate) |
---|
306 | 302 | { |
---|
307 | 303 | struct fsl_pwm_chip *fpc = to_fsl_chip(chip); |
---|
308 | | - u32 val; |
---|
| 304 | + struct pwm_state *oldstate = &pwm->state; |
---|
| 305 | + int ret = 0; |
---|
309 | 306 | |
---|
310 | | - regmap_read(fpc->regmap, FTM_POL, &val); |
---|
| 307 | + /* |
---|
| 308 | + * oldstate to newstate : action |
---|
| 309 | + * |
---|
| 310 | + * disabled to disabled : ignore |
---|
| 311 | + * enabled to disabled : disable |
---|
| 312 | + * enabled to enabled : update settings |
---|
| 313 | + * disabled to enabled : update settings + enable |
---|
| 314 | + */ |
---|
311 | 315 | |
---|
312 | | - if (polarity == PWM_POLARITY_INVERSED) |
---|
313 | | - val |= BIT(pwm->hwpwm); |
---|
314 | | - else |
---|
315 | | - val &= ~BIT(pwm->hwpwm); |
---|
| 316 | + mutex_lock(&fpc->lock); |
---|
316 | 317 | |
---|
317 | | - regmap_write(fpc->regmap, FTM_POL, val); |
---|
| 318 | + if (!newstate->enabled) { |
---|
| 319 | + if (oldstate->enabled) { |
---|
| 320 | + regmap_update_bits(fpc->regmap, FTM_OUTMASK, |
---|
| 321 | + BIT(pwm->hwpwm), BIT(pwm->hwpwm)); |
---|
| 322 | + clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]); |
---|
| 323 | + clk_disable_unprepare(fpc->clk[fpc->period.clk_select]); |
---|
| 324 | + } |
---|
318 | 325 | |
---|
319 | | - return 0; |
---|
320 | | -} |
---|
321 | | - |
---|
322 | | -static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc) |
---|
323 | | -{ |
---|
324 | | - int ret; |
---|
325 | | - |
---|
326 | | - /* select counter clock source */ |
---|
327 | | - regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_CLK_MASK, |
---|
328 | | - FTM_SC_CLK(fpc->cnt_select)); |
---|
329 | | - |
---|
330 | | - ret = clk_prepare_enable(fpc->clk[fpc->cnt_select]); |
---|
331 | | - if (ret) |
---|
332 | | - return ret; |
---|
333 | | - |
---|
334 | | - ret = clk_prepare_enable(fpc->clk[FSL_PWM_CLK_CNTEN]); |
---|
335 | | - if (ret) { |
---|
336 | | - clk_disable_unprepare(fpc->clk[fpc->cnt_select]); |
---|
337 | | - return ret; |
---|
| 326 | + goto end_mutex; |
---|
338 | 327 | } |
---|
339 | 328 | |
---|
340 | | - return 0; |
---|
341 | | -} |
---|
| 329 | + ret = fsl_pwm_apply_config(fpc, pwm, newstate); |
---|
| 330 | + if (ret) |
---|
| 331 | + goto end_mutex; |
---|
342 | 332 | |
---|
343 | | -static int fsl_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) |
---|
344 | | -{ |
---|
345 | | - struct fsl_pwm_chip *fpc = to_fsl_chip(chip); |
---|
346 | | - int ret; |
---|
| 333 | + /* check if need to enable */ |
---|
| 334 | + if (!oldstate->enabled) { |
---|
| 335 | + ret = clk_prepare_enable(fpc->clk[fpc->period.clk_select]); |
---|
| 336 | + if (ret) |
---|
| 337 | + goto end_mutex; |
---|
347 | 338 | |
---|
348 | | - mutex_lock(&fpc->lock); |
---|
349 | | - regmap_update_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm), 0); |
---|
| 339 | + ret = clk_prepare_enable(fpc->clk[FSL_PWM_CLK_CNTEN]); |
---|
| 340 | + if (ret) { |
---|
| 341 | + clk_disable_unprepare(fpc->clk[fpc->period.clk_select]); |
---|
| 342 | + goto end_mutex; |
---|
| 343 | + } |
---|
350 | 344 | |
---|
351 | | - ret = fsl_counter_clock_enable(fpc); |
---|
| 345 | + regmap_update_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm), |
---|
| 346 | + 0); |
---|
| 347 | + } |
---|
| 348 | + |
---|
| 349 | +end_mutex: |
---|
352 | 350 | mutex_unlock(&fpc->lock); |
---|
353 | | - |
---|
354 | 351 | return ret; |
---|
355 | | -} |
---|
356 | | - |
---|
357 | | -static void fsl_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) |
---|
358 | | -{ |
---|
359 | | - struct fsl_pwm_chip *fpc = to_fsl_chip(chip); |
---|
360 | | - u32 val; |
---|
361 | | - |
---|
362 | | - mutex_lock(&fpc->lock); |
---|
363 | | - regmap_update_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm), |
---|
364 | | - BIT(pwm->hwpwm)); |
---|
365 | | - |
---|
366 | | - clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]); |
---|
367 | | - clk_disable_unprepare(fpc->clk[fpc->cnt_select]); |
---|
368 | | - |
---|
369 | | - regmap_read(fpc->regmap, FTM_OUTMASK, &val); |
---|
370 | | - if ((val & 0xFF) == 0xFF) |
---|
371 | | - fpc->period_ns = 0; |
---|
372 | | - |
---|
373 | | - mutex_unlock(&fpc->lock); |
---|
374 | 352 | } |
---|
375 | 353 | |
---|
376 | 354 | static const struct pwm_ops fsl_pwm_ops = { |
---|
377 | 355 | .request = fsl_pwm_request, |
---|
378 | 356 | .free = fsl_pwm_free, |
---|
379 | | - .config = fsl_pwm_config, |
---|
380 | | - .set_polarity = fsl_pwm_set_polarity, |
---|
381 | | - .enable = fsl_pwm_enable, |
---|
382 | | - .disable = fsl_pwm_disable, |
---|
| 357 | + .apply = fsl_pwm_apply, |
---|
383 | 358 | .owner = THIS_MODULE, |
---|
384 | 359 | }; |
---|
385 | 360 | |
---|
.. | .. |
---|
403 | 378 | static bool fsl_pwm_volatile_reg(struct device *dev, unsigned int reg) |
---|
404 | 379 | { |
---|
405 | 380 | switch (reg) { |
---|
| 381 | + case FTM_FMS: |
---|
| 382 | + case FTM_MODE: |
---|
406 | 383 | case FTM_CNT: |
---|
407 | 384 | return true; |
---|
408 | 385 | } |
---|
.. | .. |
---|
520 | 497 | continue; |
---|
521 | 498 | |
---|
522 | 499 | clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]); |
---|
523 | | - clk_disable_unprepare(fpc->clk[fpc->cnt_select]); |
---|
| 500 | + clk_disable_unprepare(fpc->clk[fpc->period.clk_select]); |
---|
524 | 501 | } |
---|
525 | 502 | |
---|
526 | 503 | return 0; |
---|
.. | .. |
---|
542 | 519 | if (!pwm_is_enabled(pwm)) |
---|
543 | 520 | continue; |
---|
544 | 521 | |
---|
545 | | - clk_prepare_enable(fpc->clk[fpc->cnt_select]); |
---|
| 522 | + clk_prepare_enable(fpc->clk[fpc->period.clk_select]); |
---|
546 | 523 | clk_prepare_enable(fpc->clk[FSL_PWM_CLK_CNTEN]); |
---|
547 | 524 | } |
---|
548 | 525 | |
---|