diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c index 0e69bddeec9e..d759bf5d0264 100644 --- a/drivers/pwm/pwm-rockchip.c +++ b/drivers/pwm/pwm-rockchip.c @@ -84,6 +84,7 @@ #define COUNTER_SUPPORT BIT(11) #define WAVE_SUPPORT BIT(12) #define FILTER_SUPPORT BIT(13) +#define BIPHASIC_SUPPORT BIT(14) #define MINOR_VERSION_SHIFT 16 #define MINOR_VERSION_MASK (0xff << MINOR_VERSION_SHIFT) #define MAIN_VERSION_SHIFT 24 @@ -128,6 +129,20 @@ #define HPC 0x2c /* LPC */ #define LPC 0x30 +/* BIPHASIC_COUNTER_CTRL0 */ +#define BIPHASIC_CTRL0 0x40 +#define BIPHASIC_EN(v) HIWORD_UPDATE(v, 0, 0) +#define BIPHASIC_CONTINOUS_MODE_EN(v) HIWORD_UPDATE(v, 1, 1) +#define BIPHASIC_MODE(v) HIWORD_UPDATE(v, 3, 5) +#define BIPHASIC_SYNC_EN(v) HIWORD_UPDATE(v, 7, 7) +/* BIPHASIC_COUNTER_CTRL1 */ +#define BIPHASIC_CTRL1 0x44 +/* BIPHASIC_COUNTER_TIMER_VALUE */ +#define BIPHASIC_TIMER_VALUE 0x48 +/* BIPHASIC_COUNTER_RESULT_VALUE */ +#define BIPHASIC_RESULT_VALUE 0x4c +/* BIPHASIC_COUNTER_RESULT_VALUE_SYNC */ +#define BIPHASIC_RESULT_VALUE_SYNC 0x50 /* INTSTS*/ #define INTSTS 0x70 #define CAP_LPR_INTSTS_SHIFT 0 @@ -137,8 +152,9 @@ #define FREQ_INTSTS_SHIFT 4 #define PWR_INTSTS_SHIFT 5 #define IR_TRANS_END_INTSTS_SHIFT 6 -#define WAVE_MAX_INT_SHIFT 7 -#define WAVE_MIDDLE_INT_SHIFT 8 +#define WAVE_MAX_INTSTS_SHIFT 7 +#define WAVE_MIDDLE_INTSTS_SHIFT 8 +#define BIPHASIC_INISTS_SHIFT 9 #define CAP_LPR_INT BIT(CAP_LPR_INTSTS_SHIFT) #define CAP_HPR_INT BIT(CAP_HPR_INTSTS_SHIFT) #define ONESHOT_END_INT BIT(ONESHOT_END_INTSTS_SHIFT) @@ -146,8 +162,9 @@ #define FREQ_INT BIT(FREQ_INTSTS_SHIFT) #define PWR_INT BIT(PWR_INTSTS_SHIFT) #define IR_TRANS_END_INT BIT(IR_TRANS_END_INTSTS_SHIFT) -#define WAVE_MAX_INT BIT(WAVE_MAX_INT_SHIFT) -#define WAVE_MIDDLE_INT BIT(WAVE_MIDDLE_INT_SHIFT) +#define WAVE_MAX_INT BIT(WAVE_MAX_INTSTS_SHIFT) +#define WAVE_MIDDLE_INT BIT(WAVE_MIDDLE_INTSTS_SHIFT) +#define BIPHASIC_INT BIT(BIPHASIC_INISTS_SHIFT) /* INT_EN */ #define INT_EN 0x74 #define CAP_LPR_INT_EN(v) HIWORD_UPDATE(v, 0, 0) @@ -159,6 +176,7 @@ #define IR_TRANS_END_INT_EN(v) HIWORD_UPDATE(v, 6, 6) #define WAVE_MAX_INT_EN(v) HIWORD_UPDATE(v, 7, 7) #define WAVE_MIDDLE_INT_EN(v) HIWORD_UPDATE(v, 8, 8) +#define BIPHASIC_INT_EN(v) HIWORD_UPDATE(v, 9, 9) /* WAVE_MEM_ARBITER */ #define WAVE_MEM_ARBITER 0x80 #define WAVE_MEM_GRANT_SHIFT 0 @@ -242,6 +260,7 @@ struct rockchip_pwm_chip { struct pinctrl_state *active_state; struct delayed_work pwm_work; const struct rockchip_pwm_data *data; + const struct rockchip_pwm_biphasic_config *biphasic_config; struct resource *res; struct dentry *debugfs; void __iomem *base; @@ -256,7 +275,9 @@ struct rockchip_pwm_chip { bool freq_meter_support; bool counter_support; bool wave_support; + bool biphasic_support; bool freq_res_valid; + bool biphasic_res_valid; int channel_id; int irq; u8 main_version; @@ -295,6 +316,10 @@ struct rockchip_pwm_funcs { enum rockchip_pwm_wave_table_width_mode width_mode); int (*set_wave)(struct pwm_chip *chip, struct pwm_device *pwm, struct rockchip_pwm_wave_config *config); + int (*set_biphasic)(struct pwm_chip *chip, struct pwm_device *pwm, + struct rockchip_pwm_biphasic_config *config); + int (*get_biphasic_result)(struct pwm_chip *chip, struct pwm_device *pwm, + unsigned long *biphasic_res); irqreturn_t (*irq_handler)(int irq, void *data); }; @@ -654,6 +679,12 @@ static irqreturn_t rockchip_pwm_irq_v4(int irq, void *data) ret = IRQ_HANDLED; } + if (val & BIPHASIC_INT) { + writel_relaxed(BIPHASIC_INT, pc->base + INTSTS); + pc->biphasic_res_valid = true; + ret = IRQ_HANDLED; + } + if (val & WAVE_MIDDLE_INT) { writel_relaxed(WAVE_MIDDLE_INT, pc->base + INTSTS); @@ -1562,6 +1593,194 @@ err_disable_pclk: } EXPORT_SYMBOL_GPL(rockchip_pwm_set_wave); +static int rockchip_pwm_set_biphasic_v4(struct pwm_chip *chip, struct pwm_device *pwm, + struct rockchip_pwm_biphasic_config *config) +{ + struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); + u64 div = 0; + u32 ctrl = 0; + u32 timer_val = 0; + int ret = 0; + + if (config->enable) { + if (!config->is_continuous && !config->delay_ms) { + dev_err(chip->dev, "The delay_ms can not be 0 in normal mode for PWM%d\n", + pc->channel_id); + return -EINVAL; + } + + ret = clk_enable(pc->clk); + if (ret) + return ret; + pc->biphasic_res_valid = false; + + ctrl = BIPHASIC_EN(true) | + BIPHASIC_CONTINOUS_MODE_EN(config->is_continuous) | + BIPHASIC_MODE(config->mode == PWM_BIPHASIC_COUNTER_MODE0_FREQ ? + PWM_BIPHASIC_COUNTER_MODE0 : config->mode) | + BIPHASIC_SYNC_EN(config->is_continuous); + + div = (u64)pc->clk_rate * config->delay_ms; + timer_val = DIV_ROUND_CLOSEST_ULL(div, MSEC_PER_SEC); + + pc->biphasic_config = config; + } else { + ctrl = BIPHASIC_EN(false); + + pc->biphasic_config = NULL; + } + + writel_relaxed(BIPHASIC_INT_EN(config->enable), pc->base + INT_EN); + writel_relaxed(ctrl, pc->base + BIPHASIC_CTRL0); + writel_relaxed(timer_val, pc->base + BIPHASIC_TIMER_VALUE); + + if (!config->enable) + clk_disable(pc->clk); + + return 0; +} + +int rockchip_pwm_set_biphasic(struct pwm_device *pwm, struct rockchip_pwm_biphasic_config *config, + unsigned long *biphasic_res) +{ + struct pwm_chip *chip; + struct rockchip_pwm_chip *pc; + struct pwm_state curstate; + int ret = 0; + + if (!pwm) + return -EINVAL; + + chip = pwm->chip; + pc = to_rockchip_pwm_chip(chip); + + if (!pc->biphasic_support || + !pc->data->funcs.set_biphasic || !pc->data->funcs.get_biphasic_result) { + dev_err(chip->dev, "Unsupported biphasic counter mode\n"); + return -EINVAL; + } + + pwm_get_state(pwm, &curstate); + if (curstate.enabled) { + dev_err(chip->dev, "Failed to enable biphasic counter mode because PWM%d is busy\n", + pc->channel_id); + return -EBUSY; + } + + ret = clk_enable(pc->pclk); + if (ret) + return ret; + + ret = pinctrl_select_state(pc->pinctrl, pc->active_state); + if (ret) { + dev_err(chip->dev, "Failed to select pinctrl state\n"); + goto err_disable_pclk; + } + + ret = pc->data->funcs.set_biphasic(chip, pwm, config); + if (ret) { + dev_err(chip->dev, "Failed to setup biphasic counter mode for PWM%d\n", + pc->channel_id); + } else { + if (pc->biphasic_config->enable && !config->is_continuous) { + ret = pc->data->funcs.get_biphasic_result(chip, pwm, biphasic_res); + if (ret) { + dev_err(chip->dev, + "Failed to get biphasic counter result for PWM%d\n", + pc->channel_id); + } + config->enable = false; + pc->data->funcs.set_biphasic(chip, pwm, config); + } + } + +err_disable_pclk: + clk_disable(pc->pclk); + + return ret; +} +EXPORT_SYMBOL_GPL(rockchip_pwm_set_biphasic); + +static int rockchip_pwm_get_biphasic_result_v4(struct pwm_chip *chip, struct pwm_device *pwm, + unsigned long *biphasic_res) +{ + struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); + const struct rockchip_pwm_biphasic_config *config = pc->biphasic_config; + u32 val; + u32 biphasic_timer; + + if (!config->is_continuous) { + usleep_range(config->delay_ms * USEC_PER_MSEC, config->delay_ms * USEC_PER_MSEC); + + if (pc->biphasic_res_valid) { + *biphasic_res = readl_relaxed(pc->base + BIPHASIC_RESULT_VALUE); + if (!*biphasic_res) + return -EINVAL; + + if (pc->biphasic_config->mode == PWM_BIPHASIC_COUNTER_MODE0_FREQ) { + val = *biphasic_res; + biphasic_timer = readl_relaxed(pc->base + BIPHASIC_TIMER_VALUE); + *biphasic_res = DIV_ROUND_CLOSEST_ULL(pc->clk_rate * val, + biphasic_timer); + } + + pc->biphasic_res_valid = false; + } else { + dev_err(chip->dev, "failed to wait for biphasic counter interrupt\n"); + return -ETIMEDOUT; + } + } else { + *biphasic_res = readl_relaxed(pc->base + BIPHASIC_RESULT_VALUE_SYNC); + } + + return 0; +} + +int rockchip_pwm_get_biphasic_result(struct pwm_device *pwm, unsigned long *biphasic_res) +{ + struct pwm_chip *chip; + struct rockchip_pwm_chip *pc; + int ret = 0; + + if (!pwm) + return -EINVAL; + + chip = pwm->chip; + pc = to_rockchip_pwm_chip(chip); + + if (!pc->biphasic_support || + !pc->data->funcs.set_biphasic || !pc->data->funcs.get_biphasic_result) { + dev_err(chip->dev, "Unsupported biphasic counter mode\n"); + return -EINVAL; + } + + if (!pc->biphasic_config) { + dev_err(chip->dev, "Failed to parse biphasic counter config\n"); + return -EINVAL; + } + + if (!pc->biphasic_config->is_continuous || !pc->biphasic_config->enable) { + dev_err(chip->dev, "Unsupported to get result in real time in normal mode\n"); + return -EINVAL; + } + + ret = clk_enable(pc->pclk); + if (ret) + return ret; + + ret = pc->data->funcs.get_biphasic_result(chip, pwm, biphasic_res); + if (ret) { + dev_err(chip->dev, "Failed to get biphasic counter result for PWM%d\n", + pc->channel_id); + return -EINVAL; + } + + clk_disable(pc->pclk); + + return ret; +} +EXPORT_SYMBOL_GPL(rockchip_pwm_get_biphasic_result); + #ifdef CONFIG_DEBUG_FS static int rockchip_pwm_debugfs_show(struct seq_file *s, void *data) { @@ -1750,6 +1969,8 @@ static const struct rockchip_pwm_data pwm_data_v4 = { .global_ctrl = rockchip_pwm_global_ctrl_v4, .set_wave_table = rockchip_pwm_set_wave_table_v4, .set_wave = rockchip_pwm_set_wave_v4, + .set_biphasic = rockchip_pwm_set_biphasic_v4, + .get_biphasic_result = rockchip_pwm_get_biphasic_result_v4, .irq_handler = rockchip_pwm_irq_v4, }, }; @@ -1857,6 +2078,7 @@ static int rockchip_pwm_probe(struct platform_device *pdev) pc->freq_meter_support = !!(version & FREQ_METER_SUPPORT); pc->counter_support = !!(version & COUNTER_SUPPORT); pc->wave_support = !!(version & WAVE_SUPPORT); + pc->biphasic_support = !!(version & BIPHASIC_SUPPORT); } else { pc->channel_id = rockchip_pwm_get_channel_id(pdev->dev.of_node->full_name); } diff --git a/include/linux/pwm-rockchip.h b/include/linux/pwm-rockchip.h index b70099aa19cb..b4217b57a1db 100644 --- a/include/linux/pwm-rockchip.h +++ b/include/linux/pwm-rockchip.h @@ -124,6 +124,37 @@ struct rockchip_pwm_wave_config { u32 middle_hold; }; +/** + * enum rockchip_pwm_biphasic_mode - mode of biphasic counter + * @PWM_BIPHASIC_COUNTER_MODE0: single phase increase mode with A-phase + * @PWM_BIPHASIC_COUNTER_MODE1: single phase increase/decrease mode with A-phase + * @PWM_BIPHASIC_COUNTER_MODE2: dual phase with A/B-phase mode + * @PWM_BIPHASIC_COUNTER_MODE3: dual phase with A/B-phase 2 times frequency mode + * @PWM_BIPHASIC_COUNTER_MODE4: dual phase with A/B-phase 4 times frequency mode + */ +enum rockchip_pwm_biphasic_mode { + PWM_BIPHASIC_COUNTER_MODE0, + PWM_BIPHASIC_COUNTER_MODE1, + PWM_BIPHASIC_COUNTER_MODE2, + PWM_BIPHASIC_COUNTER_MODE3, + PWM_BIPHASIC_COUNTER_MODE4, + PWM_BIPHASIC_COUNTER_MODE0_FREQ, +}; + +/** + * struct rockchip_pwm_biphasic_config - biphasic counter config object + * @enable: enable: enable or disable biphasic counter + * @is_continuous: biphascic counter will not stop at the end of timer in continuous mode + * @mode: the mode of biphasic counter + * @delay_ms: time to wait, in milliseconds, before getting biphasic counter result + */ +struct rockchip_pwm_biphasic_config { + bool enable; + bool is_continuous; + u8 mode; + u32 delay_ms; +}; + #if IS_REACHABLE(CONFIG_PWM_ROCKCHIP) /** * rockchip_pwm_set_counter() - setup pwm counter mode @@ -177,6 +208,22 @@ int rockchip_pwm_global_ctrl(struct pwm_device *pwm, enum rockchip_pwm_global_ct * Returns: 0 on success or a negative error code on failure. */ int rockchip_pwm_set_wave(struct pwm_device *pwm, struct rockchip_pwm_wave_config *config); + +/** + * rockchip_pwm_set_biphasic() - setup biphasic counter mode + * @pwm: PWM device + * @config: config of biphasic counter mode + * @biphasic_res: biphasic counter result + */ +int rockchip_pwm_set_biphasic(struct pwm_device *pwm, struct rockchip_pwm_biphasic_config *config, + unsigned long *biphasic_res); + +/** + * rockchip_pwm_get_biphasic_result() - get biphasic counter result (valid only in continuous mode) + * @pwm: PWM device + * @biphasic_res: biphasic counter result + */ +int rockchip_pwm_get_biphasic_result(struct pwm_device *pwm, unsigned long *biphasic_res); #else static inline int rockchip_pwm_set_counter(struct pwm_device *pwm, enum rockchip_pwm_counter_input_sel input_sel, @@ -209,6 +256,19 @@ static inline int rockchip_pwm_set_wave(struct pwm_device *pwm, { return 0; } + +static inline int rockchip_pwm_set_biphasic(struct pwm_device *pwm, + struct rockchip_pwm_biphasic_config *config, + unsigned long *biphasic_res) +{ + return 0; +} + +static inline int rockchip_pwm_get_biphasic_result(struct pwm_device *pwm, + unsigned long *biphasic_res) +{ + return 0; +} #endif #endif /* _PWM_ROCKCHIP_H_ */