pwm-spear.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. /*
  2. * ST Microelectronics SPEAr Pulse Width Modulator driver
  3. *
  4. * Copyright (C) 2012 ST Microelectronics
  5. * Shiraz Hashim <shiraz.linux.kernel@gmail.com>
  6. *
  7. * This file is licensed under the terms of the GNU General Public
  8. * License version 2. This program is licensed "as is" without any
  9. * warranty of any kind, whether express or implied.
  10. */
  11. #include <linux/clk.h>
  12. #include <linux/err.h>
  13. #include <linux/io.h>
  14. #include <linux/ioport.h>
  15. #include <linux/kernel.h>
  16. #include <linux/math64.h>
  17. #include <linux/module.h>
  18. #include <linux/of.h>
  19. #include <linux/platform_device.h>
  20. #include <linux/pwm.h>
  21. #include <linux/slab.h>
  22. #include <linux/types.h>
  23. #define NUM_PWM 4
  24. /* PWM registers and bits definitions */
  25. #define PWMCR 0x00 /* Control Register */
  26. #define PWMCR_PWM_ENABLE 0x1
  27. #define PWMCR_PRESCALE_SHIFT 2
  28. #define PWMCR_MIN_PRESCALE 0x00
  29. #define PWMCR_MAX_PRESCALE 0x3FFF
  30. #define PWMDCR 0x04 /* Duty Cycle Register */
  31. #define PWMDCR_MIN_DUTY 0x0001
  32. #define PWMDCR_MAX_DUTY 0xFFFF
  33. #define PWMPCR 0x08 /* Period Register */
  34. #define PWMPCR_MIN_PERIOD 0x0001
  35. #define PWMPCR_MAX_PERIOD 0xFFFF
  36. /* Following only available on 13xx SoCs */
  37. #define PWMMCR 0x3C /* Master Control Register */
  38. #define PWMMCR_PWM_ENABLE 0x1
  39. /**
  40. * struct spear_pwm_chip - struct representing pwm chip
  41. *
  42. * @mmio_base: base address of pwm chip
  43. * @clk: pointer to clk structure of pwm chip
  44. */
  45. struct spear_pwm_chip {
  46. void __iomem *mmio_base;
  47. struct clk *clk;
  48. };
  49. static inline struct spear_pwm_chip *to_spear_pwm_chip(struct pwm_chip *chip)
  50. {
  51. return pwmchip_get_drvdata(chip);
  52. }
  53. static inline u32 spear_pwm_readl(struct spear_pwm_chip *chip, unsigned int num,
  54. unsigned long offset)
  55. {
  56. return readl_relaxed(chip->mmio_base + (num << 4) + offset);
  57. }
  58. static inline void spear_pwm_writel(struct spear_pwm_chip *chip,
  59. unsigned int num, unsigned long offset,
  60. unsigned long val)
  61. {
  62. writel_relaxed(val, chip->mmio_base + (num << 4) + offset);
  63. }
  64. static int spear_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
  65. u64 duty_ns, u64 period_ns)
  66. {
  67. struct spear_pwm_chip *pc = to_spear_pwm_chip(chip);
  68. u64 val, div, clk_rate;
  69. unsigned long prescale = PWMCR_MIN_PRESCALE, pv, dc;
  70. int ret;
  71. /*
  72. * Find pv, dc and prescale to suit duty_ns and period_ns. This is done
  73. * according to formulas described below:
  74. *
  75. * period_ns = 10^9 * (PRESCALE + 1) * PV / PWM_CLK_RATE
  76. * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
  77. *
  78. * PV = (PWM_CLK_RATE * period_ns) / (10^9 * (PRESCALE + 1))
  79. * DC = (PWM_CLK_RATE * duty_ns) / (10^9 * (PRESCALE + 1))
  80. */
  81. clk_rate = clk_get_rate(pc->clk);
  82. while (1) {
  83. div = 1000000000;
  84. div *= 1 + prescale;
  85. val = clk_rate * period_ns;
  86. pv = div64_u64(val, div);
  87. val = clk_rate * duty_ns;
  88. dc = div64_u64(val, div);
  89. /* if duty_ns and period_ns are not achievable then return */
  90. if (pv < PWMPCR_MIN_PERIOD || dc < PWMDCR_MIN_DUTY)
  91. return -EINVAL;
  92. /*
  93. * if pv and dc have crossed their upper limit, then increase
  94. * prescale and recalculate pv and dc.
  95. */
  96. if (pv > PWMPCR_MAX_PERIOD || dc > PWMDCR_MAX_DUTY) {
  97. if (++prescale > PWMCR_MAX_PRESCALE)
  98. return -EINVAL;
  99. continue;
  100. }
  101. break;
  102. }
  103. /*
  104. * NOTE: the clock to PWM has to be enabled first before writing to the
  105. * registers.
  106. */
  107. ret = clk_enable(pc->clk);
  108. if (ret)
  109. return ret;
  110. spear_pwm_writel(pc, pwm->hwpwm, PWMCR,
  111. prescale << PWMCR_PRESCALE_SHIFT);
  112. spear_pwm_writel(pc, pwm->hwpwm, PWMDCR, dc);
  113. spear_pwm_writel(pc, pwm->hwpwm, PWMPCR, pv);
  114. clk_disable(pc->clk);
  115. return 0;
  116. }
  117. static int spear_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
  118. {
  119. struct spear_pwm_chip *pc = to_spear_pwm_chip(chip);
  120. int rc = 0;
  121. u32 val;
  122. rc = clk_enable(pc->clk);
  123. if (rc)
  124. return rc;
  125. val = spear_pwm_readl(pc, pwm->hwpwm, PWMCR);
  126. val |= PWMCR_PWM_ENABLE;
  127. spear_pwm_writel(pc, pwm->hwpwm, PWMCR, val);
  128. return 0;
  129. }
  130. static void spear_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
  131. {
  132. struct spear_pwm_chip *pc = to_spear_pwm_chip(chip);
  133. u32 val;
  134. val = spear_pwm_readl(pc, pwm->hwpwm, PWMCR);
  135. val &= ~PWMCR_PWM_ENABLE;
  136. spear_pwm_writel(pc, pwm->hwpwm, PWMCR, val);
  137. clk_disable(pc->clk);
  138. }
  139. static int spear_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
  140. const struct pwm_state *state)
  141. {
  142. int err;
  143. if (state->polarity != PWM_POLARITY_NORMAL)
  144. return -EINVAL;
  145. if (!state->enabled) {
  146. if (pwm->state.enabled)
  147. spear_pwm_disable(chip, pwm);
  148. return 0;
  149. }
  150. err = spear_pwm_config(chip, pwm, state->duty_cycle, state->period);
  151. if (err)
  152. return err;
  153. if (!pwm->state.enabled)
  154. return spear_pwm_enable(chip, pwm);
  155. return 0;
  156. }
  157. static const struct pwm_ops spear_pwm_ops = {
  158. .apply = spear_pwm_apply,
  159. };
  160. static int spear_pwm_probe(struct platform_device *pdev)
  161. {
  162. struct device_node *np = pdev->dev.of_node;
  163. struct pwm_chip *chip;
  164. struct spear_pwm_chip *pc;
  165. int ret;
  166. u32 val;
  167. chip = devm_pwmchip_alloc(&pdev->dev, NUM_PWM, sizeof(*pc));
  168. if (IS_ERR(chip))
  169. return PTR_ERR(chip);
  170. pc = to_spear_pwm_chip(chip);
  171. pc->mmio_base = devm_platform_ioremap_resource(pdev, 0);
  172. if (IS_ERR(pc->mmio_base))
  173. return PTR_ERR(pc->mmio_base);
  174. pc->clk = devm_clk_get_prepared(&pdev->dev, NULL);
  175. if (IS_ERR(pc->clk))
  176. return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk),
  177. "Failed to get clock\n");
  178. chip->ops = &spear_pwm_ops;
  179. if (of_device_is_compatible(np, "st,spear1340-pwm")) {
  180. ret = clk_enable(pc->clk);
  181. if (ret)
  182. return dev_err_probe(&pdev->dev, ret,
  183. "Failed to enable clk\n");
  184. /*
  185. * Following enables PWM chip, channels would still be
  186. * enabled individually through their control register
  187. */
  188. val = readl_relaxed(pc->mmio_base + PWMMCR);
  189. val |= PWMMCR_PWM_ENABLE;
  190. writel_relaxed(val, pc->mmio_base + PWMMCR);
  191. clk_disable(pc->clk);
  192. }
  193. ret = devm_pwmchip_add(&pdev->dev, chip);
  194. if (ret < 0)
  195. return dev_err_probe(&pdev->dev, ret, "pwmchip_add() failed\n");
  196. return 0;
  197. }
  198. static const struct of_device_id spear_pwm_of_match[] = {
  199. { .compatible = "st,spear320-pwm" },
  200. { .compatible = "st,spear1340-pwm" },
  201. { }
  202. };
  203. MODULE_DEVICE_TABLE(of, spear_pwm_of_match);
  204. static struct platform_driver spear_pwm_driver = {
  205. .driver = {
  206. .name = "spear-pwm",
  207. .of_match_table = spear_pwm_of_match,
  208. },
  209. .probe = spear_pwm_probe,
  210. };
  211. module_platform_driver(spear_pwm_driver);
  212. MODULE_DESCRIPTION("ST Microelectronics SPEAr Pulse Width Modulator driver");
  213. MODULE_LICENSE("GPL");
  214. MODULE_AUTHOR("Shiraz Hashim <shiraz.linux.kernel@gmail.com>");
  215. MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.com>");
  216. MODULE_ALIAS("platform:spear-pwm");