• Home
  • Raw
  • Download

Lines Matching +full:s3c6400 +full:- +full:pwm

4  *     Ben Dooks <ben@simtec.co.uk>, <ben-linux@fluff.org>
8 * PWM driver for Samsung SoCs
24 #include <linux/pwm.h>
62 * struct samsung_pwm_channel - private data of PWM channel
74 * struct samsung_pwm_chip - private data of PWM chip
75 * @chip: generic PWM chip
77 * @inverter_mask: inverter status for all channels - one bit per channel
78 * @disabled_mask: disabled status for all channels - one bit per channel
79 * @base: base address of mapped PWM registers
98 * PWM block is shared between pwm-samsung and samsung_pwm_timer drivers
104 * because all the supported SoCs contain only one instance of the PWM
123 static void pwm_samsung_set_divisor(struct samsung_pwm_chip *pwm, in pwm_samsung_set_divisor() argument
131 bits = (fls(divisor) - 1) - pwm->variant.div_base; in pwm_samsung_set_divisor()
135 reg = readl(pwm->base + REG_TCFG1); in pwm_samsung_set_divisor()
138 writel(reg, pwm->base + REG_TCFG1); in pwm_samsung_set_divisor()
145 struct samsung_pwm_variant *variant = &chip->variant; in pwm_samsung_is_tdiv()
148 reg = readl(chip->base + REG_TCFG1); in pwm_samsung_is_tdiv()
152 return (BIT(reg) & variant->tclk_mask) == 0; in pwm_samsung_is_tdiv()
161 rate = clk_get_rate(chip->base_clk); in pwm_samsung_get_tin_rate()
163 reg = readl(chip->base + REG_TCFG0); in pwm_samsung_get_tin_rate()
174 struct samsung_pwm_variant *variant = &chip->variant; in pwm_samsung_calc_tin()
180 clk = (chan < 2) ? chip->tclk0 : chip->tclk1; in pwm_samsung_calc_tin()
187 dev_warn(chip->chip.dev, in pwm_samsung_calc_tin()
188 "tclk of PWM %d is inoperational, using tdiv\n", chan); in pwm_samsung_calc_tin()
192 dev_dbg(chip->chip.dev, "tin parent at %lu\n", rate); in pwm_samsung_calc_tin()
195 * Compare minimum PWM frequency that can be achieved with possible in pwm_samsung_calc_tin()
199 if (variant->bits < 32) { in pwm_samsung_calc_tin()
201 for (div = variant->div_base; div < 4; ++div) in pwm_samsung_calc_tin()
202 if ((rate >> (variant->bits + div)) < freq) in pwm_samsung_calc_tin()
209 div = variant->div_base; in pwm_samsung_calc_tin()
217 static int pwm_samsung_request(struct pwm_chip *chip, struct pwm_device *pwm) in pwm_samsung_request() argument
222 if (!(our_chip->variant.output_mask & BIT(pwm->hwpwm))) { in pwm_samsung_request()
223 dev_warn(chip->dev, in pwm_samsung_request()
224 "tried to request PWM channel %d without output\n", in pwm_samsung_request()
225 pwm->hwpwm); in pwm_samsung_request()
226 return -EINVAL; in pwm_samsung_request()
229 our_chan = devm_kzalloc(chip->dev, sizeof(*our_chan), GFP_KERNEL); in pwm_samsung_request()
231 return -ENOMEM; in pwm_samsung_request()
233 pwm_set_chip_data(pwm, our_chan); in pwm_samsung_request()
238 static void pwm_samsung_free(struct pwm_chip *chip, struct pwm_device *pwm) in pwm_samsung_free() argument
240 devm_kfree(chip->dev, pwm_get_chip_data(pwm)); in pwm_samsung_free()
243 static int pwm_samsung_enable(struct pwm_chip *chip, struct pwm_device *pwm) in pwm_samsung_enable() argument
246 unsigned int tcon_chan = to_tcon_channel(pwm->hwpwm); in pwm_samsung_enable()
252 tcon = readl(our_chip->base + REG_TCON); in pwm_samsung_enable()
256 writel(tcon, our_chip->base + REG_TCON); in pwm_samsung_enable()
260 writel(tcon, our_chip->base + REG_TCON); in pwm_samsung_enable()
262 our_chip->disabled_mask &= ~BIT(pwm->hwpwm); in pwm_samsung_enable()
269 static void pwm_samsung_disable(struct pwm_chip *chip, struct pwm_device *pwm) in pwm_samsung_disable() argument
272 unsigned int tcon_chan = to_tcon_channel(pwm->hwpwm); in pwm_samsung_disable()
278 tcon = readl(our_chip->base + REG_TCON); in pwm_samsung_disable()
280 writel(tcon, our_chip->base + REG_TCON); in pwm_samsung_disable()
282 our_chip->disabled_mask |= BIT(pwm->hwpwm); in pwm_samsung_disable()
288 struct pwm_device *pwm) in pwm_samsung_manual_update() argument
290 unsigned int tcon_chan = to_tcon_channel(pwm->hwpwm); in pwm_samsung_manual_update()
296 tcon = readl(chip->base + REG_TCON); in pwm_samsung_manual_update()
298 writel(tcon, chip->base + REG_TCON); in pwm_samsung_manual_update()
301 writel(tcon, chip->base + REG_TCON); in pwm_samsung_manual_update()
306 static int __pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, in __pwm_samsung_config() argument
310 struct samsung_pwm_channel *chan = pwm_get_chip_data(pwm); in __pwm_samsung_config()
311 u32 tin_ns = chan->tin_ns, tcnt, tcmp, oldtcmp; in __pwm_samsung_config()
319 return -ERANGE; in __pwm_samsung_config()
321 tcnt = readl(our_chip->base + REG_TCNTB(pwm->hwpwm)); in __pwm_samsung_config()
322 oldtcmp = readl(our_chip->base + REG_TCMPB(pwm->hwpwm)); in __pwm_samsung_config()
327 /* Check to see if we are changing the clock rate of the PWM. */ in __pwm_samsung_config()
328 if (chan->period_ns != period_ns || force_period) { in __pwm_samsung_config()
334 dev_dbg(our_chip->chip.dev, "duty_ns=%d, period_ns=%d (%u)\n", in __pwm_samsung_config()
337 tin_rate = pwm_samsung_calc_tin(our_chip, pwm->hwpwm, period); in __pwm_samsung_config()
339 dev_dbg(our_chip->chip.dev, "tin_rate=%lu\n", tin_rate); in __pwm_samsung_config()
347 return -ERANGE; in __pwm_samsung_config()
356 tcmp = tcnt - tcmp; in __pwm_samsung_config()
359 --tcnt; in __pwm_samsung_config()
360 /* -1UL will give 100% duty. */ in __pwm_samsung_config()
361 --tcmp; in __pwm_samsung_config()
363 dev_dbg(our_chip->chip.dev, in __pwm_samsung_config()
366 /* Update PWM registers. */ in __pwm_samsung_config()
367 writel(tcnt, our_chip->base + REG_TCNTB(pwm->hwpwm)); in __pwm_samsung_config()
368 writel(tcmp, our_chip->base + REG_TCMPB(pwm->hwpwm)); in __pwm_samsung_config()
371 * In case the PWM is currently at 100% duty cycle, force a manual in __pwm_samsung_config()
372 * update to prevent the signal staying high if the PWM is disabled in __pwm_samsung_config()
375 if (oldtcmp == (u32) -1) { in __pwm_samsung_config()
376 dev_dbg(our_chip->chip.dev, "Forcing manual update"); in __pwm_samsung_config()
377 pwm_samsung_manual_update(our_chip, pwm); in __pwm_samsung_config()
380 chan->period_ns = period_ns; in __pwm_samsung_config()
381 chan->tin_ns = tin_ns; in __pwm_samsung_config()
382 chan->duty_ns = duty_ns; in __pwm_samsung_config()
387 static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, in pwm_samsung_config() argument
390 return __pwm_samsung_config(chip, pwm, duty_ns, period_ns, false); in pwm_samsung_config()
402 tcon = readl(chip->base + REG_TCON); in pwm_samsung_set_invert()
405 chip->inverter_mask |= BIT(channel); in pwm_samsung_set_invert()
408 chip->inverter_mask &= ~BIT(channel); in pwm_samsung_set_invert()
412 writel(tcon, chip->base + REG_TCON); in pwm_samsung_set_invert()
418 struct pwm_device *pwm, in pwm_samsung_set_polarity() argument
425 pwm_samsung_set_invert(our_chip, pwm->hwpwm, invert); in pwm_samsung_set_polarity()
470 { .compatible = "samsung,s3c2410-pwm", .data = &s3c24xx_variant },
471 { .compatible = "samsung,s3c6400-pwm", .data = &s3c64xx_variant },
472 { .compatible = "samsung,s5p6440-pwm", .data = &s5p64x0_variant },
473 { .compatible = "samsung,s5pc100-pwm", .data = &s5pc100_variant },
474 { .compatible = "samsung,exynos4210-pwm", .data = &s5p64x0_variant },
481 struct device_node *np = chip->chip.dev->of_node; in pwm_samsung_parse_dt()
489 return -ENODEV; in pwm_samsung_parse_dt()
491 memcpy(&chip->variant, match->data, sizeof(chip->variant)); in pwm_samsung_parse_dt()
493 of_property_for_each_u32(np, "samsung,pwm-outputs", prop, cur, val) { in pwm_samsung_parse_dt()
495 dev_err(chip->chip.dev, in pwm_samsung_parse_dt()
496 "%s: invalid channel index in samsung,pwm-outputs property\n", in pwm_samsung_parse_dt()
500 chip->variant.output_mask |= BIT(val); in pwm_samsung_parse_dt()
508 return -ENODEV; in pwm_samsung_parse_dt()
514 struct device *dev = &pdev->dev; in pwm_samsung_probe()
520 chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); in pwm_samsung_probe()
522 return -ENOMEM; in pwm_samsung_probe()
524 chip->chip.dev = &pdev->dev; in pwm_samsung_probe()
525 chip->chip.ops = &pwm_samsung_ops; in pwm_samsung_probe()
526 chip->chip.base = -1; in pwm_samsung_probe()
527 chip->chip.npwm = SAMSUNG_PWM_NUM; in pwm_samsung_probe()
528 chip->inverter_mask = BIT(SAMSUNG_PWM_NUM) - 1; in pwm_samsung_probe()
530 if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { in pwm_samsung_probe()
535 chip->chip.of_xlate = of_pwm_xlate_with_flags; in pwm_samsung_probe()
536 chip->chip.of_pwm_n_cells = 3; in pwm_samsung_probe()
538 if (!pdev->dev.platform_data) { in pwm_samsung_probe()
539 dev_err(&pdev->dev, "no platform data specified\n"); in pwm_samsung_probe()
540 return -EINVAL; in pwm_samsung_probe()
543 memcpy(&chip->variant, pdev->dev.platform_data, in pwm_samsung_probe()
544 sizeof(chip->variant)); in pwm_samsung_probe()
548 chip->base = devm_ioremap_resource(&pdev->dev, res); in pwm_samsung_probe()
549 if (IS_ERR(chip->base)) in pwm_samsung_probe()
550 return PTR_ERR(chip->base); in pwm_samsung_probe()
552 chip->base_clk = devm_clk_get(&pdev->dev, "timers"); in pwm_samsung_probe()
553 if (IS_ERR(chip->base_clk)) { in pwm_samsung_probe()
555 return PTR_ERR(chip->base_clk); in pwm_samsung_probe()
558 ret = clk_prepare_enable(chip->base_clk); in pwm_samsung_probe()
565 if (chip->variant.output_mask & BIT(chan)) in pwm_samsung_probe()
569 chip->tclk0 = devm_clk_get(&pdev->dev, "pwm-tclk0"); in pwm_samsung_probe()
570 chip->tclk1 = devm_clk_get(&pdev->dev, "pwm-tclk1"); in pwm_samsung_probe()
574 ret = pwmchip_add(&chip->chip); in pwm_samsung_probe()
576 dev_err(dev, "failed to register PWM chip\n"); in pwm_samsung_probe()
577 clk_disable_unprepare(chip->base_clk); in pwm_samsung_probe()
582 clk_get_rate(chip->base_clk), in pwm_samsung_probe()
583 !IS_ERR(chip->tclk0) ? clk_get_rate(chip->tclk0) : 0, in pwm_samsung_probe()
584 !IS_ERR(chip->tclk1) ? clk_get_rate(chip->tclk1) : 0); in pwm_samsung_probe()
594 ret = pwmchip_remove(&chip->chip); in pwm_samsung_remove()
598 clk_disable_unprepare(chip->base_clk); in pwm_samsung_remove()
607 struct pwm_chip *chip = &our_chip->chip; in pwm_samsung_resume()
611 struct pwm_device *pwm = &chip->pwms[i]; in pwm_samsung_resume() local
612 struct samsung_pwm_channel *chan = pwm_get_chip_data(pwm); in pwm_samsung_resume()
617 if (our_chip->variant.output_mask & BIT(i)) in pwm_samsung_resume()
619 our_chip->inverter_mask & BIT(i)); in pwm_samsung_resume()
621 if (chan->period_ns) { in pwm_samsung_resume()
622 __pwm_samsung_config(chip, pwm, chan->duty_ns, in pwm_samsung_resume()
623 chan->period_ns, true); in pwm_samsung_resume()
624 /* needed to make PWM disable work on Odroid-XU3 */ in pwm_samsung_resume()
625 pwm_samsung_manual_update(our_chip, pwm); in pwm_samsung_resume()
628 if (our_chip->disabled_mask & BIT(i)) in pwm_samsung_resume()
629 pwm_samsung_disable(chip, pwm); in pwm_samsung_resume()
631 pwm_samsung_enable(chip, pwm); in pwm_samsung_resume()
642 .name = "samsung-pwm",
653 MODULE_ALIAS("platform:samsung-pwm");