clk-pwm.c 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (C) 2014 Philipp Zabel, Pengutronix
  4. *
  5. * PWM (mis)used as clock output
  6. */
  7. #include <linux/clk-provider.h>
  8. #include <linux/kernel.h>
  9. #include <linux/module.h>
  10. #include <linux/of.h>
  11. #include <linux/platform_device.h>
  12. #include <linux/pwm.h>
  13. struct clk_pwm {
  14. struct clk_hw hw;
  15. struct pwm_device *pwm;
  16. u32 fixed_rate;
  17. };
  18. static inline struct clk_pwm *to_clk_pwm(struct clk_hw *hw)
  19. {
  20. return container_of(hw, struct clk_pwm, hw);
  21. }
  22. static int clk_pwm_prepare(struct clk_hw *hw)
  23. {
  24. struct clk_pwm *clk_pwm = to_clk_pwm(hw);
  25. return pwm_enable(clk_pwm->pwm);
  26. }
  27. static void clk_pwm_unprepare(struct clk_hw *hw)
  28. {
  29. struct clk_pwm *clk_pwm = to_clk_pwm(hw);
  30. pwm_disable(clk_pwm->pwm);
  31. }
  32. static unsigned long clk_pwm_recalc_rate(struct clk_hw *hw,
  33. unsigned long parent_rate)
  34. {
  35. struct clk_pwm *clk_pwm = to_clk_pwm(hw);
  36. return clk_pwm->fixed_rate;
  37. }
  38. static int clk_pwm_get_duty_cycle(struct clk_hw *hw, struct clk_duty *duty)
  39. {
  40. struct clk_pwm *clk_pwm = to_clk_pwm(hw);
  41. struct pwm_state state;
  42. pwm_get_state(clk_pwm->pwm, &state);
  43. duty->num = state.duty_cycle;
  44. duty->den = state.period;
  45. return 0;
  46. }
  47. static const struct clk_ops clk_pwm_ops = {
  48. .prepare = clk_pwm_prepare,
  49. .unprepare = clk_pwm_unprepare,
  50. .recalc_rate = clk_pwm_recalc_rate,
  51. .get_duty_cycle = clk_pwm_get_duty_cycle,
  52. };
  53. static int clk_pwm_probe(struct platform_device *pdev)
  54. {
  55. struct device_node *node = pdev->dev.of_node;
  56. struct clk_init_data init;
  57. struct clk_pwm *clk_pwm;
  58. struct pwm_device *pwm;
  59. struct pwm_args pargs;
  60. const char *clk_name;
  61. int ret;
  62. clk_pwm = devm_kzalloc(&pdev->dev, sizeof(*clk_pwm), GFP_KERNEL);
  63. if (!clk_pwm)
  64. return -ENOMEM;
  65. pwm = devm_pwm_get(&pdev->dev, NULL);
  66. if (IS_ERR(pwm))
  67. return PTR_ERR(pwm);
  68. pwm_get_args(pwm, &pargs);
  69. if (!pargs.period) {
  70. dev_err(&pdev->dev, "invalid PWM period\n");
  71. return -EINVAL;
  72. }
  73. if (of_property_read_u32(node, "clock-frequency", &clk_pwm->fixed_rate))
  74. clk_pwm->fixed_rate = div64_u64(NSEC_PER_SEC, pargs.period);
  75. if (!clk_pwm->fixed_rate) {
  76. dev_err(&pdev->dev, "fixed_rate cannot be zero\n");
  77. return -EINVAL;
  78. }
  79. if (pargs.period != NSEC_PER_SEC / clk_pwm->fixed_rate &&
  80. pargs.period != DIV_ROUND_UP(NSEC_PER_SEC, clk_pwm->fixed_rate)) {
  81. dev_err(&pdev->dev,
  82. "clock-frequency does not match PWM period\n");
  83. return -EINVAL;
  84. }
  85. /*
  86. * FIXME: pwm_apply_args() should be removed when switching to the
  87. * atomic PWM API.
  88. */
  89. pwm_apply_args(pwm);
  90. ret = pwm_config(pwm, (pargs.period + 1) >> 1, pargs.period);
  91. if (ret < 0)
  92. return ret;
  93. clk_name = node->name;
  94. of_property_read_string(node, "clock-output-names", &clk_name);
  95. init.name = clk_name;
  96. init.ops = &clk_pwm_ops;
  97. init.flags = 0;
  98. init.num_parents = 0;
  99. clk_pwm->pwm = pwm;
  100. clk_pwm->hw.init = &init;
  101. ret = devm_clk_hw_register(&pdev->dev, &clk_pwm->hw);
  102. if (ret)
  103. return ret;
  104. return of_clk_add_hw_provider(node, of_clk_hw_simple_get, &clk_pwm->hw);
  105. }
  106. static int clk_pwm_remove(struct platform_device *pdev)
  107. {
  108. of_clk_del_provider(pdev->dev.of_node);
  109. return 0;
  110. }
  111. static const struct of_device_id clk_pwm_dt_ids[] = {
  112. { .compatible = "pwm-clock" },
  113. { }
  114. };
  115. MODULE_DEVICE_TABLE(of, clk_pwm_dt_ids);
  116. static struct platform_driver clk_pwm_driver = {
  117. .probe = clk_pwm_probe,
  118. .remove = clk_pwm_remove,
  119. .driver = {
  120. .name = "pwm-clock",
  121. .of_match_table = of_match_ptr(clk_pwm_dt_ids),
  122. },
  123. };
  124. module_platform_driver(clk_pwm_driver);
  125. MODULE_AUTHOR("Philipp Zabel <p.zabel@pengutronix.de>");
  126. MODULE_DESCRIPTION("PWM clock driver");
  127. MODULE_LICENSE("GPL");