aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pwm/pwm-stm32.c
diff options
context:
space:
mode:
authorFabrice Gasnier <fabrice.gasnier@st.com>2018-05-16 03:35:58 -0400
committerLee Jones <lee.jones@linaro.org>2018-05-16 04:10:53 -0400
commit53e38fe73f941291fd20794c15c3bb7b104a4a17 (patch)
tree948500fb0d45d578e1d01a16ae367bed38d51076 /drivers/pwm/pwm-stm32.c
parent0c6609805b638debcb7d9d44556546b043ded2e9 (diff)
pwm: stm32: Add capture support
Add support for PMW input mode on pwm-stm32. STM32 timers support period and duty cycle capture as long as they have at least two PWM channels. One capture channel is used for period (rising-edge), one for duty-cycle (falling-edge). When there's only one channel available, only period can be captured. Duty-cycle is simply zero'ed in such a case. Capture requires exclusive access (e.g. no pwm output running at the same time, to protect common prescaler). Timer DMA burst mode (from MFD core) is being used, to take two snapshots of capture registers (upon each period rising edge). Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com> Reviewed-by: Benjamin Gaignard <benjamin.gaignard@linaro.org> Acked-by: Thierry Reding <thierry.reding@gmail.com> Signed-off-by: Lee Jones <lee.jones@linaro.org>
Diffstat (limited to 'drivers/pwm/pwm-stm32.c')
-rw-r--r--drivers/pwm/pwm-stm32.c176
1 files changed, 176 insertions, 0 deletions
diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c
index 2708212933f7..ed3961b7b938 100644
--- a/drivers/pwm/pwm-stm32.c
+++ b/drivers/pwm/pwm-stm32.c
@@ -25,6 +25,7 @@ struct stm32_pwm {
25 struct regmap *regmap; 25 struct regmap *regmap;
26 u32 max_arr; 26 u32 max_arr;
27 bool have_complementary_output; 27 bool have_complementary_output;
28 u32 capture[4] ____cacheline_aligned; /* DMA'able buffer */
28}; 29};
29 30
30struct stm32_breakinput { 31struct stm32_breakinput {
@@ -62,6 +63,178 @@ static int write_ccrx(struct stm32_pwm *dev, int ch, u32 value)
62 return -EINVAL; 63 return -EINVAL;
63} 64}
64 65
66#define TIM_CCER_CC12P (TIM_CCER_CC1P | TIM_CCER_CC2P)
67#define TIM_CCER_CC12E (TIM_CCER_CC1E | TIM_CCER_CC2E)
68#define TIM_CCER_CC34P (TIM_CCER_CC3P | TIM_CCER_CC4P)
69#define TIM_CCER_CC34E (TIM_CCER_CC3E | TIM_CCER_CC4E)
70
71/*
72 * Capture using PWM input mode:
73 * ___ ___
74 * TI[1, 2, 3 or 4]: ........._| |________|
75 * ^0 ^1 ^2
76 * . . .
77 * . . XXXXX
78 * . . XXXXX |
79 * . XXXXX . |
80 * XXXXX . . |
81 * COUNTER: ______XXXXX . . . |_XXX
82 * start^ . . . ^stop
83 * . . . .
84 * v v . v
85 * v
86 * CCR1/CCR3: tx..........t0...........t2
87 * CCR2/CCR4: tx..............t1.........
88 *
89 * DMA burst transfer: | |
90 * v v
91 * DMA buffer: { t0, tx } { t2, t1 }
92 * DMA done: ^
93 *
94 * 0: IC1/3 snapchot on rising edge: counter value -> CCR1/CCR3
95 * + DMA transfer CCR[1/3] & CCR[2/4] values (t0, tx: doesn't care)
96 * 1: IC2/4 snapchot on falling edge: counter value -> CCR2/CCR4
97 * 2: IC1/3 snapchot on rising edge: counter value -> CCR1/CCR3
98 * + DMA transfer CCR[1/3] & CCR[2/4] values (t2, t1)
99 *
100 * DMA done, compute:
101 * - Period = t2 - t0
102 * - Duty cycle = t1 - t0
103 */
104static int stm32_pwm_raw_capture(struct stm32_pwm *priv, struct pwm_device *pwm,
105 unsigned long tmo_ms, u32 *raw_prd,
106 u32 *raw_dty)
107{
108 struct device *parent = priv->chip.dev->parent;
109 enum stm32_timers_dmas dma_id;
110 u32 ccen, ccr;
111 int ret;
112
113 /* Ensure registers have been updated, enable counter and capture */
114 regmap_update_bits(priv->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG);
115 regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, TIM_CR1_CEN);
116
117 /* Use cc1 or cc3 DMA resp for PWM input channels 1 & 2 or 3 & 4 */
118 dma_id = pwm->hwpwm < 2 ? STM32_TIMERS_DMA_CH1 : STM32_TIMERS_DMA_CH3;
119 ccen = pwm->hwpwm < 2 ? TIM_CCER_CC12E : TIM_CCER_CC34E;
120 ccr = pwm->hwpwm < 2 ? TIM_CCR1 : TIM_CCR3;
121 regmap_update_bits(priv->regmap, TIM_CCER, ccen, ccen);
122
123 /*
124 * Timer DMA burst mode. Request 2 registers, 2 bursts, to get both
125 * CCR1 & CCR2 (or CCR3 & CCR4) on each capture event.
126 * We'll get two capture snapchots: { CCR1, CCR2 }, { CCR1, CCR2 }
127 * or { CCR3, CCR4 }, { CCR3, CCR4 }
128 */
129 ret = stm32_timers_dma_burst_read(parent, priv->capture, dma_id, ccr, 2,
130 2, tmo_ms);
131 if (ret)
132 goto stop;
133
134 /* Period: t2 - t0 (take care of counter overflow) */
135 if (priv->capture[0] <= priv->capture[2])
136 *raw_prd = priv->capture[2] - priv->capture[0];
137 else
138 *raw_prd = priv->max_arr - priv->capture[0] + priv->capture[2];
139
140 /* Duty cycle capture requires at least two capture units */
141 if (pwm->chip->npwm < 2)
142 *raw_dty = 0;
143 else if (priv->capture[0] <= priv->capture[3])
144 *raw_dty = priv->capture[3] - priv->capture[0];
145 else
146 *raw_dty = priv->max_arr - priv->capture[0] + priv->capture[3];
147
148 if (*raw_dty > *raw_prd) {
149 /*
150 * Race beetween PWM input and DMA: it may happen
151 * falling edge triggers new capture on TI2/4 before DMA
152 * had a chance to read CCR2/4. It means capture[1]
153 * contains period + duty_cycle. So, subtract period.
154 */
155 *raw_dty -= *raw_prd;
156 }
157
158stop:
159 regmap_update_bits(priv->regmap, TIM_CCER, ccen, 0);
160 regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0);
161
162 return ret;
163}
164
165static int stm32_pwm_capture(struct pwm_chip *chip, struct pwm_device *pwm,
166 struct pwm_capture *result, unsigned long tmo_ms)
167{
168 struct stm32_pwm *priv = to_stm32_pwm_dev(chip);
169 unsigned long long prd, div, dty;
170 unsigned long rate;
171 unsigned int psc = 0;
172 u32 raw_prd, raw_dty;
173 int ret = 0;
174
175 mutex_lock(&priv->lock);
176
177 if (active_channels(priv)) {
178 ret = -EBUSY;
179 goto unlock;
180 }
181
182 ret = clk_enable(priv->clk);
183 if (ret) {
184 dev_err(priv->chip.dev, "failed to enable counter clock\n");
185 goto unlock;
186 }
187
188 rate = clk_get_rate(priv->clk);
189 if (!rate) {
190 ret = -EINVAL;
191 goto clk_dis;
192 }
193
194 /* prescaler: fit timeout window provided by upper layer */
195 div = (unsigned long long)rate * (unsigned long long)tmo_ms;
196 do_div(div, MSEC_PER_SEC);
197 prd = div;
198 while ((div > priv->max_arr) && (psc < MAX_TIM_PSC)) {
199 psc++;
200 div = prd;
201 do_div(div, psc + 1);
202 }
203 regmap_write(priv->regmap, TIM_ARR, priv->max_arr);
204 regmap_write(priv->regmap, TIM_PSC, psc);
205
206 /* Map TI1 or TI2 PWM input to IC1 & IC2 (or TI3/4 to IC3 & IC4) */
207 regmap_update_bits(priv->regmap,
208 pwm->hwpwm < 2 ? TIM_CCMR1 : TIM_CCMR2,
209 TIM_CCMR_CC1S | TIM_CCMR_CC2S, pwm->hwpwm & 0x1 ?
210 TIM_CCMR_CC1S_TI2 | TIM_CCMR_CC2S_TI2 :
211 TIM_CCMR_CC1S_TI1 | TIM_CCMR_CC2S_TI1);
212
213 /* Capture period on IC1/3 rising edge, duty cycle on IC2/4 falling. */
214 regmap_update_bits(priv->regmap, TIM_CCER, pwm->hwpwm < 2 ?
215 TIM_CCER_CC12P : TIM_CCER_CC34P, pwm->hwpwm < 2 ?
216 TIM_CCER_CC2P : TIM_CCER_CC4P);
217
218 ret = stm32_pwm_raw_capture(priv, pwm, tmo_ms, &raw_prd, &raw_dty);
219 if (ret)
220 goto stop;
221
222 prd = (unsigned long long)raw_prd * (psc + 1) * NSEC_PER_SEC;
223 result->period = DIV_ROUND_UP_ULL(prd, rate);
224 dty = (unsigned long long)raw_dty * (psc + 1) * NSEC_PER_SEC;
225 result->duty_cycle = DIV_ROUND_UP_ULL(dty, rate);
226stop:
227 regmap_write(priv->regmap, TIM_CCER, 0);
228 regmap_write(priv->regmap, pwm->hwpwm < 2 ? TIM_CCMR1 : TIM_CCMR2, 0);
229 regmap_write(priv->regmap, TIM_PSC, 0);
230clk_dis:
231 clk_disable(priv->clk);
232unlock:
233 mutex_unlock(&priv->lock);
234
235 return ret;
236}
237
65static int stm32_pwm_config(struct stm32_pwm *priv, int ch, 238static int stm32_pwm_config(struct stm32_pwm *priv, int ch,
66 int duty_ns, int period_ns) 239 int duty_ns, int period_ns)
67{ 240{
@@ -230,6 +403,9 @@ static int stm32_pwm_apply_locked(struct pwm_chip *chip, struct pwm_device *pwm,
230static const struct pwm_ops stm32pwm_ops = { 403static const struct pwm_ops stm32pwm_ops = {
231 .owner = THIS_MODULE, 404 .owner = THIS_MODULE,
232 .apply = stm32_pwm_apply_locked, 405 .apply = stm32_pwm_apply_locked,
406#if IS_ENABLED(CONFIG_DMA_ENGINE)
407 .capture = stm32_pwm_capture,
408#endif
233}; 409};
234 410
235static int stm32_pwm_set_breakinput(struct stm32_pwm *priv, 411static int stm32_pwm_set_breakinput(struct stm32_pwm *priv,