mirror of
https://gitee.com/Vancouver2017/luban-lite-t3e-pro.git
synced 2025-12-15 19:08:54 +00:00
444 lines
12 KiB
C
444 lines
12 KiB
C
/*
|
|
* Copyright (c) 2022-2024, ArtInChip Technology Co., Ltd
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Authors: zrq <ruiqi.zheng@artinchip.com>
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <math.h>
|
|
|
|
#include "aic_core.h"
|
|
#include "aic_hal_clk.h"
|
|
|
|
#include "hal_epwm.h"
|
|
|
|
/* Register definition of PWMCS Controller */
|
|
#define EPWM_BASE (PWMCS_BASE)
|
|
#define GLB_BASE_E (PWMCS_BASE + 0xF000)
|
|
|
|
#ifdef AIC_EPWM_DRV_V11
|
|
#define GLB_CLK_CTL_E 0x020
|
|
#define GLB_DLL_LDO_EN 0x080
|
|
#else
|
|
#define GLB_CLK_CTL_E 0x000
|
|
#endif
|
|
#define GLB_PWM_INT_STS 0x004
|
|
#define GLB_PWM_EN 0x014
|
|
|
|
#define EPWM_CNT_PRDV(n) ((((n) & 0xF) << 8) + 0x000)
|
|
#define EPWM_CNT_V(n) ((((n) & 0xF) << 8) + 0x008)
|
|
#define EPWM_CNT_CONF(n) ((((n) & 0xF) << 8) + 0x00C)
|
|
#define EPWM_CNT_AV(n) ((((n) & 0xF) << 8) + 0x014)
|
|
#define EPWM_CNT_BV(n) ((((n) & 0xF) << 8) + 0x018)
|
|
#define EPWMA_ACT(n) ((((n) & 0xF) << 8) + 0x020)
|
|
#define EPWMB_ACT(n) ((((n) & 0xF) << 8) + 0x024)
|
|
#define EPWM_SW_ACT(n) ((((n) & 0xF) << 8) + 0x028)
|
|
#define EPWM_ACT_SW_CT(n) ((((n) & 0xF) << 8) + 0x02C)
|
|
#define EPWM_ADC_INT_CTL(n) ((((n) & 0xF) << 8) + 0x058)
|
|
#define EPWM_ADC_INT_PRE(n) ((((n) & 0xF) << 8) + 0x05C)
|
|
#define EPWM_EVNT_FLAG(n) ((((n) & 0xF) << 8) + 0x060)
|
|
#define EPWM_EVENT_CLR(n) ((((n) & 0xF) << 8) + 0x064)
|
|
#define EPWM_HRPWM_EN(n) ((((n) & 0xF) << 8) + 0x080)
|
|
#define EPWM_HRPWM_CFG(n) ((((n) & 0xF) << 8) + 0x084)
|
|
#define EPWM_VERSION(n) ((((n) & 0xF) << 8) + 0x0FC)
|
|
|
|
#define GLB_EPWM_EN_B BIT(0)
|
|
#define EPWM_S0_CLK_EN BIT(0)
|
|
#define EPMW_SX_CLK_EN(n) (EPWM_S0_CLK_EN << (n))
|
|
#define EPWMA_ACT_CNTDBV_SHIFT 10
|
|
#define EPWMA_ACT_CNTUBV_SHIFT 8
|
|
#define EPWMA_ACT_CNTDAV_SHIFT 6
|
|
#define EPWMA_ACT_CNTUAV_SHIFT 4
|
|
#define EPWMA_ACT_CNTPRD_SHIFT 2
|
|
#define EPWM_TBPRD_MAX 0xFFFF
|
|
#define EPWM_A_INIT BIT(16)
|
|
#define EPWM_B_INIT BIT(18)
|
|
#define EPWM_CLK_DIV1_MAX 0x7
|
|
#define EPWM_CLK_DIV2_SHIFT 10
|
|
#define EPWM_CLK_DIV1_SHIFT 7
|
|
#define EPWM_CLK_DIV2_MASK GENMASK(12, 10)
|
|
#define EPWM_CLK_DIV1_MASK GENMASK(9, 7)
|
|
#define EPWM_CNT_MOD_MASK GENMASK(1, 0)
|
|
#define EPWM_CNT_MOD_SHIFT 0
|
|
#define EPWM_INT_EN_SHITF 3
|
|
#define EPWM_INT_SEL_SHIFT 0
|
|
#define EPWM_INT_CLR BIT(0)
|
|
#define EPWM_ACT_SW_CT_UPDT 6
|
|
#define EPWM_SWACT_UPDT 3 << EPWM_ACT_SW_CT_UPDT
|
|
#define EPWM_ACT_SW_NONE 0x0
|
|
#define EPWM_ACT_SW_HIGH 0xA
|
|
#define EPWM_ACT_SW_LOW 0x5
|
|
#define HRPWM_OUTPUT_MODE_NO_ACT 0xF00
|
|
#define EPWM_CNT_PH_EN BIT(2)
|
|
#define EPWM_SW_FRC_SYNC BIT(6)
|
|
|
|
#ifndef NSEC_PER_SEC
|
|
#define NSEC_PER_SEC 1000000000
|
|
#endif
|
|
|
|
static struct aic_epwm_arg g_epwm_args[AIC_EPWM_CH_NUM] = {{0}};
|
|
|
|
static inline void epwm_writel(u32 val, int reg)
|
|
{
|
|
writel(val, EPWM_BASE + reg);
|
|
}
|
|
|
|
static inline u32 epwm_readl(int reg)
|
|
{
|
|
return readl(EPWM_BASE + reg);
|
|
}
|
|
|
|
static inline void epwm_writel_bits(u32 val, u32 mask, u32 shift, int reg)
|
|
{
|
|
writel_bits(val, mask, shift, EPWM_BASE + reg);
|
|
}
|
|
|
|
static void aic_epwm_ch_info(char *buf, u32 ch, u32 en, struct aic_epwm_arg *arg)
|
|
{
|
|
static const char *mode[] = {"Up", "Down", "UpDw"};
|
|
static const char *act[] = {"-", "Low", "Hgh", "Inv"};
|
|
|
|
snprintf(buf, 128, "%2d %2d %4s %11d %3d %3s %3s %3s %3s %3s %3s\n"
|
|
"%30s %3s %3s %3s %3s %3s\n",
|
|
ch, en & EPMW_SX_CLK_EN(ch) ? 1 : 0,
|
|
mode[arg->mode], arg->tb_clk_rate, arg->def_level,
|
|
act[arg->action0.CBD], act[arg->action0.CBU],
|
|
act[arg->action0.CAD], act[arg->action0.CAU],
|
|
act[arg->action0.PRD], act[arg->action0.ZRO],
|
|
act[arg->action1.CBD], act[arg->action1.CBU],
|
|
act[arg->action1.CAD], act[arg->action1.CAU],
|
|
act[arg->action1.PRD], act[arg->action1.ZRO]);
|
|
}
|
|
|
|
void hal_epwm_status_show(void)
|
|
{
|
|
int enable = readl(GLB_BASE_E + GLB_CLK_CTL_E);
|
|
char info[AIC_EPWM_CH_NUM][128] = {{0}};
|
|
u32 i;
|
|
|
|
printf("Ch En Mode Tb-clk-rate Def CBD CBU CAD CAU PRD ZRO\n");
|
|
|
|
for (i = 0; i < AIC_EPWM_CH_NUM; i++) {
|
|
aic_epwm_ch_info(info[i], i, enable, &g_epwm_args[i]);
|
|
printf("%s", info[i]);
|
|
}
|
|
}
|
|
|
|
static void epwm_reg_enable(int addr, int bit, int enable)
|
|
{
|
|
int tmp;
|
|
|
|
tmp = readl((ulong)addr);
|
|
if (enable)
|
|
tmp |= bit;
|
|
else
|
|
tmp &= ~bit;
|
|
|
|
writel(tmp, (ulong)addr);
|
|
}
|
|
|
|
u32 hal_epwm_int_sts(u32 ch)
|
|
{
|
|
return epwm_readl(EPWM_EVNT_FLAG(ch));
|
|
}
|
|
|
|
void hal_epwm_clr_int(u32 stat, u32 ch)
|
|
{
|
|
epwm_writel(stat, EPWM_EVENT_CLR(ch));
|
|
}
|
|
|
|
void hal_epwm_int_config(u32 ch, u8 irq_mode, u8 enable)
|
|
{
|
|
epwm_writel(enable, EPWM_ADC_INT_PRE(ch));
|
|
epwm_writel((enable << EPWM_INT_EN_SHITF) | ((irq_mode + 2) << EPWM_INT_SEL_SHIFT), EPWM_ADC_INT_CTL(ch));
|
|
}
|
|
|
|
void hal_epwm_ch_init(u32 ch, bool sync_mode, enum aic_epwm_mode mode, u32 default_level,
|
|
struct aic_epwm_action *a0, struct aic_epwm_action *a1)
|
|
{
|
|
struct aic_epwm_arg *arg = &g_epwm_args[ch];
|
|
|
|
arg->sync_mode = sync_mode;
|
|
arg->mode = mode;
|
|
arg->available = 1;
|
|
arg->def_level = default_level;
|
|
memcpy(&arg->action0, a0, sizeof(struct aic_epwm_action));
|
|
memcpy(&arg->action1, a1, sizeof(struct aic_epwm_action));
|
|
|
|
epwm_reg_enable(GLB_BASE_E + GLB_CLK_CTL_E, EPMW_SX_CLK_EN(ch), 1);
|
|
epwm_writel(EPWM_SWACT_UPDT, EPWM_SW_ACT(ch));
|
|
|
|
if ((ch >= 0) && (ch <= 5)) {
|
|
epwm_writel(1, EPWM_HRPWM_EN(ch));
|
|
epwm_writel(EPWM_SWACT_UPDT, EPWM_SW_ACT(ch));
|
|
epwm_writel(HRPWM_OUTPUT_MODE_NO_ACT, EPWM_HRPWM_CFG(ch));
|
|
}
|
|
}
|
|
|
|
static int hal_epwm_calculate_div(u32 ch, float tar_freq)
|
|
{
|
|
struct aic_epwm_arg *arg = &g_epwm_args[ch];
|
|
int div1 = 0, div2 = 0;
|
|
int div1_index = 0, div2_index = 0;
|
|
int available_div1[] = {1, 2, 4, 6, 8, 10, 12, 14};
|
|
int available_div2[] = {1, 2, 4, 8, 16, 32, 64, 128};
|
|
float min_error = 1e6;
|
|
|
|
for (int i = 0; i < sizeof(available_div1) / sizeof(available_div1[0]); ++i) {
|
|
for (int j = 0; j < sizeof(available_div2) / sizeof(available_div2[0]); ++j) {
|
|
int current_div = available_div1[i] * available_div2[j];
|
|
int count_value = arg->clk_rate / (current_div * tar_freq);
|
|
if (count_value < EPWM_TBPRD_MAX) {
|
|
float current_freq = arg->clk_rate / (current_div * count_value);
|
|
float error = fabs(current_freq - tar_freq);
|
|
|
|
if (error < min_error) {
|
|
min_error = error;
|
|
div1 = available_div1[i];
|
|
div2 = available_div2[j];
|
|
div1_index = i;
|
|
div2_index = j;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
hal_log_debug("div1:%d div2:%d\n", div1, div2);
|
|
|
|
if (div1 == 0 && div2 == 0) {
|
|
hal_log_err("calculate div error,should adjust the EPWM_CLK_RATE.\n");
|
|
return -1;
|
|
}
|
|
|
|
epwm_writel_bits(div1_index, EPWM_CLK_DIV1_MASK, EPWM_CLK_DIV1_SHIFT, EPWM_CNT_CONF(ch));
|
|
epwm_writel_bits(div2_index, EPWM_CLK_DIV2_MASK, EPWM_CLK_DIV2_SHIFT, EPWM_CNT_CONF(ch));
|
|
arg->tb_clk_rate = arg->clk_rate / (div1 * div2);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hal_epwm_set(u32 ch, u32 duty_ns, u32 period_ns, u32 output)
|
|
{
|
|
struct aic_epwm_arg *arg = &g_epwm_args[ch];
|
|
u32 prd = 0;
|
|
u64 duty = 0;
|
|
|
|
if (!arg->available) {
|
|
hal_log_err("ch%d is unavailable\n", ch);
|
|
return -EINVAL;
|
|
}
|
|
|
|
arg->freq = (float)NSEC_PER_SEC / period_ns;
|
|
|
|
if (hal_epwm_calculate_div(ch, arg->freq) < 0)
|
|
return -ERANGE;
|
|
|
|
prd = arg->tb_clk_rate / arg->freq;
|
|
|
|
printf("output:Freq %f Hz, prd value %d, Time-base %d Hz\n", arg->freq, prd, arg->tb_clk_rate);
|
|
|
|
if (arg->mode == EPWM_MODE_UP_DOWN_COUNT)
|
|
prd >>= 1;
|
|
else
|
|
prd--;
|
|
|
|
if (prd > EPWM_TBPRD_MAX) {
|
|
hal_log_err("ch%d period %d is too big\n", ch, prd);
|
|
return -ERANGE;
|
|
}
|
|
arg->period = prd;
|
|
epwm_writel(prd, EPWM_CNT_PRDV(ch));
|
|
|
|
duty = (u64)duty_ns * (u64)prd;
|
|
do_div(duty, period_ns);
|
|
if (duty == prd)
|
|
duty++;
|
|
|
|
arg->duty = (u32)duty;
|
|
|
|
switch (output) {
|
|
case EPWM_SET_CMPA:
|
|
epwm_writel((u32)duty, EPWM_CNT_AV(ch));
|
|
hal_log_debug("ch%d Set CMPA %u/%u\n", ch, (u32)duty, prd);
|
|
break;
|
|
case EPWM_SET_CMPB:
|
|
epwm_writel((u32)duty, EPWM_CNT_BV(ch));
|
|
hal_log_debug("ch%d Set CMPB %u/%u\n", ch, (u32)duty, prd);
|
|
break;
|
|
case EPWM_SET_CMPA_CMPB:
|
|
epwm_writel((u32)duty, EPWM_CNT_AV(ch));
|
|
epwm_writel((u32)duty, EPWM_CNT_BV(ch));
|
|
hal_log_debug("ch%d Set CMPA&B %u/%u\n", ch, (u32)duty, prd);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hal_epwm_get(u32 ch, u32 *duty_ns, u32 *period_ns)
|
|
{
|
|
struct aic_epwm_arg *arg = &g_epwm_args[ch];
|
|
|
|
if (!arg->available) {
|
|
hal_log_err("ch%d is unavailable\n", ch);
|
|
return -EINVAL;
|
|
}
|
|
|
|
*duty_ns = arg->duty;
|
|
*period_ns = arg->period;
|
|
return 0;
|
|
}
|
|
|
|
static void epwm_action_set(u32 ch, struct aic_epwm_action *act, char *name)
|
|
{
|
|
u32 offset;
|
|
u32 action = 0;
|
|
|
|
if (strcmp(name, "action0") == 0)
|
|
offset = EPWMA_ACT(ch);
|
|
else
|
|
offset = EPWMB_ACT(ch);
|
|
|
|
action |= (act->CBD << EPWMA_ACT_CNTDBV_SHIFT) |
|
|
(act->CBU << EPWMA_ACT_CNTUBV_SHIFT) |
|
|
(act->CAD << EPWMA_ACT_CNTDAV_SHIFT) |
|
|
(act->CAU << EPWMA_ACT_CNTUAV_SHIFT) |
|
|
(act->PRD << EPWMA_ACT_CNTPRD_SHIFT) | act->ZRO;
|
|
epwm_writel(action, offset);
|
|
}
|
|
|
|
int hal_epwm_enable(u32 ch)
|
|
{
|
|
u32 div1_index = 0x4, div2_index = 0x0;
|
|
struct aic_epwm_arg *arg = &g_epwm_args[ch];
|
|
|
|
if (!arg->available) {
|
|
hal_log_err("ch%d is unavailable\n", ch);
|
|
return -EINVAL;
|
|
}
|
|
|
|
epwm_writel(EPWM_ACT_SW_NONE, EPWM_ACT_SW_CT(ch));
|
|
|
|
epwm_action_set(ch, &arg->action0, "action0");
|
|
epwm_action_set(ch, &arg->action1, "action1");
|
|
|
|
hal_log_debug("ch%d tb_clk_rate: %d Hz\n", ch, arg->tb_clk_rate);
|
|
|
|
//Set the default time-base
|
|
epwm_writel_bits(div1_index, EPWM_CLK_DIV1_MASK, EPWM_CLK_DIV1_SHIFT, EPWM_CNT_CONF(ch));
|
|
epwm_writel_bits(div2_index, EPWM_CLK_DIV2_MASK, EPWM_CLK_DIV2_SHIFT, EPWM_CNT_CONF(ch));
|
|
|
|
//For sync mode
|
|
if (arg->sync_mode) {
|
|
epwm_reg_enable(EPWM_BASE + EPWM_CNT_CONF(ch), EPWM_CNT_PH_EN, 1);
|
|
epwm_reg_enable(EPWM_BASE + EPWM_CNT_CONF(ch), EPWM_SW_FRC_SYNC, 1);
|
|
}
|
|
|
|
epwm_writel_bits(arg->mode, EPWM_CNT_MOD_MASK, EPWM_CNT_MOD_SHIFT, EPWM_CNT_CONF(ch));
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef AIC_EPWM_DRV_V11
|
|
static void hal_epwm_dll_ldo_en(void)
|
|
{
|
|
writel(0x10, GLB_BASE_E + GLB_DLL_LDO_EN);
|
|
aic_udelay(100);
|
|
writel(0x11, GLB_BASE_E + GLB_DLL_LDO_EN);
|
|
aic_udelay(100);
|
|
writel(0x13, GLB_BASE_E + GLB_DLL_LDO_EN);
|
|
aic_udelay(100);
|
|
writel(0x17, GLB_BASE_E + GLB_DLL_LDO_EN);
|
|
aic_udelay(100);
|
|
writel(0x1F, GLB_BASE_E + GLB_DLL_LDO_EN);
|
|
aic_udelay(100);
|
|
}
|
|
#endif
|
|
|
|
int hal_epwm_disable(u32 ch)
|
|
{
|
|
struct aic_epwm_arg *arg = &g_epwm_args[ch];
|
|
|
|
if (!arg->available) {
|
|
hal_log_err("ch%d is unavailable\n", ch);
|
|
return -EINVAL;
|
|
}
|
|
|
|
hal_log_debug("ch%d disable\n", ch);
|
|
|
|
if (arg->def_level)
|
|
epwm_writel(EPWM_ACT_SW_HIGH, EPWM_ACT_SW_CT(ch));
|
|
else
|
|
epwm_writel(EPWM_ACT_SW_LOW, EPWM_ACT_SW_CT(ch));
|
|
|
|
epwm_writel((u32)EPWM_MODE_STOP_COUNT, EPWM_CNT_CONF(ch));
|
|
|
|
epwm_writel(0, EPWM_CNT_V(ch));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hal_epwm_init(void)
|
|
{
|
|
int i, ret = 0;
|
|
u32 clk_id = 0;
|
|
int clock_rate;
|
|
|
|
#ifdef AIC_EPWM_DRV_V11
|
|
clk_id = CLK_PWMCS_SDFM;
|
|
#else
|
|
clk_id = CLK_PWMCS;
|
|
#endif
|
|
|
|
ret = hal_clk_set_freq(clk_id, EPWM_CLK_RATE);
|
|
if (ret < 0) {
|
|
hal_log_err("Failed to set EPWM clk %d\n", EPWM_CLK_RATE);
|
|
return -1;
|
|
}
|
|
|
|
clock_rate = hal_clk_get_freq(clk_id);
|
|
if (clock_rate < 0) {
|
|
hal_log_err("Failed to get EPWM clk\n");
|
|
return -1;
|
|
}
|
|
|
|
ret = hal_clk_enable(CLK_PWMCS);
|
|
if (ret < 0) {
|
|
hal_log_err("Failed to enable EPWM clk\n");
|
|
return -1;
|
|
}
|
|
|
|
ret = hal_clk_enable_deassertrst(CLK_PWMCS);
|
|
if (ret < 0) {
|
|
hal_log_err("Failed to reset EPWM deassert\n");
|
|
return -1;
|
|
}
|
|
|
|
epwm_reg_enable(GLB_BASE_E + GLB_PWM_EN, GLB_EPWM_EN_B, 1);
|
|
|
|
#ifdef AIC_EPWM_DRV_V11
|
|
hal_epwm_dll_ldo_en();
|
|
#endif
|
|
|
|
/* default configuration */
|
|
for (i = 0; i < AIC_EPWM_CH_NUM; i++) {
|
|
g_epwm_args[i].id = i;
|
|
g_epwm_args[i].mode = EPWM_MODE_UP_COUNT;
|
|
g_epwm_args[i].tb_clk_rate = clock_rate / 8;
|
|
g_epwm_args[i].clk_rate = clock_rate;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hal_epwm_deinit(void)
|
|
{
|
|
hal_clk_disable_assertrst(CLK_PWMCS);
|
|
hal_clk_disable(CLK_PWMCS);
|
|
return 0;
|
|
}
|