hal_pwm.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. /* Copyright (c) 2019-2025 Allwinner Technology Co., Ltd. ALL rights reserved.
  2. * Allwinner is a trademark of Allwinner Technology Co.,Ltd., registered in
  3. * the the People's Republic of China and other countries.
  4. * All Allwinner Technology Co.,Ltd. trademarks are used with permission.
  5. * DISCLAIMER
  6. * THIRD PARTY LICENCES MAY BE REQUIRED TO IMPLEMENT THE SOLUTION/PRODUCT.
  7. * IF YOU NEED TO INTEGRATE THIRD PARTY’S TECHNOLOGY (SONY, DTS, DOLBY, AVS OR MPEGLA, ETC.)
  8. * IN ALLWINNERS’SDK OR PRODUCTS, YOU SHALL BE SOLELY RESPONSIBLE TO OBTAIN
  9. * ALL APPROPRIATELY REQUIRED THIRD PARTY LICENCES.
  10. * ALLWINNER SHALL HAVE NO WARRANTY, INDEMNITY OR OTHER OBLIGATIONS WITH RESPECT TO MATTERS
  11. * COVERED UNDER ANY REQUIRED THIRD PARTY LICENSE.
  12. * YOU ARE SOLELY RESPONSIBLE FOR YOUR USAGE OF THIRD PARTY’S TECHNOLOGY.
  13. * THIS SOFTWARE IS PROVIDED BY ALLWINNER"AS IS" AND TO THE MAXIMUM EXTENT
  14. * PERMITTED BY LAW, ALLWINNER EXPRESSLY DISCLAIMS ALL WARRANTIES OF ANY KIND,
  15. * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION REGARDING
  16. * THE TITLE, NON-INFRINGEMENT, ACCURACY, CONDITION, COMPLETENESS, PERFORMANCE
  17. * OR MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  18. * IN NO EVENT SHALL ALLWINNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  19. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  20. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  21. * LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION)
  22. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  23. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  24. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  25. * OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. /*
  28. * ===========================================================================================
  29. *
  30. * Filename: hal_pwm.c
  31. *
  32. * Description: spi driver core hal,be used by drv_pwm.c
  33. *
  34. * Version: Melis3.0
  35. * Create: 2019-12-23
  36. * Revision: none
  37. * Compiler: GCC:version 9.2.1 20170904 (release),ARM/embedded-7-branch revision 255204
  38. *
  39. * Author: liuyus@allwinnertech.com
  40. * Organization: SWC-BPD
  41. * Last Modified: 2019-12-31 17:55
  42. *
  43. * ===========================================================================================
  44. */
  45. #include <hal_log.h>
  46. #include <stdio.h>
  47. #include <stdint.h>
  48. #include <hal_clk.h>
  49. #include <hal_reset.h>
  50. #include <sunxi_hal_common.h>
  51. #include <sunxi_hal_pwm.h>
  52. #include "sunxi/clk.h"
  53. #include <hal_cfg.h>
  54. #include <script.h>
  55. // #include <standby/standby.h>
  56. hal_pwm_t sunxi_pwm;
  57. static int pwm_init = 0;
  58. #define SET_REG_VAL(reg_val, shift, width, set_val) ((reg_val & ~((-1UL) >> (32 - width) << shift)) | (set_val << shift))
  59. #define pwm_do_div(n,base) ({ \
  60. u32 __base = (base); \
  61. u32 __rem; \
  62. __rem = ((u64)(n)) % __base; \
  63. (n) = ((u64)(n)) / __base; \
  64. if (__rem > __base / 2) \
  65. ++(n); \
  66. __rem; \
  67. })
  68. /************** config *************************/
  69. /*
  70. * pwm_set_clk_src(): pwm clock source selection
  71. *
  72. * @channel_in: pwm channel number
  73. * pwm01 pwm23 pwm45 pwm67 pwm89
  74. *
  75. * @clk_src: The clock you want to set
  76. * 0:OSC24M 1:APB1
  77. */
  78. void hal_pwm_clk_src_set(uint32_t channel_in, hal_pwm_clk_src clk_src)
  79. {
  80. unsigned long reg_addr = PWM_BASE + PWM_PCCR_BASE;
  81. uint32_t reg_val;
  82. uint32_t channel = channel_in / 2;
  83. reg_addr += 4 * channel;
  84. /*set clock source OSC24M or apb1*/
  85. reg_val = hal_readl(reg_addr);
  86. reg_val = SET_REG_VAL(reg_val, PWM_CLK_SRC_SHIFT, PWM_CLK_SRC_WIDTH, clk_src);
  87. hal_writel(reg_val, reg_addr);
  88. }
  89. /*
  90. * pwm_clk_div_m(): pwm clock divide
  91. *
  92. * @div_m: 1 2 4 8 16 32 64 128 256
  93. */
  94. void hal_pwm_clk_div_m(uint32_t channel_in, uint32_t div_m)
  95. {
  96. unsigned long reg_addr = PWM_BASE + PWM_PCCR_BASE;
  97. uint32_t reg_val;
  98. uint32_t channel = channel_in / 2;
  99. reg_addr += 4 * channel;
  100. /*set clock div_m*/
  101. reg_val = hal_readl(reg_addr);
  102. reg_val = SET_REG_VAL(reg_val, PWM_DIV_M_SHIFT, PWM_DIV_M_WIDTH, div_m);
  103. hal_writel(reg_val, reg_addr);
  104. }
  105. void hal_pwm_prescal_set(uint32_t channel_in, uint32_t prescal)
  106. {
  107. unsigned long reg_addr = PWM_BASE + PWM_PCR;
  108. uint32_t reg_val;
  109. uint32_t channel = channel_in;
  110. reg_addr += 0x20 * channel;
  111. /*set prescal*/
  112. reg_val = hal_readl(reg_addr);
  113. reg_val = SET_REG_VAL(reg_val, PWM_PRESCAL_SHIFT, PWM_PRESCAL_WIDTH, prescal);
  114. hal_writel(reg_val, reg_addr);
  115. }
  116. /* active cycles */
  117. void hal_pwm_set_active_cycles(uint32_t channel_in, uint32_t active_cycles) //64
  118. {
  119. unsigned long reg_addr = PWM_BASE + PWM_PPR ;
  120. uint32_t reg_val;
  121. uint32_t channel = channel_in;
  122. reg_addr += 0x20 * channel;
  123. /*set active*/
  124. reg_val = hal_readl(reg_addr);
  125. reg_val = SET_REG_VAL(reg_val, PWM_ACTIVE_CYCLES_SHIFT, PWM_ACTIVE_CYCLES_WIDTH, active_cycles);
  126. hal_writel(reg_val, reg_addr);
  127. }
  128. /* entire cycles */
  129. void hal_pwm_set_period_cycles(uint32_t channel_in, uint32_t period_cycles)
  130. {
  131. unsigned long reg_addr = PWM_BASE + PWM_PPR ;
  132. uint32_t reg_val;
  133. uint32_t channel = channel_in;
  134. reg_addr += 0x20 * channel;
  135. /*set clock BYPASS*/
  136. reg_val = hal_readl(reg_addr);
  137. reg_val = SET_REG_VAL(reg_val, PWM_PERIOD_SHIFT, PWM_PERIOD_WIDTH, period_cycles);
  138. hal_writel(reg_val, reg_addr);
  139. }
  140. static uint32_t get_pccr_reg_offset(uint32_t channel)
  141. {
  142. uint32_t val;
  143. switch (channel)
  144. {
  145. case 0:
  146. case 1:
  147. return PWM_PCCR01;
  148. break;
  149. case 2:
  150. case 3:
  151. return PWM_PCCR23;
  152. break;
  153. case 4:
  154. case 5:
  155. return PWM_PCCR45;
  156. break;
  157. case 6:
  158. case 7:
  159. return PWM_PCCR67;
  160. break;
  161. default :
  162. PWM_ERR("channel is error \n");
  163. return PWM_PCCR01;
  164. break;
  165. }
  166. }
  167. /************ enable **************/
  168. void hal_pwm_enable_clk_gating(uint32_t channel_in)
  169. {
  170. unsigned long reg_addr = PWM_BASE + PWM_PCGR;
  171. uint32_t reg_val;
  172. uint32_t channel = channel_in / 2;
  173. /*enable clk_gating*/
  174. reg_addr += 4 * channel;
  175. reg_val = hal_readl(reg_addr);
  176. reg_val = SET_REG_VAL(reg_val, PWM_CLK_GATING_SHIFT, PWM_CLK_GATING_WIDTH, 1);
  177. hal_writel(reg_val, reg_addr);
  178. }
  179. void hal_pwm_enable_controller(uint32_t channel_in)
  180. {
  181. unsigned long reg_addr = PWM_BASE + PWM_PER;
  182. uint32_t reg_val;
  183. reg_val = readl(reg_addr);
  184. reg_val |= 1 << channel_in;
  185. writel(reg_val, reg_addr);
  186. }
  187. /************ disable **************/
  188. void hal_pwm_disable_controller(uint32_t channel_in)
  189. {
  190. unsigned long reg_val;
  191. unsigned long reg_addr = PWM_BASE + PWM_PER;
  192. reg_val = readl(reg_addr);
  193. reg_val |= 1 << channel_in;
  194. writel(reg_val, reg_addr);
  195. }
  196. /*************** polarity *****************/
  197. void hal_pwm_porality(uint32_t channel_in, hal_pwm_polarity polarity)
  198. {
  199. uint32_t reg_val;
  200. unsigned long reg_addr = PWM_BASE + PWM_PCR;
  201. uint32_t channel = channel_in;
  202. reg_addr += 0x20 * channel;
  203. /*set polarity*/
  204. reg_val = hal_readl(reg_addr);
  205. reg_val = SET_REG_VAL(reg_val, PWM_ACT_STA_SHIFT, PWM_ACT_STA_WIDTH, polarity);
  206. hal_writel(reg_val, reg_addr);
  207. }
  208. static int hal_pwm_pinctrl_init(hal_pwm_t sunxi_pwm, int channel)
  209. {
  210. user_gpio_set_t gpio_cfg = {0};
  211. char pwm_name[16];
  212. int count, ret;
  213. sprintf(pwm_name, "pwm%d", channel);
  214. count = Hal_Cfg_GetGPIOSecKeyCount(pwm_name);
  215. if (!count)
  216. {
  217. PWM_ERR("[pwm%d] not support in sys_config\n", channel);
  218. return -1;
  219. }
  220. Hal_Cfg_GetGPIOSecData(pwm_name, &gpio_cfg, count);
  221. sunxi_pwm.pin[channel] = (gpio_cfg.port - 1) * 32 + gpio_cfg.port_num;
  222. sunxi_pwm.enable_muxsel[channel] = gpio_cfg.mul_sel;
  223. ret = hal_gpio_pinmux_set_function(sunxi_pwm.pin[channel], sunxi_pwm.enable_muxsel[channel]);
  224. if (ret)
  225. {
  226. PWM_ERR("[pwm%d] PIN%u set function failed! return %d\n", channel, sunxi_pwm.pin[channel], ret);
  227. return -1;
  228. }
  229. ret = hal_gpio_set_driving_level(sunxi_pwm.pin[channel], gpio_cfg.drv_level);
  230. if (ret)
  231. {
  232. PWM_ERR("[spi%d] PIN%u set driving level failed! return %d\n", channel, gpio_cfg.drv_level, ret);
  233. return -1;
  234. }
  235. if (gpio_cfg.pull)
  236. {
  237. return hal_gpio_set_pull(sunxi_pwm.pin[channel], gpio_cfg.pull);
  238. }
  239. return 0;
  240. }
  241. static int hal_pwm_pinctrl_exit(hal_pwm_t sunxi_pwm, uint32_t channel)
  242. {
  243. if (sunxi_pwm.pin[channel]) //sys_config
  244. {
  245. return hal_gpio_pinmux_set_function(sunxi_pwm.pin[channel], 0); //gpio_in
  246. }
  247. else
  248. {
  249. return hal_gpio_pinmux_set_function(pwm_gpio[channel].pwm_pin, 0);
  250. }
  251. }
  252. /****the function provide for pwm driverr******************************************/
  253. pwm_status_t hal_pwm_init(void)
  254. {
  255. PWM_INFO("pwm init start");
  256. if (pwm_init)
  257. {
  258. pwm_init++;
  259. return 0;
  260. }
  261. sunxi_pwm.pwm_clk_type = SUNXI_PWM_CLK_TYPE;
  262. sunxi_pwm.pwm_bus_clk_id = SUNXI_PWM_CLK_ID;
  263. sunxi_pwm.pwm_reset_type = SUNXI_PWM_RESET_TYPE;
  264. sunxi_pwm.pwm_reset_id = SUNXI_PWM_RESET_ID;
  265. if (!sunxi_pwm.pwm_reset)
  266. {
  267. sunxi_pwm.pwm_reset = hal_reset_control_get(sunxi_pwm.pwm_reset_type, sunxi_pwm.pwm_reset_id);
  268. }
  269. hal_reset_control_deassert(sunxi_pwm.pwm_reset);
  270. if (!sunxi_pwm.pwm_bus_clk)
  271. {
  272. sunxi_pwm.pwm_bus_clk = hal_clock_get(sunxi_pwm.pwm_clk_type, sunxi_pwm.pwm_bus_clk_id);
  273. PWM_INFO("pwm_bus_clk name:%s", sunxi_pwm.pwm_bus_clk->name);
  274. }
  275. if (hal_clock_enable(sunxi_pwm.pwm_bus_clk))
  276. {
  277. return -1;
  278. }
  279. #ifdef CONFIG_STANDBY
  280. register_pm_dev_notify(hal_pwm_suspend, hal_pwm_resume, NULL);
  281. #endif
  282. PWM_INFO("pwm init end ");
  283. pwm_init++;
  284. return 0;
  285. }
  286. pwm_status_t hal_pwm_deinit(void)
  287. {
  288. if (pwm_init)
  289. {
  290. pwm_init--;
  291. if (!pwm_init)
  292. {
  293. hal_reset_control_assert(sunxi_pwm.pwm_reset);
  294. hal_reset_control_put(sunxi_pwm.pwm_reset);
  295. hal_clock_disable(sunxi_pwm.pwm_bus_clk);
  296. hal_clock_put(sunxi_pwm.pwm_bus_clk);
  297. }
  298. }
  299. PWM_INFO("pwm deinit end");
  300. return 0;
  301. }
  302. pwm_status_t hal_pwm_control(int channel, struct pwm_config *config_pwm)
  303. {
  304. PWM_INFO("pwm control start");
  305. uint32_t ret;
  306. unsigned int temp;
  307. unsigned long long c = 0;
  308. unsigned long entire_cycles = 256, active_cycles = 192;
  309. unsigned int reg_offset, reg_shift, reg_width;
  310. unsigned int reg_bypass_shift /*, group_reg_offset*/;
  311. unsigned int reg_clk_src_shift, reg_clk_src_width;
  312. unsigned int reg_div_m_shift, reg_div_m_width, value;
  313. PWM_INFO("period_ns = %ld", config_pwm->period_ns);
  314. PWM_INFO("duty_ns = %ld", config_pwm->duty_ns);
  315. PWM_INFO("polarity = %d", config_pwm->polarity);
  316. PWM_INFO("channel = %d", channel);
  317. if ((config_pwm->period_ns < config_pwm->duty_ns) || (!config_pwm->period_ns))
  318. {
  319. PWM_ERR("paremeter error : period_ns can't greater than duty_ns and period_ns can't be 0");
  320. return -1;
  321. }
  322. /* pwm set port */
  323. ret = hal_pwm_pinctrl_init(sunxi_pwm, channel);
  324. if (ret)
  325. {
  326. hal_gpio_pinmux_set_function(pwm_gpio[channel].pwm_pin, pwm_gpio[channel].pwm_function);
  327. }
  328. /* pwm enable controller */
  329. hal_pwm_enable_controller(channel);
  330. /* pwm set polarity */
  331. hal_pwm_porality(channel, config_pwm->polarity);
  332. /* pwm config function */
  333. uint32_t pre_scal_id = 0, div_m = 0, prescale = 0;
  334. uint32_t pre_scal[][2] =
  335. {
  336. /*reg_val clk_pre_div*/
  337. {0, 1},
  338. {1, 2},
  339. {2, 4},
  340. {3, 8},
  341. {4, 16},
  342. {5, 32},
  343. {6, 64},
  344. {7, 128},
  345. {8, 256},
  346. };
  347. reg_clk_src_shift = PWM_CLK_SRC_SHIFT;
  348. reg_clk_src_width = PWM_CLK_SRC_WIDTH;
  349. reg_offset = get_pccr_reg_offset(channel);
  350. if (config_pwm->period_ns > 0 && config_pwm->period_ns <= 10)
  351. {
  352. /* if freq lt 100M, then direct output 100M clock,set by pass. */
  353. c = 100000000;
  354. reg_bypass_shift = channel;
  355. reg_offset = get_pccr_reg_offset(channel);
  356. temp = hal_readl(PWM_BASE + PWM_PCGR);
  357. temp = SET_BITS(reg_bypass_shift, 1, temp, 1); /* bypass set */
  358. hal_writel(temp, PWM_BASE + PWM_PCGR);
  359. /*clk_src_reg*/
  360. temp = hal_readl(PWM_BASE + reg_offset);
  361. temp = SET_BITS(reg_clk_src_shift, reg_clk_src_width, temp, 1);/*clock source*/
  362. hal_writel(temp, PWM_BASE + reg_offset);
  363. return 0;
  364. }
  365. else if (config_pwm->period_ns > 10 && config_pwm->period_ns <= 334)
  366. {
  367. /* if freq between 3M~100M, then select 100M as clock */
  368. c = 100000000;
  369. /*clk_src_reg : use APB1 clock */
  370. temp = hal_readl(PWM_BASE + reg_offset);
  371. temp = SET_BITS(reg_clk_src_shift, reg_clk_src_width, temp, 1);
  372. hal_writel(temp, PWM_BASE + reg_offset);
  373. }
  374. else if (config_pwm->period_ns > 334)
  375. {
  376. /* if freq < 3M, then select 24M clock */
  377. c = 24000000;
  378. /*clk_src_reg : use OSC24M clock */
  379. temp = hal_readl(PWM_BASE + reg_offset);
  380. temp = SET_BITS(reg_clk_src_shift, reg_clk_src_width, temp, 0);
  381. hal_writel(temp, PWM_BASE + reg_offset);
  382. }
  383. c = c * config_pwm->period_ns;
  384. pwm_do_div(c, 1000000000);
  385. entire_cycles = (unsigned long)c;
  386. for (pre_scal_id = 0; pre_scal_id < 9; pre_scal_id++)
  387. {
  388. if (entire_cycles <= 65536)
  389. {
  390. break;
  391. }
  392. for (prescale = 0; prescale < PRESCALE_MAX + 1; prescale++)
  393. {
  394. entire_cycles = ((unsigned long)c / pre_scal[pre_scal_id][1]) / (prescale + 1);
  395. if (entire_cycles <= 65536)
  396. {
  397. div_m = pre_scal[pre_scal_id][0];
  398. break;
  399. }
  400. }
  401. }
  402. c = (unsigned long long)entire_cycles * config_pwm->duty_ns;
  403. pwm_do_div(c, config_pwm->period_ns);
  404. active_cycles = c;
  405. if (entire_cycles == 0)
  406. {
  407. entire_cycles++;
  408. }
  409. /* config clk div_m */
  410. reg_div_m_shift = PWM_DIV_M_SHIFT;
  411. reg_div_m_width = PWM_DIV_M_WIDTH;
  412. temp = hal_readl(PWM_BASE + reg_offset);
  413. temp = SET_BITS(reg_div_m_shift, reg_div_m_width, temp, div_m);
  414. hal_writel(temp, PWM_BASE + reg_offset);
  415. /* config gating */
  416. reg_shift = channel;
  417. value = hal_readl(PWM_BASE + PWM_PCGR);
  418. value = SET_BITS(reg_shift, 1, value, 1);/* set gating */
  419. hal_writel(value, PWM_BASE + PWM_PCGR);
  420. /* config prescal */
  421. reg_offset = PWM_PCR + 0x20 * channel;
  422. reg_shift = PWM_PRESCAL_SHIFT;
  423. reg_width = PWM_PRESCAL_WIDTH;
  424. temp = hal_readl(PWM_BASE + reg_offset);
  425. temp = SET_BITS(reg_shift, reg_width, temp, prescale);
  426. hal_writel(temp, PWM_BASE + reg_offset);
  427. /* config active cycles */
  428. reg_offset = PWM_PPR + 0x20 * channel;
  429. reg_shift = PWM_ACT_CYCLES_SHIFT;
  430. reg_width = PWM_ACT_CYCLES_WIDTH;
  431. temp = hal_readl(PWM_BASE + reg_offset);
  432. temp = SET_BITS(reg_shift, reg_width, temp, active_cycles);
  433. hal_writel(temp, PWM_BASE + reg_offset);
  434. /* config period cycles */
  435. reg_offset = PWM_PPR + 0x20 * channel;
  436. reg_shift = PWM_PERIOD_CYCLES_SHIFT;
  437. reg_width = PWM_PERIOD_CYCLES_WIDTH;
  438. temp = hal_readl(PWM_BASE + reg_offset);
  439. temp = SET_BITS(reg_shift, reg_width, temp, (entire_cycles - 1));
  440. hal_writel(temp, PWM_BASE + reg_offset);
  441. PWM_INFO("pwm control end ");
  442. return 0;
  443. }
  444. pwm_status_t hal_pwm_resume(void)
  445. {
  446. if (hal_reset_control_assert(sunxi_pwm.pwm_reset))
  447. {
  448. return -1;
  449. }
  450. if (hal_reset_control_deassert(sunxi_pwm.pwm_reset))
  451. {
  452. return -1;
  453. }
  454. if (hal_clock_enable(sunxi_pwm.pwm_bus_clk))
  455. {
  456. return -1;
  457. }
  458. PWM_INFO("hal pwm resume");
  459. return 0;
  460. }
  461. pwm_status_t hal_pwm_suspend(void)
  462. {
  463. if (hal_reset_control_assert(sunxi_pwm.pwm_reset))
  464. {
  465. return -1;
  466. }
  467. if (hal_clock_disable(sunxi_pwm.pwm_bus_clk))
  468. {
  469. return -1;
  470. }
  471. PWM_INFO("hal pwm suspend");
  472. return 0;
  473. }