mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-05 18:41:58 +09:00
pwm: rockchip: support for biphasic counter mode
Change-Id: I4a6fd5c3fdf3f2a5ccde9755f8664ad00a00d9ae Signed-off-by: Damon Ding <damon.ding@rock-chips.com>
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
Reference in New Issue
Block a user