pwm-clps711x.c 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Cirrus Logic CLPS711X PWM driver
  4. * Author: Alexander Shiyan <[email protected]>
  5. */
  6. #include <linux/clk.h>
  7. #include <linux/io.h>
  8. #include <linux/module.h>
  9. #include <linux/of.h>
  10. #include <linux/platform_device.h>
  11. #include <linux/pwm.h>
  12. struct clps711x_chip {
  13. struct pwm_chip chip;
  14. void __iomem *pmpcon;
  15. struct clk *clk;
  16. spinlock_t lock;
  17. };
  18. static inline struct clps711x_chip *to_clps711x_chip(struct pwm_chip *chip)
  19. {
  20. return container_of(chip, struct clps711x_chip, chip);
  21. }
  22. static int clps711x_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
  23. {
  24. struct clps711x_chip *priv = to_clps711x_chip(chip);
  25. unsigned int freq = clk_get_rate(priv->clk);
  26. if (!freq)
  27. return -EINVAL;
  28. /* Store constant period value */
  29. pwm->args.period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, freq);
  30. return 0;
  31. }
  32. static int clps711x_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
  33. const struct pwm_state *state)
  34. {
  35. struct clps711x_chip *priv = to_clps711x_chip(chip);
  36. /* PWM0 - bits 4..7, PWM1 - bits 8..11 */
  37. u32 shift = (pwm->hwpwm + 1) * 4;
  38. unsigned long flags;
  39. u32 pmpcon, val;
  40. if (state->polarity != PWM_POLARITY_NORMAL)
  41. return -EINVAL;
  42. if (state->period != pwm->args.period)
  43. return -EINVAL;
  44. if (state->enabled)
  45. val = mul_u64_u64_div_u64(state->duty_cycle, 0xf, state->period);
  46. else
  47. val = 0;
  48. spin_lock_irqsave(&priv->lock, flags);
  49. pmpcon = readl(priv->pmpcon);
  50. pmpcon &= ~(0xf << shift);
  51. pmpcon |= val << shift;
  52. writel(pmpcon, priv->pmpcon);
  53. spin_unlock_irqrestore(&priv->lock, flags);
  54. return 0;
  55. }
  56. static const struct pwm_ops clps711x_pwm_ops = {
  57. .request = clps711x_pwm_request,
  58. .apply = clps711x_pwm_apply,
  59. .owner = THIS_MODULE,
  60. };
  61. static struct pwm_device *clps711x_pwm_xlate(struct pwm_chip *chip,
  62. const struct of_phandle_args *args)
  63. {
  64. if (args->args[0] >= chip->npwm)
  65. return ERR_PTR(-EINVAL);
  66. return pwm_request_from_chip(chip, args->args[0], NULL);
  67. }
  68. static int clps711x_pwm_probe(struct platform_device *pdev)
  69. {
  70. struct clps711x_chip *priv;
  71. priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
  72. if (!priv)
  73. return -ENOMEM;
  74. priv->pmpcon = devm_platform_ioremap_resource(pdev, 0);
  75. if (IS_ERR(priv->pmpcon))
  76. return PTR_ERR(priv->pmpcon);
  77. priv->clk = devm_clk_get(&pdev->dev, NULL);
  78. if (IS_ERR(priv->clk))
  79. return PTR_ERR(priv->clk);
  80. priv->chip.ops = &clps711x_pwm_ops;
  81. priv->chip.dev = &pdev->dev;
  82. priv->chip.npwm = 2;
  83. priv->chip.of_xlate = clps711x_pwm_xlate;
  84. priv->chip.of_pwm_n_cells = 1;
  85. spin_lock_init(&priv->lock);
  86. return devm_pwmchip_add(&pdev->dev, &priv->chip);
  87. }
  88. static const struct of_device_id __maybe_unused clps711x_pwm_dt_ids[] = {
  89. { .compatible = "cirrus,ep7209-pwm", },
  90. { }
  91. };
  92. MODULE_DEVICE_TABLE(of, clps711x_pwm_dt_ids);
  93. static struct platform_driver clps711x_pwm_driver = {
  94. .driver = {
  95. .name = "clps711x-pwm",
  96. .of_match_table = of_match_ptr(clps711x_pwm_dt_ids),
  97. },
  98. .probe = clps711x_pwm_probe,
  99. };
  100. module_platform_driver(clps711x_pwm_driver);
  101. MODULE_AUTHOR("Alexander Shiyan <[email protected]>");
  102. MODULE_DESCRIPTION("Cirrus Logic CLPS711X PWM driver");
  103. MODULE_LICENSE("GPL");