pwm-bfin.c 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  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. if (duty_ns < 0 || duty_ns > period_ns)
  57. return -EINVAL;
  58. val = (unsigned long long)get_sclk() * period_ns;
  59. do_div(val, NSEC_PER_SEC);
  60. period = val;
  61. val = (unsigned long long)period * duty_ns;
  62. do_div(val, period_ns);
  63. duty = period - val;
  64. if (duty >= period)
  65. duty = period - 1;
  66. set_gptimer_config(priv->pin, TIMER_MODE_PWM | TIMER_PERIOD_CNT);
  67. set_gptimer_pwidth(priv->pin, duty);
  68. set_gptimer_period(priv->pin, period);
  69. return 0;
  70. }
  71. static int bfin_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
  72. {
  73. struct bfin_pwm *priv = pwm_get_chip_data(pwm);
  74. enable_gptimer(priv->pin);
  75. return 0;
  76. }
  77. static void bfin_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
  78. {
  79. struct bfin_pwm *priv = pwm_get_chip_data(pwm);
  80. disable_gptimer(priv->pin);
  81. }
  82. static struct pwm_ops bfin_pwm_ops = {
  83. .request = bfin_pwm_request,
  84. .free = bfin_pwm_free,
  85. .config = bfin_pwm_config,
  86. .enable = bfin_pwm_enable,
  87. .disable = bfin_pwm_disable,
  88. .owner = THIS_MODULE,
  89. };
  90. static int bfin_pwm_probe(struct platform_device *pdev)
  91. {
  92. struct bfin_pwm_chip *pwm;
  93. int ret;
  94. pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
  95. if (!pwm) {
  96. dev_err(&pdev->dev, "failed to allocate memory\n");
  97. return -ENOMEM;
  98. }
  99. platform_set_drvdata(pdev, pwm);
  100. pwm->chip.dev = &pdev->dev;
  101. pwm->chip.ops = &bfin_pwm_ops;
  102. pwm->chip.base = -1;
  103. pwm->chip.npwm = 12;
  104. ret = pwmchip_add(&pwm->chip);
  105. if (ret < 0) {
  106. dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
  107. return ret;
  108. }
  109. return 0;
  110. }
  111. static int __devexit bfin_pwm_remove(struct platform_device *pdev)
  112. {
  113. struct bfin_pwm_chip *pwm = platform_get_drvdata(pdev);
  114. return pwmchip_remove(&pwm->chip);
  115. }
  116. static struct platform_driver bfin_pwm_driver = {
  117. .driver = {
  118. .name = "bfin-pwm",
  119. },
  120. .probe = bfin_pwm_probe,
  121. .remove = __devexit_p(bfin_pwm_remove),
  122. };
  123. module_platform_driver(bfin_pwm_driver);
  124. MODULE_LICENSE("GPL");