diff options
Diffstat (limited to 'drivers/pwm/pwm-tiehrpwm.c')
-rw-r--r-- | drivers/pwm/pwm-tiehrpwm.c | 102 |
1 files changed, 80 insertions, 22 deletions
diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c index c3756d1be19..d3c1dff0a0d 100644 --- a/drivers/pwm/pwm-tiehrpwm.c +++ b/drivers/pwm/pwm-tiehrpwm.c @@ -81,6 +81,15 @@ #define AQCTL_ZRO_FRCHIGH BIT(1) #define AQCTL_ZRO_FRCTOGGLE (BIT(1) | BIT(0)) +#define AQCTL_CHANA_POLNORMAL (AQCTL_CAU_FRCLOW | AQCTL_PRD_FRCHIGH | \ + AQCTL_ZRO_FRCHIGH) +#define AQCTL_CHANA_POLINVERSED (AQCTL_CAU_FRCHIGH | AQCTL_PRD_FRCLOW | \ + AQCTL_ZRO_FRCLOW) +#define AQCTL_CHANB_POLNORMAL (AQCTL_CBU_FRCLOW | AQCTL_PRD_FRCHIGH | \ + AQCTL_ZRO_FRCHIGH) +#define AQCTL_CHANB_POLINVERSED (AQCTL_CBU_FRCHIGH | AQCTL_PRD_FRCLOW | \ + AQCTL_ZRO_FRCLOW) + #define AQSFRC_RLDCSF_MASK (BIT(7) | BIT(6)) #define AQSFRC_RLDCSF_ZRO 0 #define AQSFRC_RLDCSF_PRD BIT(6) @@ -104,6 +113,8 @@ struct ehrpwm_pwm_chip { struct pwm_chip chip; unsigned int clk_rate; void __iomem *mmio_base; + unsigned long period_cycles[NUM_PWM_CHANNEL]; + enum pwm_polarity polarity[NUM_PWM_CHANNEL]; }; static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip) @@ -164,39 +175,37 @@ static int set_prescale_div(unsigned long rqst_prescaler, return 1; } -static void configure_chans(struct ehrpwm_pwm_chip *pc, int chan, - unsigned long duty_cycles) +static void configure_polarity(struct ehrpwm_pwm_chip *pc, int chan) { - int cmp_reg, aqctl_reg; + int aqctl_reg; unsigned short aqctl_val, aqctl_mask; /* - * Channels can be configured from action qualifier module. - * Channel 0 configured with compare A register and for - * up-counter mode. - * Channel 1 configured with compare B register and for - * up-counter mode. + * Configure PWM output to HIGH/LOW level on counter + * reaches compare register value and LOW/HIGH level + * on counter value reaches period register value and + * zero value on counter */ if (chan == 1) { aqctl_reg = AQCTLB; - cmp_reg = CMPB; - /* Configure PWM Low from compare B value */ - aqctl_val = AQCTL_CBU_FRCLOW; aqctl_mask = AQCTL_CBU_MASK; + + if (pc->polarity[chan] == PWM_POLARITY_INVERSED) + aqctl_val = AQCTL_CHANB_POLINVERSED; + else + aqctl_val = AQCTL_CHANB_POLNORMAL; } else { - cmp_reg = CMPA; aqctl_reg = AQCTLA; - /* Configure PWM Low from compare A value*/ - aqctl_val = AQCTL_CAU_FRCLOW; aqctl_mask = AQCTL_CAU_MASK; + + if (pc->polarity[chan] == PWM_POLARITY_INVERSED) + aqctl_val = AQCTL_CHANA_POLINVERSED; + else + aqctl_val = AQCTL_CHANA_POLNORMAL; } - /* Configure PWM High from period value and zero value */ - aqctl_val |= AQCTL_PRD_FRCHIGH | AQCTL_ZRO_FRCHIGH; aqctl_mask |= AQCTL_PRD_MASK | AQCTL_ZRO_MASK; - ehrpwm_modify(pc->mmio_base, aqctl_reg, aqctl_mask, aqctl_val); - - ehrpwm_write(pc->mmio_base, cmp_reg, duty_cycles); + ehrpwm_modify(pc->mmio_base, aqctl_reg, aqctl_mask, aqctl_val); } /* @@ -210,8 +219,9 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, unsigned long long c; unsigned long period_cycles, duty_cycles; unsigned short ps_divval, tb_divval; + int i, cmp_reg; - if (period_ns < 0 || duty_ns < 0 || period_ns > NSEC_PER_SEC) + if (period_ns > NSEC_PER_SEC) return -ERANGE; c = pc->clk_rate; @@ -229,6 +239,28 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, duty_cycles = (unsigned long)c; } + /* + * Period values should be same for multiple PWM channels as IP uses + * same period register for multiple channels. + */ + for (i = 0; i < NUM_PWM_CHANNEL; i++) { + if (pc->period_cycles[i] && + (pc->period_cycles[i] != period_cycles)) { + /* + * Allow channel to reconfigure period if no other + * channels being configured. + */ + if (i == pwm->hwpwm) + continue; + + dev_err(chip->dev, "Period value conflicts with channel %d\n", + i); + return -EINVAL; + } + } + + pc->period_cycles[pwm->hwpwm] = period_cycles; + /* Configure clock prescaler to support Low frequency PWM wave */ if (set_prescale_div(period_cycles/PERIOD_MAX, &ps_divval, &tb_divval)) { @@ -254,12 +286,29 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_CTRMODE_MASK, TBCTL_CTRMODE_UP); - /* Configure the channel for duty cycle */ - configure_chans(pc, pwm->hwpwm, duty_cycles); + if (pwm->hwpwm == 1) + /* Channel 1 configured with compare B register */ + cmp_reg = CMPB; + else + /* Channel 0 configured with compare A register */ + cmp_reg = CMPA; + + ehrpwm_write(pc->mmio_base, cmp_reg, duty_cycles); + pm_runtime_put_sync(chip->dev); return 0; } +static int ehrpwm_pwm_set_polarity(struct pwm_chip *chip, + struct pwm_device *pwm, enum pwm_polarity polarity) +{ + struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); + + /* Configuration of polarity in hardware delayed, do at enable */ + pc->polarity[pwm->hwpwm] = polarity; + return 0; +} + static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); @@ -283,6 +332,9 @@ static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val); + /* Channels polarity can be configured from action qualifier module */ + configure_polarity(pc, pwm->hwpwm); + /* Enable time counter for free_run */ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_FREE_RUN); return 0; @@ -320,15 +372,21 @@ static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) static void ehrpwm_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) { + struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); + if (test_bit(PWMF_ENABLED, &pwm->flags)) { dev_warn(chip->dev, "Removing PWM device without disabling\n"); pm_runtime_put_sync(chip->dev); } + + /* set period value to zero on free */ + pc->period_cycles[pwm->hwpwm] = 0; } static const struct pwm_ops ehrpwm_pwm_ops = { .free = ehrpwm_pwm_free, .config = ehrpwm_pwm_config, + .set_polarity = ehrpwm_pwm_set_polarity, .enable = ehrpwm_pwm_enable, .disable = ehrpwm_pwm_disable, .owner = THIS_MODULE, |