123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563 |
- /* Copyright (c) 2019-2025 Allwinner Technology Co., Ltd. ALL rights reserved.
- * Allwinner is a trademark of Allwinner Technology Co.,Ltd., registered in
- * the the People's Republic of China and other countries.
- * All Allwinner Technology Co.,Ltd. trademarks are used with permission.
- * DISCLAIMER
- * THIRD PARTY LICENCES MAY BE REQUIRED TO IMPLEMENT THE SOLUTION/PRODUCT.
- * IF YOU NEED TO INTEGRATE THIRD PARTY’S TECHNOLOGY (SONY, DTS, DOLBY, AVS OR MPEGLA, ETC.)
- * IN ALLWINNERS’SDK OR PRODUCTS, YOU SHALL BE SOLELY RESPONSIBLE TO OBTAIN
- * ALL APPROPRIATELY REQUIRED THIRD PARTY LICENCES.
- * ALLWINNER SHALL HAVE NO WARRANTY, INDEMNITY OR OTHER OBLIGATIONS WITH RESPECT TO MATTERS
- * COVERED UNDER ANY REQUIRED THIRD PARTY LICENSE.
- * YOU ARE SOLELY RESPONSIBLE FOR YOUR USAGE OF THIRD PARTY’S TECHNOLOGY.
- * THIS SOFTWARE IS PROVIDED BY ALLWINNER"AS IS" AND TO THE MAXIMUM EXTENT
- * PERMITTED BY LAW, ALLWINNER EXPRESSLY DISCLAIMS ALL WARRANTIES OF ANY KIND,
- * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION REGARDING
- * THE TITLE, NON-INFRINGEMENT, ACCURACY, CONDITION, COMPLETENESS, PERFORMANCE
- * OR MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- * IN NO EVENT SHALL ALLWINNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- /*
- * ===========================================================================================
- *
- * Filename: hal_pwm.c
- *
- * Description: spi driver core hal,be used by drv_pwm.c
- *
- * Version: Melis3.0
- * Create: 2019-12-23
- * Revision: none
- * Compiler: GCC:version 9.2.1 20170904 (release),ARM/embedded-7-branch revision 255204
- *
- * Author: liuyus@allwinnertech.com
- * Organization: SWC-BPD
- * Last Modified: 2019-12-31 17:55
- *
- * ===========================================================================================
- */
- #include <hal_log.h>
- #include <stdio.h>
- #include <stdint.h>
- #include <hal_clk.h>
- #include <hal_reset.h>
- #include <sunxi_hal_common.h>
- #include <sunxi_hal_pwm.h>
- #include "sunxi/clk.h"
- #include <hal_cfg.h>
- #include <script.h>
- // #include <standby/standby.h>
- hal_pwm_t sunxi_pwm;
- static int pwm_init = 0;
- #define SET_REG_VAL(reg_val, shift, width, set_val) ((reg_val & ~((-1UL) >> (32 - width) << shift)) | (set_val << shift))
- #define pwm_do_div(n,base) ({ \
- u32 __base = (base); \
- u32 __rem; \
- __rem = ((u64)(n)) % __base; \
- (n) = ((u64)(n)) / __base; \
- if (__rem > __base / 2) \
- ++(n); \
- __rem; \
- })
- /************** config *************************/
- /*
- * pwm_set_clk_src(): pwm clock source selection
- *
- * @channel_in: pwm channel number
- * pwm01 pwm23 pwm45 pwm67 pwm89
- *
- * @clk_src: The clock you want to set
- * 0:OSC24M 1:APB1
- */
- void hal_pwm_clk_src_set(uint32_t channel_in, hal_pwm_clk_src clk_src)
- {
- unsigned long reg_addr = PWM_BASE + PWM_PCCR_BASE;
- uint32_t reg_val;
- uint32_t channel = channel_in / 2;
- reg_addr += 4 * channel;
- /*set clock source OSC24M or apb1*/
- reg_val = hal_readl(reg_addr);
- reg_val = SET_REG_VAL(reg_val, PWM_CLK_SRC_SHIFT, PWM_CLK_SRC_WIDTH, clk_src);
- hal_writel(reg_val, reg_addr);
- }
- /*
- * pwm_clk_div_m(): pwm clock divide
- *
- * @div_m: 1 2 4 8 16 32 64 128 256
- */
- void hal_pwm_clk_div_m(uint32_t channel_in, uint32_t div_m)
- {
- unsigned long reg_addr = PWM_BASE + PWM_PCCR_BASE;
- uint32_t reg_val;
- uint32_t channel = channel_in / 2;
- reg_addr += 4 * channel;
- /*set clock div_m*/
- reg_val = hal_readl(reg_addr);
- reg_val = SET_REG_VAL(reg_val, PWM_DIV_M_SHIFT, PWM_DIV_M_WIDTH, div_m);
- hal_writel(reg_val, reg_addr);
- }
- void hal_pwm_prescal_set(uint32_t channel_in, uint32_t prescal)
- {
- unsigned long reg_addr = PWM_BASE + PWM_PCR;
- uint32_t reg_val;
- uint32_t channel = channel_in;
- reg_addr += 0x20 * channel;
- /*set prescal*/
- reg_val = hal_readl(reg_addr);
- reg_val = SET_REG_VAL(reg_val, PWM_PRESCAL_SHIFT, PWM_PRESCAL_WIDTH, prescal);
- hal_writel(reg_val, reg_addr);
- }
- /* active cycles */
- void hal_pwm_set_active_cycles(uint32_t channel_in, uint32_t active_cycles) //64
- {
- unsigned long reg_addr = PWM_BASE + PWM_PPR ;
- uint32_t reg_val;
- uint32_t channel = channel_in;
- reg_addr += 0x20 * channel;
- /*set active*/
- reg_val = hal_readl(reg_addr);
- reg_val = SET_REG_VAL(reg_val, PWM_ACTIVE_CYCLES_SHIFT, PWM_ACTIVE_CYCLES_WIDTH, active_cycles);
- hal_writel(reg_val, reg_addr);
- }
- /* entire cycles */
- void hal_pwm_set_period_cycles(uint32_t channel_in, uint32_t period_cycles)
- {
- unsigned long reg_addr = PWM_BASE + PWM_PPR ;
- uint32_t reg_val;
- uint32_t channel = channel_in;
- reg_addr += 0x20 * channel;
- /*set clock BYPASS*/
- reg_val = hal_readl(reg_addr);
- reg_val = SET_REG_VAL(reg_val, PWM_PERIOD_SHIFT, PWM_PERIOD_WIDTH, period_cycles);
- hal_writel(reg_val, reg_addr);
- }
- static uint32_t get_pccr_reg_offset(uint32_t channel)
- {
- uint32_t val;
- switch (channel)
- {
- case 0:
- case 1:
- return PWM_PCCR01;
- break;
- case 2:
- case 3:
- return PWM_PCCR23;
- break;
- case 4:
- case 5:
- return PWM_PCCR45;
- break;
- case 6:
- case 7:
- return PWM_PCCR67;
- break;
- default :
- PWM_ERR("channel is error \n");
- return PWM_PCCR01;
- break;
- }
- }
- /************ enable **************/
- void hal_pwm_enable_clk_gating(uint32_t channel_in)
- {
- unsigned long reg_addr = PWM_BASE + PWM_PCGR;
- uint32_t reg_val;
- uint32_t channel = channel_in / 2;
- /*enable clk_gating*/
- reg_addr += 4 * channel;
- reg_val = hal_readl(reg_addr);
- reg_val = SET_REG_VAL(reg_val, PWM_CLK_GATING_SHIFT, PWM_CLK_GATING_WIDTH, 1);
- hal_writel(reg_val, reg_addr);
- }
- void hal_pwm_enable_controller(uint32_t channel_in)
- {
- unsigned long reg_addr = PWM_BASE + PWM_PER;
- uint32_t reg_val;
- reg_val = readl(reg_addr);
- reg_val |= 1 << channel_in;
- writel(reg_val, reg_addr);
- }
- /************ disable **************/
- void hal_pwm_disable_controller(uint32_t channel_in)
- {
- unsigned long reg_val;
- unsigned long reg_addr = PWM_BASE + PWM_PER;
- reg_val = readl(reg_addr);
- reg_val |= 1 << channel_in;
- writel(reg_val, reg_addr);
- }
- /*************** polarity *****************/
- void hal_pwm_porality(uint32_t channel_in, hal_pwm_polarity polarity)
- {
- uint32_t reg_val;
- unsigned long reg_addr = PWM_BASE + PWM_PCR;
- uint32_t channel = channel_in;
- reg_addr += 0x20 * channel;
- /*set polarity*/
- reg_val = hal_readl(reg_addr);
- reg_val = SET_REG_VAL(reg_val, PWM_ACT_STA_SHIFT, PWM_ACT_STA_WIDTH, polarity);
- hal_writel(reg_val, reg_addr);
- }
- static int hal_pwm_pinctrl_init(hal_pwm_t sunxi_pwm, int channel)
- {
- user_gpio_set_t gpio_cfg = {0};
- char pwm_name[16];
- int count, ret;
- sprintf(pwm_name, "pwm%d", channel);
- count = Hal_Cfg_GetGPIOSecKeyCount(pwm_name);
- if (!count)
- {
- PWM_ERR("[pwm%d] not support in sys_config\n", channel);
- return -1;
- }
- Hal_Cfg_GetGPIOSecData(pwm_name, &gpio_cfg, count);
- sunxi_pwm.pin[channel] = (gpio_cfg.port - 1) * 32 + gpio_cfg.port_num;
- sunxi_pwm.enable_muxsel[channel] = gpio_cfg.mul_sel;
- ret = hal_gpio_pinmux_set_function(sunxi_pwm.pin[channel], sunxi_pwm.enable_muxsel[channel]);
- if (ret)
- {
- PWM_ERR("[pwm%d] PIN%u set function failed! return %d\n", channel, sunxi_pwm.pin[channel], ret);
- return -1;
- }
- ret = hal_gpio_set_driving_level(sunxi_pwm.pin[channel], gpio_cfg.drv_level);
- if (ret)
- {
- PWM_ERR("[spi%d] PIN%u set driving level failed! return %d\n", channel, gpio_cfg.drv_level, ret);
- return -1;
- }
- if (gpio_cfg.pull)
- {
- return hal_gpio_set_pull(sunxi_pwm.pin[channel], gpio_cfg.pull);
- }
- return 0;
- }
- static int hal_pwm_pinctrl_exit(hal_pwm_t sunxi_pwm, uint32_t channel)
- {
- if (sunxi_pwm.pin[channel]) //sys_config
- {
- return hal_gpio_pinmux_set_function(sunxi_pwm.pin[channel], 0); //gpio_in
- }
- else
- {
- return hal_gpio_pinmux_set_function(pwm_gpio[channel].pwm_pin, 0);
- }
- }
- /****the function provide for pwm driverr******************************************/
- pwm_status_t hal_pwm_init(void)
- {
- PWM_INFO("pwm init start");
- if (pwm_init)
- {
- pwm_init++;
- return 0;
- }
- sunxi_pwm.pwm_clk_type = SUNXI_PWM_CLK_TYPE;
- sunxi_pwm.pwm_bus_clk_id = SUNXI_PWM_CLK_ID;
- sunxi_pwm.pwm_reset_type = SUNXI_PWM_RESET_TYPE;
- sunxi_pwm.pwm_reset_id = SUNXI_PWM_RESET_ID;
- if (!sunxi_pwm.pwm_reset)
- {
- sunxi_pwm.pwm_reset = hal_reset_control_get(sunxi_pwm.pwm_reset_type, sunxi_pwm.pwm_reset_id);
- }
- hal_reset_control_deassert(sunxi_pwm.pwm_reset);
- if (!sunxi_pwm.pwm_bus_clk)
- {
- sunxi_pwm.pwm_bus_clk = hal_clock_get(sunxi_pwm.pwm_clk_type, sunxi_pwm.pwm_bus_clk_id);
- PWM_INFO("pwm_bus_clk name:%s", sunxi_pwm.pwm_bus_clk->name);
- }
- if (hal_clock_enable(sunxi_pwm.pwm_bus_clk))
- {
- return -1;
- }
- #ifdef CONFIG_STANDBY
- register_pm_dev_notify(hal_pwm_suspend, hal_pwm_resume, NULL);
- #endif
- PWM_INFO("pwm init end ");
- pwm_init++;
- return 0;
- }
- pwm_status_t hal_pwm_deinit(void)
- {
- if (pwm_init)
- {
- pwm_init--;
- if (!pwm_init)
- {
- hal_reset_control_assert(sunxi_pwm.pwm_reset);
- hal_reset_control_put(sunxi_pwm.pwm_reset);
- hal_clock_disable(sunxi_pwm.pwm_bus_clk);
- hal_clock_put(sunxi_pwm.pwm_bus_clk);
- }
- }
- PWM_INFO("pwm deinit end");
- return 0;
- }
- pwm_status_t hal_pwm_control(int channel, struct pwm_config *config_pwm)
- {
- PWM_INFO("pwm control start");
- uint32_t ret;
- unsigned int temp;
- unsigned long long c = 0;
- unsigned long entire_cycles = 256, active_cycles = 192;
- unsigned int reg_offset, reg_shift, reg_width;
- unsigned int reg_bypass_shift /*, group_reg_offset*/;
- unsigned int reg_clk_src_shift, reg_clk_src_width;
- unsigned int reg_div_m_shift, reg_div_m_width, value;
- PWM_INFO("period_ns = %ld", config_pwm->period_ns);
- PWM_INFO("duty_ns = %ld", config_pwm->duty_ns);
- PWM_INFO("polarity = %d", config_pwm->polarity);
- PWM_INFO("channel = %d", channel);
- if ((config_pwm->period_ns < config_pwm->duty_ns) || (!config_pwm->period_ns))
- {
- PWM_ERR("paremeter error : period_ns can't greater than duty_ns and period_ns can't be 0");
- return -1;
- }
- /* pwm set port */
- ret = hal_pwm_pinctrl_init(sunxi_pwm, channel);
- if (ret)
- {
- hal_gpio_pinmux_set_function(pwm_gpio[channel].pwm_pin, pwm_gpio[channel].pwm_function);
- }
- /* pwm enable controller */
- hal_pwm_enable_controller(channel);
- /* pwm set polarity */
- hal_pwm_porality(channel, config_pwm->polarity);
- /* pwm config function */
- uint32_t pre_scal_id = 0, div_m = 0, prescale = 0;
- uint32_t pre_scal[][2] =
- {
- /*reg_val clk_pre_div*/
- {0, 1},
- {1, 2},
- {2, 4},
- {3, 8},
- {4, 16},
- {5, 32},
- {6, 64},
- {7, 128},
- {8, 256},
- };
- reg_clk_src_shift = PWM_CLK_SRC_SHIFT;
- reg_clk_src_width = PWM_CLK_SRC_WIDTH;
- reg_offset = get_pccr_reg_offset(channel);
- if (config_pwm->period_ns > 0 && config_pwm->period_ns <= 10)
- {
- /* if freq lt 100M, then direct output 100M clock,set by pass. */
- c = 100000000;
- reg_bypass_shift = channel;
- reg_offset = get_pccr_reg_offset(channel);
- temp = hal_readl(PWM_BASE + PWM_PCGR);
- temp = SET_BITS(reg_bypass_shift, 1, temp, 1); /* bypass set */
- hal_writel(temp, PWM_BASE + PWM_PCGR);
- /*clk_src_reg*/
- temp = hal_readl(PWM_BASE + reg_offset);
- temp = SET_BITS(reg_clk_src_shift, reg_clk_src_width, temp, 1);/*clock source*/
- hal_writel(temp, PWM_BASE + reg_offset);
- return 0;
- }
- else if (config_pwm->period_ns > 10 && config_pwm->period_ns <= 334)
- {
- /* if freq between 3M~100M, then select 100M as clock */
- c = 100000000;
- /*clk_src_reg : use APB1 clock */
- temp = hal_readl(PWM_BASE + reg_offset);
- temp = SET_BITS(reg_clk_src_shift, reg_clk_src_width, temp, 1);
- hal_writel(temp, PWM_BASE + reg_offset);
- }
- else if (config_pwm->period_ns > 334)
- {
- /* if freq < 3M, then select 24M clock */
- c = 24000000;
- /*clk_src_reg : use OSC24M clock */
- temp = hal_readl(PWM_BASE + reg_offset);
- temp = SET_BITS(reg_clk_src_shift, reg_clk_src_width, temp, 0);
- hal_writel(temp, PWM_BASE + reg_offset);
- }
- c = c * config_pwm->period_ns;
- pwm_do_div(c, 1000000000);
- entire_cycles = (unsigned long)c;
- for (pre_scal_id = 0; pre_scal_id < 9; pre_scal_id++)
- {
- if (entire_cycles <= 65536)
- {
- break;
- }
- for (prescale = 0; prescale < PRESCALE_MAX + 1; prescale++)
- {
- entire_cycles = ((unsigned long)c / pre_scal[pre_scal_id][1]) / (prescale + 1);
- if (entire_cycles <= 65536)
- {
- div_m = pre_scal[pre_scal_id][0];
- break;
- }
- }
- }
- c = (unsigned long long)entire_cycles * config_pwm->duty_ns;
- pwm_do_div(c, config_pwm->period_ns);
- active_cycles = c;
- if (entire_cycles == 0)
- {
- entire_cycles++;
- }
- /* config clk div_m */
- reg_div_m_shift = PWM_DIV_M_SHIFT;
- reg_div_m_width = PWM_DIV_M_WIDTH;
- temp = hal_readl(PWM_BASE + reg_offset);
- temp = SET_BITS(reg_div_m_shift, reg_div_m_width, temp, div_m);
- hal_writel(temp, PWM_BASE + reg_offset);
- /* config gating */
- reg_shift = channel;
- value = hal_readl(PWM_BASE + PWM_PCGR);
- value = SET_BITS(reg_shift, 1, value, 1);/* set gating */
- hal_writel(value, PWM_BASE + PWM_PCGR);
- /* config prescal */
- reg_offset = PWM_PCR + 0x20 * channel;
- reg_shift = PWM_PRESCAL_SHIFT;
- reg_width = PWM_PRESCAL_WIDTH;
- temp = hal_readl(PWM_BASE + reg_offset);
- temp = SET_BITS(reg_shift, reg_width, temp, prescale);
- hal_writel(temp, PWM_BASE + reg_offset);
- /* config active cycles */
- reg_offset = PWM_PPR + 0x20 * channel;
- reg_shift = PWM_ACT_CYCLES_SHIFT;
- reg_width = PWM_ACT_CYCLES_WIDTH;
- temp = hal_readl(PWM_BASE + reg_offset);
- temp = SET_BITS(reg_shift, reg_width, temp, active_cycles);
- hal_writel(temp, PWM_BASE + reg_offset);
- /* config period cycles */
- reg_offset = PWM_PPR + 0x20 * channel;
- reg_shift = PWM_PERIOD_CYCLES_SHIFT;
- reg_width = PWM_PERIOD_CYCLES_WIDTH;
- temp = hal_readl(PWM_BASE + reg_offset);
- temp = SET_BITS(reg_shift, reg_width, temp, (entire_cycles - 1));
- hal_writel(temp, PWM_BASE + reg_offset);
- PWM_INFO("pwm control end ");
- return 0;
- }
- pwm_status_t hal_pwm_resume(void)
- {
- if (hal_reset_control_assert(sunxi_pwm.pwm_reset))
- {
- return -1;
- }
- if (hal_reset_control_deassert(sunxi_pwm.pwm_reset))
- {
- return -1;
- }
- if (hal_clock_enable(sunxi_pwm.pwm_bus_clk))
- {
- return -1;
- }
- PWM_INFO("hal pwm resume");
- return 0;
- }
- pwm_status_t hal_pwm_suspend(void)
- {
- if (hal_reset_control_assert(sunxi_pwm.pwm_reset))
- {
- return -1;
- }
- if (hal_clock_disable(sunxi_pwm.pwm_bus_clk))
- {
- return -1;
- }
- PWM_INFO("hal pwm suspend");
- return 0;
- }
|