Merge tag 'pwm/for-3.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 21 Oct 2014 15:17:43 +0000 (08:17 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 21 Oct 2014 15:17:43 +0000 (08:17 -0700)
Pull pwm changes from Thierry Reding:
 "There are no new drivers here, only a couple of fixes all over the
  place"

* tag 'pwm/for-3.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm:
  pwm: Let PWM_CLPS711X depend on HAS_IOMEM
  pwm: atmel: Fix calculation of prescale value
  pwm: Fix uninitialized warnings in pwm_get()
  pwm: rockchip: Allow polarity invert on rk3288
  pwm: imx: Avoid sample FIFO overflow for i.MX PWM version2
  pwm: imx: Cleanup indentation for register definitions
  pwm: imx: Fix the macro MX3_PWMCR_PRESCALER(x) definition
  pwm: Fix possible ZERO_SIZE_PTR pointer dereferencing error.
  pwm: lpss: make it buildable only on X86
  pwm: lpss: use c99 initializers in structures
  pwm: lpss: Fix build failure on PowerPC
  pwm: lpss: pci: Move to use pcim_enable_device()
  pwm: lpss: Properly split driver to parts
  pwm: lpss: Add ACPI and PCI IDs for Intel Braswell
  pwm: fsl-ftm: Select REGMAP_MMIO
  pwm: fsl-ftm: Document 'big-endian' property
  pwm: fsl-ftm: Convert to direct regmap API usage
  pwm: fsl-ftm: Clean up the code

13 files changed:
Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt
Documentation/devicetree/bindings/pwm/pwm-rockchip.txt
drivers/pwm/Kconfig
drivers/pwm/Makefile
drivers/pwm/core.c
drivers/pwm/pwm-atmel.c
drivers/pwm/pwm-fsl-ftm.c
drivers/pwm/pwm-imx.c
drivers/pwm/pwm-lpss-pci.c [new file with mode: 0644]
drivers/pwm/pwm-lpss-platform.c [new file with mode: 0644]
drivers/pwm/pwm-lpss.c
drivers/pwm/pwm-lpss.h [new file with mode: 0644]
drivers/pwm/pwm-rockchip.c

index 0bda229a6171d219a393d7133305a5853943bd1a..3899d6a557c15af8482f3c0574404e437b462e5d 100644 (file)
@@ -1,5 +1,20 @@
 Freescale FlexTimer Module (FTM) PWM controller
 
+The same FTM PWM device can have a different endianness on different SoCs. The
+device tree provides a property to describing this so that an operating system
+device driver can handle all variants of the device. Refer to the table below
+for the endianness of the FTM PWM block as integrated into the existing SoCs:
+
+       SoC     | FTM-PWM endianness
+       --------+-------------------
+       Vybrid  | LE
+       LS1     | BE
+       LS2     | LE
+
+Please see ../regmap/regmap.txt for more detail about how to specify endian
+modes in device tree.
+
+
 Required properties:
 - compatible: Should be "fsl,vf610-ftm-pwm".
 - reg: Physical base address and length of the controller's registers
@@ -16,7 +31,8 @@ Required properties:
 - pinctrl-names: Must contain a "default" entry.
 - pinctrl-NNN: One property must exist for each entry in pinctrl-names.
   See pinctrl/pinctrl-bindings.txt for details of the property values.
-
+- big-endian: Boolean property, required if the FTM PWM registers use a big-
+  endian rather than little-endian layout.
 
 Example:
 
@@ -32,4 +48,5 @@ pwm0: pwm@40038000 {
                        <&clks VF610_CLK_FTM0_EXT_FIX_EN>;
                pinctrl-names = "default";
                pinctrl-0 = <&pinctrl_pwm0_1>;
+               big-endian;
 };
index d47d15a6a2985e7f3eae08db0d9ad3986e80c227..b8be3d09ee26b7522fa373eefd1903aef8ce3d41 100644 (file)
@@ -7,8 +7,8 @@ Required properties:
    "rockchip,vop-pwm": found integrated in VOP on RK3288 SoC
  - reg: physical base address and length of the controller's registers
  - clocks: phandle and clock specifier of the PWM reference clock
- - #pwm-cells: should be 2. See pwm.txt in this directory for a
-   description of the cell format.
+ - #pwm-cells: must be 2 (rk2928) or 3 (rk3288). See pwm.txt in this directory
+   for a description of the cell format.
 
 Example:
 
index b800783800a3a133895d0cf6dcb3498e48528516..ef2dd2e4754bacd784cd519ec7f4b66216a13e99 100644 (file)
@@ -83,6 +83,7 @@ config PWM_BFIN
 config PWM_CLPS711X
        tristate "CLPS711X PWM support"
        depends on ARCH_CLPS711X || COMPILE_TEST
+       depends on HAS_IOMEM
        help
          Generic PWM framework driver for Cirrus Logic CLPS711X.
 
@@ -101,6 +102,7 @@ config PWM_EP93XX
 config PWM_FSL_FTM
        tristate "Freescale FlexTimer Module (FTM) PWM support"
        depends on OF
+       select REGMAP_MMIO
        help
          Generic FTM PWM framework driver for Freescale VF610 and
          Layerscape LS-1 SoCs.
@@ -149,7 +151,7 @@ config PWM_LPC32XX
 
 config PWM_LPSS
        tristate "Intel LPSS PWM support"
