aboutsummaryrefslogtreecommitdiff
path: root/drivers/pwm/pwm-axi-pwmgen.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pwm/pwm-axi-pwmgen.c')
-rw-r--r--drivers/pwm/pwm-axi-pwmgen.c37
1 files changed, 27 insertions, 10 deletions
diff --git a/drivers/pwm/pwm-axi-pwmgen.c b/drivers/pwm/pwm-axi-pwmgen.c
index 39d184417c7c..4259a0db9ff4 100644
--- a/drivers/pwm/pwm-axi-pwmgen.c
+++ b/drivers/pwm/pwm-axi-pwmgen.c
@@ -9,7 +9,7 @@
*
* Limitations:
* - The writes to registers for period and duty are shadowed until
- * LOAD_CONFIG is written to AXI_PWMGEN_REG_CONFIG, at which point
+ * LOAD_CONFIG is written to AXI_PWMGEN_REG_RSTN, at which point
* they take effect.
* - Writing LOAD_CONFIG also has the effect of re-synchronizing all
* enabled channels, which could cause glitching on other channels. It
@@ -33,14 +33,16 @@
#define AXI_PWMGEN_REG_ID 0x04
#define AXI_PWMGEN_REG_SCRATCHPAD 0x08
#define AXI_PWMGEN_REG_CORE_MAGIC 0x0C
-#define AXI_PWMGEN_REG_CONFIG 0x10
+#define AXI_PWMGEN_REG_RSTN 0x10
+#define AXI_PWMGEN_REG_RSTN_LOAD_CONFIG BIT(1)
+#define AXI_PWMGEN_REG_RSTN_RESET BIT(0)
#define AXI_PWMGEN_REG_NPWM 0x14
+#define AXI_PWMGEN_REG_CONFIG 0x18
+#define AXI_PWMGEN_REG_CONFIG_FORCE_ALIGN BIT(1)
#define AXI_PWMGEN_CHX_PERIOD(ch) (0x40 + (4 * (ch)))
#define AXI_PWMGEN_CHX_DUTY(ch) (0x80 + (4 * (ch)))
#define AXI_PWMGEN_CHX_OFFSET(ch) (0xC0 + (4 * (ch)))
#define AXI_PWMGEN_REG_CORE_MAGIC_VAL 0x601A3471 /* Identification number to test during setup */
-#define AXI_PWMGEN_LOAD_CONFIG BIT(1)
-#define AXI_PWMGEN_REG_CONFIG_RESET BIT(0)
struct axi_pwmgen_ddata {
struct regmap *regmap;
@@ -61,13 +63,18 @@ struct axi_pwmgen_waveform {
u32 duty_offset_cnt;
};
+static struct axi_pwmgen_ddata *axi_pwmgen_ddata_from_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
static int axi_pwmgen_round_waveform_tohw(struct pwm_chip *chip,
struct pwm_device *pwm,
const struct pwm_waveform *wf,
void *_wfhw)
{
struct axi_pwmgen_waveform *wfhw = _wfhw;
- struct axi_pwmgen_ddata *ddata = pwmchip_get_drvdata(chip);
+ struct axi_pwmgen_ddata *ddata = axi_pwmgen_ddata_from_chip(chip);
if (wf->period_length_ns == 0) {
*wfhw = (struct axi_pwmgen_waveform){
@@ -111,7 +118,7 @@ static int axi_pwmgen_round_waveform_fromhw(struct pwm_chip *chip, struct pwm_de
const void *_wfhw, struct pwm_waveform *wf)
{
const struct axi_pwmgen_waveform *wfhw = _wfhw;
- struct axi_pwmgen_ddata *ddata = pwmchip_get_drvdata(chip);
+ struct axi_pwmgen_ddata *ddata = axi_pwmgen_ddata_from_chip(chip);
wf->period_length_ns = DIV64_U64_ROUND_UP((u64)wfhw->period_cnt * NSEC_PER_SEC,
ddata->clk_rate_hz);
@@ -130,7 +137,7 @@ static int axi_pwmgen_write_waveform(struct pwm_chip *chip,
const void *_wfhw)
{
const struct axi_pwmgen_waveform *wfhw = _wfhw;
- struct axi_pwmgen_ddata *ddata = pwmchip_get_drvdata(chip);
+ struct axi_pwmgen_ddata *ddata = axi_pwmgen_ddata_from_chip(chip);
struct regmap *regmap = ddata->regmap;
unsigned int ch = pwm->hwpwm;
int ret;
@@ -147,7 +154,7 @@ static int axi_pwmgen_write_waveform(struct pwm_chip *chip,
if (ret)
return ret;
- return regmap_write(regmap, AXI_PWMGEN_REG_CONFIG, AXI_PWMGEN_LOAD_CONFIG);
+ return regmap_write(regmap, AXI_PWMGEN_REG_RSTN, AXI_PWMGEN_REG_RSTN_LOAD_CONFIG);
}
static int axi_pwmgen_read_waveform(struct pwm_chip *chip,
@@ -155,7 +162,7 @@ static int axi_pwmgen_read_waveform(struct pwm_chip *chip,
void *_wfhw)
{
struct axi_pwmgen_waveform *wfhw = _wfhw;
- struct axi_pwmgen_ddata *ddata = pwmchip_get_drvdata(chip);
+ struct axi_pwmgen_ddata *ddata = axi_pwmgen_ddata_from_chip(chip);
struct regmap *regmap = ddata->regmap;
unsigned int ch = pwm->hwpwm;
int ret;
@@ -218,7 +225,17 @@ static int axi_pwmgen_setup(struct regmap *regmap, struct device *dev)
}
/* Enable the core */
- ret = regmap_clear_bits(regmap, AXI_PWMGEN_REG_CONFIG, AXI_PWMGEN_REG_CONFIG_RESET);
+ ret = regmap_clear_bits(regmap, AXI_PWMGEN_REG_RSTN, AXI_PWMGEN_REG_RSTN_RESET);
+ if (ret)
+ return ret;
+
+ /*
+ * Enable force align so that changes to PWM period and duty cycle take
+ * effect immediately. Otherwise, the effect of the change is delayed
+ * until the period of all channels run out, which can be long after the
+ * apply function returns.
+ */
+ ret = regmap_set_bits(regmap, AXI_PWMGEN_REG_CONFIG, AXI_PWMGEN_REG_CONFIG_FORCE_ALIGN);
if (ret)
return ret;