pwm: rockchip: add dclk scale config for wave generator mode

Change-Id: I0e578ad0b8da88b694d30b96c652483ff2e0f6b5
Signed-off-by: Damon Ding <damon.ding@rock-chips.com>
This commit is contained in:
Damon Ding
2024-02-25 15:34:54 +08:00
parent d4bbfe8793
commit 1504b8ffcf
2 changed files with 41 additions and 14 deletions

View File

@@ -280,6 +280,7 @@ struct rockchip_pwm_chip {
bool biphasic_res_valid; bool biphasic_res_valid;
int channel_id; int channel_id;
int irq; int irq;
u32 scaler;
u8 main_version; u8 main_version;
u8 capture_cnt; u8 capture_cnt;
}; };
@@ -349,7 +350,9 @@ static int rockchip_pwm_get_state(struct pwm_chip *chip,
{ {
struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
u32 enable_conf = pc->data->enable_conf; u32 enable_conf = pc->data->enable_conf;
u64 clk_rate_kHz = pc->clk_rate / 1000;
u64 tmp; u64 tmp;
u32 scaler = pc->scaler ? pc->scaler * 2 : 1;
u32 val; u32 val;
u32 dclk_div = 1; u32 dclk_div = 1;
int ret; int ret;
@@ -364,12 +367,12 @@ static int rockchip_pwm_get_state(struct pwm_chip *chip,
dclk_div = pc->oneshot_en ? 2 : 1; dclk_div = pc->oneshot_en ? 2 : 1;
tmp = readl_relaxed(pc->base + pc->data->regs.period); tmp = readl_relaxed(pc->base + pc->data->regs.period);
tmp *= dclk_div * pc->data->prescaler * NSEC_PER_SEC; tmp *= dclk_div * pc->data->prescaler * scaler * USEC_PER_SEC;
state->period = DIV_ROUND_CLOSEST_ULL(tmp, pc->clk_rate); state->period = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate_kHz);
tmp = readl_relaxed(pc->base + pc->data->regs.duty); tmp = readl_relaxed(pc->base + pc->data->regs.duty);
tmp *= dclk_div * pc->data->prescaler * NSEC_PER_SEC; tmp *= dclk_div * pc->data->prescaler * scaler * USEC_PER_SEC;
state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, pc->clk_rate); state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate_kHz);
if (pc->main_version >= 4) { if (pc->main_version >= 4) {
val = readl_relaxed(pc->base + pc->data->regs.enable); val = readl_relaxed(pc->base + pc->data->regs.enable);
@@ -709,20 +712,24 @@ static void rockchip_pwm_config_v4(struct pwm_chip *chip, struct pwm_device *pwm
{ {
struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
unsigned long period, duty; unsigned long period, duty;
u64 clk_rate_kHz = pc->clk_rate / 1000;
u64 div = 0; u64 div = 0;
u64 tmp = 0;
u32 scaler = pc->scaler ? pc->scaler * 2 : 1;
u32 rpt = 0; u32 rpt = 0;
u32 offset = 0; u32 offset = 0;
tmp = (u64)pc->data->prescaler * scaler * USEC_PER_SEC;
/* /*
* Since period and duty cycle registers have a width of 32 * Since period and duty cycle registers have a width of 32
* bits, every possible input period can be obtained using the * bits, every possible input period can be obtained using the
* default prescaler value for all practical clock rate values. * default prescaler value for all practical clock rate values.
*/ */
div = (u64)pc->clk_rate * state->period; div = (u64)clk_rate_kHz * state->period;
period = DIV_ROUND_CLOSEST_ULL(div, pc->data->prescaler * NSEC_PER_SEC); period = DIV_ROUND_CLOSEST_ULL(div, tmp);
div = (u64)pc->clk_rate * state->duty_cycle; div = (u64)clk_rate_kHz * state->duty_cycle;
duty = DIV_ROUND_CLOSEST_ULL(div, pc->data->prescaler * NSEC_PER_SEC); duty = DIV_ROUND_CLOSEST_ULL(div, tmp);
writel_relaxed(period, pc->base + PERIOD); writel_relaxed(period, pc->base + PERIOD);
writel_relaxed(duty, pc->base + DUTY); writel_relaxed(duty, pc->base + DUTY);
@@ -745,8 +752,8 @@ static void rockchip_pwm_config_v4(struct pwm_chip *chip, struct pwm_device *pwm
if (state->duty_offset > 0 && if (state->duty_offset > 0 &&
state->duty_offset <= (state->period - state->duty_cycle)) { state->duty_offset <= (state->period - state->duty_cycle)) {
div = (u64)pc->clk_rate * state->duty_offset; div = (u64)clk_rate_kHz * state->duty_offset;
offset = DIV_ROUND_CLOSEST_ULL(div, pc->data->prescaler * NSEC_PER_SEC); offset = DIV_ROUND_CLOSEST_ULL(div, tmp);
} else if (state->duty_offset > (state->period - state->duty_cycle)) { } else if (state->duty_offset > (state->period - state->duty_cycle)) {
dev_err(chip->dev, "Duty_offset must be between %lld and %lld.\n", dev_err(chip->dev, "Duty_offset must be between %lld and %lld.\n",
state->duty_cycle, state->period); state->duty_cycle, state->period);
@@ -1413,7 +1420,10 @@ static int rockchip_pwm_set_wave_table_v4(struct pwm_chip *chip, struct pwm_devi
{ {
struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
u64 table_val = 0; u64 table_val = 0;
u64 clk_rate_kHz = pc->clk_rate / 1000;
u64 div = 0; u64 div = 0;
u64 tmp = 0;
u32 scaler = pc->scaler ? pc->scaler * 2 : 1;
u32 arbiter = 0; u32 arbiter = 0;
u32 val; u32 val;
u16 table_max; u16 table_max;
@@ -1444,8 +1454,9 @@ static int rockchip_pwm_set_wave_table_v4(struct pwm_chip *chip, struct pwm_devi
if (width_mode == PWM_WAVE_TABLE_16BITS_WIDTH) { if (width_mode == PWM_WAVE_TABLE_16BITS_WIDTH) {
for (i = 0; i < table_config->len; i++) { for (i = 0; i < table_config->len; i++) {
div = (u64)pc->clk_rate * table_config->table[i]; div = (u64)clk_rate_kHz * table_config->table[i];
table_val = DIV_ROUND_CLOSEST_ULL(div, pc->data->prescaler * NSEC_PER_SEC); tmp = (u64)pc->data->prescaler * scaler * USEC_PER_SEC;
table_val = DIV_ROUND_CLOSEST_ULL(div, tmp);
writel_relaxed(table_val & 0xff, writel_relaxed(table_val & 0xff,
pc->base + WAVE_MEM + (table_config->offset + i) * 2 * 4); pc->base + WAVE_MEM + (table_config->offset + i) * 2 * 4);
if (readl_poll_timeout(pc->base + WAVE_MEM_STATUS, if (readl_poll_timeout(pc->base + WAVE_MEM_STATUS,
@@ -1471,8 +1482,9 @@ static int rockchip_pwm_set_wave_table_v4(struct pwm_chip *chip, struct pwm_devi
} }
} else { } else {
for (i = 0; i < table_config->len; i++) { for (i = 0; i < table_config->len; i++) {
div = (u64)pc->clk_rate * table_config->table[i]; div = (u64)clk_rate_kHz * table_config->table[i];
table_val = DIV_ROUND_CLOSEST_ULL(div, pc->data->prescaler * NSEC_PER_SEC); tmp = (u64)pc->data->prescaler * scaler * USEC_PER_SEC;
table_val = DIV_ROUND_CLOSEST_ULL(div, tmp);
writel_relaxed(table_val, writel_relaxed(table_val,
pc->base + WAVE_MEM + (table_config->offset + i) * 4); pc->base + WAVE_MEM + (table_config->offset + i) * 4);
if (readl_poll_timeout(pc->base + WAVE_MEM_STATUS, if (readl_poll_timeout(pc->base + WAVE_MEM_STATUS,
@@ -1526,9 +1538,11 @@ static int rockchip_pwm_set_wave_v4(struct pwm_chip *chip, struct pwm_device *pw
rpt = config->rpt << FIRST_DIMENSIONAL_SHIFT; rpt = config->rpt << FIRST_DIMENSIONAL_SHIFT;
} else { } else {
pc->scaler = 0;
ctrl = WAVE_DUTY_EN(false) | WAVE_PERIOD_EN(false); ctrl = WAVE_DUTY_EN(false) | WAVE_PERIOD_EN(false);
} }
writel_relaxed(CLK_SCALE(pc->scaler), pc->base + CLK_CTRL);
writel_relaxed(ctrl, pc->base + WAVE_CTRL); writel_relaxed(ctrl, pc->base + WAVE_CTRL);
writel_relaxed(max_val, pc->base + WAVE_MAX); writel_relaxed(max_val, pc->base + WAVE_MAX);
writel_relaxed(min_val, pc->base + WAVE_MIN); writel_relaxed(min_val, pc->base + WAVE_MIN);
@@ -1562,6 +1576,18 @@ int rockchip_pwm_set_wave(struct pwm_device *pwm, struct rockchip_pwm_wave_confi
return -EINVAL; return -EINVAL;
} }
if (!config->clk_rate) {
dev_err(chip->dev, "clk rate can not be 0\n");
return -EINVAL;
}
pc->scaler = DIV_ROUND_CLOSEST_ULL(pc->clk_rate, config->clk_rate * 2);
if (pc->scaler > 256) {
dev_err(chip->dev, "Unsupported scale factor %d(max: 512) for PWM%d\n",
pc->scaler * 2, pc->channel_id);
return -EINVAL;
}
ret = clk_enable(pc->pclk); ret = clk_enable(pc->pclk);
if (ret) if (ret)
return ret; return ret;

View File

@@ -110,6 +110,7 @@ struct rockchip_pwm_wave_config {
bool enable; bool enable;
bool duty_en; bool duty_en;
bool period_en; bool period_en;
unsigned long clk_rate;
u16 rpt; u16 rpt;
u32 width_mode; u32 width_mode;
u32 update_mode; u32 update_mode;