Merge tag 'pwm/for-5.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 13 Mar 2019 17:01:10 +0000 (10:01 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 13 Mar 2019 17:01:10 +0000 (10:01 -0700)
Pull pwm updates from Thierry Reding:
 "The changes for this cycle are across the board.

  The bulk of it is cleanups, but there's also new device support in
  some drivers as well as more conversions to the atomic API"

* tag 'pwm/for-5.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm: (24 commits)
  pwm: atmel: Remove useless symbolic definitions
  pwm: bcm-kona: Update macros to remove braces around numbers
  pwm: imx27: Only enable the clocks once in .get_state()
  pwm: rcar: Improve calculation of divider
  pwm: rcar: Remove legacy APIs
  pwm: rcar: Use "atomic" API on rcar_pwm_resume()
  pwm: rcar: Add support "atomic" API
  pwm: atmel: Add support for SAM9X60's PWM controller
  pwm: atmel: Add PWM binding for SAM9X60
  pwm: atmel: Rename objects of type atmel_pwm_data
  pwm: atmel: Add support for controllers with 32 bit counters
  pwm: atmel: Add struct atmel_pwm_data
  pwm: Add MediaTek MT8183 display PWM driver support
  pwm: hibvt: Add hi3559v100 support
  dt-bindings: pwm: hibvt: Add hi3559v100 support
  pwm: hibvt: Use individual struct per of-data
  pwm: imx: Signedness bug in imx_pwm_get_state()
  pwm: imx: Split into two drivers
  pwm: imx: Don't print an error on -EPROBE_DEFER
  pwm: imx: Set driver data earlier simplifying the end of ->probe()
  ...

13 files changed:
Documentation/devicetree/bindings/pwm/atmel-pwm.txt
Documentation/devicetree/bindings/pwm/pwm-hibvt.txt
drivers/pwm/Kconfig
drivers/pwm/Makefile
drivers/pwm/core.c
drivers/pwm/pwm-atmel.c
drivers/pwm/pwm-bcm-kona.c
drivers/pwm/pwm-hibvt.c
drivers/pwm/pwm-imx1.c [new file with mode: 0644]
drivers/pwm/pwm-imx27.c [moved from drivers/pwm/pwm-imx.c with 59% similarity]
drivers/pwm/pwm-mtk-disp.c
drivers/pwm/pwm-rcar.c
include/linux/pwm.h

index c8c831d7b0d1bb9d90cfc9b3022b57b9e6a2f74d..591ecdd39c7b93f41c3f1da70ce987e04ddc6415 100644 (file)
@@ -5,6 +5,7 @@ Required properties:
     - "atmel,at91sam9rl-pwm"
     - "atmel,sama5d3-pwm"
     - "atmel,sama5d2-pwm"
+    - "microchip,sam9x60-pwm"
   - reg: physical base address and length of the controller's registers
   - #pwm-cells: Should be 3. See pwm.txt in this directory for a
     description of the cells format.
index fa7849d67836b1dbc39c69f65e2767e7128c1310..daedfef09bb61c244442a2015a08d8d21b9c8400 100644 (file)
@@ -5,6 +5,8 @@ Required properties:
  The SoC specific strings supported including:
        "hisilicon,hi3516cv300-pwm"
        "hisilicon,hi3519v100-pwm"
+       "hisilicon,hi3559v100-shub-pwm"
+       "hisilicon,hi3559v100-pwm
 - reg: physical base address and length of the controller's registers.
 - clocks: phandle and clock specifier of the PWM reference clock.
 - resets: phandle and reset specifier for the PWM controller reset.
index a8f47df0655a6f1a7112aba36b375fd18ccbe6b6..54f8238aac0dece5e2469c0ba02f5e7c7d1d49c0 100644 (file)
@@ -192,14 +192,23 @@ config PWM_IMG
          To compile this driver as a module, choose M here: the module
          will be called pwm-img
 
-config PWM_IMX
-       tristate "i.MX PWM support"
+config PWM_IMX1
+       tristate "i.MX1 PWM support"
        depends on ARCH_MXC
        help
-         Generic PWM framework driver for i.MX.
+         Generic PWM framework driver for i.MX1 and i.MX21
 
          To compile this driver as a module, choose M here: the module
-         will be called pwm-imx.
+         will be called pwm-imx1.
+
+config PWM_IMX27
+       tristate "i.MX27 PWM support"
+       depends on ARCH_MXC
+       help
+         Generic PWM framework driver for i.MX27 and later i.MX SoCs.
+
+         To compile this driver as a module, choose M here: the module
+         will be called pwm-imx27.
 
 config PWM_JZ4740
        tristate "Ingenic JZ47xx PWM support"
index 9c676a0dadf55e2b06fe81a6d620806393c98d48..448825e892bc17b1de1549319dcd900be7917901 100644 (file)
@@ -17,7 +17,8 @@ obj-$(CONFIG_PWM_EP93XX)      += pwm-ep93xx.o
 obj-$(CONFIG_PWM_FSL_FTM)      += pwm-fsl-ftm.o
 obj-$(CONFIG_PWM_HIBVT)                += pwm-hibvt.o
 obj-$(CONFIG_PWM_IMG)          += pwm-img.o
-obj-$(CONFIG_PWM_IMX)          += pwm-imx.o
+obj-$(CONFIG_PWM_IMX1)         += pwm-imx1.o
+obj-$(CONFIG_PWM_IMX27)                += pwm-imx27.o
 obj-$(CONFIG_PWM_JZ4740)       += pwm-jz4740.o
 obj-$(CONFIG_PWM_LP3943)       += pwm-lp3943.o
 obj-$(CONFIG_PWM_LPC18XX_SCT)  += pwm-lpc18xx-sct.o
index 1581f6ab1b1f425986cb8693a961cee7fa4f42f8..3149204567f3cc3b64b4ddf15fb75cbdc8a32d85 100644 (file)
@@ -472,7 +472,10 @@ int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state)
            state->duty_cycle > state->period)
                return -EINVAL;
 
-       if (!memcmp(state, &pwm->state, sizeof(*state)))
+       if (state->period == pwm->state.period &&
+           state->duty_cycle == pwm->state.duty_cycle &&
+           state->polarity == pwm->state.polarity &&
+           state->enabled == pwm->state.enabled)
                return 0;
 
        if (pwm->chip->ops->apply) {
@@ -1033,10 +1036,7 @@ static int pwm_seq_show(struct seq_file *s, void *v)
                   dev_name(chip->dev), chip->npwm,
                   (chip->npwm != 1) ? "s" : "");
 
