pwm-clps711x.c 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Cirrus Logic CLPS711X PWM driver
  4. * Author: Alexander Shiyan <shc_work@mail.ru>
  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 void clps711x_pwm_update_val(struct clps711x_chip *priv, u32 n, u32 v)
  23. {
  24. /* PWM0 - bits 4..7, PWM1 - bits 8..11 */
  25. u32 shift = (n + 1) * 4;
  26. unsigned long flags;
  27. u32 tmp;
  28. spin_lock_irqsave(&priv->lock, flags);
  29. tmp = readl(priv->pmpcon);
  30. tmp &= ~(0xf << shift);
  31. tmp |= v << shift;
  32. writel(tmp, priv->pmpcon);
  33. spin_unlock_irqrestore(&priv->lock, flags);
  34. }
  35. static unsigned int clps711x_get_duty(struct pwm_device *pwm, unsigned int v)
  36. {
  37. /* Duty cycle 0..15 max */
  38. return DIV64_U64_ROUND_CLOSEST(v * 0xf, pwm->args.period);
  39. }
  40. static int clps711x_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
  41. {
  42. struct clps711x_chip *priv = to_clps711x_chip(chip);
  43. unsigned int freq = clk_get_rate(priv->clk);
  44. if (!freq)
  45. return -EINVAL;
  46. /* Store constant period value */
  47. pwm->args.period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, freq);
  48. return 0;
  49. }
  50. static int clps711x_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
  51. int duty_ns, int period_ns)
  52. {
  53. struct clps711x_chip *priv = to_clps711x_chip(chip);
  54. unsigned int duty;
  55. if (period_ns != pwm->args.period)
  56. return -EINVAL;
  57. duty = clps711x_get_duty(pwm, duty_ns);
  58. clps711x_pwm_update_val(priv, pwm->hwpwm, duty);
  59. return 0;
  60. }
  61. static int clps711x_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
  62. {
  63. struct clps711x_chip *priv = to_clps711x_chip(chip);
  64. unsigned int duty;
  65. duty = clps711x_get_duty(pwm, pwm_get_duty_cycle(pwm));
  66. clps711x_pwm_update_val(priv, pwm->hwpwm, duty);
  67. return 0;
  68. }
  69. static void clps711x_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
  70. {
  71. struct clps711x_chip *priv = to_clps711x_chip(chip);
  72. clps711x_pwm_update_val(priv, pwm->hwpwm, 0);
  73. }
  74. static const struct pwm_ops clps711x_pwm_ops = {
  75. .request = clps711x_pwm_request,
  76. .config = clps711x_pwm_config,
  77. .enable = clps711x_pwm_enable,
  78. .disable = clps711x_pwm_disable,
  79. .owner = THIS_MODULE,
  80. };
  81. static struct pwm_device *clps711x_pwm_xlate(struct pwm_chip *chip,
  82. const struct of_phandle_args *args)
  83. {
  84. if (args->args[0] >= chip->npwm)
  85. return ERR_PTR(-EINVAL);
  86. return pwm_request_from_chip(chip, args->args[0], NULL);
  87. }
  88. static int clps711x_pwm_probe(struct platform_device *pdev)
  89. {
  90. struct clps711x_chip *priv;
  91. struct resource *res;
  92. priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
  93. if (!priv)
  94. return -ENOMEM;
  95. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  96. priv->pmpcon = devm_ioremap_resource(&pdev->dev, res);
  97. if (IS_ERR(priv->pmpcon))
  98. return PTR_ERR(priv->pmpcon);
  99. priv->clk = devm_clk_get(&pdev->dev, NULL);
  100. if (IS_ERR(priv->clk))
  101. return PTR_ERR(priv->clk);
  102. priv->chip.ops = &clps711x_pwm_ops;
  103. priv->chip.dev = &pdev->dev;
  104. priv->chip.base = -1;
  105. priv->chip.npwm = 2;
  106. priv->chip.of_xlate = clps711x_pwm_xlate;
  107. priv->chip.of_pwm_n_cells = 1;
  108. spin_lock_init(&priv->lock);
  109. platform_set_drvdata(pdev, priv);
  110. return pwmchip_add(&priv->chip);
  111. }
  112. static int clps711x_pwm_remove(struct platform_device *pdev)
  113. {
  114. struct clps711x_chip *priv = platform_get_drvdata(pdev);
  115. return pwmchip_remove(&priv->chip);
  116. }
  117. static const struct of_device_id __maybe_unused clps711x_pwm_dt_ids[] = {
  118. { .compatible = "cirrus,ep7209-pwm", },
  119. { }
  120. };
  121. MODULE_DEVICE_TABLE(of, clps711x_pwm_dt_ids);
  122. static struct platform_driver clps711x_pwm_driver = {
  123. .driver = {
  124. .name = "clps711x-pwm",
  125. .of_match_table = of_match_ptr(clps711x_pwm_dt_ids),
  126. },
  127. .probe = clps711x_pwm_probe,
  128. .remove = clps711x_pwm_remove,
  129. };
  130. module_platform_driver(clps711x_pwm_driver);
  131. MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
  132. MODULE_DESCRIPTION("Cirrus Logic CLPS711X PWM driver");
  133. MODULE_LICENSE("GPL");