pwm-clk.c 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Clock based PWM controller
  4. *
  5. * Copyright (c) 2021 Nikita Travkin <[email protected]>
  6. *
  7. * This is an "adapter" driver that allows PWM consumers to use
  8. * system clocks with duty cycle control as PWM outputs.
  9. *
  10. * Limitations:
  11. * - Due to the fact that exact behavior depends on the underlying
  12. * clock driver, various limitations are possible.
  13. * - Underlying clock may not be able to give 0% or 100% duty cycle
  14. * (constant off or on), exact behavior will depend on the clock.
  15. * - When the PWM is disabled, the clock will be disabled as well,
  16. * line state will depend on the clock.
  17. * - The clk API doesn't expose the necessary calls to implement
  18. * .get_state().
  19. */
  20. #include <linux/kernel.h>
  21. #include <linux/math64.h>
  22. #include <linux/err.h>
  23. #include <linux/module.h>
  24. #include <linux/of.h>
  25. #include <linux/platform_device.h>
  26. #include <linux/clk.h>
  27. #include <linux/pwm.h>
  28. struct pwm_clk_chip {
  29. struct pwm_chip chip;
  30. struct clk *clk;
  31. bool clk_enabled;
  32. };
  33. #define to_pwm_clk_chip(_chip) container_of(_chip, struct pwm_clk_chip, chip)
  34. static int pwm_clk_apply(struct pwm_chip *chip, struct pwm_device *pwm,
  35. const struct pwm_state *state)
  36. {
  37. struct pwm_clk_chip *pcchip = to_pwm_clk_chip(chip);
  38. int ret;
  39. u32 rate;
  40. u64 period = state->period;
  41. u64 duty_cycle = state->duty_cycle;
  42. if (!state->enabled) {
  43. if (pwm->state.enabled) {
  44. clk_disable(pcchip->clk);
  45. pcchip->clk_enabled = false;
  46. }
  47. return 0;
  48. } else if (!pwm->state.enabled) {
  49. ret = clk_enable(pcchip->clk);
  50. if (ret)
  51. return ret;
  52. pcchip->clk_enabled = true;
  53. }
  54. /*
  55. * We have to enable the clk before setting the rate and duty_cycle,
  56. * that however results in a window where the clk is on with a
  57. * (potentially) different setting. Also setting period and duty_cycle
  58. * are two separate calls, so that probably isn't atomic either.
  59. */
  60. rate = DIV64_U64_ROUND_UP(NSEC_PER_SEC, period);
  61. ret = clk_set_rate(pcchip->clk, rate);
  62. if (ret)
  63. return ret;
  64. if (state->polarity == PWM_POLARITY_INVERSED)
  65. duty_cycle = period - duty_cycle;
  66. return clk_set_duty_cycle(pcchip->clk, duty_cycle, period);
  67. }
  68. static const struct pwm_ops pwm_clk_ops = {
  69. .apply = pwm_clk_apply,
  70. .owner = THIS_MODULE,
  71. };
  72. static int pwm_clk_probe(struct platform_device *pdev)
  73. {
  74. struct pwm_clk_chip *pcchip;
  75. int ret;
  76. pcchip = devm_kzalloc(&pdev->dev, sizeof(*pcchip), GFP_KERNEL);
  77. if (!pcchip)
  78. return -ENOMEM;
  79. pcchip->clk = devm_clk_get(&pdev->dev, NULL);
  80. if (IS_ERR(pcchip->clk))
  81. return dev_err_probe(&pdev->dev, PTR_ERR(pcchip->clk),
  82. "Failed to get clock\n");
  83. pcchip->chip.dev = &pdev->dev;
  84. pcchip->chip.ops = &pwm_clk_ops;
  85. pcchip->chip.npwm = 1;
  86. ret = clk_prepare(pcchip->clk);
  87. if (ret < 0)
  88. return dev_err_probe(&pdev->dev, ret, "Failed to prepare clock\n");
  89. ret = pwmchip_add(&pcchip->chip);
  90. if (ret < 0) {
  91. clk_unprepare(pcchip->clk);
  92. return dev_err_probe(&pdev->dev, ret, "Failed to add pwm chip\n");
  93. }
  94. platform_set_drvdata(pdev, pcchip);
  95. return 0;
  96. }
  97. static int pwm_clk_remove(struct platform_device *pdev)
  98. {
  99. struct pwm_clk_chip *pcchip = platform_get_drvdata(pdev);
  100. pwmchip_remove(&pcchip->chip);
  101. if (pcchip->clk_enabled)
  102. clk_disable(pcchip->clk);
  103. clk_unprepare(pcchip->clk);
  104. return 0;
  105. }
  106. static const struct of_device_id pwm_clk_dt_ids[] = {
  107. { .compatible = "clk-pwm", },
  108. { /* sentinel */ }
  109. };
  110. MODULE_DEVICE_TABLE(of, pwm_clk_dt_ids);
  111. static struct platform_driver pwm_clk_driver = {
  112. .driver = {
  113. .name = "pwm-clk",
  114. .of_match_table = pwm_clk_dt_ids,
  115. },
  116. .probe = pwm_clk_probe,
  117. .remove = pwm_clk_remove,
  118. };
  119. module_platform_driver(pwm_clk_driver);
  120. MODULE_ALIAS("platform:pwm-clk");
  121. MODULE_AUTHOR("Nikita Travkin <[email protected]>");
  122. MODULE_DESCRIPTION("Clock based PWM driver");
  123. MODULE_LICENSE("GPL");