-       depends on ACPI
+       depends on X86
        help
          Generic PWM framework driver for Intel Low Power Subsystem PWM
          controller.
@@ -157,6 +159,24 @@ config PWM_LPSS
          To compile this driver as a module, choose M here: the module
          will be called pwm-lpss.
 
+config PWM_LPSS_PCI
+       tristate "Intel LPSS PWM PCI driver"
+       depends on PWM_LPSS && PCI
+       help
+         The PCI driver for Intel Low Power Subsystem PWM controller.
+
+         To compile this driver as a module, choose M here: the module
+         will be called pwm-lpss-pci.
+
+config PWM_LPSS_PLATFORM
+       tristate "Intel LPSS PWM platform driver"
+       depends on PWM_LPSS && ACPI
+       help
+         The platform driver for Intel Low Power Subsystem PWM controller.
+
+         To compile this driver as a module, choose M here: the module
+         will be called pwm-lpss-platform.
+
 config PWM_MXS
        tristate "Freescale MXS PWM support"
        depends on ARCH_MXS && OF
index f8c577d410916e7b7c4c889a4138d62d3c0fc326..c458606c3755c327ed5e648c3ddc68e2b884ffeb 100644 (file)
@@ -13,6 +13,8 @@ obj-$(CONFIG_PWM_JZ4740)      += pwm-jz4740.o
 obj-$(CONFIG_PWM_LP3943)       += pwm-lp3943.o
 obj-$(CONFIG_PWM_LPC32XX)      += pwm-lpc32xx.o
 obj-$(CONFIG_PWM_LPSS)         += pwm-lpss.o
+obj-$(CONFIG_PWM_LPSS_PCI)     += pwm-lpss-pci.o
+obj-$(CONFIG_PWM_LPSS_PLATFORM)        += pwm-lpss-platform.o
 obj-$(CONFIG_PWM_MXS)          += pwm-mxs.o
 obj-$(CONFIG_PWM_PCA9685)      += pwm-pca9685.o
 obj-$(CONFIG_PWM_PUV3)         += pwm-puv3.o
index d2c35920ff08e7e84e4f1a6be3ce1c15454b2a4b..966497d10c6ef201144fd6cd66edef7298b18c99 100644 (file)
@@ -236,7 +236,7 @@ int pwmchip_add(struct pwm_chip *chip)
        int ret;
 
        if (!chip || !chip->dev || !chip->ops || !chip->ops->config ||
-           !chip->ops->enable || !chip->ops->disable)
+           !chip->ops->enable || !chip->ops->disable || !chip->npwm)
                return -EINVAL;
 
        mutex_lock(&pwm_lock);
@@ -602,12 +602,9 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)
        struct pwm_device *pwm = ERR_PTR(-EPROBE_DEFER);
        const char *dev_id = dev ? dev_name(dev) : NULL;
        struct pwm_chip *chip = NULL;
-       unsigned int index = 0;
        unsigned int best = 0;
-       struct pwm_lookup *p;
+       struct pwm_lookup *p, *chosen = NULL;
        unsigned int match;
-       unsigned int period;
-       enum pwm_polarity polarity;
 
        /* look up via DT first */
        if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node)
@@ -653,10 +650,7 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)
                }
 
                if (match > best) {
-                       chip = pwmchip_find_by_name(p->provider);
-                       index = p->index;
-                       period = p->period;
-                       polarity = p->polarity;
+                       chosen = p;
 
                        if (match != 3)
                                best = match;
@@ -665,17 +659,22 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)
                }
        }
 
-       mutex_unlock(&pwm_lookup_lock);
+       if (!chosen)
+               goto out;
 
-       if (chip)
-               pwm = pwm_request_from_chip(chip, index, con_id ?: dev_id);
-       if (IS_ERR(pwm))
-               return pwm;
+       chip = pwmchip_find_by_name(chosen->provider);
+       if (!chip)
+               goto out;
 
-       pwm_set_period(pwm, period);
-       pwm_set_polarity(pwm, polarity);
+       pwm = pwm_request_from_chip(chip, chosen->index, con_id ?: dev_id);
+       if (IS_ERR(pwm))
+               goto out;
 
+       pwm_set_period(pwm, chosen->period);
+       pwm_set_polarity(pwm, chosen->polarity);
 
+out:
+       mutex_unlock(&pwm_lookup_lock);
        return pwm;
 }
 EXPORT_SYMBOL_GPL(pwm_get);
index 6e700a541ca32ded0d5881842214a53d87ffe0f6..d3c22de9ee47b8e7546557bc756e62a353c62556 100644 (file)
@@ -102,7 +102,7 @@ static int atmel_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
                            int duty_ns, int period_ns)
 {
        struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
-       unsigned long clk_rate, prd, dty;
+       unsigned long prd, dty;
        unsigned long long div;
        unsigned int pres = 0;
        u32 val;
@@ -113,20 +113,18 @@ static int atmel_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
                return -EBUSY;
        }
 
-       clk_rate = clk_get_rate(atmel_pwm->clk);
-       div = clk_rate;
+       /* Calculate the period cycles and prescale value */
+       div = (unsigned long long)clk_get_rate(atmel_pwm->clk) * period_ns;
+       do_div(div, NSEC_PER_SEC);
 