-       if (chip->ops->dbg_show)
-               chip->ops->dbg_show(chip, s);
-       else
-               pwm_dbg_show(chip, s);
+       pwm_dbg_show(chip, s);
 
        return 0;
 }
index 530d7dc5f1b5cdad7d10d6c9c675ffc3e1430b3a..a9fd6f0d408c318e56538e15e30d241d9a885ee3 100644 (file)
 #define PWMV2_CPRD             0x0C
 #define PWMV2_CPRDUPD          0x10
 
-/*
- * Max value for duty and period
- *
- * Although the duty and period register is 32 bit,
- * however only the LSB 16 bits are significant.
- */
-#define PWM_MAX_DTY            0xFFFF
-#define PWM_MAX_PRD            0xFFFF
-#define PRD_MAX_PRES           10
-
 struct atmel_pwm_registers {
        u8 period;
        u8 period_upd;
@@ -65,11 +55,21 @@ struct atmel_pwm_registers {
        u8 duty_upd;
 };
 
+struct atmel_pwm_config {
+       u32 max_period;
+       u32 max_pres;
+};
+
+struct atmel_pwm_data {
+       struct atmel_pwm_registers regs;
+       struct atmel_pwm_config cfg;
+};
+
 struct atmel_pwm_chip {
        struct pwm_chip chip;
        struct clk *clk;
        void __iomem *base;
-       const struct atmel_pwm_registers *regs;
+       const struct atmel_pwm_data *data;
 
        unsigned int updated_pwms;
        /* ISR is cleared when read, ensure only one thread does that */
@@ -121,10 +121,10 @@ static int atmel_pwm_calculate_cprd_and_pres(struct pwm_chip *chip,
        cycles *= clk_get_rate(atmel_pwm->clk);
        do_div(cycles, NSEC_PER_SEC);
 
-       for (*pres = 0; cycles > PWM_MAX_PRD; cycles >>= 1)
+       for (*pres = 0; cycles > atmel_pwm->data->cfg.max_period; cycles >>= 1)
                (*pres)++;
 
-       if (*pres > PRD_MAX_PRES) {
+       if (*pres > atmel_pwm->data->cfg.max_pres) {
                dev_err(chip->dev, "pres exceeds the maximum value\n");
                return -EINVAL;
        }
@@ -150,15 +150,15 @@ static void atmel_pwm_update_cdty(struct pwm_chip *chip, struct pwm_device *pwm,
        struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
        u32 val;
 
-       if (atmel_pwm->regs->duty_upd ==
-           atmel_pwm->regs->period_upd) {
+       if (atmel_pwm->data->regs.duty_upd ==
+           atmel_pwm->data->regs.period_upd) {
                val = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, PWM_CMR);
                val &= ~PWM_CMR_UPD_CDTY;
                atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, val);
        }
 
        atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm,
-                           atmel_pwm->regs->duty_upd, cdty);
+                           atmel_pwm->data->regs.duty_upd, cdty);
 }
 
 static void atmel_pwm_set_cprd_cdty(struct pwm_chip *chip,
@@ -168,9 +168,9 @@ static void atmel_pwm_set_cprd_cdty(struct pwm_chip *chip,
        struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
 
        atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm,
-                           atmel_pwm->regs->duty, cdty);
+                           atmel_pwm->data->regs.duty, cdty);
        atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm,
-                           atmel_pwm->regs->period, cprd);
+                           atmel_pwm->data->regs.period, cprd);
 }
 
 static void atmel_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm,
@@ -225,7 +225,7 @@ static int atmel_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
                    cstate.polarity == state->polarity &&
                    cstate.period == state->period) {
                        cprd = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm,
-                                                 atmel_pwm->regs->period);
+                                                 atmel_pwm->data->regs.period);
                        atmel_pwm_calculate_cdty(state, cprd, &cdty);
                        atmel_pwm_update_cdty(chip, pwm, cdty);
                        return 0;
@@ -277,27 +277,55 @@ static const struct pwm_ops atmel_pwm_ops = {
        .owner = THIS_MODULE,
 };
 
