pwm-mediatek.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. /*
  2. * Mediatek Pulse Width Modulator driver
  3. *
  4. * Copyright (C) 2015 John Crispin <blogic@openwrt.org>
  5. * Copyright (C) 2017 Zhi Mao <zhi.mao@mediatek.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/err.h>
  12. #include <linux/io.h>
  13. #include <linux/ioport.h>
  14. #include <linux/kernel.h>
  15. #include <linux/module.h>
  16. #include <linux/clk.h>
  17. #include <linux/of.h>
  18. #include <linux/of_device.h>
  19. #include <linux/platform_device.h>
  20. #include <linux/pwm.h>
  21. #include <linux/slab.h>
  22. #include <linux/types.h>
  23. /* PWM registers and bits definitions */
  24. #define PWMCON 0x00
  25. #define PWMHDUR 0x04
  26. #define PWMLDUR 0x08
  27. #define PWMGDUR 0x0c
  28. #define PWMWAVENUM 0x28
  29. #define PWMDWIDTH 0x2c
  30. #define PWM45DWIDTH_FIXUP 0x30
  31. #define PWMTHRES 0x30
  32. #define PWM45THRES_FIXUP 0x34
  33. #define PWM_CLK_DIV_MAX 7
  34. enum {
  35. MTK_CLK_MAIN = 0,
  36. MTK_CLK_TOP,
  37. MTK_CLK_PWM1,
  38. MTK_CLK_PWM2,
  39. MTK_CLK_PWM3,
  40. MTK_CLK_PWM4,
  41. MTK_CLK_PWM5,
  42. MTK_CLK_PWM6,
  43. MTK_CLK_PWM7,
  44. MTK_CLK_PWM8,
  45. MTK_CLK_MAX,
  46. };
  47. static const char * const mtk_pwm_clk_name[MTK_CLK_MAX] = {
  48. "main", "top", "pwm1", "pwm2", "pwm3", "pwm4", "pwm5", "pwm6", "pwm7",
  49. "pwm8"
  50. };
  51. struct mtk_pwm_platform_data {
  52. unsigned int num_pwms;
  53. bool pwm45_fixup;
  54. bool has_clks;
  55. };
  56. /**
  57. * struct mtk_pwm_chip - struct representing PWM chip
  58. * @chip: linux PWM chip representation
  59. * @regs: base address of PWM chip
  60. * @clks: list of clocks
  61. */
  62. struct mtk_pwm_chip {
  63. struct pwm_chip chip;
  64. void __iomem *regs;
  65. struct clk *clks[MTK_CLK_MAX];
  66. const struct mtk_pwm_platform_data *soc;
  67. };
  68. static const unsigned int mtk_pwm_reg_offset[] = {
  69. 0x0010, 0x0050, 0x0090, 0x00d0, 0x0110, 0x0150, 0x0190, 0x0220
  70. };
  71. static inline struct mtk_pwm_chip *to_mtk_pwm_chip(struct pwm_chip *chip)
  72. {
  73. return container_of(chip, struct mtk_pwm_chip, chip);
  74. }
  75. static int mtk_pwm_clk_enable(struct pwm_chip *chip, struct pwm_device *pwm)
  76. {
  77. struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip);
  78. int ret;
  79. if (!pc->soc->has_clks)
  80. return 0;
  81. ret = clk_prepare_enable(pc->clks[MTK_CLK_TOP]);
  82. if (ret < 0)
  83. return ret;
  84. ret = clk_prepare_enable(pc->clks[MTK_CLK_MAIN]);
  85. if (ret < 0)
  86. goto disable_clk_top;
  87. ret = clk_prepare_enable(pc->clks[MTK_CLK_PWM1 + pwm->hwpwm]);
  88. if (ret < 0)
  89. goto disable_clk_main;
  90. return 0;
  91. disable_clk_main:
  92. clk_disable_unprepare(pc->clks[MTK_CLK_MAIN]);
  93. disable_clk_top:
  94. clk_disable_unprepare(pc->clks[MTK_CLK_TOP]);
  95. return ret;
  96. }
  97. static void mtk_pwm_clk_disable(struct pwm_chip *chip, struct pwm_device *pwm)
  98. {
  99. struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip);
  100. if (!pc->soc->has_clks)
  101. return;
  102. clk_disable_unprepare(pc->clks[MTK_CLK_PWM1 + pwm->hwpwm]);
  103. clk_disable_unprepare(pc->clks[MTK_CLK_MAIN]);
  104. clk_disable_unprepare(pc->clks[MTK_CLK_TOP]);
  105. }
  106. static inline u32 mtk_pwm_readl(struct mtk_pwm_chip *chip, unsigned int num,
  107. unsigned int offset)
  108. {
  109. return readl(chip->regs + mtk_pwm_reg_offset[num] + offset);
  110. }
  111. static inline void mtk_pwm_writel(struct mtk_pwm_chip *chip,
  112. unsigned int num, unsigned int offset,
  113. u32 value)
  114. {
  115. writel(value, chip->regs + mtk_pwm_reg_offset[num] + offset);
  116. }
  117. static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
  118. int duty_ns, int period_ns)
  119. {
  120. struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip);
  121. struct clk *clk = pc->clks[MTK_CLK_PWM1 + pwm->hwpwm];
  122. u32 clkdiv = 0, cnt_period, cnt_duty, reg_width = PWMDWIDTH,
  123. reg_thres = PWMTHRES;
  124. u64 resolution;
  125. int ret;
  126. ret = mtk_pwm_clk_enable(chip, pwm);
  127. if (ret < 0)
  128. return ret;
  129. /* Using resolution in picosecond gets accuracy higher */
  130. resolution = (u64)NSEC_PER_SEC * 1000;
  131. do_div(resolution, clk_get_rate(clk));
  132. cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, resolution);
  133. while (cnt_period > 8191) {
  134. resolution *= 2;
  135. clkdiv++;
  136. cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000,
  137. resolution);
  138. }
  139. if (clkdiv > PWM_CLK_DIV_MAX) {
  140. mtk_pwm_clk_disable(chip, pwm);
  141. dev_err(chip->dev, "period %d not supported\n", period_ns);
  142. return -EINVAL;
  143. }
  144. if (pc->soc->pwm45_fixup && pwm->hwpwm > 2) {
  145. /*
  146. * PWM[4,5] has distinct offset for PWMDWIDTH and PWMTHRES
  147. * from the other PWMs on MT7623.
  148. */
  149. reg_width = PWM45DWIDTH_FIXUP;
  150. reg_thres = PWM45THRES_FIXUP;
  151. }
  152. cnt_duty = DIV_ROUND_CLOSEST_ULL((u64)duty_ns * 1000, resolution);
  153. mtk_pwm_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv);
  154. mtk_pwm_writel(pc, pwm->hwpwm, reg_width, cnt_period);
  155. mtk_pwm_writel(pc, pwm->hwpwm, reg_thres, cnt_duty);
  156. mtk_pwm_clk_disable(chip, pwm);
  157. return 0;
  158. }
  159. static int mtk_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
  160. {
  161. struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip);
  162. u32 value;
  163. int ret;
  164. ret = mtk_pwm_clk_enable(chip, pwm);
  165. if (ret < 0)
  166. return ret;
  167. value = readl(pc->regs);
  168. value |= BIT(pwm->hwpwm);
  169. writel(value, pc->regs);
  170. return 0;
  171. }
  172. static void mtk_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
  173. {
  174. struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip);
  175. u32 value;
  176. value = readl(pc->regs);
  177. value &= ~BIT(pwm->hwpwm);
  178. writel(value, pc->regs);
  179. mtk_pwm_clk_disable(chip, pwm);
  180. }
  181. static const struct pwm_ops mtk_pwm_ops = {
  182. .config = mtk_pwm_config,
  183. .enable = mtk_pwm_enable,
  184. .disable = mtk_pwm_disable,
  185. .owner = THIS_MODULE,
  186. };
  187. static int mtk_pwm_probe(struct platform_device *pdev)
  188. {
  189. const struct mtk_pwm_platform_data *data;
  190. struct mtk_pwm_chip *pc;
  191. struct resource *res;
  192. unsigned int i;
  193. int ret;
  194. pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
  195. if (!pc)
  196. return -ENOMEM;
  197. data = of_device_get_match_data(&pdev->dev);
  198. if (data == NULL)
  199. return -EINVAL;
  200. pc->soc = data;
  201. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  202. pc->regs = devm_ioremap_resource(&pdev->dev, res);
  203. if (IS_ERR(pc->regs))
  204. return PTR_ERR(pc->regs);
  205. for (i = 0; i < data->num_pwms + 2 && pc->soc->has_clks; i++) {
  206. pc->clks[i] = devm_clk_get(&pdev->dev, mtk_pwm_clk_name[i]);
  207. if (IS_ERR(pc->clks[i])) {
  208. dev_err(&pdev->dev, "clock: %s fail: %ld\n",
  209. mtk_pwm_clk_name[i], PTR_ERR(pc->clks[i]));
  210. return PTR_ERR(pc->clks[i]);
  211. }
  212. }
  213. platform_set_drvdata(pdev, pc);
  214. pc->chip.dev = &pdev->dev;
  215. pc->chip.ops = &mtk_pwm_ops;
  216. pc->chip.base = -1;
  217. pc->chip.npwm = data->num_pwms;
  218. ret = pwmchip_add(&pc->chip);
  219. if (ret < 0) {
  220. dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
  221. return ret;
  222. }
  223. return 0;
  224. }
  225. static int mtk_pwm_remove(struct platform_device *pdev)
  226. {
  227. struct mtk_pwm_chip *pc = platform_get_drvdata(pdev);
  228. return pwmchip_remove(&pc->chip);
  229. }
  230. static const struct mtk_pwm_platform_data mt2712_pwm_data = {
  231. .num_pwms = 8,
  232. .pwm45_fixup = false,
  233. .has_clks = true,
  234. };
  235. static const struct mtk_pwm_platform_data mt7622_pwm_data = {
  236. .num_pwms = 6,
  237. .pwm45_fixup = false,
  238. .has_clks = true,
  239. };
  240. static const struct mtk_pwm_platform_data mt7623_pwm_data = {
  241. .num_pwms = 5,
  242. .pwm45_fixup = true,
  243. .has_clks = true,
  244. };
  245. static const struct mtk_pwm_platform_data mt7628_pwm_data = {
  246. .num_pwms = 4,
  247. .pwm45_fixup = true,
  248. .has_clks = false,
  249. };
  250. static const struct mtk_pwm_platform_data mt8167_pwm_data = {
  251. .num_pwms = 5,
  252. .pwm45_fixup = false,
  253. .has_clks = true,
  254. };
  255. static const struct mtk_pwm_platform_data mt8168_pwm_data = {
  256. .num_pwms = 3,
  257. .pwm45_fixup = false,
  258. .has_clks = true,
  259. };
  260. static const struct of_device_id mtk_pwm_of_match[] = {
  261. { .compatible = "mediatek,mt2712-pwm", .data = &mt2712_pwm_data },
  262. { .compatible = "mediatek,mt7622-pwm", .data = &mt7622_pwm_data },
  263. { .compatible = "mediatek,mt7623-pwm", .data = &mt7623_pwm_data },
  264. { .compatible = "mediatek,mt7628-pwm", .data = &mt7628_pwm_data },
  265. { .compatible = "mediatek,mt8167-pwm", .data = &mt8167_pwm_data },
  266. { .compatible = "mediatek,mt8168-pwm", .data = &mt8168_pwm_data },
  267. { },
  268. };
  269. MODULE_DEVICE_TABLE(of, mtk_pwm_of_match);
  270. static struct platform_driver mtk_pwm_driver = {
  271. .driver = {
  272. .name = "mtk-pwm",
  273. .of_match_table = mtk_pwm_of_match,
  274. },
  275. .probe = mtk_pwm_probe,
  276. .remove = mtk_pwm_remove,
  277. };
  278. module_platform_driver(mtk_pwm_driver);
  279. MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
  280. MODULE_LICENSE("GPL");