Update the kona driver to support Broadcom iproc pwm controller Signed-off-by: Yendapally Reddy Dhananjaya Reddy <yendapally.reddy@xxxxxxxxxxxx> --- drivers/pwm/Kconfig | 6 +- drivers/pwm/pwm-bcm-kona.c | 183 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 163 insertions(+), 26 deletions(-) diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index c182efc..e45ea33 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -76,9 +76,11 @@ config PWM_ATMEL_TCB config PWM_BCM_KONA tristate "Kona PWM support" - depends on ARCH_BCM_MOBILE + depends on ARCH_BCM_MOBILE || ARCH_BCM_IPROC + default ARCH_BCM_IPROC help - Generic PWM framework driver for Broadcom Kona PWM block. + Generic PWM framework driver for Broadcom Kona PWM block. The + same block is also used in Broadcom iProc SoC's. To compile this driver as a module, choose M here: the module will be called pwm-bcm-kona. diff --git a/drivers/pwm/pwm-bcm-kona.c b/drivers/pwm/pwm-bcm-kona.c index c634183..ef152e3a 100644 --- a/drivers/pwm/pwm-bcm-kona.c +++ b/drivers/pwm/pwm-bcm-kona.c @@ -19,6 +19,7 @@ #include <linux/math64.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pwm.h> #include <linux/slab.h> @@ -47,30 +48,90 @@ #define PWM_CONTROL_OFFSET (0x00000000) #define PWM_CONTROL_SMOOTH_SHIFT(chan) (24 + (chan)) -#define PWM_CONTROL_TYPE_SHIFT(chan) (16 + (chan)) +#define PWM_CONTROL_TYPE_SHIFT(shift, chan) (shift + chan) #define PWM_CONTROL_POLARITY_SHIFT(chan) (8 + (chan)) #define PWM_CONTROL_TRIGGER_SHIFT(chan) (chan) #define PRESCALE_OFFSET (0x00000004) -#define PRESCALE_SHIFT(chan) ((chan) << 2) -#define PRESCALE_MASK(chan) (0x7 << PRESCALE_SHIFT(chan)) +#define PRESCALE_SHIFT (0x00000004) +#define PRESCALE_MASK (0x00000007) #define PRESCALE_MIN (0x00000000) #define PRESCALE_MAX (0x00000007) -#define PERIOD_COUNT_OFFSET(chan) (0x00000008 + ((chan) << 3)) +#define PERIOD_COUNT_OFFSET(offset, chan) (offset + (chan << 3)) #define PERIOD_COUNT_MIN (0x00000002) #define PERIOD_COUNT_MAX (0x00ffffff) +#define KONA_PERIOD_COUNT_OFFSET (0x00000008) -#define DUTY_CYCLE_HIGH_OFFSET(chan) (0x0000000c + ((chan) << 3)) +#define DUTY_CYCLE_HIGH_OFFSET(offset, chan) (offset + (chan << 3)) #define DUTY_CYCLE_HIGH_MIN (0x00000000) #define DUTY_CYCLE_HIGH_MAX (0x00ffffff) +#define KONA_DUTY_CYCLE_HIGH_OFFSET (0x0000000c) + +#define PWM_CHANNEL_CNT (0x00000006) +#define SIGNAL_PUSH_PULL (0x00000001) +#define PWMOUT_TYPE_SHIFT (0x00000010) + +#define IPROC_PRESCALE_OFFSET (0x00000024) +#define IPROC_PRESCALE_SHIFT (0x00000006) +#define IPROC_PRESCALE_MAX (0x0000003f) + +#define IPROC_PERIOD_COUNT_OFFSET (0x00000004) +#define IPROC_PERIOD_COUNT_MIN (0x00000002) +#define IPROC_PERIOD_COUNT_MAX (0x0000ffff) + +#define IPROC_DUTY_CYCLE_HIGH_OFFSET (0x00000008) +#define IPROC_DUTY_CYCLE_HIGH_MIN (0x00000000) +#define IPROC_DUTY_CYCLE_HIGH_MAX (0x0000ffff) + +#define IPROC_PWM_CHANNEL_CNT (0x00000004) +#define IPROC_SIGNAL_PUSH_PULL (0x00000000) +#define IPROC_PWMOUT_TYPE_SHIFT (0x0000000f) + +/* + * pwm controller reg structure + * + * @prescale_offset: prescale register offset + * @period_offset: period register offset + * @duty_offset: duty register offset + * @no_of_channels: number of channels + * @out_type_shift: out type shift in the register + * @signal_type: push-pull or open drain + * @prescale_max: prescale max + * @prescale_shift: prescale shift in register + * @prescale_ch_ascending: prescale ch order in prescale register + * @duty_cycle_max: value of max duty cycle + * @duty_cycle_min: value of min duty cycle + * @period_count_max: max period count val + * @period_count_min: min period count val + * @smooth_output_support: pwm smooth output support + */ +struct kona_pwmc_reg { + u32 prescale_offset; + u32 period_offset; + u32 duty_offset; + u32 no_of_channels; + u32 out_type_shift; + u32 signal_type; + u32 prescale_max; + u32 prescale_shift; + bool prescale_ch_ascending; + u32 duty_cycle_max; + u32 duty_cycle_min; + u32 period_count_max; + u32 period_count_min; + bool smooth_output_support; +}; struct kona_pwmc { struct pwm_chip chip; void __iomem *base; struct clk *clk; + const struct kona_pwmc_reg *reg; }; +static const struct of_device_id bcm_kona_pwmc_dt[]; + static inline struct kona_pwmc *to_kona_pwmc(struct pwm_chip *_chip) { return container_of(_chip, struct kona_pwmc, chip); @@ -84,7 +145,9 @@ static void kona_pwmc_prepare_for_settings(struct kona_pwmc *kp, { unsigned int value = readl(kp->base + PWM_CONTROL_OFFSET); - value |= 1 << PWM_CONTROL_SMOOTH_SHIFT(chan); + if (kp->reg->smooth_output_support) + value |= 1 << PWM_CONTROL_SMOOTH_SHIFT(chan); + value &= ~(1 << PWM_CONTROL_TRIGGER_SHIFT(chan)); writel(value, kp->base + PWM_CONTROL_OFFSET); @@ -100,7 +163,9 @@ static void kona_pwmc_apply_settings(struct kona_pwmc *kp, unsigned int chan) unsigned int value = readl(kp->base + PWM_CONTROL_OFFSET); /* Set trigger bit and clear smooth bit to apply new settings */ - value &= ~(1 << PWM_CONTROL_SMOOTH_SHIFT(chan)); + if (kp->reg->smooth_output_support) + value &= ~(1 << PWM_CONTROL_SMOOTH_SHIFT(chan)); + value |= 1 << PWM_CONTROL_TRIGGER_SHIFT(chan); writel(value, kp->base + PWM_CONTROL_OFFSET); @@ -138,15 +203,17 @@ static int kona_pwmc_config(struct pwm_chip *chip, struct pwm_device *pwm, dc = div64_u64(val, div); /* If duty_ns or period_ns are not achievable then return */ - if (pc < PERIOD_COUNT_MIN || dc < DUTY_CYCLE_HIGH_MIN) + if (pc < kp->reg->period_count_min || + dc < kp->reg->duty_cycle_min) return -EINVAL; /* If pc and dc are in bounds, the calculation is done */ - if (pc <= PERIOD_COUNT_MAX && dc <= DUTY_CYCLE_HIGH_MAX) + if (pc <= kp->reg->period_count_max && + dc <= kp->reg->duty_cycle_max) break; /* Otherwise, increase prescale and recalculate pc and dc */ - if (++prescale > PRESCALE_MAX) + if (++prescale > kp->reg->prescale_max) return -EINVAL; } @@ -156,16 +223,30 @@ static int kona_pwmc_config(struct pwm_chip *chip, struct pwm_device *pwm, * validated immediately instead of on enable. */ if (pwm_is_enabled(pwm)) { + u32 ch_pre_shift = kp->reg->prescale_shift; + kona_pwmc_prepare_for_settings(kp, chan); - value = readl(kp->base + PRESCALE_OFFSET); - value &= ~PRESCALE_MASK(chan); - value |= prescale << PRESCALE_SHIFT(chan); - writel(value, kp->base + PRESCALE_OFFSET); + if (kp->reg->prescale_ch_ascending) + /* + * The prescale bits mask is in ascending + * order in the register + * "ch(n-2)bits..ch(n-1)bits..ch(n)bits". + */ + ch_pre_shift *= ((kp->reg->no_of_channels - 1) - chan); + else + ch_pre_shift *= chan; - writel(pc, kp->base + PERIOD_COUNT_OFFSET(chan)); + value = readl(kp->base + kp->reg->prescale_offset); + value &= ~(kp->reg->prescale_max << ch_pre_shift); + value |= prescale << ch_pre_shift; + writel(value, kp->base + kp->reg->prescale_offset); - writel(dc, kp->base + DUTY_CYCLE_HIGH_OFFSET(chan)); + writel(pc, kp->base + + PERIOD_COUNT_OFFSET(kp->reg->period_offset, chan)); + + writel(dc, kp->base + + DUTY_CYCLE_HIGH_OFFSET(kp->reg->duty_offset, chan)); kona_pwmc_apply_settings(kp, chan); } @@ -231,17 +312,29 @@ static void kona_pwmc_disable(struct pwm_chip *chip, struct pwm_device *pwm) struct kona_pwmc *kp = to_kona_pwmc(chip); unsigned int chan = pwm->hwpwm; unsigned int value; + u32 ch_pre_shift = kp->reg->prescale_shift; + + if (kp->reg->prescale_ch_ascending) + /* + * The prescale bits mask is in ascending order + * in the register "ch(n-2)bits..ch(n-1)bits..ch(n)bits". + */ + ch_pre_shift *= ((kp->reg->no_of_channels - 1) - chan); + else + ch_pre_shift *= chan; kona_pwmc_prepare_for_settings(kp, chan); /* Simulate a disable by configuring for zero duty */ - writel(0, kp->base + DUTY_CYCLE_HIGH_OFFSET(chan)); - writel(0, kp->base + PERIOD_COUNT_OFFSET(chan)); + writel(0, kp->base + + DUTY_CYCLE_HIGH_OFFSET(kp->reg->duty_offset, chan)); + writel(0, kp->base + + PERIOD_COUNT_OFFSET(kp->reg->period_offset, chan)); /* Set prescale to 0 for this channel */ - value = readl(kp->base + PRESCALE_OFFSET); - value &= ~PRESCALE_MASK(chan); - writel(value, kp->base + PRESCALE_OFFSET); + value = readl(kp->base + kp->reg->prescale_offset); + value &= ~(kp->reg->prescale_max << ch_pre_shift); + writel(value, kp->base + kp->reg->prescale_offset); kona_pwmc_apply_settings(kp, chan); @@ -263,17 +356,23 @@ static int kona_pwmc_probe(struct platform_device *pdev) unsigned int chan; unsigned int value = 0; int ret = 0; + const struct of_device_id *match; kp = devm_kzalloc(&pdev->dev, sizeof(*kp), GFP_KERNEL); if (kp == NULL) return -ENOMEM; + match = of_match_device(bcm_kona_pwmc_dt, &pdev->dev); + if (!match) + return -EINVAL; + + kp->reg = (struct kona_pwmc_reg *)match->data; platform_set_drvdata(pdev, kp); kp->chip.dev = &pdev->dev; kp->chip.ops = &kona_pwm_ops; kp->chip.base = -1; - kp->chip.npwm = 6; + kp->chip.npwm = kp->reg->no_of_channels; kp->chip.of_xlate = of_pwm_xlate_with_flags; kp->chip.of_pwm_n_cells = 3; kp->chip.can_sleep = true; @@ -298,7 +397,8 @@ static int kona_pwmc_probe(struct platform_device *pdev) /* Set push/pull for all channels */ for (chan = 0; chan < kp->chip.npwm; chan++) - value |= (1 << PWM_CONTROL_TYPE_SHIFT(chan)); + value |= (kp->reg->signal_type << + PWM_CONTROL_TYPE_SHIFT(kp->reg->out_type_shift, chan)); writel(value, kp->base + PWM_CONTROL_OFFSET); @@ -323,8 +423,43 @@ static int kona_pwmc_remove(struct platform_device *pdev) return pwmchip_remove(&kp->chip); } +static const struct kona_pwmc_reg kona_pwmc_reg_data = { + .prescale_offset = PRESCALE_OFFSET, + .period_offset = KONA_PERIOD_COUNT_OFFSET, + .duty_offset = KONA_DUTY_CYCLE_HIGH_OFFSET, + .no_of_channels = PWM_CHANNEL_CNT, + .out_type_shift = PWMOUT_TYPE_SHIFT, + .signal_type = SIGNAL_PUSH_PULL, + .prescale_max = PRESCALE_MAX, + .prescale_shift = PRESCALE_SHIFT, + .prescale_ch_ascending = false, + .duty_cycle_max = DUTY_CYCLE_HIGH_MAX, + .duty_cycle_min = DUTY_CYCLE_HIGH_MIN, + .period_count_max = PERIOD_COUNT_MAX, + .period_count_min = PERIOD_COUNT_MIN, + .smooth_output_support = true, +}; + +static const struct kona_pwmc_reg iproc_pwmc_reg_data = { + .prescale_offset = IPROC_PRESCALE_OFFSET, + .period_offset = IPROC_PERIOD_COUNT_OFFSET, + .duty_offset = IPROC_DUTY_CYCLE_HIGH_OFFSET, + .no_of_channels = IPROC_PWM_CHANNEL_CNT, + .out_type_shift = IPROC_PWMOUT_TYPE_SHIFT, + .signal_type = IPROC_SIGNAL_PUSH_PULL, + .prescale_max = IPROC_PRESCALE_MAX, + .prescale_shift = IPROC_PRESCALE_SHIFT, + .prescale_ch_ascending = true, + .duty_cycle_max = IPROC_DUTY_CYCLE_HIGH_MAX, + .duty_cycle_min = IPROC_DUTY_CYCLE_HIGH_MIN, + .period_count_max = IPROC_PERIOD_COUNT_MAX, + .period_count_min = IPROC_PERIOD_COUNT_MIN, + .smooth_output_support = false, +}; + static const struct of_device_id bcm_kona_pwmc_dt[] = { - { .compatible = "brcm,kona-pwm" }, + { .compatible = "brcm,kona-pwm", .data = &kona_pwmc_reg_data}, + { .compatible = "brcm,iproc-pwm", .data = &iproc_pwmc_reg_data}, { }, }; MODULE_DEVICE_TABLE(of, bcm_kona_pwmc_dt); -- 2.1.0 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html