-static const struct atmel_pwm_registers atmel_pwm_regs_v1 = {
-       .period         = PWMV1_CPRD,
-       .period_upd     = PWMV1_CUPD,
-       .duty           = PWMV1_CDTY,
-       .duty_upd       = PWMV1_CUPD,
+static const struct atmel_pwm_data atmel_sam9rl_pwm_data = {
+       .regs = {
+               .period         = PWMV1_CPRD,
+               .period_upd     = PWMV1_CUPD,
+               .duty           = PWMV1_CDTY,
+               .duty_upd       = PWMV1_CUPD,
+       },
+       .cfg = {
+               /* 16 bits to keep period and duty. */
+               .max_period     = 0xffff,
+               .max_pres       = 10,
+       },
+};
+
+static const struct atmel_pwm_data atmel_sama5_pwm_data = {
+       .regs = {
+               .period         = PWMV2_CPRD,
+               .period_upd     = PWMV2_CPRDUPD,
+               .duty           = PWMV2_CDTY,
+               .duty_upd       = PWMV2_CDTYUPD,
+       },
+       .cfg = {
+               /* 16 bits to keep period and duty. */
+               .max_period     = 0xffff,
+               .max_pres       = 10,
+       },
 };
 
-static const struct atmel_pwm_registers atmel_pwm_regs_v2 = {
-       .period         = PWMV2_CPRD,
-       .period_upd     = PWMV2_CPRDUPD,
-       .duty           = PWMV2_CDTY,
-       .duty_upd       = PWMV2_CDTYUPD,
+static const struct atmel_pwm_data mchp_sam9x60_pwm_data = {
+       .regs = {
+               .period         = PWMV1_CPRD,
+               .period_upd     = PWMV1_CUPD,
+               .duty           = PWMV1_CDTY,
+               .duty_upd       = PWMV1_CUPD,
+       },
+       .cfg = {
+               /* 32 bits to keep period and duty. */
+               .max_period     = 0xffffffff,
+               .max_pres       = 10,
+       },
 };
 
 static const struct platform_device_id atmel_pwm_devtypes[] = {
        {
                .name = "at91sam9rl-pwm",
-               .driver_data = (kernel_ulong_t)&atmel_pwm_regs_v1,
+               .driver_data = (kernel_ulong_t)&atmel_sam9rl_pwm_data,
        }, {
                .name = "sama5d3-pwm",
-               .driver_data = (kernel_ulong_t)&atmel_pwm_regs_v2,
+               .driver_data = (kernel_ulong_t)&atmel_sama5_pwm_data,
        }, {
                /* sentinel */
        },
@@ -307,20 +335,23 @@ MODULE_DEVICE_TABLE(platform, atmel_pwm_devtypes);
 static const struct of_device_id atmel_pwm_dt_ids[] = {
        {
                .compatible = "atmel,at91sam9rl-pwm",
-               .data = &atmel_pwm_regs_v1,
+               .data = &atmel_sam9rl_pwm_data,
        }, {
                .compatible = "atmel,sama5d3-pwm",
-               .data = &atmel_pwm_regs_v2,
+               .data = &atmel_sama5_pwm_data,
        }, {
                .compatible = "atmel,sama5d2-pwm",
-               .data = &atmel_pwm_regs_v2,
+               .data = &atmel_sama5_pwm_data,
+       }, {
+               .compatible = "microchip,sam9x60-pwm",
+               .data = &mchp_sam9x60_pwm_data,
        }, {
                /* sentinel */
        },
 };
 MODULE_DEVICE_TABLE(of, atmel_pwm_dt_ids);
 
-static inline const struct atmel_pwm_registers *
+static inline const struct atmel_pwm_data *
 atmel_pwm_get_driver_data(struct platform_device *pdev)
 {
        const struct platform_device_id *id;
@@ -330,18 +361,18 @@ atmel_pwm_get_driver_data(struct platform_device *pdev)
 
        id = platform_get_device_id(pdev);
 
-       return (struct atmel_pwm_registers *)id->driver_data;
+       return (struct atmel_pwm_data *)id->driver_data;
 }
 
 static int atmel_pwm_probe(struct platform_device *pdev)
 {
-       const struct atmel_pwm_registers *regs;
+       const struct atmel_pwm_data *data;
        struct atmel_pwm_chip *atmel_pwm;
        struct resource *res;
        int ret;
 
-       regs = atmel_pwm_get_driver_data(pdev);
-       if (!regs)
+       data = atmel_pwm_get_driver_data(pdev);
+       if (!data)
                return -ENODEV;
 
        atmel_pwm = devm_kzalloc(&pdev->dev, sizeof(*atmel_pwm), GFP_KERNEL);
@@ -373,7 +404,7 @@ static int atmel_pwm_probe(struct platform_device *pdev)
 
        atmel_pwm->chip.base = -1;
        atmel_pwm->chip.npwm = 4;
-       atmel_pwm->regs = regs;
+       atmel_pwm->data = data;
        atmel_pwm->updated_pwms = 0;
        mutex_init(&atmel_pwm->isr_lock);
 
index 09a95aeb3a70ff79bb11819126ac5874f2d4a909..81da91df2529ab0b8801620b4eb26490cfc5f0c6 100644 (file)
  *    high or low depending on its state at that exact instant.
  */
 
-#define PWM_CONTROL_OFFSET                     (0x00000000)
+#define PWM_CONTROL_OFFSET                     0x00000000
 #define PWM_CONTROL_SMOOTH_SHIFT(chan)         (24 + (chan))
 #define PWM_CONTROL_TYPE_SHIFT(chan)           (16 + (chan))
 #define PWM_CONTROL_POLARITY_SHIFT(chan)       (8 + (chan))
 #define PWM_CONTROL_TRIGGER_SHIFT(chan)                (chan)
 
-#define PRESCALE_OFFSET                                (0x00000004)
+#define PRESCALE_OFFSET                                0x00000004
 #define PRESCALE_SHIFT(chan)                   ((chan) << 2)
 #define PRESCALE_MASK(chan)                    (0x7 << PRESCALE_SHIFT(chan))
-#define PRESCALE_MIN                           (0x00000000)
-#define PRESCALE_MAX                           (0x00000007)
+#define PRESCALE_MIN                           0x00000000
+#define PRESCALE_MAX                           0x00000007
 
 #define PERIOD_COUNT_OFFSET(chan)              (0x00000008 + ((chan) << 3))
-#define PERIOD_COUNT_MIN                       (0x00000002)
-#define PERIOD_COUNT_MAX                       (0x00ffffff)
+#define PERIOD_COUNT_MIN                       0x00000002
+#define PERIOD_COUNT_MAX                       0x00ffffff
 
 #define DUTY_CYCLE_HIGH_OFFSET(chan)           (0x0000000c + ((chan) << 3))
-#define DUTY_CYCLE_HIGH_MIN                    (0x00000000)
-#define DUTY_CYCLE_HIGH_MAX                    (0x00ffffff)
+#define DUTY_CYCLE_HIGH_MIN                    0x00000000
+#define DUTY_CYCLE_HIGH_MAX                    0x00ffffff
 
 struct kona_pwmc {
        struct pwm_chip chip;
index 27c107e78d59adb37151c148435ad2c97fe68bde..a0b09603d13d8f43701bd766c5007c31e3d67968 100644 (file)
@@ -49,15 +49,30 @@ struct hibvt_pwm_chip {
        struct clk *clk;
        void __iomem *base;
        struct reset_control *rstc;
+       const struct hibvt_pwm_soc *soc;
 };
 
 struct hibvt_pwm_soc {
        u32 num_pwms;
+       bool quirk_force_enable;
 };
 
-static const struct hibvt_pwm_soc pwm_soc[2] = {
-       { .num_pwms = 4 },
-       { .num_pwms = 8 },
+static const struct hibvt_pwm_soc hi3516cv300_soc_info = {
+       .num_pwms = 4,
+};
+
+static const struct hibvt_pwm_soc hi3519v100_soc_info = {
+       .num_pwms = 8,
+};
+
+static const struct hibvt_pwm_soc hi3559v100_shub_soc_info = {
+       .num_pwms = 8,
+       .quirk_force_enable = true,
+};
+
+static const struct hibvt_pwm_soc hi3559v100_soc_info = {
+       .num_pwms = 2,
+       .quirk_force_enable = true,
 };
 
 static inline struct hibvt_pwm_chip *to_hibvt_pwm_chip(struct pwm_chip *chip)
@@ -148,13 +163,23 @@ static void hibvt_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
 static int hibvt_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
                                struct pwm_state *state)
 {
+       struct hibvt_pwm_chip *hi_pwm_chip = to_hibvt_pwm_chip(chip);
+
        if (state->polarity != pwm->state.polarity)
                hibvt_pwm_set_polarity(chip, pwm, state->polarity);
 
        if (state->period != pwm->state.period ||
-               state->duty_cycle != pwm->state.duty_cycle)
+           state->duty_cycle != pwm->state.duty_cycle) {
                hibvt_pwm_config(chip, pwm, state->duty_cycle, state->period);
 
+               /*
+                * Some implementations require the PWM to be enabled twice
+                * each time the duty cycle is refreshed.
+                */
+               if (hi_pwm_chip->soc->quirk_force_enable && state->enabled)
+                       hibvt_pwm_enable(chip, pwm);
+       }
+
        if (state->enabled != pwm->state.enabled) {
                if (state->enabled)
                        hibvt_pwm_enable(chip, pwm);
@@ -198,6 +223,7 @@ static int hibvt_pwm_probe(struct platform_device *pdev)
        pwm_chip->chip.npwm = soc->num_pwms;
        pwm_chip->chip.of_xlate = of_pwm_xlate_with_flags;
        pwm_chip->chip.of_pwm_n_cells = 3;
+       pwm_chip->soc = soc;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        pwm_chip->base = devm_ioremap_resource(&pdev->dev, res);
@@ -250,8 +276,14 @@ static int hibvt_pwm_remove(struct platform_device *pdev)
 }
 
 static const struct of_device_id hibvt_pwm_of_match[] = {
-       { .compatible = "hisilicon,hi3516cv300-pwm", .data = &pwm_soc[0] },
-       { .compatible = "hisilicon,hi3519v100-pwm", .data = &pwm_soc[1] },
+       { .compatible = "hisilicon,hi3516cv300-pwm",
+         .data = &hi3516cv300_soc_info },
+       { .compatible = "hisilicon,hi3519v100-pwm",
+         .data = &hi3519v100_soc_info },
+       { .compatible = "hisilicon,hi3559v100-shub-pwm",
+         .data = &hi3559v100_shub_soc_info },
+       { .compatible = "hisilicon,hi3559v100-pwm",
+         .data = &hi3559v100_soc_info },
        {  }
 };
 MODULE_DEVICE_TABLE(of, hibvt_pwm_of_match);
diff --git a/drivers/pwm/pwm-imx1.c b/drivers/pwm/pwm-imx1.c
new file mode 100644 (file)
index 0000000..f8b2c2e
--- /dev/null
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * simple driver for PWM (Pulse Width Modulator) controller
+ *
+ * Derived from pxa PWM driver by eric miao <eric.miao@marvell.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.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>
+
+#define MX1_PWMC                       0x00   /* PWM Control Register */
+#define MX1_PWMS                       0x04   /* PWM Sample Register */
+#define MX1_PWMP                       0x08   /* PWM Period Register */
+
+#define MX1_PWMC_EN                    BIT(4)
+
+struct pwm_imx1_chip {
+       struct clk *clk_ipg;
+       struct clk *clk_per;
+       void __iomem *mmio_base;
+       struct pwm_chip chip;
+};
+
+#define to_pwm_imx1_chip(chip) container_of(chip, struct pwm_imx1_chip, chip)
+
+static int pwm_imx1_clk_prepare_enable(struct pwm_chip *chip)
+{
+       struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip);
+       int ret;
+
+       ret = clk_prepare_enable(imx->clk_ipg);
+       if (ret)
+               return ret;
+
+       ret = clk_prepare_enable(imx->clk_per);
+       if (ret) {
+               clk_disable_unprepare(imx->clk_ipg);
+               return ret;
+       }
+
+       return 0;
+}
+
+static void pwm_imx1_clk_disable_unprepare(struct pwm_chip *chip)
+{
+       struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip);
+
+       clk_disable_unprepare(imx->clk_per);
+       clk_disable_unprepare(imx->clk_ipg);
+}
+
+static int pwm_imx1_config(struct pwm_chip *chip,
+                          struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+       struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip);
+       u32 max, p;
+
+       /*
+        * The PWM subsystem allows for exact frequencies. However,
+        * I cannot connect a scope on my device to the PWM line and
+        * thus cannot provide the program the PWM controller
+        * exactly. Instead, I'm relying on the fact that the
+        * Bootloader (u-boot or WinCE+haret) has programmed the PWM
+        * function group already. So I'll just modify the PWM sample
+        * register to follow the ratio of duty_ns vs. period_ns
+        * accordingly.
+        *
+        * This is good enough for programming the brightness of
+        * the LCD backlight.
+        *
+        * The real implementation would divide PERCLK[0] first by
+        * both the prescaler (/1 .. /128) and then by CLKSEL
+        * (/2 .. /16).
+        */
+       max = readl(imx->mmio_base + MX1_PWMP);
+       p = max * duty_ns / period_ns;
+
+       writel(max - p, imx->mmio_base + MX1_PWMS);
+
+       return 0;
+}
+
+static int pwm_imx1_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip);
+       u32 value;
+       int ret;
+
+       ret = pwm_imx1_clk_prepare_enable(chip);
+       if (ret < 0)
+               return ret;
+
+       value = readl(imx->mmio_base + MX1_PWMC);
+       value |= MX1_PWMC_EN;
+       writel(value, imx->mmio_base + MX1_PWMC);
+
+       return 0;
+}
+
+static void pwm_imx1_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip);
+       u32 value;
+
+       value = readl(imx->mmio_base + MX1_PWMC);
+       value &= ~MX1_PWMC_EN;
+       writel(value, imx->mmio_base + MX1_PWMC);
+
+       pwm_imx1_clk_disable_unprepare(chip);
+}
+
+static const struct pwm_ops pwm_imx1_ops = {
+       .enable = pwm_imx1_enable,
+       .disable = pwm_imx1_disable,
+       .config = pwm_imx1_config,
+       .owner = THIS_MODULE,
+};
+
+static const struct of_device_id pwm_imx1_dt_ids[] = {
+       { .compatible = "fsl,imx1-pwm", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, pwm_imx1_dt_ids);
+
+static int pwm_imx1_probe(struct platform_device *pdev)
+{
+       struct pwm_imx1_chip *imx;
+       struct resource *r;
+
+       imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL);
+       if (!imx)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, imx);
+
+       imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
+       if (IS_ERR(imx->clk_ipg)) {
+               dev_err(&pdev->dev, "getting ipg clock failed with %ld\n",
+                               PTR_ERR(imx->clk_ipg));
+               return PTR_ERR(imx->clk_ipg);
+       }
+
+       imx->clk_per = devm_clk_get(&pdev->dev, "per");
+       if (IS_ERR(imx->clk_per)) {
+               int ret = PTR_ERR(imx->clk_per);
+
+               if (ret != -EPROBE_DEFER)
+                       dev_err(&pdev->dev,
+                               "failed to get peripheral clock: %d\n",
+                               ret);
+
+               return ret;
+       }
+
+       imx->chip.ops = &pwm_imx1_ops;
+       imx->chip.dev = &pdev->dev;
+       imx->chip.base = -1;
+       imx->chip.npwm = 1;
+
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       imx->mmio_base = devm_ioremap_resource(&pdev->dev, r);
+       if (IS_ERR(imx->mmio_base))
+               return PTR_ERR(imx->mmio_base);
+
+       return pwmchip_add(&imx->chip);
+}
+
+static int pwm_imx1_remove(struct platform_device *pdev)
+{
+       struct pwm_imx1_chip *imx = platform_get_drvdata(pdev);
+
+       pwm_imx1_clk_disable_unprepare(&imx->chip);
+
+       return pwmchip_remove(&imx->chip);
+}
+
+static struct platform_driver pwm_imx1_driver = {
+       .driver = {
+               .name = "pwm-imx1",
+               .of_match_table = pwm_imx1_dt_ids,
+       },
+       .probe = pwm_imx1_probe,
+       .remove = pwm_imx1_remove,
+};
+module_platform_driver(pwm_imx1_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
similarity index 59%
rename from drivers/pwm/pwm-imx.c
rename to drivers/pwm/pwm-imx27.c
index 55a3a363d5be94708b48bbc08ce792d1a81a62f0..806130654211f1517c5b91915f04ad8f41122801 100644 (file)
 #include <linux/pwm.h>
 #include <linux/slab.h>
 
-/* i.MX1 and i.MX21 share the same PWM function block: */
-
-#define MX1_PWMC                       0x00   /* PWM Control Register */
-#define MX1_PWMS                       0x04   /* PWM Sample Register */
-#define MX1_PWMP                       0x08   /* PWM Period Register */
-
-#define MX1_PWMC_EN                    BIT(4)
-
-/* i.MX27, i.MX31, i.MX35 share the same PWM function block: */
-
 #define MX3_PWMCR                      0x00    /* PWM Control Register */
 #define MX3_PWMSR                      0x04    /* PWM Status Register */
 #define MX3_PWMSAR                     0x0C    /* PWM Sample Register */
 /* PWMPR register value of 0xffff has the same effect as 0xfffe */
 #define MX3_PWMPR_MAX                  0xfffe
 
-struct imx_chip {
+struct pwm_imx27_chip {
        struct clk      *clk_ipg;
-
        struct clk      *clk_per;
-
        void __iomem    *mmio_base;
-
        struct pwm_chip chip;
 };
 
-#define to_imx_chip(chip)      container_of(chip, struct imx_chip, chip)
+#define to_pwm_imx27_chip(chip)        container_of(chip, struct pwm_imx27_chip, chip)
 
-static int imx_pwm_clk_prepare_enable(struct pwm_chip *chip)
+static int pwm_imx27_clk_prepare_enable(struct pwm_chip *chip)
 {
-       struct imx_chip *imx = to_imx_chip(chip);
+       struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
        int ret;
 
        ret = clk_prepare_enable(imx->clk_ipg);
@@ -116,35 +103,32 @@ static int imx_pwm_clk_prepare_enable(struct pwm_chip *chip)
        return 0;
 }
 
-static void imx_pwm_clk_disable_unprepare(struct pwm_chip *chip)
+static void pwm_imx27_clk_disable_unprepare(struct pwm_chip *chip)
 {
-       struct imx_chip *imx = to_imx_chip(chip);
+       struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
 
        clk_disable_unprepare(imx->clk_per);
        clk_disable_unprepare(imx->clk_ipg);
 }
 
-static void imx_pwm_get_state(struct pwm_chip *chip,
-               struct pwm_device *pwm, struct pwm_state *state)
+static void pwm_imx27_get_state(struct pwm_chip *chip,
+                               struct pwm_device *pwm, struct pwm_state *state)
 {
-       struct imx_chip *imx = to_imx_chip(chip);
-       u32 period, prescaler, pwm_clk, ret, val;
+       struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
+       u32 period, prescaler, pwm_clk, val;
        u64 tmp;
+       int ret;
 
-       ret = imx_pwm_clk_prepare_enable(chip);
+       ret = pwm_imx27_clk_prepare_enable(chip);
        if (ret < 0)
                return;
 
        val = readl(imx->mmio_base + MX3_PWMCR);
 
-       if (val & MX3_PWMCR_EN) {
+       if (val & MX3_PWMCR_EN)
                state->enabled = true;
-               ret = imx_pwm_clk_prepare_enable(chip);
-               if (ret)
-                       return;
-       } else {
+       else
                state->enabled = false;
-       }
 
        switch (FIELD_GET(MX3_PWMCR_POUTC, val)) {
        case MX3_PWMCR_POUTC_NORMAL:
@@ -176,70 +160,13 @@ static void imx_pwm_get_state(struct pwm_chip *chip,
                state->duty_cycle = 0;
        }
 
-       imx_pwm_clk_disable_unprepare(chip);
-}
-
-static int imx_pwm_config_v1(struct pwm_chip *chip,
-               struct pwm_device *pwm, int duty_ns, int period_ns)
-{
-       struct imx_chip *imx = to_imx_chip(chip);
-
-       /*
-        * The PWM subsystem allows for exact frequencies. However,
-        * I cannot connect a scope on my device to the PWM line and
-        * thus cannot provide the program the PWM controller
-        * exactly. Instead, I'm relying on the fact that the
-        * Bootloader (u-boot or WinCE+haret) has programmed the PWM
-        * function group already. So I'll just modify the PWM sample
-        * register to follow the ratio of duty_ns vs. period_ns
-        * accordingly.
-        *
-        * This is good enough for programming the brightness of
-        * the LCD backlight.
-        *
-        * The real implementation would divide PERCLK[0] first by
-        * both the prescaler (/1 .. /128) and then by CLKSEL
-        * (/2 .. /16).
-        */
-       u32 max = readl(imx->mmio_base + MX1_PWMP);
-       u32 p = max * duty_ns / period_ns;
-       writel(max - p, imx->mmio_base + MX1_PWMS);
-
-       return 0;
-}
-
-static int imx_pwm_enable_v1(struct pwm_chip *chip, struct pwm_device *pwm)
-{
-       struct imx_chip *imx = to_imx_chip(chip);
-       u32 val;
-       int ret;
-
-       ret = imx_pwm_clk_prepare_enable(chip);
-       if (ret < 0)
-               return ret;
-
-       val = readl(imx->mmio_base + MX1_PWMC);
-       val |= MX1_PWMC_EN;
-       writel(val, imx->mmio_base + MX1_PWMC);
-
-       return 0;
-}
-
-static void imx_pwm_disable_v1(struct pwm_chip *chip, struct pwm_device *pwm)
-{
-       struct imx_chip *imx = to_imx_chip(chip);
-       u32 val;
-
-       val = readl(imx->mmio_base + MX1_PWMC);
-       val &= ~MX1_PWMC_EN;
-       writel(val, imx->mmio_base + MX1_PWMC);
-
-       imx_pwm_clk_disable_unprepare(chip);
+       if (!state->enabled)
+               pwm_imx27_clk_disable_unprepare(chip);
 }
 
-static void imx_pwm_sw_reset(struct pwm_chip *chip)
+static void pwm_imx27_sw_reset(struct pwm_chip *chip)
 {
-       struct imx_chip *imx = to_imx_chip(chip);
+       struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
        struct device *dev = chip->dev;
        int wait_count = 0;
        u32 cr;
@@ -255,10 +182,10 @@ static void imx_pwm_sw_reset(struct pwm_chip *chip)
                dev_warn(dev, "software reset timeout\n");
 }
 
-static void imx_pwm_wait_fifo_slot(struct pwm_chip *chip,
-                                  struct pwm_device *pwm)
+static void pwm_imx27_wait_fifo_slot(struct pwm_chip *chip,
+                                    struct pwm_device *pwm)
 {
-       struct imx_chip *imx = to_imx_chip(chip);
+       struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
        struct device *dev = chip->dev;
        unsigned int period_ms;
        int fifoav;
@@ -277,11 +204,11 @@ static void imx_pwm_wait_fifo_slot(struct pwm_chip *chip,
        }
 }
 
-static int imx_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm,
-                           struct pwm_state *state)
+static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+                          struct pwm_state *state)
 {
        unsigned long period_cycles, duty_cycles, prescale;
-       struct imx_chip *imx = to_imx_chip(chip);
+       struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
        struct pwm_state cstate;
        unsigned long long c;
        int ret;
@@ -318,13 +245,13 @@ static int imx_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm,
                 * enabled.
                 */
                if (cstate.enabled) {
-                       imx_pwm_wait_fifo_slot(chip, pwm);
+                       pwm_imx27_wait_fifo_slot(chip, pwm);
                } else {
-                       ret = imx_pwm_clk_prepare_enable(chip);
+                       ret = pwm_imx27_clk_prepare_enable(chip);
                        if (ret)
                                return ret;
 
-                       imx_pwm_sw_reset(chip);
+                       pwm_imx27_sw_reset(chip);
                }
 
                writel(duty_cycles, imx->mmio_base + MX3_PWMSAR);
@@ -343,64 +270,35 @@ static int imx_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm,
        } else if (cstate.enabled) {
                writel(0, imx->mmio_base + MX3_PWMCR);
 
-               imx_pwm_clk_disable_unprepare(chip);
+               pwm_imx27_clk_disable_unprepare(chip);
        }
 
        return 0;
 }
 