-       /* Calculate the period cycles */
        while (div > PWM_MAX_PRD) {
-               div = clk_rate / (1 << pres);
-               div = div * period_ns;
-               /* 1/Hz = 100000000 ns */
-               do_div(div, 1000000000);
-
-               if (pres++ > PRD_MAX_PRES) {
-                       dev_err(chip->dev, "pres exceeds the maximum value\n");
-                       return -EINVAL;
-               }
+               div >>= 1;
+               pres++;
+       }
+
+       if (pres > PRD_MAX_PRES) {
+               dev_err(chip->dev, "pres exceeds the maximum value\n");
+               return -EINVAL;
        }
 
        /* Calculate the duty cycles */
index a18bc8fea3853bf96671938ab021cb1d9c71f66b..0f2cc7ef77848d3cec24ad9e4e6e428bbad7f318 100644 (file)
 #include <linux/of_address.h>
 #include <linux/platform_device.h>
 #include <linux/pwm.h>
+#include <linux/regmap.h>
 #include <linux/slab.h>
 
 #define FTM_SC         0x00
-#define FTM_SC_CLK_MASK        0x3
-#define FTM_SC_CLK_SHIFT       3
-#define FTM_SC_CLK(c)  (((c) + 1) << FTM_SC_CLK_SHIFT)
+#define FTM_SC_CLK_MASK_SHIFT  3
+#define FTM_SC_CLK_MASK        (3 << FTM_SC_CLK_MASK_SHIFT)
+#define FTM_SC_CLK(c)  (((c) + 1) << FTM_SC_CLK_MASK_SHIFT)
 #define FTM_SC_PS_MASK 0x7
-#define FTM_SC_PS_SHIFT        0
 
 #define FTM_CNT                0x04
 #define FTM_MOD                0x08
@@ -83,7 +83,7 @@ struct fsl_pwm_chip {
        unsigned int cnt_select;
        unsigned int clk_ps;
 
-       void __iomem *base;
+       struct regmap *regmap;
 
        int period_ns;
 
@@ -219,10 +219,11 @@ static unsigned long fsl_pwm_calculate_duty(struct fsl_pwm_chip *fpc,
                                            unsigned long period_ns,
                                            unsigned long duty_ns)
 {
-       unsigned long long val, duty;
+       unsigned long long duty;
+       u32 val;
 
-       val = readl(fpc->base + FTM_MOD);
-       duty = duty_ns * (val + 1);
+       regmap_read(fpc->regmap, FTM_MOD, &val);
+       duty = (unsigned long long)duty_ns * (val + 1);
        do_div(duty, period_ns);
 
        return (unsigned long)duty;
@@ -232,7 +233,7 @@ static int fsl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
                          int duty_ns, int period_ns)
 {
        struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
-       u32 val, period, duty;
+       u32 period, duty;
 
        mutex_lock(&fpc->lock);
 
@@ -257,11 +258,9 @@ static int fsl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
                        return -EINVAL;
                }
 
-               val = readl(fpc->base + FTM_SC);
-               val &= ~(FTM_SC_PS_MASK << FTM_SC_PS_SHIFT);
-               val |= fpc->clk_ps;
-               writel(val, fpc->base + FTM_SC);
-               writel(period - 1, fpc->base + FTM_MOD);
+               regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_PS_MASK,
+                                  fpc->clk_ps);
+               regmap_write(fpc->regmap, FTM_MOD, period - 1);
 
                fpc->period_ns = period_ns;
        }
@@ -270,8 +269,9 @@ static int fsl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 
        duty = fsl_pwm_calculate_duty(fpc, period_ns, duty_ns);
 
-       writel(FTM_CSC_MSB | FTM_CSC_ELSB, fpc->base + FTM_CSC(pwm->hwpwm));
-       writel(duty, fpc->base + FTM_CV(pwm->hwpwm));
+       regmap_write(fpc->regmap, FTM_CSC(pwm->hwpwm),
+                    FTM_CSC_MSB | FTM_CSC_ELSB);
+       regmap_write(fpc->regmap, FTM_CV(pwm->hwpwm), duty);
 
        return 0;
 }
@@ -283,31 +283,28 @@ static int fsl_pwm_set_polarity(struct pwm_chip *chip,
        struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
        u32 val;
 
-       val = readl(fpc->base + FTM_POL);
+       regmap_read(fpc->regmap, FTM_POL, &val);
 
        if (polarity == PWM_POLARITY_INVERSED)
                val |= BIT(pwm->hwpwm);
        else
                val &= ~BIT(pwm->hwpwm);
 
-       writel(val, fpc->base + FTM_POL);
+       regmap_write(fpc->regmap, FTM_POL, val);
 
        return 0;
 }
 
 static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc)
 {
-       u32 val;
        int ret;
 
        if (fpc->use_count != 0)
                return 0;
 
        /* select counter clock source */
-       val = readl(fpc->base + FTM_SC);
-       val &= ~(FTM_SC_CLK_MASK << FTM_SC_CLK_SHIFT);
-       val |= FTM_SC_CLK(fpc->cnt_select);
-       writel(val, fpc->base + FTM_SC);
+       regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_CLK_MASK,
+                          FTM_SC_CLK(fpc->cnt_select));
 
        ret = clk_prepare_enable(fpc->clk[fpc->cnt_select]);
        if (ret)
@@ -327,13 +324,10 @@ static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc)
 static int fsl_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
        struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
