pwm-bfin.c 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. /*
  2. * Blackfin Pulse Width Modulation (PWM) core
  3. *
  4. * Copyright (c) 2011 Analog Devices Inc.
  5. *
  6. * Licensed under the GPL-2 or later.
  7. */
  8. #include <linux/module.h>
  9. #include <linux/platform_device.h>
  10. #include <linux/pwm.h>
  11. #include <linux/slab.h>
  12. #include <asm/gptimers.h>
  13. #include <asm/portmux.h>
  14. struct bfin_pwm_chip {
  15. struct pwm_chip chip;
  16. };
  17. struct bfin_pwm {
  18. unsigned short pin;
  19. };
  20. static const unsigned short pwm_to_gptimer_per[] = {
  21. P_TMR0, P_TMR1, P_TMR2, P_TMR3, P_TMR4, P_TMR5,
  22. P_TMR6, P_TMR7, P_TMR8, P_TMR9, P_TMR10, P_TMR11,
  23. };
  24. static int bfin_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
  25. {
  26. struct bfin_pwm *priv;
  27. int ret;
  28. if (pwm->hwpwm >= ARRAY_SIZE(pwm_to_gptimer_per))
  29. return -EINVAL;
  30. priv = kzalloc(sizeof(*priv), GFP_KERNEL);
  31. if (!priv)
  32. return -ENOMEM;
  33. priv->pin = pwm_to_gptimer_per[pwm->hwpwm];
  34. ret = peripheral_request(priv->pin, NULL);
  35. if (ret) {
  36. kfree(priv);
  37. return ret;
  38. }
  39. pwm_set_chip_data(pwm, priv);
  40. return 0;
  41. }
  42. static void bfin_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
  43. {
  44. struct bfin_pwm *priv = pwm_get_chip_data(pwm);
  45. if (priv) {
  46. peripheral_free(priv->pin);
  47. kfree(priv);
  48. }
  49. }
  50. static int bfin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
  51. int duty_ns, int period_ns)
  52. {
  53. struct bfin_pwm *priv = pwm_get_chip_data(pwm);
  54. unsigned long period, duty;
  55. unsigned long long val;
  56. val = (unsigned long long)get_sclk() * period_ns;
  57. do_div(val, NSEC_PER_SEC);
  58. period = val;
  59. val = (unsigned long long)period * duty_ns;
  60. do_div(val, period_ns);
  61. duty = period - val;
  62. if (duty >= period)
  63. duty = period - 1;
  64. set_gptimer_config(priv->pin, TIMER_MODE_PWM | TIMER_PERIOD_CNT);
  65. set_gptimer_pwidth(priv->pin, duty);
  66. set_gptimer_period(priv->pin, period);
  67. return 0;
  68. }
  69. static int bfin_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
  70. {
  71. struct bfin_pwm *priv = pwm_get_chip_data(pwm);
  72. enable_gptimer(priv->pin);
  73. return 0;
  74. }
  75. static void bfin_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
  76. {
  77. struct bfin_pwm *priv = pwm_get_chip_data(pwm);
  78. disable_gptimer(priv->pin);
  79. }
  80. static struct pwm_ops bfin_pwm_ops = {
  81. .request = bfin_pwm_request,
  82. .free = bfin_pwm_free,
  83. .config = bfin_pwm_config,
  84. .enable = bfin_pwm_enable,
  85. .disable = bfin_pwm_disable,
  86. .owner = THIS_MODULE,
  87. };
  88. static int bfin_pwm_probe(struct platform_device *pdev)
  89. {
  90. struct bfin_pwm_chip *pwm;
  91. int ret;
  92. pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
  93. if (!pwm) {
  94. dev_err(&pdev->dev, "failed to allocate memory\n");
  95. return -ENOMEM;
  96. }
  97. platform_set_drvdata(pdev, pwm);
  98. pwm->chip.dev = &pdev->dev;
  99. pwm->chip.ops = &bfin_pwm_ops;
  100. pwm->chip.base = -1;
  101. pwm->chip.npwm = 12;
  102. ret = pwmchip_add(&pwm->chip);
  103. if (ret < 0) {
  104. dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
  105. return ret;
  106. }
  107. return 0;
  108. }
  109. static int bfin_pwm_remove(struct platform_device *pdev)
  110. {
  111. struct bfin_pwm_chip *pwm = platform_get_drvdata(pdev);
  112. return pwmchip_remove(&pwm->chip);
  113. }
  114. static struct platform_driver bfin_pwm_driver = {
  115. .driver = {
  116. .name = "bfin-pwm",
  117. },
  118. .probe = bfin_pwm_probe,
  119. .remove = bfin_pwm_remove,
  120. };
  121. module_platform_driver(bfin_pwm_driver);
  122. MODULE_LICENSE("GPL");