diff options
Diffstat (limited to 'drivers/pwm/pwm-meson.c')
-rw-r--r-- | drivers/pwm/pwm-meson.c | 64 |
1 files changed, 49 insertions, 15 deletions
diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c index 4ae5d774443e..fb5a369b1a8d 100644 --- a/drivers/pwm/pwm-meson.c +++ b/drivers/pwm/pwm-meson.c | |||
@@ -111,6 +111,10 @@ struct meson_pwm { | |||
111 | const struct meson_pwm_data *data; | 111 | const struct meson_pwm_data *data; |
112 | void __iomem *base; | 112 | void __iomem *base; |
113 | u8 inverter_mask; | 113 | u8 inverter_mask; |
114 | /* | ||
115 | * Protects register (write) access to the REG_MISC_AB register | ||
116 | * that is shared between the two PWMs. | ||
117 | */ | ||
114 | spinlock_t lock; | 118 | spinlock_t lock; |
115 | }; | 119 | }; |
116 | 120 | ||
@@ -184,7 +188,7 @@ static int meson_pwm_calc(struct meson_pwm *meson, | |||
184 | do_div(fin_ps, fin_freq); | 188 | do_div(fin_ps, fin_freq); |
185 | 189 | ||
186 | /* Calc pre_div with the period */ | 190 | /* Calc pre_div with the period */ |
187 | for (pre_div = 0; pre_div < MISC_CLK_DIV_MASK; pre_div++) { | 191 | for (pre_div = 0; pre_div <= MISC_CLK_DIV_MASK; pre_div++) { |
188 | cnt = DIV_ROUND_CLOSEST_ULL((u64)period * 1000, | 192 | cnt = DIV_ROUND_CLOSEST_ULL((u64)period * 1000, |
189 | fin_ps * (pre_div + 1)); | 193 | fin_ps * (pre_div + 1)); |
190 | dev_dbg(meson->chip.dev, "fin_ps=%llu pre_div=%u cnt=%u\n", | 194 | dev_dbg(meson->chip.dev, "fin_ps=%llu pre_div=%u cnt=%u\n", |
@@ -193,7 +197,7 @@ static int meson_pwm_calc(struct meson_pwm *meson, | |||
193 | break; | 197 | break; |
194 | } | 198 | } |
195 | 199 | ||
196 | if (pre_div == MISC_CLK_DIV_MASK) { | 200 | if (pre_div > MISC_CLK_DIV_MASK) { |
197 | dev_err(meson->chip.dev, "unable to get period pre_div\n"); | 201 | dev_err(meson->chip.dev, "unable to get period pre_div\n"); |
198 | return -EINVAL; | 202 | return -EINVAL; |
199 | } | 203 | } |
@@ -235,6 +239,7 @@ static void meson_pwm_enable(struct meson_pwm *meson, | |||
235 | { | 239 | { |
236 | u32 value, clk_shift, clk_enable, enable; | 240 | u32 value, clk_shift, clk_enable, enable; |
237 | unsigned int offset; | 241 | unsigned int offset; |
242 | unsigned long flags; | ||
238 | 243 | ||
239 | switch (id) { | 244 | switch (id) { |
240 | case 0: | 245 | case 0: |
@@ -255,6 +260,8 @@ static void meson_pwm_enable(struct meson_pwm *meson, | |||
255 | return; | 260 | return; |
256 | } | 261 | } |
257 | 262 | ||
263 | spin_lock_irqsave(&meson->lock, flags); | ||
264 | |||
258 | value = readl(meson->base + REG_MISC_AB); | 265 | value = readl(meson->base + REG_MISC_AB); |
259 | value &= ~(MISC_CLK_DIV_MASK << clk_shift); | 266 | value &= ~(MISC_CLK_DIV_MASK << clk_shift); |
260 | value |= channel->pre_div << clk_shift; | 267 | value |= channel->pre_div << clk_shift; |
@@ -267,11 +274,14 @@ static void meson_pwm_enable(struct meson_pwm *meson, | |||
267 | value = readl(meson->base + REG_MISC_AB); | 274 | value = readl(meson->base + REG_MISC_AB); |
268 | value |= enable; | 275 | value |= enable; |
269 | writel(value, meson->base + REG_MISC_AB); | 276 | writel(value, meson->base + REG_MISC_AB); |
277 | |||
278 | spin_unlock_irqrestore(&meson->lock, flags); | ||
270 | } | 279 | } |
271 | 280 | ||
272 | static void meson_pwm_disable(struct meson_pwm *meson, unsigned int id) | 281 | static void meson_pwm_disable(struct meson_pwm *meson, unsigned int id) |
273 | { | 282 | { |
274 | u32 value, enable; | 283 | u32 value, enable; |
284 | unsigned long flags; | ||
275 | 285 | ||
276 | switch (id) { | 286 | switch (id) { |
277 | case 0: | 287 | case 0: |
@@ -286,9 +296,13 @@ static void meson_pwm_disable(struct meson_pwm *meson, unsigned int id) | |||
286 | return; | 296 | return; |
287 | } | 297 | } |
288 | 298 | ||
299 | spin_lock_irqsave(&meson->lock, flags); | ||
300 | |||
289 | value = readl(meson->base + REG_MISC_AB); | 301 | value = readl(meson->base + REG_MISC_AB); |
290 | value &= ~enable; | 302 | value &= ~enable; |
291 | writel(value, meson->base + REG_MISC_AB); | 303 | writel(value, meson->base + REG_MISC_AB); |
304 | |||
305 | spin_unlock_irqrestore(&meson->lock, flags); | ||
292 | } | 306 | } |
293 | 307 | ||
294 | static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, | 308 | static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, |
@@ -296,29 +310,21 @@ static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, | |||
296 | { | 310 | { |
297 | struct meson_pwm_channel *channel = pwm_get_chip_data(pwm); | 311 | struct meson_pwm_channel *channel = pwm_get_chip_data(pwm); |
298 | struct meson_pwm *meson = to_meson_pwm(chip); | 312 | struct meson_pwm *meson = to_meson_pwm(chip); |
299 | unsigned long flags; | ||
300 | int err = 0; | 313 | int err = 0; |
301 | 314 | ||
302 | if (!state) | 315 | if (!state) |
303 | return -EINVAL; | 316 | return -EINVAL; |
304 | 317 | ||
305 | spin_lock_irqsave(&meson->lock, flags); | ||
306 | |||
307 | if (!state->enabled) { | 318 | if (!state->enabled) { |
308 | meson_pwm_disable(meson, pwm->hwpwm); | 319 | meson_pwm_disable(meson, pwm->hwpwm); |
309 | channel->state.enabled = false; | 320 | channel->state.enabled = false; |
310 | 321 | ||
311 | goto unlock; | 322 | return 0; |
312 | } | 323 | } |
313 | 324 | ||
314 | if (state->period != channel->state.period || | 325 | if (state->period != channel->state.period || |
315 | state->duty_cycle != channel->state.duty_cycle || | 326 | state->duty_cycle != channel->state.duty_cycle || |
316 | state->polarity != channel->state.polarity) { | 327 | state->polarity != channel->state.polarity) { |
317 | if (channel->state.enabled) { | ||
318 | meson_pwm_disable(meson, pwm->hwpwm); | ||
319 | channel->state.enabled = false; | ||
320 | } | ||
321 | |||
322 | if (state->polarity != channel->state.polarity) { | 328 | if (state->polarity != channel->state.polarity) { |
323 | if (state->polarity == PWM_POLARITY_NORMAL) | 329 | if (state->polarity == PWM_POLARITY_NORMAL) |
324 | meson->inverter_mask |= BIT(pwm->hwpwm); | 330 | meson->inverter_mask |= BIT(pwm->hwpwm); |
@@ -329,7 +335,7 @@ static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, | |||
329 | err = meson_pwm_calc(meson, channel, pwm->hwpwm, | 335 | err = meson_pwm_calc(meson, channel, pwm->hwpwm, |
330 | state->duty_cycle, state->period); | 336 | state->duty_cycle, state->period); |
331 | if (err < 0) | 337 | if (err < 0) |
332 | goto unlock; | 338 | return err; |
333 | 339 | ||
334 | channel->state.polarity = state->polarity; | 340 | channel->state.polarity = state->polarity; |
335 | channel->state.period = state->period; | 341 | channel->state.period = state->period; |
@@ -341,9 +347,7 @@ static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, | |||
341 | channel->state.enabled = true; | 347 | channel->state.enabled = true; |
342 | } | 348 | } |
343 | 349 | ||
344 | unlock: | 350 | return 0; |
345 | spin_unlock_irqrestore(&meson->lock, flags); | ||
346 | return err; | ||
347 | } | 351 | } |
348 | 352 | ||
349 | static void meson_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, | 353 | static void meson_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, |
@@ -429,6 +433,24 @@ static const struct meson_pwm_data pwm_axg_ao_data = { | |||
429 | .num_parents = ARRAY_SIZE(pwm_axg_ao_parent_names), | 433 | .num_parents = ARRAY_SIZE(pwm_axg_ao_parent_names), |
430 | }; | 434 | }; |
431 | 435 | ||
436 | static const char * const pwm_g12a_ao_cd_parent_names[] = { | ||
437 | "aoclk81", "xtal", | ||
438 | }; | ||
439 | |||
440 | static const struct meson_pwm_data pwm_g12a_ao_cd_data = { | ||
441 | .parent_names = pwm_g12a_ao_cd_parent_names, | ||
442 | .num_parents = ARRAY_SIZE(pwm_g12a_ao_cd_parent_names), | ||
443 | }; | ||
444 | |||
445 | static const char * const pwm_g12a_ee_parent_names[] = { | ||
446 | "xtal", "hdmi_pll", "fclk_div4", "fclk_div3" | ||
447 | }; | ||
448 | |||
449 | static const struct meson_pwm_data pwm_g12a_ee_data = { | ||
450 | .parent_names = pwm_g12a_ee_parent_names, | ||
451 | .num_parents = ARRAY_SIZE(pwm_g12a_ee_parent_names), | ||
452 | }; | ||
453 | |||
432 | static const struct of_device_id meson_pwm_matches[] = { | 454 | static const struct of_device_id meson_pwm_matches[] = { |
433 | { | 455 | { |
434 | .compatible = "amlogic,meson8b-pwm", | 456 | .compatible = "amlogic,meson8b-pwm", |
@@ -450,6 +472,18 @@ static const struct of_device_id meson_pwm_matches[] = { | |||
450 | .compatible = "amlogic,meson-axg-ao-pwm", | 472 | .compatible = "amlogic,meson-axg-ao-pwm", |
451 | .data = &pwm_axg_ao_data | 473 | .data = &pwm_axg_ao_data |
452 | }, | 474 | }, |
475 | { | ||
476 | .compatible = "amlogic,meson-g12a-ee-pwm", | ||
477 | .data = &pwm_g12a_ee_data | ||
478 | }, | ||
479 | { | ||
480 | .compatible = "amlogic,meson-g12a-ao-pwm-ab", | ||
481 | .data = &pwm_axg_ao_data | ||
482 | }, | ||
483 | { | ||
484 | .compatible = "amlogic,meson-g12a-ao-pwm-cd", | ||
485 | .data = &pwm_g12a_ao_cd_data | ||
486 | }, | ||
453 | {}, | 487 | {}, |
454 | }; | 488 | }; |
455 | MODULE_DEVICE_TABLE(of, meson_pwm_matches); | 489 | MODULE_DEVICE_TABLE(of, meson_pwm_matches); |