-       u32 val;
        int ret;
 
        mutex_lock(&fpc->lock);
-       val = readl(fpc->base + FTM_OUTMASK);
-       val &= ~BIT(pwm->hwpwm);
-       writel(val, fpc->base + FTM_OUTMASK);
+       regmap_update_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm), 0);
 
        ret = fsl_counter_clock_enable(fpc);
        mutex_unlock(&fpc->lock);
@@ -343,8 +337,6 @@ static int fsl_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
 
 static void fsl_counter_clock_disable(struct fsl_pwm_chip *fpc)
 {
-       u32 val;
-
        /*
         * already disabled, do nothing
         */
@@ -356,9 +348,7 @@ static void fsl_counter_clock_disable(struct fsl_pwm_chip *fpc)
                return;
 
        /* no users left, disable PWM counter clock */
-       val = readl(fpc->base + FTM_SC);
-       val &= ~(FTM_SC_CLK_MASK << FTM_SC_CLK_SHIFT);
-       writel(val, fpc->base + FTM_SC);
+       regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_CLK_MASK, 0);
 
        clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]);
        clk_disable_unprepare(fpc->clk[fpc->cnt_select]);
@@ -370,14 +360,12 @@ static void fsl_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
        u32 val;
 
        mutex_lock(&fpc->lock);
-       val = readl(fpc->base + FTM_OUTMASK);
-       val |= BIT(pwm->hwpwm);
-       writel(val, fpc->base + FTM_OUTMASK);
+       regmap_update_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm),
+                          BIT(pwm->hwpwm));
 
        fsl_counter_clock_disable(fpc);
 
-       val = readl(fpc->base + FTM_OUTMASK);
-
+       regmap_read(fpc->regmap, FTM_OUTMASK, &val);
        if ((val & 0xFF) == 0xFF)
                fpc->period_ns = 0;
 
@@ -402,19 +390,28 @@ static int fsl_pwm_init(struct fsl_pwm_chip *fpc)
        if (ret)
                return ret;
 
-       writel(0x00, fpc->base + FTM_CNTIN);
-       writel(0x00, fpc->base + FTM_OUTINIT);
-       writel(0xFF, fpc->base + FTM_OUTMASK);
+       regmap_write(fpc->regmap, FTM_CNTIN, 0x00);
+       regmap_write(fpc->regmap, FTM_OUTINIT, 0x00);
+       regmap_write(fpc->regmap, FTM_OUTMASK, 0xFF);
 
        clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]);
 
        return 0;
 }
 
+static const struct regmap_config fsl_pwm_regmap_config = {
+       .reg_bits = 32,
+       .reg_stride = 4,
+       .val_bits = 32,
+
+       .max_register = FTM_PWMLOAD,
+};
+
 static int fsl_pwm_probe(struct platform_device *pdev)
 {
        struct fsl_pwm_chip *fpc;
        struct resource *res;
+       void __iomem *base;
        int ret;
 
        fpc = devm_kzalloc(&pdev->dev, sizeof(*fpc), GFP_KERNEL);
@@ -426,9 +423,16 @@ static int fsl_pwm_probe(struct platform_device *pdev)
        fpc->chip.dev = &pdev->dev;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       fpc->base = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(fpc->base))