-static const struct pwm_ops imx_pwm_ops_v1 = {
-       .enable = imx_pwm_enable_v1,
-       .disable = imx_pwm_disable_v1,
-       .config = imx_pwm_config_v1,
+static const struct pwm_ops pwm_imx27_ops = {
+       .apply = pwm_imx27_apply,
+       .get_state = pwm_imx27_get_state,
        .owner = THIS_MODULE,
 };
 
-static const struct pwm_ops imx_pwm_ops_v2 = {
-       .apply = imx_pwm_apply_v2,
-       .get_state = imx_pwm_get_state,
-       .owner = THIS_MODULE,
-};
-
-struct imx_pwm_data {
-       bool polarity_supported;
-       const struct pwm_ops *ops;
-};
-
-static struct imx_pwm_data imx_pwm_data_v1 = {
-       .ops = &imx_pwm_ops_v1,
-};
-
-static struct imx_pwm_data imx_pwm_data_v2 = {
-       .polarity_supported = true,
-       .ops = &imx_pwm_ops_v2,
-};
-
-static const struct of_device_id imx_pwm_dt_ids[] = {
-       { .compatible = "fsl,imx1-pwm", .data = &imx_pwm_data_v1, },
-       { .compatible = "fsl,imx27-pwm", .data = &imx_pwm_data_v2, },
+static const struct of_device_id pwm_imx27_dt_ids[] = {
+       { .compatible = "fsl,imx27-pwm", },
        { /* sentinel */ }
 };
-MODULE_DEVICE_TABLE(of, imx_pwm_dt_ids);
+MODULE_DEVICE_TABLE(of, pwm_imx27_dt_ids);
 
-static int imx_pwm_probe(struct platform_device *pdev)
+static int pwm_imx27_probe(struct platform_device *pdev)
 {
-       const struct of_device_id *of_id =
-                       of_match_device(imx_pwm_dt_ids, &pdev->dev);
-       const struct imx_pwm_data *data;
-       struct imx_chip *imx;
+       struct pwm_imx27_chip *imx;
        struct resource *r;
-       int ret = 0;
-
-       if (!of_id)
-               return -ENODEV;
-
-       data = of_id->data;
 
        imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL);
        if (imx == NULL)
                return -ENOMEM;
 
+       platform_set_drvdata(pdev, imx);
+
        imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
        if (IS_ERR(imx->clk_ipg)) {
                dev_err(&pdev->dev, "getting ipg clock failed with %ld\n",
@@ -410,57 +308,51 @@ static int imx_pwm_probe(struct platform_device *pdev)
 
        imx->clk_per = devm_clk_get(&pdev->dev, "per");
        if (IS_ERR(imx->clk_per)) {
-               dev_err(&pdev->dev, "getting per clock failed with %ld\n",
-                               PTR_ERR(imx->clk_per));
-               return PTR_ERR(imx->clk_per);
+               int ret = PTR_ERR(imx->clk_per);
+
+               if (ret != -EPROBE_DEFER)
+                       dev_err(&pdev->dev,
+                               "failed to get peripheral clock: %d\n",
+                               ret);
+
+               return ret;
        }
 
-       imx->chip.ops = data->ops;
+       imx->chip.ops = &pwm_imx27_ops;
        imx->chip.dev = &pdev->dev;
        imx->chip.base = -1;
        imx->chip.npwm = 1;
 
-       if (data->polarity_supported) {
-               dev_dbg(&pdev->dev, "PWM supports output inversion\n");
-               imx->chip.of_xlate = of_pwm_xlate_with_flags;
-               imx->chip.of_pwm_n_cells = 3;
-       }
+       imx->chip.of_xlate = of_pwm_xlate_with_flags;
+       imx->chip.of_pwm_n_cells = 3;
 
        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        imx->mmio_base = devm_ioremap_resource(&pdev->dev, r);
        if (IS_ERR(imx->mmio_base))
                return PTR_ERR(imx->mmio_base);
 
-       ret = pwmchip_add(&imx->chip);
-       if (ret < 0)
-               return ret;
-
-       platform_set_drvdata(pdev, imx);
-       return 0;
+       return pwmchip_add(&imx->chip);
 }
 
-static int imx_pwm_remove(struct platform_device *pdev)
+static int pwm_imx27_remove(struct platform_device *pdev)
 {
-       struct imx_chip *imx;
+       struct pwm_imx27_chip *imx;
 
        imx = platform_get_drvdata(pdev);
-       if (imx == NULL)
-               return -ENODEV;
 
-       imx_pwm_clk_disable_unprepare(&imx->chip);
+       pwm_imx27_clk_disable_unprepare(&imx->chip);
 
        return pwmchip_remove(&imx->chip);
 }
 
 static struct platform_driver imx_pwm_driver = {
-       .driver         = {
-               .name   = "imx-pwm",
-               .of_match_table = imx_pwm_dt_ids,
+       .driver = {
+               .name = "pwm-imx27",
+               .of_match_table = pwm_imx27_dt_ids,
        },
-       .probe          = imx_pwm_probe,
-       .remove         = imx_pwm_remove,
+       .probe = pwm_imx27_probe,
+       .remove = pwm_imx27_remove,
 };
-
 module_platform_driver(imx_pwm_driver);
 
 MODULE_LICENSE("GPL v2");
index 893940d45f0d0859d5aaeaa6cd70ff67bb5dd34b..15803c71fe80dda431ea51a9c79ba3f7450615ba 100644 (file)
@@ -277,10 +277,21 @@ static const struct mtk_pwm_data mt8173_pwm_data = {
        .commit_mask = 0x1,
 };
 
+static const struct mtk_pwm_data mt8183_pwm_data = {
+       .enable_mask = BIT(0),
+       .con0 = 0x18,
+       .con0_sel = 0x0,
+       .con1 = 0x1c,
+       .has_commit = false,
+       .bls_debug = 0x80,
+       .bls_debug_mask = 0x3,
+};
+
 static const struct of_device_id mtk_disp_pwm_of_match[] = {
        { .compatible = "mediatek,mt2701-disp-pwm", .data = &mt2701_pwm_data},
        { .compatible = "mediatek,mt6595-disp-pwm", .data = &mt8173_pwm_data},
        { .compatible = "mediatek,mt8173-disp-pwm", .data = &mt8173_pwm_data},
+       { .compatible = "mediatek,mt8183-disp-pwm", .data = &mt8183_pwm_data},
        { }
 };
 MODULE_DEVICE_TABLE(of, mtk_disp_pwm_of_match);
index a41812fc6f95733a2568eb0d6b5c7f44fe5a1741..cfe7dd1b448e1b15ce72b41cbaafd7b65703106c 100644 (file)
@@ -8,6 +8,8 @@
 #include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/io.h>
+#include <linux/log2.h>
+#include <linux/math64.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
@@ -68,19 +70,15 @@ static void rcar_pwm_update(struct rcar_pwm_chip *rp, u32 mask, u32 data,
 static int rcar_pwm_get_clock_division(struct rcar_pwm_chip *rp, int period_ns)
 {
        unsigned long clk_rate = clk_get_rate(rp->clk);
-       unsigned long long max; /* max cycle / nanoseconds */
-       unsigned int div;
+       u64 div, tmp;
 
        if (clk_rate == 0)
                return -EINVAL;
 
-       for (div = 0; div <= RCAR_PWM_MAX_DIVISION; div++) {
-               max = (unsigned long long)NSEC_PER_SEC * RCAR_PWM_MAX_CYCLE *
-                       (1 << div);
-               do_div(max, clk_rate);
-               if (period_ns <= max)
-                       break;
-       }
+       div = (u64)NSEC_PER_SEC * RCAR_PWM_MAX_CYCLE;
+       tmp = (u64)period_ns * clk_rate + div - 1;
+       tmp = div64_u64(tmp, div);
+       div = ilog2(tmp - 1) + 1;
 
        return (div <= RCAR_PWM_MAX_DIVISION) ? div : -ERANGE;
 }
@@ -139,39 +137,8 @@ static void rcar_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
        pm_runtime_put(chip->dev);
 }
 
-static int rcar_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
-                          int duty_ns, int period_ns)
+static int rcar_pwm_enable(struct rcar_pwm_chip *rp)
 {
-       struct rcar_pwm_chip *rp = to_rcar_pwm_chip(chip);
-       int div, ret;
-
-       div = rcar_pwm_get_clock_division(rp, period_ns);
-       if (div < 0)
-               return div;
-
-       /*
-        * Let the core driver set pwm->period if disabled and duty_ns == 0.
-        * But, this driver should prevent to set the new duty_ns if current
-        * duty_cycle is not set
-        */
-       if (!pwm_is_enabled(pwm) && !duty_ns && !pwm->state.duty_cycle)
-               return 0;
-
-       rcar_pwm_update(rp, RCAR_PWMCR_SYNC, RCAR_PWMCR_SYNC, RCAR_PWMCR);
-
-       ret = rcar_pwm_set_counter(rp, div, duty_ns, period_ns);
-       if (!ret)
-               rcar_pwm_set_clock_control(rp, div);
-
-       /* The SYNC should be set to 0 even if rcar_pwm_set_counter failed */
-       rcar_pwm_update(rp, RCAR_PWMCR_SYNC, 0, RCAR_PWMCR);
-
-       return ret;
-}
-
-static int rcar_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
-{
-       struct rcar_pwm_chip *rp = to_rcar_pwm_chip(chip);
        u32 value;
 
        /* Don't enable the PWM device if CYC0 or PH0 is 0 */
@@ -185,19 +152,51 @@ static int rcar_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
        return 0;
 }
 
-static void rcar_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+static void rcar_pwm_disable(struct rcar_pwm_chip *rp)
+{
+       rcar_pwm_update(rp, RCAR_PWMCR_EN0, 0, RCAR_PWMCR);
+}
+
+static int rcar_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+                         struct pwm_state *state)
 {
        struct rcar_pwm_chip *rp = to_rcar_pwm_chip(chip);
+       struct pwm_state cur_state;
+       int div, ret;
 
-       rcar_pwm_update(rp, RCAR_PWMCR_EN0, 0, RCAR_PWMCR);
+       /* This HW/driver only supports normal polarity */
+       pwm_get_state(pwm, &cur_state);
+       if (state->polarity != PWM_POLARITY_NORMAL)
+               return -ENOTSUPP;
+
+       if (!state->enabled) {
+               rcar_pwm_disable(rp);
+               return 0;
+       }
+
+       div = rcar_pwm_get_clock_division(rp, state->period);
+       if (div < 0)
+               return div;
+
+       rcar_pwm_update(rp, RCAR_PWMCR_SYNC, RCAR_PWMCR_SYNC, RCAR_PWMCR);
+
+       ret = rcar_pwm_set_counter(rp, div, state->duty_cycle, state->period);
+       if (!ret)
+               rcar_pwm_set_clock_control(rp, div);
+
+       /* The SYNC should be set to 0 even if rcar_pwm_set_counter failed */
+       rcar_pwm_update(rp, RCAR_PWMCR_SYNC, 0, RCAR_PWMCR);
+
+       if (!ret && state->enabled)
+               ret = rcar_pwm_enable(rp);
+
+       return ret;
 }
 
 static const struct pwm_ops rcar_pwm_ops = {
        .request = rcar_pwm_request,
        .free = rcar_pwm_free,
-       .config = rcar_pwm_config,
-       .enable = rcar_pwm_enable,
-       .disable = rcar_pwm_disable,
+       .apply = rcar_pwm_apply,
        .owner = THIS_MODULE,
 };
 
@@ -279,18 +278,16 @@ static int rcar_pwm_suspend(struct device *dev)
 static int rcar_pwm_resume(struct device *dev)
 {
        struct pwm_device *pwm = rcar_pwm_dev_to_pwm_dev(dev);
+       struct pwm_state state;
 
        if (!test_bit(PWMF_REQUESTED, &pwm->flags))
                return 0;
 
        pm_runtime_get_sync(dev);
 
-       rcar_pwm_config(pwm->chip, pwm, pwm->state.duty_cycle,
-                       pwm->state.period);
-       if (pwm_is_enabled(pwm))
-               rcar_pwm_enable(pwm->chip, pwm);
+       pwm_get_state(pwm, &state);
 
-       return 0;
+       return rcar_pwm_apply(pwm->chip, pwm, &state);
 }
 #endif /* CONFIG_PM_SLEEP */
 static SIMPLE_DEV_PM_OPS(rcar_pwm_pm_ops, rcar_pwm_suspend, rcar_pwm_resume);
index d5199b507d79527a6f5278194596b0c42f452e9c..b628abfffacc26a253214352b7de7b1580e4dfe6 100644 (file)
@@ -242,11 +242,7 @@ pwm_set_relative_duty_cycle(struct pwm_state *state, unsigned int duty_cycle,
  * struct pwm_ops - PWM controller operations
  * @request: optional hook for requesting a PWM
  * @free: optional hook for freeing a PWM
- * @config: configure duty cycles and period length for this PWM
- * @set_polarity: configure the polarity of this PWM
  * @capture: capture and report PWM signal
- * @enable: enable PWM output toggling
- * @disable: disable PWM output toggling
  * @apply: atomically apply a new PWM config. The state argument
  *        should be adjusted with the real hardware config (if the
  *        approximate the period or duty_cycle value, state should
@@ -254,53 +250,56 @@ pwm_set_relative_duty_cycle(struct pwm_state *state, unsigned int duty_cycle,
  * @get_state: get the current PWM state. This function is only
  *            called once per PWM device when the PWM chip is
  *            registered.
- * @dbg_show: optional routine to show contents in debugfs
  * @owner: helps prevent removal of modules exporting active PWMs
+ * @config: configure duty cycles and period length for this PWM
+ * @set_polarity: configure the polarity of this PWM
+ * @enable: enable PWM output toggling
+ * @disable: disable PWM output toggling
  */
 struct pwm_ops {
        int (*request)(struct pwm_chip *chip, struct pwm_device *pwm);
        void (*free)(struct pwm_chip *chip, struct pwm_device *pwm);
-       int (*config)(struct pwm_chip *chip, struct pwm_device *pwm,
-                     int duty_ns, int period_ns);
-       int (*set_polarity)(struct pwm_chip *chip, struct pwm_device *pwm,
-                           enum pwm_polarity polarity);
        int (*capture)(struct pwm_chip *chip, struct pwm_device *pwm,
                       struct pwm_capture *result, unsigned long timeout);
-       int (*enable)(struct pwm_chip *chip, struct pwm_device *pwm);
-       void (*disable)(struct pwm_chip *chip, struct pwm_device *pwm);
        int (*apply)(struct pwm_chip *chip, struct pwm_device *pwm,
                     struct pwm_state *state);
        void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm,
                          struct pwm_state *state);
-#ifdef CONFIG_DEBUG_FS
-       void (*dbg_show)(struct pwm_chip *chip, struct seq_file *s);
-#endif
        struct module *owner;
+
+       /* Only used by legacy drivers */
+       int (*config)(struct pwm_chip *chip, struct pwm_device *pwm,
+                     int duty_ns, int period_ns);
+       int (*set_polarity)(struct pwm_chip *chip, struct pwm_device *pwm,
+                           enum pwm_polarity polarity);
+       int (*enable)(struct pwm_chip *chip, struct pwm_device *pwm);
+       void (*disable)(struct pwm_chip *chip, struct pwm_device *pwm);
 };
 
 /**
  * struct pwm_chip - abstract a PWM controller
  * @dev: device providing the PWMs
- * @list: list node for internal use
  * @ops: callbacks for this PWM controller
  * @base: number of first PWM controlled by this chip
  * @npwm: number of PWMs controlled by this chip
- * @pwms: array of PWM devices allocated by the framework
  * @of_xlate: request a PWM device given a device tree PWM specifier
  * @of_pwm_n_cells: number of cells expected in the device tree PWM specifier
+ * @list: list node for internal use
+ * @pwms: array of PWM devices allocated by the framework
  */
 struct pwm_chip {
        struct device *dev;
-       struct list_head list;
        const struct pwm_ops *ops;
        int base;
        unsigned int npwm;
 
-       struct pwm_device *pwms;
-
        struct pwm_device * (*of_xlate)(struct pwm_chip *pc,
                                        const struct of_phandle_args *args);
        unsigned int of_pwm_n_cells;
+
+       /* only used internally by the PWM framework */
+       struct list_head list;
+       struct pwm_device *pwms;
 };
 
 /**