Update the kona driver to support Broadcom iproc pwm controller

Signed-off-by: Yendapally Reddy Dhananjaya Reddy <[email protected]>
---
 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

Reply via email to