-               return PTR_ERR(fpc->base);
+       base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(base))
+               return PTR_ERR(base);
+
+       fpc->regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base,
+                                               &fsl_pwm_regmap_config);
+       if (IS_ERR(fpc->regmap)) {
+               dev_err(&pdev->dev, "regmap init failed\n");
+               return PTR_ERR(fpc->regmap);
+       }
 
        fpc->clk[FSL_PWM_CLK_SYS] = devm_clk_get(&pdev->dev, "ftm_sys");
        if (IS_ERR(fpc->clk[FSL_PWM_CLK_SYS])) {
index 5449d9150d40d245fdd2380ca291ca53aa2b0b31..f8b5f109c1abc570eb891e57a9ce86f05da91e07 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/slab.h>
 #include <linux/err.h>
 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/pwm.h>
 #include <linux/of.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                       0x00   /* PWM Control Register */
+#define MX1_PWMS                       0x04   /* PWM Sample Register */
+#define MX1_PWMP                       0x08   /* PWM Period Register */
 
-#define MX1_PWMC_EN            (1 << 4)
+#define MX1_PWMC_EN                    (1 << 4)
 
 /* i.MX27, i.MX31, i.MX35 share the same PWM function block: */
 
-#define MX3_PWMCR                 0x00    /* PWM Control Register */
-#define MX3_PWMSAR                0x0C    /* PWM Sample Register */
-#define MX3_PWMPR                 0x10    /* PWM Period Register */
-#define MX3_PWMCR_PRESCALER(x)    (((x - 1) & 0xFFF) << 4)
-#define MX3_PWMCR_DOZEEN                (1 << 24)
-#define MX3_PWMCR_WAITEN                (1 << 23)
+#define MX3_PWMCR                      0x00    /* PWM Control Register */
+#define MX3_PWMSR                      0x04    /* PWM Status Register */
+#define MX3_PWMSAR                     0x0C    /* PWM Sample Register */
+#define MX3_PWMPR                      0x10    /* PWM Period Register */
+#define MX3_PWMCR_PRESCALER(x)         ((((x) - 1) & 0xFFF) << 4)
+#define MX3_PWMCR_DOZEEN               (1 << 24)
+#define MX3_PWMCR_WAITEN               (1 << 23)
 #define MX3_PWMCR_DBGEN                        (1 << 22)
-#define MX3_PWMCR_CLKSRC_IPG_HIGH (2 << 16)
-#define MX3_PWMCR_CLKSRC_IPG      (1 << 16)
-#define MX3_PWMCR_EN              (1 << 0)
+#define MX3_PWMCR_CLKSRC_IPG_HIGH      (2 << 16)
+#define MX3_PWMCR_CLKSRC_IPG           (1 << 16)
+#define MX3_PWMCR_SWR                  (1 << 3)
+#define MX3_PWMCR_EN                   (1 << 0)
+#define MX3_PWMSR_FIFOAV_4WORDS                0x4
+#define MX3_PWMSR_FIFOAV_MASK          0x7
+
+#define MX3_PWM_SWR_LOOP               5
 
 struct imx_chip {
        struct clk      *clk_per;
@@ -103,9 +110,43 @@ static int imx_pwm_config_v2(struct pwm_chip *chip,
                struct pwm_device *pwm, int duty_ns, int period_ns)
 {
        struct imx_chip *imx = to_imx_chip(chip);
+       struct device *dev = chip->dev;
        unsigned long long c;
        unsigned long period_cycles, duty_cycles, prescale;
-       u32 cr;
+       unsigned int period_ms;
+       bool enable = test_bit(PWMF_ENABLED, &pwm->flags);
+       int wait_count = 0, fifoav;
+       u32 cr, sr;
+
+       /*
+        * i.MX PWMv2 has a 4-word sample FIFO.
+        * In order to avoid FIFO overflow issue, we do software reset
+        * to clear all sample FIFO if the controller is disabled or
+        * wait for a full PWM cycle to get a relinquished FIFO slot
+        * when the controller is enabled and the FIFO is fully loaded.
+        */
+       if (enable) {
+               sr = readl(imx->mmio_base + MX3_PWMSR);
+               fifoav = sr & MX3_PWMSR_FIFOAV_MASK;
+               if (fifoav == MX3_PWMSR_FIFOAV_4WORDS) {
+                       period_ms = DIV_ROUND_UP(pwm->period, NSEC_PER_MSEC);
+                       msleep(period_ms);
+
+                       sr = readl(imx->mmio_base + MX3_PWMSR);
+                       if (fifoav == (sr & MX3_PWMSR_FIFOAV_MASK))
+                               dev_warn(dev, "there is no free FIFO slot\n");
+               }
+       } else {
+               writel(MX3_PWMCR_SWR, imx->mmio_base + MX3_PWMCR);
+               do {
+                       usleep_range(200, 1000);
+                       cr = readl(imx->mmio_base + MX3_PWMCR);
+               } while ((cr & MX3_PWMCR_SWR) &&
+                        (wait_count++ < MX3_PWM_SWR_LOOP));
+
+               if (cr & MX3_PWMCR_SWR)
+                       dev_warn(dev, "software reset timeout\n");
+       }
 
        c = clk_get_rate(imx->clk_per);
        c = c * period_ns;
@@ -135,7 +176,7 @@ static int imx_pwm_config_v2(struct pwm_chip *chip,
                MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN |
                MX3_PWMCR_DBGEN | MX3_PWMCR_CLKSRC_IPG_HIGH;
 
-       if (test_bit(PWMF_ENABLED, &pwm->flags))
+       if (enable)
                cr |= MX3_PWMCR_EN;
 
        writel(cr, imx->mmio_base + MX3_PWMCR);
diff --git a/drivers/pwm/pwm-lpss-pci.c b/drivers/pwm/pwm-lpss-pci.c
new file mode 100644 (file)
index 0000000..cf20d2b
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Intel Low Power Subsystem PWM controller PCI driver
+ *
+ * Copyright (C) 2014, Intel Corporation
+ *
+ * Derived from the original pwm-lpss.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include "pwm-lpss.h"
+
+static int pwm_lpss_probe_pci(struct pci_dev *pdev,
+                             const struct pci_device_id *id)
+{
+       const struct pwm_lpss_boardinfo *info;
+       struct pwm_lpss_chip *lpwm;
+       int err;
+
+       err = pcim_enable_device(pdev);
+       if (err < 0)
+               return err;
+
+       info = (struct pwm_lpss_boardinfo *)id->driver_data;
+       lpwm = pwm_lpss_probe(&pdev->dev, &pdev->resource[0], info);
+       if (IS_ERR(lpwm))
+               return PTR_ERR(lpwm);
+
+       pci_set_drvdata(pdev, lpwm);
+       return 0;
+}
+
+static void pwm_lpss_remove_pci(struct pci_dev *pdev)
+{
+       struct pwm_lpss_chip *lpwm = pci_get_drvdata(pdev);
+
+       pwm_lpss_remove(lpwm);
+}
+
+static const struct pci_device_id pwm_lpss_pci_ids[] = {
+       { PCI_VDEVICE(INTEL, 0x0f08), (unsigned long)&pwm_lpss_byt_info},
+       { PCI_VDEVICE(INTEL, 0x0f09), (unsigned long)&pwm_lpss_byt_info},
+       { PCI_VDEVICE(INTEL, 0x2288), (unsigned long)&pwm_lpss_bsw_info},
+       { PCI_VDEVICE(INTEL, 0x2289), (unsigned long)&pwm_lpss_bsw_info},
+       { },
+};
+MODULE_DEVICE_TABLE(pci, pwm_lpss_pci_ids);
+
+static struct pci_driver pwm_lpss_driver_pci = {
+       .name = "pwm-lpss",
+       .id_table = pwm_lpss_pci_ids,
+       .probe = pwm_lpss_probe_pci,
+       .remove = pwm_lpss_remove_pci,
+};
+module_pci_driver(pwm_lpss_driver_pci);
+
+MODULE_DESCRIPTION("PWM PCI driver for Intel LPSS");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-lpss-platform.c b/drivers/pwm/pwm-lpss-platform.c
new file mode 100644 (file)
index 0000000..18a9c88
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Intel Low Power Subsystem PWM controller driver
+ *
+ * Copyright (C) 2014, Intel Corporation
+ *
+ * Derived from the original pwm-lpss.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/acpi.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "pwm-lpss.h"
+
+static int pwm_lpss_probe_platform(struct platform_device *pdev)
+{
+       const struct pwm_lpss_boardinfo *info;
+       const struct acpi_device_id *id;
+       struct pwm_lpss_chip *lpwm;
+       struct resource *r;
+
+       id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev);
+       if (!id)
+               return -ENODEV;
+
+       info = (const struct pwm_lpss_boardinfo *)id->driver_data;
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       lpwm = pwm_lpss_probe(&pdev->dev, r, info);
+       if (IS_ERR(lpwm))
+               return PTR_ERR(lpwm);
+
+       platform_set_drvdata(pdev, lpwm);
+       return 0;
+}
+
+static int pwm_lpss_remove_platform(struct platform_device *pdev)
+{
+       struct pwm_lpss_chip *lpwm = platform_get_drvdata(pdev);
+
+       return pwm_lpss_remove(lpwm);
+}
+
+static const struct acpi_device_id pwm_lpss_acpi_match[] = {
+       { "80860F09", (unsigned long)&pwm_lpss_byt_info },
+       { "80862288", (unsigned long)&pwm_lpss_bsw_info },
+       { },
+};
+MODULE_DEVICE_TABLE(acpi, pwm_lpss_acpi_match);
+
+static struct platform_driver pwm_lpss_driver_platform = {
+       .driver = {
+               .name = "pwm-lpss",
+               .acpi_match_table = pwm_lpss_acpi_match,
+       },
+       .probe = pwm_lpss_probe_platform,
+       .remove = pwm_lpss_remove_platform,
+};
+module_platform_driver(pwm_lpss_driver_platform);
+
+MODULE_DESCRIPTION("PWM platform driver for Intel LPSS");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:pwm-lpss");
index 4df994f72d9604071f936e72b1f3e52dce5633d6..e9798253a16fcfb76117c219c1c7212e171f8fe5 100644 (file)
  * published by the Free Software Foundation.
  */
 
-#include <linux/acpi.h>
-#include <linux/device.h>
+#include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/pwm.h>
-#include <linux/platform_device.h>
-#include <linux/pci.h>
 
-static int pci_drv, plat_drv;  /* So we know which drivers registered */
+#include "pwm-lpss.h"
 
 #define PWM                            0x00000000
 #define PWM_ENABLE                     BIT(31)
@@ -39,14 +35,17 @@ struct pwm_lpss_chip {
        unsigned long clk_rate;
 };
 
-struct pwm_lpss_boardinfo {
-       unsigned long clk_rate;
+/* BayTrail */
+const struct pwm_lpss_boardinfo pwm_lpss_byt_info = {
+       .clk_rate = 25000000
 };
+EXPORT_SYMBOL_GPL(pwm_lpss_byt_info);
 
-/* BayTrail */
-static const struct pwm_lpss_boardinfo byt_info = {
-       25000000
+/* Braswell */
+const struct pwm_lpss_boardinfo pwm_lpss_bsw_info = {
+       .clk_rate = 19200000
 };
+EXPORT_SYMBOL_GPL(pwm_lpss_bsw_info);
 
 static inline struct pwm_lpss_chip *to_lpwm(struct pwm_chip *chip)
 {
@@ -118,9 +117,8 @@ static const struct pwm_ops pwm_lpss_ops = {
        .owner = THIS_MODULE,
 };
 
-static struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev,
-                                           struct resource *r,
-                                           const struct pwm_lpss_boardinfo *info)
+struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, struct resource *r,
+                                    const struct pwm_lpss_boardinfo *info)
 {
        struct pwm_lpss_chip *lpwm;
        int ret;
@@ -147,8 +145,9 @@ static struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev,
 
        return lpwm;
 }
+EXPORT_SYMBOL_GPL(pwm_lpss_probe);
 
-static int pwm_lpss_remove(struct pwm_lpss_chip *lpwm)
+int pwm_lpss_remove(struct pwm_lpss_chip *lpwm)
 {
        u32 ctrl;
 
@@ -157,114 +156,8 @@ static int pwm_lpss_remove(struct pwm_lpss_chip *lpwm)
 
        return pwmchip_remove(&lpwm->chip);
 }
-
-static int pwm_lpss_probe_pci(struct pci_dev *pdev,
-                             const struct pci_device_id *id)
-{
-       const struct pwm_lpss_boardinfo *info;
-       struct pwm_lpss_chip *lpwm;
-       int err;
-
-       err = pci_enable_device(pdev);
-       if (err < 0)
-               return err;
-
-       info = (struct pwm_lpss_boardinfo *)id->driver_data;
-       lpwm = pwm_lpss_probe(&pdev->dev, &pdev->resource[0], info);
-       if (IS_ERR(lpwm))
-               return PTR_ERR(lpwm);
-
-       pci_set_drvdata(pdev, lpwm);
-       return 0;
-}
-
-static void pwm_lpss_remove_pci(struct pci_dev *pdev)
-{
-       struct pwm_lpss_chip *lpwm = pci_get_drvdata(pdev);
-
-       pwm_lpss_remove(lpwm);
-       pci_disable_device(pdev);
-}
-
-static struct pci_device_id pwm_lpss_pci_ids[] = {
-       { PCI_VDEVICE(INTEL, 0x0f08), (unsigned long)&byt_info},
-       { PCI_VDEVICE(INTEL, 0x0f09), (unsigned long)&byt_info},
-       { },
-};
-MODULE_DEVICE_TABLE(pci, pwm_lpss_pci_ids);
-
-static struct pci_driver pwm_lpss_driver_pci = {
-       .name = "pwm-lpss",
-       .id_table = pwm_lpss_pci_ids,
-       .probe = pwm_lpss_probe_pci,
-       .remove = pwm_lpss_remove_pci,
-};
-
-static int pwm_lpss_probe_platform(struct platform_device *pdev)
-{
-       const struct pwm_lpss_boardinfo *info;
-       const struct acpi_device_id *id;
-       struct pwm_lpss_chip *lpwm;
-       struct resource *r;
-
-       id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev);
-       if (!id)
-               return -ENODEV;
-
-       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
-       info = (struct pwm_lpss_boardinfo *)id->driver_data;
-       lpwm = pwm_lpss_probe(&pdev->dev, r, info);
-       if (IS_ERR(lpwm))
-               return PTR_ERR(lpwm);
-
-       platform_set_drvdata(pdev, lpwm);
-       return 0;
-}
-
-static int pwm_lpss_remove_platform(struct platform_device *pdev)
-{
-       struct pwm_lpss_chip *lpwm = platform_get_drvdata(pdev);
-
-       return pwm_lpss_remove(lpwm);
-}
-
-static const struct acpi_device_id pwm_lpss_acpi_match[] = {
-       { "80860F09", (unsigned long)&byt_info },
-       { },
-};
-MODULE_DEVICE_TABLE(acpi, pwm_lpss_acpi_match);
-
-static struct platform_driver pwm_lpss_driver_platform = {
-       .driver = {
-               .name = "pwm-lpss",
-               .acpi_match_table = pwm_lpss_acpi_match,
-       },
-       .probe = pwm_lpss_probe_platform,
-       .remove = pwm_lpss_remove_platform,
-};
-
-static int __init pwm_init(void)
-{
-       pci_drv = pci_register_driver(&pwm_lpss_driver_pci);
-       plat_drv = platform_driver_register(&pwm_lpss_driver_platform);
-       if (pci_drv && plat_drv)
-               return pci_drv;
-
-       return 0;
-}
-module_init(pwm_init);
-
-static void __exit pwm_exit(void)
-{
-       if (!pci_drv)
-               pci_unregister_driver(&pwm_lpss_driver_pci);
-       if (!plat_drv)
-               platform_driver_unregister(&pwm_lpss_driver_platform);
-}
-module_exit(pwm_exit);
+EXPORT_SYMBOL_GPL(pwm_lpss_remove);
 
 MODULE_DESCRIPTION("PWM driver for Intel LPSS");
 MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
 MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:pwm-lpss");
diff --git a/drivers/pwm/pwm-lpss.h b/drivers/pwm/pwm-lpss.h
new file mode 100644 (file)
index 0000000..aa041bb
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Intel Low Power Subsystem PWM controller driver
+ *
+ * Copyright (C) 2014, Intel Corporation
+ *
+ * Derived from the original pwm-lpss.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __PWM_LPSS_H
+#define __PWM_LPSS_H
+
+#include <linux/device.h>
+#include <linux/pwm.h>
+
+struct pwm_lpss_chip;
+
+struct pwm_lpss_boardinfo {
+       unsigned long clk_rate;
+};
+
+extern const struct pwm_lpss_boardinfo pwm_lpss_byt_info;
+extern const struct pwm_lpss_boardinfo pwm_lpss_bsw_info;
+
+struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, struct resource *r,
+                                    const struct pwm_lpss_boardinfo *info);
+int pwm_lpss_remove(struct pwm_lpss_chip *lpwm);
+
+#endif /* __PWM_LPSS_H */
index bdd8644c01cf18bb62ad169cf3de4729b29421e8..9442df244101772259a0ddb6824fe6035c4dd568 100644 (file)
@@ -24,7 +24,9 @@
 #define PWM_ENABLE             (1 << 0)
 #define PWM_CONTINUOUS         (1 << 1)
 #define PWM_DUTY_POSITIVE      (1 << 3)
+#define PWM_DUTY_NEGATIVE      (0 << 3)
 #define PWM_INACTIVE_NEGATIVE  (0 << 4)
+#define PWM_INACTIVE_POSITIVE  (1 << 4)
 #define PWM_OUTPUT_LEFT                (0 << 5)
 #define PWM_LP_DISABLE         (0 << 8)
 
@@ -45,8 +47,10 @@ struct rockchip_pwm_regs {
 struct rockchip_pwm_data {
        struct rockchip_pwm_regs regs;
        unsigned int prescaler;
+       const struct pwm_ops *ops;
 
-       void (*set_enable)(struct pwm_chip *chip, bool enable);
+       void (*set_enable)(struct pwm_chip *chip,
+                          struct pwm_device *pwm, bool enable);
 };
 
 static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c)
@@ -54,7 +58,8 @@ static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c)
        return container_of(c, struct rockchip_pwm_chip, chip);
 }
 
