pwm-clps711x.c 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. /*
  2. * Cirrus Logic CLPS711X PWM driver
  3. *
  4. * Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. */
  11. #include <linux/clk.h>
  12. #include <linux/io.h>
  13. #include <linux/module.h>
  14. #include <linux/of.h>
  15. #include <linux/platform_device.h>
  16. #include <linux/pwm.h>
  17. struct clps711x_chip {
  18. struct pwm_chip chip;
  19. void __iomem *pmpcon;
  20. struct clk *clk;
  21. spinlock_t lock;
  22. };
  23. static inline struct clps711x_chip *to_clps711x_chip(struct pwm_chip *chip)
  24. {
  25. return container_of(chip, struct clps711x_chip, chip);
  26. }
  27. static void clps711x_pwm_update_val(struct clps711x_chip *priv, u32 n, u32 v)
  28. {
  29. /* PWM0 - bits 4..7, PWM1 - bits 8..11 */
  30. u32 shift = (n + 1) * 4;
  31. unsigned long flags;
  32. u32 tmp;
  33. spin_lock_irqsave(&priv->lock, flags);
  34. tmp = readl(priv->pmpcon);
  35. tmp &= ~(0xf << shift);
  36. tmp |= v << shift;
  37. writel(tmp, priv->pmpcon);
  38. spin_unlock_irqrestore(&priv->lock, flags);
  39. }
  40. static unsigned int clps711x_get_duty(struct pwm_device *pwm, unsigned int v)
  41. {
  42. /* Duty cycle 0..15 max */
  43. return DIV_ROUND_CLOSEST(v * 0xf, pwm_get_period(pwm));
  44. }
  45. static int clps711x_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
  46. {
  47. struct clps711x_chip *priv = to_clps711x_chip(chip);
  48. unsigned int freq = clk_get_rate(priv->clk);
  49. if (!freq)
  50. return -EINVAL;
  51. /* Store constant period value */
  52. pwm_set_period(pwm, DIV_ROUND_CLOSEST(NSEC_PER_SEC, freq));
  53. return 0;
  54. }
  55. static int clps711x_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
  56. int duty_ns, int period_ns)
  57. {
  58. struct clps711x_chip *priv = to_clps711x_chip(chip);
  59. unsigned int duty;
  60. if (period_ns != pwm_get_period(pwm))
  61. return -EINVAL;
  62. duty = clps711x_get_duty(pwm, duty_ns);
  63. clps711x_pwm_update_val(priv, pwm->hwpwm, duty);
  64. return 0;
  65. }
  66. static int clps711x_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
  67. {
  68. struct clps711x_chip *priv = to_clps711x_chip(chip);
  69. unsigned int duty;
  70. duty = clps711x_get_duty(pwm, pwm_get_duty_cycle(pwm));
  71. clps711x_pwm_update_val(priv, pwm->hwpwm, duty);
  72. return 0;
  73. }
  74. static void clps711x_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
  75. {
  76. struct clps711x_chip *priv = to_clps711x_chip(chip);
  77. clps711x_pwm_update_val(priv, pwm->hwpwm, 0);
  78. }
  79. static const struct pwm_ops clps711x_pwm_ops = {
  80. .request = clps711x_pwm_request,
  81. .config = clps711x_pwm_config,
  82. .enable = clps711x_pwm_enable,
  83. .disable = clps711x_pwm_disable,
  84. .owner = THIS_MODULE,
  85. };
  86. static struct pwm_device *clps711x_pwm_xlate(struct pwm_chip *chip,
  87. const struct of_phandle_args *args)
  88. {
  89. if (args->args[0] >= chip->npwm)
  90. return ERR_PTR(-EINVAL);
  91. return pwm_request_from_chip(chip, args->args[0], NULL);
  92. }
  93. static int clps711x_pwm_probe(struct platform_device *pdev)
  94. {
  95. struct clps711x_chip *priv;
  96. struct resource *res;
  97. priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
  98. if (!priv)
  99. return -ENOMEM;
  100. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  101. priv->pmpcon = devm_ioremap_resource(&pdev->dev, res);
  102. if (IS_ERR(priv->pmpcon))
  103. return PTR_ERR(priv->pmpcon);
  104. priv->clk = devm_clk_get(&pdev->dev, NULL);
  105. if (IS_ERR(priv->clk))
  106. return PTR_ERR(priv->clk);
  107. priv->chip.ops = &clps711x_pwm_ops;
  108. priv->chip.dev = &pdev->dev;
  109. priv->chip.base = -1;
  110. priv->chip.npwm = 2;
  111. priv->chip.of_xlate = clps711x_pwm_xlate;
  112. priv->chip.of_pwm_n_cells = 1;
  113. spin_lock_init(&priv->lock);
  114. platform_set_drvdata(pdev, priv);
  115. return pwmchip_add(&priv->chip);
  116. }
  117. static int clps711x_pwm_remove(struct platform_device *pdev)
  118. {
  119. struct clps711x_chip *priv = platform_get_drvdata(pdev);
  120. return pwmchip_remove(&priv->chip);
  121. }
  122. static const struct of_device_id __maybe_unused clps711x_pwm_dt_ids[] = {
  123. { .compatible = "cirrus,clps711x-pwm", },
  124. { }
  125. };
  126. MODULE_DEVICE_TABLE(of, clps711x_pwm_dt_ids);
  127. static struct platform_driver clps711x_pwm_driver = {
  128. .driver = {
  129. .name = "clps711x-pwm",
  130. .of_match_table = of_match_ptr(clps711x_pwm_dt_ids),
  131. },
  132. .probe = clps711x_pwm_probe,
  133. .remove = clps711x_pwm_remove,
  134. };
  135. module_platform_driver(clps711x_pwm_driver);
  136. MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
  137. MODULE_DESCRIPTION("Cirrus Logic CLPS711X PWM driver");
  138. MODULE_LICENSE("GPL");