-static void rockchip_pwm_set_enable_v1(struct pwm_chip *chip, bool enable)
+static void rockchip_pwm_set_enable_v1(struct pwm_chip *chip,
+                                      struct pwm_device *pwm, bool enable)
 {
        struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
        u32 enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN;
@@ -70,14 +75,19 @@ static void rockchip_pwm_set_enable_v1(struct pwm_chip *chip, bool enable)
        writel_relaxed(val, pc->base + pc->data->regs.ctrl);
 }
 
-static void rockchip_pwm_set_enable_v2(struct pwm_chip *chip, bool enable)
+static void rockchip_pwm_set_enable_v2(struct pwm_chip *chip,
+                                      struct pwm_device *pwm, bool enable)
 {
        struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
        u32 enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE |
-                         PWM_CONTINUOUS | PWM_DUTY_POSITIVE |
-                         PWM_INACTIVE_NEGATIVE;
+                         PWM_CONTINUOUS;
        u32 val;
 
+       if (pwm->polarity == PWM_POLARITY_INVERSED)
+               enable_conf |= PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSITIVE;
+       else
+               enable_conf |= PWM_DUTY_POSITIVE | PWM_INACTIVE_NEGATIVE;
+
        val = readl_relaxed(pc->base + pc->data->regs.ctrl);
 
        if (enable)
@@ -124,6 +134,19 @@ static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
        return 0;
 }
 
+static int rockchip_pwm_set_polarity(struct pwm_chip *chip,
+                                    struct pwm_device *pwm,
+                                    enum pwm_polarity polarity)
+{
+       /*
+        * No action needed here because pwm->polarity will be set by the core
+        * and the core will only change polarity when the PWM is not enabled.
+        * We'll handle things in set_enable().
+        */
+
+       return 0;
+}
+
 static int rockchip_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
        struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
@@ -133,7 +156,7 @@ static int rockchip_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
        if (ret)
                return ret;
 
-       pc->data->set_enable(chip, true);
+       pc->data->set_enable(chip, pwm, true);
 
        return 0;
 }
@@ -142,18 +165,26 @@ static void rockchip_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
        struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
 
-       pc->data->set_enable(chip, false);
+       pc->data->set_enable(chip, pwm, false);
 
        clk_disable(pc->clk);
 }
 
-static const struct pwm_ops rockchip_pwm_ops = {
+static const struct pwm_ops rockchip_pwm_ops_v1 = {
        .config = rockchip_pwm_config,
        .enable = rockchip_pwm_enable,
        .disable = rockchip_pwm_disable,
        .owner = THIS_MODULE,
 };
 
+static const struct pwm_ops rockchip_pwm_ops_v2 = {
+       .config = rockchip_pwm_config,
+       .set_polarity = rockchip_pwm_set_polarity,
+       .enable = rockchip_pwm_enable,
+       .disable = rockchip_pwm_disable,
+       .owner = THIS_MODULE,
+};
+
 static const struct rockchip_pwm_data pwm_data_v1 = {
        .regs = {
                .duty = 0x04,
@@ -162,6 +193,7 @@ static const struct rockchip_pwm_data pwm_data_v1 = {
                .ctrl = 0x0c,
        },
        .prescaler = 2,
+       .ops = &rockchip_pwm_ops_v1,
        .set_enable = rockchip_pwm_set_enable_v1,
 };
 
@@ -173,6 +205,7 @@ static const struct rockchip_pwm_data pwm_data_v2 = {
                .ctrl = 0x0c,
        },
        .prescaler = 1,
+       .ops = &rockchip_pwm_ops_v2,
        .set_enable = rockchip_pwm_set_enable_v2,
 };
 
@@ -184,6 +217,7 @@ static const struct rockchip_pwm_data pwm_data_vop = {
                .ctrl = 0x00,
        },
        .prescaler = 1,
+       .ops = &rockchip_pwm_ops_v2,
        .set_enable = rockchip_pwm_set_enable_v2,
 };
 
@@ -227,10 +261,15 @@ static int rockchip_pwm_probe(struct platform_device *pdev)
 
        pc->data = id->data;
        pc->chip.dev = &pdev->dev;
-       pc->chip.ops = &rockchip_pwm_ops;
+       pc->chip.ops = pc->data->ops;
        pc->chip.base = -1;
        pc->chip.npwm = 1;
 
+       if (pc->data->ops->set_polarity) {
+               pc->chip.of_xlate = of_pwm_xlate_with_flags;
+               pc->chip.of_pwm_n_cells = 3;
+       }
+
        ret = pwmchip_add(&pc->chip);
        if (ret < 0) {
                clk_unprepare(pc->clk);