Browse Source

Merge branch 'for-4.8/drivers' into for-next

Thierry Reding 9 years ago
parent
commit
070d9a9306

+ 21 - 0
Documentation/devicetree/bindings/pwm/brcm,iproc-pwm.txt

@@ -0,0 +1,21 @@
+Broadcom iProc PWM controller device tree bindings
+
+This controller has 4 channels.
+
+Required Properties :
+- compatible: must be "brcm,iproc-pwm"
+- reg: physical base address and length of the controller's registers
+- clocks: phandle + clock specifier pair for the external clock
+- #pwm-cells: Should be 3. See pwm.txt in this directory for a
+  description of the cells format.
+
+Refer to clocks/clock-bindings.txt for generic clock consumer properties.
+
+Example:
+
+pwm: pwm@18031000 {
+	compatible = "brcm,iproc-pwm";
+	reg = <0x18031000 0x28>;
+	clocks = <&osc>;
+	#pwm-cells = <3>;
+};

+ 23 - 0
Documentation/devicetree/bindings/pwm/google,cros-ec-pwm.txt

@@ -0,0 +1,23 @@
+* PWM controlled by ChromeOS EC
+
+Google's ChromeOS EC PWM is a simple PWM attached to the Embedded Controller
+(EC) and controlled via a host-command interface.
+
+An EC PWM node should be only found as a sub-node of the EC node (see
+Documentation/devicetree/bindings/mfd/cros-ec.txt).
+
+Required properties:
+- compatible: Must contain "google,cros-ec-pwm"
+- #pwm-cells: Should be 1. The cell specifies the PWM index.
+
+Example:
+	cros-ec@0 {
+		compatible = "google,cros-ec-spi";
+
+		...
+
+		cros_ec_pwm: ec-pwm {
+			compatible = "google,cros-ec-pwm";
+			#pwm-cells = <1>;
+		};
+	};

+ 8 - 4
Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt

@@ -1,10 +1,14 @@
 Tegra SoC PWFM controller
 
 Required properties:
-- compatible: For Tegra20, must contain "nvidia,tegra20-pwm".  For Tegra30,
-  must contain "nvidia,tegra30-pwm".  Otherwise, must contain
-  "nvidia,<chip>-pwm", plus one of the above, where <chip> is tegra114,
-  tegra124, tegra132, or tegra210.
+- compatible: Must be:
+  - "nvidia,tegra20-pwm": for Tegra20
+  - "nvidia,tegra30-pwm", "nvidia,tegra20-pwm": for Tegra30
+  - "nvidia,tegra114-pwm", "nvidia,tegra20-pwm": for Tegra114
+  - "nvidia,tegra124-pwm", "nvidia,tegra20-pwm": for Tegra124
+  - "nvidia,tegra132-pwm", "nvidia,tegra20-pwm": for Tegra132
+  - "nvidia,tegra210-pwm", "nvidia,tegra20-pwm": for Tegra210
+  - "nvidia,tegra186-pwm": for Tegra186
 - reg: physical base address and length of the controller's registers
 - #pwm-cells: should be 2. See pwm.txt in this directory for a description of
   the cells format.

+ 2 - 2
Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt

@@ -15,14 +15,14 @@ Optional properties:
 
 Example:
 
-ehrpwm0: ehrpwm@0 { /* EHRPWM on am33xx */
+ehrpwm0: pwm@48300200 { /* EHRPWM on am33xx */
 	compatible = "ti,am33xx-ehrpwm";
 	#pwm-cells = <3>;
 	reg = <0x48300200 0x100>;
 	ti,hwmods = "ehrpwm0";
 };
 
-ehrpwm0: ehrpwm@0 { /* EHRPWM on da850 */
+ehrpwm0: pwm@300000 { /* EHRPWM on da850 */
 	compatible = "ti,da850-ehrpwm", "ti,am33xx-ehrpwm";
 	#pwm-cells = <3>;
 	reg = <0x300000 0x2000>;

+ 1 - 0
Documentation/devicetree/bindings/pwm/renesas,pwm-rcar.txt

@@ -7,6 +7,7 @@ Required Properties:
  - "renesas,pwm-r8a7790": for R-Car H2
  - "renesas,pwm-r8a7791": for R-Car M2-W
  - "renesas,pwm-r8a7794": for R-Car E2
+ - "renesas,pwm-r8a7795": for R-Car H3
 - reg: base address and length of the registers block for the PWM.
 - #pwm-cells: should be 2. See pwm.txt in this directory for a description of
   the cells format.

+ 18 - 0
Documentation/devicetree/bindings/pwm/st,stmpe-pwm.txt

@@ -0,0 +1,18 @@
+== ST STMPE PWM controller ==
+
+This is a PWM block embedded in the ST Microelectronics STMPE
+(ST Multi-Purpose Expander) chips. The PWM is registered as a
+subdevices of the STMPE MFD device.
+
+Required properties:
+- compatible: should be:
+  - "st,stmpe-pwm"
+- #pwm-cells: should be 2. See pwm.txt in this directory for a description of
+  the cells format.
+
+Example:
+
+pwm0: pwm {
+	compatible = "st,stmpe-pwm";
+	#pwm-cells = <2>;
+};

+ 25 - 1
drivers/pwm/Kconfig

@@ -74,6 +74,16 @@ config PWM_ATMEL_TCB
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-atmel-tcb.
 
+config PWM_BCM_IPROC
+	tristate "iProc PWM support"
+	depends on ARCH_BCM_IPROC
+	help
+	  Generic PWM framework driver for Broadcom iProc PWM block. This
+	  block is used in Broadcom iProc SoC's.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-bcm-iproc.
+
 config PWM_BCM_KONA
 	tristate "Kona PWM support"
 	depends on ARCH_BCM_MOBILE
@@ -137,6 +147,13 @@ config PWM_CRC
 	  Generic PWM framework driver for Crystalcove (CRC) PMIC based PWM
 	  control.
 
+config PWM_CROS_EC
+	tristate "ChromeOS EC PWM driver"
+	depends on MFD_CROS_EC
+	help
+	  PWM driver for exposing a PWM attached to the ChromeOS Embedded
+	  Controller.
+
 config PWM_EP93XX
 	tristate "Cirrus Logic EP93xx PWM support"
 	depends on ARCH_EP93XX
@@ -305,7 +322,7 @@ config PWM_PXA
 
 config PWM_RCAR
 	tristate "Renesas R-Car PWM support"
-	depends on ARCH_RCAR_GEN1 || ARCH_RCAR_GEN2 || COMPILE_TEST
+	depends on ARCH_RENESAS || COMPILE_TEST
 	depends on HAS_IOMEM
 	help
 	  This driver exposes the PWM Timer controller found in Renesas
@@ -362,6 +379,13 @@ config PWM_STI
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-sti.
 
+config PWM_STMPE
+	bool "STMPE expander PWM export"
+	depends on MFD_STMPE
+	help
+	  This enables support for the PWMs found in the STMPE I/O
+	  expanders.
+
 config PWM_SUN4I
 	tristate "Allwinner PWM support"
 	depends on ARCH_SUNXI || COMPILE_TEST

+ 3 - 0
drivers/pwm/Makefile

@@ -4,6 +4,7 @@ obj-$(CONFIG_PWM_AB8500)	+= pwm-ab8500.o
 obj-$(CONFIG_PWM_ATMEL)		+= pwm-atmel.o
 obj-$(CONFIG_PWM_ATMEL_HLCDC_PWM)	+= pwm-atmel-hlcdc.o
 obj-$(CONFIG_PWM_ATMEL_TCB)	+= pwm-atmel-tcb.o
+obj-$(CONFIG_PWM_BCM_IPROC)	+= pwm-bcm-iproc.o
 obj-$(CONFIG_PWM_BCM_KONA)	+= pwm-bcm-kona.o
 obj-$(CONFIG_PWM_BCM2835)	+= pwm-bcm2835.o
 obj-$(CONFIG_PWM_BERLIN)	+= pwm-berlin.o
@@ -11,6 +12,7 @@ obj-$(CONFIG_PWM_BFIN)		+= pwm-bfin.o
 obj-$(CONFIG_PWM_BRCMSTB)	+= pwm-brcmstb.o
 obj-$(CONFIG_PWM_CLPS711X)	+= pwm-clps711x.o
 obj-$(CONFIG_PWM_CRC)		+= pwm-crc.o
+obj-$(CONFIG_PWM_CROS_EC)	+= pwm-cros-ec.o
 obj-$(CONFIG_PWM_EP93XX)	+= pwm-ep93xx.o
 obj-$(CONFIG_PWM_FSL_FTM)	+= pwm-fsl-ftm.o
 obj-$(CONFIG_PWM_IMG)		+= pwm-img.o
@@ -34,6 +36,7 @@ obj-$(CONFIG_PWM_ROCKCHIP)	+= pwm-rockchip.o
 obj-$(CONFIG_PWM_SAMSUNG)	+= pwm-samsung.o
 obj-$(CONFIG_PWM_SPEAR)		+= pwm-spear.o
 obj-$(CONFIG_PWM_STI)		+= pwm-sti.o
+obj-$(CONFIG_PWM_STMPE)		+= pwm-stmpe.o
 obj-$(CONFIG_PWM_SUN4I)		+= pwm-sun4i.o
 obj-$(CONFIG_PWM_TEGRA)		+= pwm-tegra.o
 obj-$(CONFIG_PWM_TIECAP)	+= pwm-tiecap.o

+ 17 - 13
drivers/pwm/pwm-atmel.c

@@ -64,7 +64,8 @@ struct atmel_pwm_chip {
 	void __iomem *base;
 
 	unsigned int updated_pwms;
-	struct mutex isr_lock; /* ISR is cleared when read, ensure only one thread does that */
+	/* ISR is cleared when read, ensure only one thread does that */
+	struct mutex isr_lock;
 
 	void (*config)(struct pwm_chip *chip, struct pwm_device *pwm,
 		       unsigned long dty, unsigned long prd);
@@ -271,6 +272,16 @@ static void atmel_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 	mutex_unlock(&atmel_pwm->isr_lock);
 	atmel_pwm_writel(atmel_pwm, PWM_DIS, 1 << pwm->hwpwm);
 
+	/*
+	 * Wait for the PWM channel disable operation to be effective before
+	 * stopping the clock.
+	 */
+	timeout = jiffies + 2 * HZ;
+
+	while ((atmel_pwm_readl(atmel_pwm, PWM_SR) & (1 << pwm->hwpwm)) &&
+	       time_before(jiffies, timeout))
+		usleep_range(10, 100);
+
 	clk_disable(atmel_pwm->clk);
 }
 
@@ -324,21 +335,14 @@ MODULE_DEVICE_TABLE(of, atmel_pwm_dt_ids);
 static inline const struct atmel_pwm_data *
 atmel_pwm_get_driver_data(struct platform_device *pdev)
 {
-	if (pdev->dev.of_node) {
-		const struct of_device_id *match;
-
-		match = of_match_device(atmel_pwm_dt_ids, &pdev->dev);
-		if (!match)
-			return NULL;
+	const struct platform_device_id *id;
 
-		return match->data;
-	} else {
-		const struct platform_device_id *id;
+	if (pdev->dev.of_node)
+		return of_device_get_match_data(&pdev->dev);
 
-		id = platform_get_device_id(pdev);
+	id = platform_get_device_id(pdev);
 
-		return (struct atmel_pwm_data *)id->driver_data;
-	}
+	return (struct atmel_pwm_data *)id->driver_data;
 }
 
 static int atmel_pwm_probe(struct platform_device *pdev)

+ 277 - 0
drivers/pwm/pwm-bcm-iproc.c

@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2016 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+
+#define IPROC_PWM_CTRL_OFFSET			0x00
+#define IPROC_PWM_CTRL_TYPE_SHIFT(ch)		(15 + (ch))
+#define IPROC_PWM_CTRL_POLARITY_SHIFT(ch)	(8 + (ch))
+#define IPROC_PWM_CTRL_EN_SHIFT(ch)		(ch)
+
+#define IPROC_PWM_PERIOD_OFFSET(ch)		(0x04 + ((ch) << 3))
+#define IPROC_PWM_PERIOD_MIN			0x02
+#define IPROC_PWM_PERIOD_MAX			0xffff
+
+#define IPROC_PWM_DUTY_CYCLE_OFFSET(ch)		(0x08 + ((ch) << 3))
+#define IPROC_PWM_DUTY_CYCLE_MIN		0x00
+#define IPROC_PWM_DUTY_CYCLE_MAX		0xffff
+
+#define IPROC_PWM_PRESCALE_OFFSET		0x24
+#define IPROC_PWM_PRESCALE_BITS			0x06
+#define IPROC_PWM_PRESCALE_SHIFT(ch)		((3 - (ch)) * \
+						 IPROC_PWM_PRESCALE_BITS)
+#define IPROC_PWM_PRESCALE_MASK(ch)		(IPROC_PWM_PRESCALE_MAX << \
+						 IPROC_PWM_PRESCALE_SHIFT(ch))
+#define IPROC_PWM_PRESCALE_MIN			0x00
+#define IPROC_PWM_PRESCALE_MAX			0x3f
+
+struct iproc_pwmc {
+	struct pwm_chip chip;
+	void __iomem *base;
+	struct clk *clk;
+};
+
+static inline struct iproc_pwmc *to_iproc_pwmc(struct pwm_chip *chip)
+{
+	return container_of(chip, struct iproc_pwmc, chip);
+}
+
+static void iproc_pwmc_enable(struct iproc_pwmc *ip, unsigned int channel)
+{
+	u32 value;
+
+	value = readl(ip->base + IPROC_PWM_CTRL_OFFSET);
+	value |= 1 << IPROC_PWM_CTRL_EN_SHIFT(channel);
+	writel(value, ip->base + IPROC_PWM_CTRL_OFFSET);
+
+	/* must be a 400 ns delay between clearing and setting enable bit */
+	ndelay(400);
+}
+
+static void iproc_pwmc_disable(struct iproc_pwmc *ip, unsigned int channel)
+{
+	u32 value;
+
+	value = readl(ip->base + IPROC_PWM_CTRL_OFFSET);
+	value &= ~(1 << IPROC_PWM_CTRL_EN_SHIFT(channel));
+	writel(value, ip->base + IPROC_PWM_CTRL_OFFSET);
+
+	/* must be a 400 ns delay between clearing and setting enable bit */
+	ndelay(400);
+}
+
+static void iproc_pwmc_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+				 struct pwm_state *state)
+{
+	struct iproc_pwmc *ip = to_iproc_pwmc(chip);
+	u64 tmp, multi, rate;
+	u32 value, prescale;
+
+	rate = clk_get_rate(ip->clk);
+
+	value = readl(ip->base + IPROC_PWM_CTRL_OFFSET);
+
+	if (value & BIT(IPROC_PWM_CTRL_EN_SHIFT(pwm->hwpwm)))
+		state->enabled = true;
+	else
+		state->enabled = false;
+
+	if (value & BIT(IPROC_PWM_CTRL_POLARITY_SHIFT(pwm->hwpwm)))
+		state->polarity = PWM_POLARITY_NORMAL;
+	else
+		state->polarity = PWM_POLARITY_INVERSED;
+
+	value = readl(ip->base + IPROC_PWM_PRESCALE_OFFSET);
+	prescale = value >> IPROC_PWM_PRESCALE_SHIFT(pwm->hwpwm);
+	prescale &= IPROC_PWM_PRESCALE_MAX;
+
+	multi = NSEC_PER_SEC * (prescale + 1);
+
+	value = readl(ip->base + IPROC_PWM_PERIOD_OFFSET(pwm->hwpwm));
+	tmp = (value & IPROC_PWM_PERIOD_MAX) * multi;
+	state->period = div64_u64(tmp, rate);
+
+	value = readl(ip->base + IPROC_PWM_DUTY_CYCLE_OFFSET(pwm->hwpwm));
+	tmp = (value & IPROC_PWM_PERIOD_MAX) * multi;
+	state->duty_cycle = div64_u64(tmp, rate);
+}
+
+static int iproc_pwmc_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+			    struct pwm_state *state)
+{
+	unsigned long prescale = IPROC_PWM_PRESCALE_MIN;
+	struct iproc_pwmc *ip = to_iproc_pwmc(chip);
+	u32 value, period, duty;
+	u64 rate;
+
+	rate = clk_get_rate(ip->clk);
+
+	/*
+	 * Find period count, duty count and prescale to suit duty_cycle and
+	 * period. This is done according to formulas described below:
+	 *
+	 * period_ns = 10^9 * (PRESCALE + 1) * PC / PWM_CLK_RATE
+	 * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
+	 *
+	 * PC = (PWM_CLK_RATE * period_ns) / (10^9 * (PRESCALE + 1))
+	 * DC = (PWM_CLK_RATE * duty_ns) / (10^9 * (PRESCALE + 1))
+	 */
+	while (1) {
+		u64 value, div;
+
+		div = NSEC_PER_SEC * (prescale + 1);
+		value = rate * state->period;
+		period = div64_u64(value, div);
+		value = rate * state->duty_cycle;
+		duty = div64_u64(value, div);
+
+		if (period < IPROC_PWM_PERIOD_MIN ||
+		    duty < IPROC_PWM_DUTY_CYCLE_MIN)
+			return -EINVAL;
+
+		if (period <= IPROC_PWM_PERIOD_MAX &&
+		     duty <= IPROC_PWM_DUTY_CYCLE_MAX)
+			break;
+
+		/* Otherwise, increase prescale and recalculate counts */
+		if (++prescale > IPROC_PWM_PRESCALE_MAX)
+			return -EINVAL;
+	}
+
+	iproc_pwmc_disable(ip, pwm->hwpwm);
+
+	/* Set prescale */
+	value = readl(ip->base + IPROC_PWM_PRESCALE_OFFSET);
+	value &= ~IPROC_PWM_PRESCALE_MASK(pwm->hwpwm);
+	value |= prescale << IPROC_PWM_PRESCALE_SHIFT(pwm->hwpwm);
+	writel(value, ip->base + IPROC_PWM_PRESCALE_OFFSET);
+
+	/* set period and duty cycle */
+	writel(period, ip->base + IPROC_PWM_PERIOD_OFFSET(pwm->hwpwm));
+	writel(duty, ip->base + IPROC_PWM_DUTY_CYCLE_OFFSET(pwm->hwpwm));
+
+	/* set polarity */
+	value = readl(ip->base + IPROC_PWM_CTRL_OFFSET);
+
+	if (state->polarity == PWM_POLARITY_NORMAL)
+		value |= 1 << IPROC_PWM_CTRL_POLARITY_SHIFT(pwm->hwpwm);
+	else
+		value &= ~(1 << IPROC_PWM_CTRL_POLARITY_SHIFT(pwm->hwpwm));
+
+	writel(value, ip->base + IPROC_PWM_CTRL_OFFSET);
+
+	if (state->enabled)
+		iproc_pwmc_enable(ip, pwm->hwpwm);
+
+	return 0;
+}
+
+static const struct pwm_ops iproc_pwm_ops = {
+	.apply = iproc_pwmc_apply,
+	.get_state = iproc_pwmc_get_state,
+};
+
+static int iproc_pwmc_probe(struct platform_device *pdev)
+{
+	struct iproc_pwmc *ip;
+	struct resource *res;
+	unsigned int i;
+	u32 value;
+	int ret;
+
+	ip = devm_kzalloc(&pdev->dev, sizeof(*ip), GFP_KERNEL);
+	if (!ip)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, ip);
+
+	ip->chip.dev = &pdev->dev;
+	ip->chip.ops = &iproc_pwm_ops;
+	ip->chip.base = -1;
+	ip->chip.npwm = 4;
+	ip->chip.of_xlate = of_pwm_xlate_with_flags;
+	ip->chip.of_pwm_n_cells = 3;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	ip->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(ip->base))
+		return PTR_ERR(ip->base);
+
+	ip->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(ip->clk)) {
+		dev_err(&pdev->dev, "failed to get clock: %ld\n",
+			PTR_ERR(ip->clk));
+		return PTR_ERR(ip->clk);
+	}
+
+	ret = clk_prepare_enable(ip->clk);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to enable clock: %d\n", ret);
+		return ret;
+	}
+
+	/* Set full drive and normal polarity for all channels */
+	value = readl(ip->base + IPROC_PWM_CTRL_OFFSET);
+
+	for (i = 0; i < ip->chip.npwm; i++) {
+		value &= ~(1 << IPROC_PWM_CTRL_TYPE_SHIFT(i));
+		value |= 1 << IPROC_PWM_CTRL_POLARITY_SHIFT(i);
+	}
+
+	writel(value, ip->base + IPROC_PWM_CTRL_OFFSET);
+
+	ret = pwmchip_add(&ip->chip);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret);
+		clk_disable_unprepare(ip->clk);
+	}
+
+	return ret;
+}
+
+static int iproc_pwmc_remove(struct platform_device *pdev)
+{
+	struct iproc_pwmc *ip = platform_get_drvdata(pdev);
+
+	clk_disable_unprepare(ip->clk);
+
+	return pwmchip_remove(&ip->chip);
+}
+
+static const struct of_device_id bcm_iproc_pwmc_dt[] = {
+	{ .compatible = "brcm,iproc-pwm" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, bcm_iproc_pwmc_dt);
+
+static struct platform_driver iproc_pwmc_driver = {
+	.driver = {
+		.name = "bcm-iproc-pwm",
+		.of_match_table = bcm_iproc_pwmc_dt,
+	},
+	.probe = iproc_pwmc_probe,
+	.remove = iproc_pwmc_remove,
+};
+module_platform_driver(iproc_pwmc_driver);
+
+MODULE_AUTHOR("Yendapally Reddy Dhananjaya Reddy <yendapally.reddy@broadcom.com>");
+MODULE_DESCRIPTION("Broadcom iProc PWM driver");
+MODULE_LICENSE("GPL v2");

+ 260 - 0
drivers/pwm/pwm-cros-ec.c

@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2016 Google, Inc
+ *
+ * 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.
+ *
+ * Expose a PWM controlled by the ChromeOS EC to the host processor.
+ */
+
+#include <linux/module.h>
+#include <linux/mfd/cros_ec.h>
+#include <linux/mfd/cros_ec_commands.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+/**
+ * struct cros_ec_pwm_device - Driver data for EC PWM
+ *
+ * @dev: Device node
+ * @ec: Pointer to EC device
+ * @chip: PWM controller chip
+ */
+struct cros_ec_pwm_device {
+	struct device *dev;
+	struct cros_ec_device *ec;
+	struct pwm_chip chip;
+};
+
+static inline struct cros_ec_pwm_device *pwm_to_cros_ec_pwm(struct pwm_chip *c)
+{
+	return container_of(c, struct cros_ec_pwm_device, chip);
+}
+
+static int cros_ec_pwm_set_duty(struct cros_ec_device *ec, u8 index, u16 duty)
+{
+	struct {
+		struct cros_ec_command msg;
+		struct ec_params_pwm_set_duty params;
+	} buf;
+	struct ec_params_pwm_set_duty *params = &buf.params;
+	struct cros_ec_command *msg = &buf.msg;
+
+	memset(&buf, 0, sizeof(buf));
+
+	msg->version = 0;
+	msg->command = EC_CMD_PWM_SET_DUTY;
+	msg->insize = 0;
+	msg->outsize = sizeof(*params);
+
+	params->duty = duty;
+	params->pwm_type = EC_PWM_TYPE_GENERIC;
+	params->index = index;
+
+	return cros_ec_cmd_xfer_status(ec, msg);
+}
+
+static int __cros_ec_pwm_get_duty(struct cros_ec_device *ec, u8 index,
+				  u32 *result)
+{
+	struct {
+		struct cros_ec_command msg;
+		union {
+			struct ec_params_pwm_get_duty params;
+			struct ec_response_pwm_get_duty resp;
+		};
+	} buf;
+	struct ec_params_pwm_get_duty *params = &buf.params;
+	struct ec_response_pwm_get_duty *resp = &buf.resp;
+	struct cros_ec_command *msg = &buf.msg;
+	int ret;
+
+	memset(&buf, 0, sizeof(buf));
+
+	msg->version = 0;
+	msg->command = EC_CMD_PWM_GET_DUTY;
+	msg->insize = sizeof(*params);
+	msg->outsize = sizeof(*resp);
+
+	params->pwm_type = EC_PWM_TYPE_GENERIC;
+	params->index = index;
+
+	ret = cros_ec_cmd_xfer_status(ec, msg);
+	if (result)
+		*result = msg->result;
+	if (ret < 0)
+		return ret;
+
+	return resp->duty;
+}
+
+static int cros_ec_pwm_get_duty(struct cros_ec_device *ec, u8 index)
+{
+	return __cros_ec_pwm_get_duty(ec, index, NULL);
+}
+
+static int cros_ec_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+			     struct pwm_state *state)
+{
+	struct cros_ec_pwm_device *ec_pwm = pwm_to_cros_ec_pwm(chip);
+	int duty_cycle;
+
+	/* The EC won't let us change the period */
+	if (state->period != EC_PWM_MAX_DUTY)
+		return -EINVAL;
+
+	/*
+	 * EC doesn't separate the concept of duty cycle and enabled, but
+	 * kernel does. Translate.
+	 */
+	duty_cycle = state->enabled ? state->duty_cycle : 0;
+
+	return cros_ec_pwm_set_duty(ec_pwm->ec, pwm->hwpwm, duty_cycle);
+}
+
+static void cros_ec_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+				  struct pwm_state *state)
+{
+	struct cros_ec_pwm_device *ec_pwm = pwm_to_cros_ec_pwm(chip);
+	int ret;
+
+	ret = cros_ec_pwm_get_duty(ec_pwm->ec, pwm->hwpwm);
+	if (ret < 0) {
+		dev_err(chip->dev, "error getting initial duty: %d\n", ret);
+		return;
+	}
+
+	state->enabled = (ret > 0);
+	state->period = EC_PWM_MAX_DUTY;
+
+	/* Note that "disabled" and "duty cycle == 0" are treated the same */
+	state->duty_cycle = ret;
+}
+
+static struct pwm_device *
+cros_ec_pwm_xlate(struct pwm_chip *pc, const struct of_phandle_args *args)
+{
+	struct pwm_device *pwm;
+
+	if (args->args[0] >= pc->npwm)
+		return ERR_PTR(-EINVAL);
+
+	pwm = pwm_request_from_chip(pc, args->args[0], NULL);
+	if (IS_ERR(pwm))
+		return pwm;
+
+	/* The EC won't let us change the period */
+	pwm->args.period = EC_PWM_MAX_DUTY;
+
+	return pwm;
+}
+
+static const struct pwm_ops cros_ec_pwm_ops = {
+	.get_state	= cros_ec_pwm_get_state,
+	.apply		= cros_ec_pwm_apply,
+	.owner		= THIS_MODULE,
+};
+
+static int cros_ec_num_pwms(struct cros_ec_device *ec)
+{
+	int i, ret;
+
+	/* The index field is only 8 bits */
+	for (i = 0; i <= U8_MAX; i++) {
+		u32 result = 0;
+
+		ret = __cros_ec_pwm_get_duty(ec, i, &result);
+		/* We want to parse EC protocol errors */
+		if (ret < 0 && !(ret == -EPROTO && result))
+			return ret;
+
+		/*
+		 * We look for SUCCESS, INVALID_COMMAND, or INVALID_PARAM
+		 * responses; everything else is treated as an error.
+		 */
+		if (result == EC_RES_INVALID_COMMAND)
+			return -ENODEV;
+		else if (result == EC_RES_INVALID_PARAM)
+			return i;
+		else if (result)
+			return -EPROTO;
+	}
+
+	return U8_MAX;
+}
+
+static int cros_ec_pwm_probe(struct platform_device *pdev)
+{
+	struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
+	struct device *dev = &pdev->dev;
+	struct cros_ec_pwm_device *ec_pwm;
+	struct pwm_chip *chip;
+	int ret;
+
+	if (!ec) {
+		dev_err(dev, "no parent EC device\n");
+		return -EINVAL;
+	}
+
+	ec_pwm = devm_kzalloc(dev, sizeof(*ec_pwm), GFP_KERNEL);
+	if (!ec_pwm)
+		return -ENOMEM;
+	chip = &ec_pwm->chip;
+	ec_pwm->ec = ec;
+
+	/* PWM chip */
+	chip->dev = dev;
+	chip->ops = &cros_ec_pwm_ops;
+	chip->of_xlate = cros_ec_pwm_xlate;
+	chip->of_pwm_n_cells = 1;
+	chip->base = -1;
+	ret = cros_ec_num_pwms(ec);
+	if (ret < 0) {
+		dev_err(dev, "Couldn't find PWMs: %d\n", ret);
+		return ret;
+	}
+	chip->npwm = ret;
+	dev_dbg(dev, "Probed %u PWMs\n", chip->npwm);
+
+	ret = pwmchip_add(chip);
+	if (ret < 0) {
+		dev_err(dev, "cannot register PWM: %d\n", ret);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, ec_pwm);
+
+	return ret;
+}
+
+static int cros_ec_pwm_remove(struct platform_device *dev)
+{
+	struct cros_ec_pwm_device *ec_pwm = platform_get_drvdata(dev);
+	struct pwm_chip *chip = &ec_pwm->chip;
+
+	return pwmchip_remove(chip);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id cros_ec_pwm_of_match[] = {
+	{ .compatible = "google,cros-ec-pwm" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, cros_ec_pwm_of_match);
+#endif
+
+static struct platform_driver cros_ec_pwm_driver = {
+	.probe = cros_ec_pwm_probe,
+	.remove = cros_ec_pwm_remove,
+	.driver = {
+		.name = "cros-ec-pwm",
+		.of_match_table = of_match_ptr(cros_ec_pwm_of_match),
+	},
+};
+module_platform_driver(cros_ec_pwm_driver);
+
+MODULE_ALIAS("platform:cros-ec-pwm");
+MODULE_DESCRIPTION("ChromeOS EC PWM driver");
+MODULE_LICENSE("GPL v2");

+ 7 - 0
drivers/pwm/pwm-lpc32xx.c

@@ -25,6 +25,7 @@ struct lpc32xx_pwm_chip {
 };
 
 #define PWM_ENABLE	BIT(31)
+#define PWM_PIN_LEVEL	BIT(30)
 
 #define to_lpc32xx_pwm_chip(_chip) \
 	container_of(_chip, struct lpc32xx_pwm_chip, chip)
@@ -103,6 +104,7 @@ static int lpc32xx_pwm_probe(struct platform_device *pdev)
 	struct lpc32xx_pwm_chip *lpc32xx;
 	struct resource *res;
 	int ret;
+	u32 val;
 
 	lpc32xx = devm_kzalloc(&pdev->dev, sizeof(*lpc32xx), GFP_KERNEL);
 	if (!lpc32xx)
@@ -128,6 +130,11 @@ static int lpc32xx_pwm_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	/* When PWM is disable, configure the output to the default value */
+	val = readl(lpc32xx->base + (lpc32xx->chip.pwms[0].hwpwm << 2));
+	val &= ~PWM_PIN_LEVEL;
+	writel(val, lpc32xx->base + (lpc32xx->chip.pwms[0].hwpwm << 2));
+
 	platform_set_drvdata(pdev, lpc32xx);
 
 	return 0;

+ 1 - 0
drivers/pwm/pwm-lpss-pci.c

@@ -76,6 +76,7 @@ static const struct pci_device_id pwm_lpss_pci_ids[] = {
 	{ PCI_VDEVICE(INTEL, 0x0ac8), (unsigned long)&pwm_lpss_bxt_info},
 	{ PCI_VDEVICE(INTEL, 0x0f08), (unsigned long)&pwm_lpss_byt_info},
 	{ PCI_VDEVICE(INTEL, 0x0f09), (unsigned long)&pwm_lpss_byt_info},
+	{ PCI_VDEVICE(INTEL, 0x11a5), (unsigned long)&pwm_lpss_bxt_info},
 	{ PCI_VDEVICE(INTEL, 0x1ac8), (unsigned long)&pwm_lpss_bxt_info},
 	{ PCI_VDEVICE(INTEL, 0x2288), (unsigned long)&pwm_lpss_bsw_info},
 	{ PCI_VDEVICE(INTEL, 0x2289), (unsigned long)&pwm_lpss_bsw_info},

+ 14 - 12
drivers/pwm/pwm-lpss.c

@@ -27,7 +27,6 @@
 #define PWM_SW_UPDATE			BIT(30)
 #define PWM_BASE_UNIT_SHIFT		8
 #define PWM_ON_TIME_DIV_MASK		0x000000ff
-#define PWM_DIVISION_CORRECTION		0x2
 
 /* Size of each PWM register space if multiple */
 #define PWM_SIZE			0x400
@@ -92,8 +91,8 @@ static int pwm_lpss_config(struct pwm_chip *chip, struct pwm_device *pwm,
 			   int duty_ns, int period_ns)
 {
 	struct pwm_lpss_chip *lpwm = to_lpwm(chip);
-	u8 on_time_div;
-	unsigned long c, base_unit_range;
+	unsigned long long on_time_div;
+	unsigned long c = lpwm->info->clk_rate, base_unit_range;
 	unsigned long long base_unit, freq = NSEC_PER_SEC;
 	u32 ctrl;
 
@@ -101,21 +100,18 @@ static int pwm_lpss_config(struct pwm_chip *chip, struct pwm_device *pwm,
 
 	/*
 	 * The equation is:
-	 * base_unit = ((freq / c) * base_unit_range) + correction
+	 * base_unit = round(base_unit_range * freq / c)
 	 */
 	base_unit_range = BIT(lpwm->info->base_unit_bits);
-	base_unit = freq * base_unit_range;
+	freq *= base_unit_range;
 
-	c = lpwm->info->clk_rate;
-	if (!c)
-		return -EINVAL;
-
-	do_div(base_unit, c);
-	base_unit += PWM_DIVISION_CORRECTION;
+	base_unit = DIV_ROUND_CLOSEST_ULL(freq, c);
 
 	if (duty_ns <= 0)
 		duty_ns = 1;
-	on_time_div = 255 - (255 * duty_ns / period_ns);
+	on_time_div = 255ULL * duty_ns;
+	do_div(on_time_div, period_ns);
+	on_time_div = 255ULL - on_time_div;
 
 	pm_runtime_get_sync(chip->dev);
 
@@ -169,6 +165,7 @@ struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, struct resource *r,
 				     const struct pwm_lpss_boardinfo *info)
 {
 	struct pwm_lpss_chip *lpwm;
+	unsigned long c;
 	int ret;
 
 	lpwm = devm_kzalloc(dev, sizeof(*lpwm), GFP_KERNEL);
@@ -180,6 +177,11 @@ struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, struct resource *r,
 		return ERR_CAST(lpwm->regs);
 
 	lpwm->info = info;
+
+	c = lpwm->info->clk_rate;
+	if (!c)
+		return ERR_PTR(-EINVAL);
+
 	lpwm->chip.dev = dev;
 	lpwm->chip.ops = &pwm_lpss_ops;
 	lpwm->chip.base = -1;

+ 132 - 46
drivers/pwm/pwm-rockchip.c

@@ -47,10 +47,14 @@ struct rockchip_pwm_regs {
 struct rockchip_pwm_data {
 	struct rockchip_pwm_regs regs;
 	unsigned int prescaler;
+	bool supports_polarity;
 	const struct pwm_ops *ops;
 
 	void (*set_enable)(struct pwm_chip *chip,
-			   struct pwm_device *pwm, bool enable);
+			   struct pwm_device *pwm, bool enable,
+			   enum pwm_polarity polarity);
+	void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm,
+			  struct pwm_state *state);
 };
 
 static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c)
@@ -59,7 +63,8 @@ static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c)
 }
 
 static void rockchip_pwm_set_enable_v1(struct pwm_chip *chip,
-				       struct pwm_device *pwm, bool enable)
+				       struct pwm_device *pwm, bool enable,
+				       enum pwm_polarity polarity)
 {
 	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
 	u32 enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN;
@@ -75,15 +80,29 @@ static void rockchip_pwm_set_enable_v1(struct pwm_chip *chip,
 	writel_relaxed(val, pc->base + pc->data->regs.ctrl);
 }
 
+static void rockchip_pwm_get_state_v1(struct pwm_chip *chip,
+				      struct pwm_device *pwm,
+				      struct pwm_state *state)
+{
+	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
+	u32 enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN;
+	u32 val;
+
+	val = readl_relaxed(pc->base + pc->data->regs.ctrl);
+	if ((val & enable_conf) == enable_conf)
+		state->enabled = true;
+}
+
 static void rockchip_pwm_set_enable_v2(struct pwm_chip *chip,
-				       struct pwm_device *pwm, bool enable)
+				       struct pwm_device *pwm, bool enable,
+				       enum pwm_polarity polarity)
 {
 	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
 	u32 enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE |
 			  PWM_CONTINUOUS;
 	u32 val;
 
-	if (pwm_get_polarity(pwm) == PWM_POLARITY_INVERSED)
+	if (polarity == PWM_POLARITY_INVERSED)
 		enable_conf |= PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSITIVE;
 	else
 		enable_conf |= PWM_DUTY_POSITIVE | PWM_INACTIVE_NEGATIVE;
@@ -98,13 +117,59 @@ static void rockchip_pwm_set_enable_v2(struct pwm_chip *chip,
 	writel_relaxed(val, pc->base + pc->data->regs.ctrl);
 }
 
+static void rockchip_pwm_get_state_v2(struct pwm_chip *chip,
+				      struct pwm_device *pwm,
+				      struct pwm_state *state)
+{
+	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
+	u32 enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE |
+			  PWM_CONTINUOUS;
+	u32 val;
+
+	val = readl_relaxed(pc->base + pc->data->regs.ctrl);
+	if ((val & enable_conf) != enable_conf)
+		return;
+
+	state->enabled = true;
+
+	if (!(val & PWM_DUTY_POSITIVE))
+		state->polarity = PWM_POLARITY_INVERSED;
+}
+
+static void rockchip_pwm_get_state(struct pwm_chip *chip,
+				   struct pwm_device *pwm,
+				   struct pwm_state *state)
+{
+	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
+	unsigned long clk_rate;
+	u64 tmp;
+	int ret;
+
+	ret = clk_enable(pc->clk);
+	if (ret)
+		return;
+
+	clk_rate = clk_get_rate(pc->clk);
+
+	tmp = readl_relaxed(pc->base + pc->data->regs.period);
+	tmp *= pc->data->prescaler * NSEC_PER_SEC;
+	state->period = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate);
+
+	tmp = readl_relaxed(pc->base + pc->data->regs.duty);
+	tmp *= pc->data->prescaler * NSEC_PER_SEC;
+	state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate);
+
+	pc->data->get_state(chip, pwm, state);
+
+	clk_disable(pc->clk);
+}
+
 static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 			       int duty_ns, int period_ns)
 {
 	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
 	unsigned long period, duty;
 	u64 clk_rate, div;
-	int ret;
 
 	clk_rate = clk_get_rate(pc->clk);
 
@@ -114,74 +179,72 @@ static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 	 * default prescaler value for all practical clock rate values.
 	 */
 	div = clk_rate * period_ns;
-	do_div(div, pc->data->prescaler * NSEC_PER_SEC);
-	period = div;
+	period = DIV_ROUND_CLOSEST_ULL(div,
+				       pc->data->prescaler * NSEC_PER_SEC);
 
 	div = clk_rate * duty_ns;
-	do_div(div, pc->data->prescaler * NSEC_PER_SEC);
-	duty = div;
-
-	ret = clk_enable(pc->clk);
-	if (ret)
-		return ret;
+	duty = DIV_ROUND_CLOSEST_ULL(div, pc->data->prescaler * NSEC_PER_SEC);
 
 	writel(period, pc->base + pc->data->regs.period);
 	writel(duty, pc->base + pc->data->regs.duty);
-	writel(0, pc->base + pc->data->regs.cntr);
-
-	clk_disable(pc->clk);
-
-	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)
+static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+			      struct pwm_state *state)
 {
 	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
+	struct pwm_state curstate;
+	bool enabled;
 	int ret;
 
+	pwm_get_state(pwm, &curstate);
+	enabled = curstate.enabled;
+
 	ret = clk_enable(pc->clk);
 	if (ret)
 		return ret;
 
-	pc->data->set_enable(chip, pwm, true);
+	if (state->polarity != curstate.polarity && enabled) {
+		pc->data->set_enable(chip, pwm, false, state->polarity);
+		enabled = false;
+	}
 
-	return 0;
-}
+	ret = rockchip_pwm_config(chip, pwm, state->duty_cycle, state->period);
+	if (ret) {
+		if (enabled != curstate.enabled)
+			pc->data->set_enable(chip, pwm, !enabled,
+					     state->polarity);
 
-static void rockchip_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
-{
-	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
+		goto out;
+	}
+
+	if (state->enabled != enabled)
+		pc->data->set_enable(chip, pwm, state->enabled,
+				     state->polarity);
 
-	pc->data->set_enable(chip, pwm, false);
+	/*
+	 * Update the state with the real hardware, which can differ a bit
+	 * because of period/duty_cycle approximation.
+	 */
+	rockchip_pwm_get_state(chip, pwm, state);
 
+out:
 	clk_disable(pc->clk);
+
+	return ret;
 }
 
 static const struct pwm_ops rockchip_pwm_ops_v1 = {
-	.config = rockchip_pwm_config,
-	.enable = rockchip_pwm_enable,
-	.disable = rockchip_pwm_disable,
+	.get_state = rockchip_pwm_get_state,
+	.apply = rockchip_pwm_apply,
 	.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,
+	.get_state = rockchip_pwm_get_state,
+	.apply = rockchip_pwm_apply,
 	.owner = THIS_MODULE,
 };
 
@@ -195,6 +258,7 @@ static const struct rockchip_pwm_data pwm_data_v1 = {
 	.prescaler = 2,
 	.ops = &rockchip_pwm_ops_v1,
 	.set_enable = rockchip_pwm_set_enable_v1,
+	.get_state = rockchip_pwm_get_state_v1,
 };
 
 static const struct rockchip_pwm_data pwm_data_v2 = {
@@ -205,8 +269,10 @@ static const struct rockchip_pwm_data pwm_data_v2 = {
 		.ctrl = 0x0c,
 	},
 	.prescaler = 1,
+	.supports_polarity = true,
 	.ops = &rockchip_pwm_ops_v2,
 	.set_enable = rockchip_pwm_set_enable_v2,
+	.get_state = rockchip_pwm_get_state_v2,
 };
 
 static const struct rockchip_pwm_data pwm_data_vop = {
@@ -217,8 +283,10 @@ static const struct rockchip_pwm_data pwm_data_vop = {
 		.ctrl = 0x00,
 	},
 	.prescaler = 1,
+	.supports_polarity = true,
 	.ops = &rockchip_pwm_ops_v2,
 	.set_enable = rockchip_pwm_set_enable_v2,
+	.get_state = rockchip_pwm_get_state_v2,
 };
 
 static const struct of_device_id rockchip_pwm_dt_ids[] = {
@@ -253,7 +321,7 @@ static int rockchip_pwm_probe(struct platform_device *pdev)
 	if (IS_ERR(pc->clk))
 		return PTR_ERR(pc->clk);
 
-	ret = clk_prepare(pc->clk);
+	ret = clk_prepare_enable(pc->clk);
 	if (ret)
 		return ret;
 
@@ -265,7 +333,7 @@ static int rockchip_pwm_probe(struct platform_device *pdev)
 	pc->chip.base = -1;
 	pc->chip.npwm = 1;
 
-	if (pc->data->ops->set_polarity) {
+	if (pc->data->supports_polarity) {
 		pc->chip.of_xlate = of_pwm_xlate_with_flags;
 		pc->chip.of_pwm_n_cells = 3;
 	}
@@ -276,6 +344,10 @@ static int rockchip_pwm_probe(struct platform_device *pdev)
 		dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
 	}
 
+	/* Keep the PWM clk enabled if the PWM appears to be up and running. */
+	if (!pwm_is_enabled(pc->chip.pwms))
+		clk_disable(pc->clk);
+
 	return ret;
 }
 
@@ -283,6 +355,20 @@ static int rockchip_pwm_remove(struct platform_device *pdev)
 {
 	struct rockchip_pwm_chip *pc = platform_get_drvdata(pdev);
 
+	/*
+	 * Disable the PWM clk before unpreparing it if the PWM device is still
+	 * running. This should only happen when the last PWM user left it
+	 * enabled, or when nobody requested a PWM that was previously enabled
+	 * by the bootloader.
+	 *
+	 * FIXME: Maybe the core should disable all PWM devices in
+	 * pwmchip_remove(). In this case we'd only have to call
+	 * clk_unprepare() after pwmchip_remove().
+	 *
+	 */
+	if (pwm_is_enabled(pc->chip.pwms))
+		clk_disable(pc->clk);
+
 	clk_unprepare(pc->clk);
 
 	return pwmchip_remove(&pc->chip);

+ 319 - 0
drivers/pwm/pwm-stmpe.c

@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2016 Linaro Ltd.
+ *
+ * Author: Linus Walleij <linus.walleij@linaro.org>
+ *
+ * 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/bitops.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/mfd/stmpe.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+#define STMPE24XX_PWMCS		0x30
+#define PWMCS_EN_PWM0		BIT(0)
+#define PWMCS_EN_PWM1		BIT(1)
+#define PWMCS_EN_PWM2		BIT(2)
+#define STMPE24XX_PWMIC0	0x38
+#define STMPE24XX_PWMIC1	0x39
+#define STMPE24XX_PWMIC2	0x3a
+
+#define STMPE_PWM_24XX_PINBASE	21
+
+struct stmpe_pwm {
+	struct stmpe *stmpe;
+	struct pwm_chip chip;
+	u8 last_duty;
+};
+
+static inline struct stmpe_pwm *to_stmpe_pwm(struct pwm_chip *chip)
+{
+	return container_of(chip, struct stmpe_pwm, chip);
+}
+
+static int stmpe_24xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	struct stmpe_pwm *stmpe_pwm = to_stmpe_pwm(chip);
+	u8 value;
+	int ret;
+
+	ret = stmpe_reg_read(stmpe_pwm->stmpe, STMPE24XX_PWMCS);
+	if (ret < 0) {
+		dev_err(chip->dev, "error reading PWM#%u control\n",
+			pwm->hwpwm);
+		return ret;
+	}
+
+	value = ret | BIT(pwm->hwpwm);
+
+	ret = stmpe_reg_write(stmpe_pwm->stmpe, STMPE24XX_PWMCS, value);
+	if (ret) {
+		dev_err(chip->dev, "error writing PWM#%u control\n",
+			pwm->hwpwm);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void stmpe_24xx_pwm_disable(struct pwm_chip *chip,
+				   struct pwm_device *pwm)
+{
+	struct stmpe_pwm *stmpe_pwm = to_stmpe_pwm(chip);
+	u8 value;
+	int ret;
+
+	ret = stmpe_reg_read(stmpe_pwm->stmpe, STMPE24XX_PWMCS);
+	if (ret < 0) {
+		dev_err(chip->dev, "error reading PWM#%u control\n",
+			pwm->hwpwm);
+		return;
+	}
+
+	value = ret & ~BIT(pwm->hwpwm);
+
+	ret = stmpe_reg_write(stmpe_pwm->stmpe, STMPE24XX_PWMCS, value);
+	if (ret) {
+		dev_err(chip->dev, "error writing PWM#%u control\n",
+			pwm->hwpwm);
+		return;
+	}
+}
+
+/* STMPE 24xx PWM instructions */
+#define SMAX		0x007f
+#define SMIN		0x00ff
+#define GTS		0x0000
+#define LOAD		BIT(14) /* Only available on 2403 */
+#define RAMPUP		0x0000
+#define RAMPDOWN	BIT(7)
+#define PRESCALE_512	BIT(14)
+#define STEPTIME_1	BIT(8)
+#define BRANCH		(BIT(15) | BIT(13))
+
+static int stmpe_24xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+				 int duty_ns, int period_ns)
+{
+	struct stmpe_pwm *stmpe_pwm = to_stmpe_pwm(chip);
+	unsigned int i, pin;
+	u16 program[3] = {
+		SMAX,
+		GTS,
+		GTS,
+	};
+	u8 offset;
+	int ret;
+
+	/* Make sure we are disabled */
+	if (pwm_is_enabled(pwm)) {
+		stmpe_24xx_pwm_disable(chip, pwm);
+	} else {
+		/* Connect the PWM to the pin */
+		pin = pwm->hwpwm;
+
+		/* On STMPE2401 and 2403 pins 21,22,23 are used */
+		if (stmpe_pwm->stmpe->partnum == STMPE2401 ||
+		    stmpe_pwm->stmpe->partnum == STMPE2403)
+			pin += STMPE_PWM_24XX_PINBASE;
+
+		ret = stmpe_set_altfunc(stmpe_pwm->stmpe, BIT(pin),
+					STMPE_BLOCK_PWM);
+		if (ret) {
+			dev_err(chip->dev, "unable to connect PWM#%u to pin\n",
+				pwm->hwpwm);
+			return ret;
+		}
+	}
+
+	/* STMPE24XX */
+	switch (pwm->hwpwm) {
+	case 0:
+		offset = STMPE24XX_PWMIC0;
+		break;
+
+	case 1:
+		offset = STMPE24XX_PWMIC1;
+		break;
+
+	case 2:
+		offset = STMPE24XX_PWMIC1;
+		break;
+
+	default:
+		/* Should not happen as npwm is 3 */
+		return -ENODEV;
+	}
+
+	dev_dbg(chip->dev, "PWM#%u: config duty %d ns, period %d ns\n",
+		pwm->hwpwm, duty_ns, period_ns);
+
+	if (duty_ns == 0) {
+		if (stmpe_pwm->stmpe->partnum == STMPE2401)
+			program[0] = SMAX; /* off all the time */
+
+		if (stmpe_pwm->stmpe->partnum == STMPE2403)
+			program[0] = LOAD | 0xff; /* LOAD 0xff */
+
+		stmpe_pwm->last_duty = 0x00;
+	} else if (duty_ns == period_ns) {
+		if (stmpe_pwm->stmpe->partnum == STMPE2401)
+			program[0] = SMIN; /* on all the time */
+
+		if (stmpe_pwm->stmpe->partnum == STMPE2403)
+			program[0] = LOAD | 0x00; /* LOAD 0x00 */
+
+		stmpe_pwm->last_duty = 0xff;
+	} else {
+		u8 value, last = stmpe_pwm->last_duty;
+		unsigned long duty;
+
+		/*
+		 * Counter goes from 0x00 to 0xff repeatedly at 32768 Hz,
+		 * (means a period of 30517 ns) then this is compared to the
+		 * counter from the ramp, if this is >= PWM counter the output
+		 * is high. With LOAD we can define how much of the cycle it
+		 * is on.
+		 *
+		 * Prescale = 0 -> 2 kHz -> T = 1/f = 488281.25 ns
+		 */
+
+		/* Scale to 0..0xff */
+		duty = duty_ns * 256;
+		duty = DIV_ROUND_CLOSEST(duty, period_ns);
+		value = duty;
+
+		if (value == last) {
+			/* Run the old program */
+			if (pwm_is_enabled(pwm))
+				stmpe_24xx_pwm_enable(chip, pwm);
+
+			return 0;
+		} else if (stmpe_pwm->stmpe->partnum == STMPE2403) {
+			/* STMPE2403 can simply set the right PWM value */
+			program[0] = LOAD | value;
+			program[1] = 0x0000;
+		} else if (stmpe_pwm->stmpe->partnum == STMPE2401) {
+			/* STMPE2401 need a complex program */
+			u16 incdec = 0x0000;
+
+			if (last < value)
+				/* Count up */
+				incdec = RAMPUP | (value - last);
+			else
+				/* Count down */
+				incdec = RAMPDOWN | (last - value);
+
+			/* Step to desired value, smoothly */
+			program[0] = PRESCALE_512 | STEPTIME_1 | incdec;
+
+			/* Loop eternally to 0x00 */
+			program[1] = BRANCH;
+		}
+
+		dev_dbg(chip->dev,
+			"PWM#%u: value = %02x, last_duty = %02x, program=%04x,%04x,%04x\n",
+			pwm->hwpwm, value, last, program[0], program[1],
+			program[2]);
+		stmpe_pwm->last_duty = value;
+	}
+
+	/*
+	 * We can write programs of up to 64 16-bit words into this channel.
+	 */
+	for (i = 0; i < ARRAY_SIZE(program); i++) {
+		u8 value;
+
+		value = (program[i] >> 8) & 0xff;
+
+		ret = stmpe_reg_write(stmpe_pwm->stmpe, offset, value);
+		if (ret) {
+			dev_err(chip->dev, "error writing register %02x: %d\n",
+				offset, ret);
+			return ret;
+		}
+
+		value = program[i] & 0xff;
+
+		ret = stmpe_reg_write(stmpe_pwm->stmpe, offset, value);
+		if (ret) {
+			dev_err(chip->dev, "error writing register %02x: %d\n",
+				offset, ret);
+			return ret;
+		}
+	}
+
+	/* If we were enabled, re-enable this PWM */
+	if (pwm_is_enabled(pwm))
+		stmpe_24xx_pwm_enable(chip, pwm);
+
+	/* Sleep for 200ms so we're sure it will take effect */
+	msleep(200);
+
+	dev_dbg(chip->dev, "programmed PWM#%u, %u bytes\n", pwm->hwpwm, i);
+
+	return 0;
+}
+
+static const struct pwm_ops stmpe_24xx_pwm_ops = {
+	.config = stmpe_24xx_pwm_config,
+	.enable = stmpe_24xx_pwm_enable,
+	.disable = stmpe_24xx_pwm_disable,
+	.owner = THIS_MODULE,
+};
+
+static int __init stmpe_pwm_probe(struct platform_device *pdev)
+{
+	struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent);
+	struct stmpe_pwm *pwm;
+	int ret;
+
+	pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
+	if (!pwm)
+		return -ENOMEM;
+
+	pwm->stmpe = stmpe;
+	pwm->chip.dev = &pdev->dev;
+	pwm->chip.base = -1;
+
+	if (stmpe->partnum == STMPE2401 || stmpe->partnum == STMPE2403) {
+		pwm->chip.ops = &stmpe_24xx_pwm_ops;
+		pwm->chip.npwm = 3;
+	} else {
+		if (stmpe->partnum == STMPE1601)
+			dev_err(&pdev->dev, "STMPE1601 not yet supported\n");
+		else
+			dev_err(&pdev->dev, "Unknown STMPE PWM\n");
+
+		return -ENODEV;
+	}
+
+	ret = stmpe_enable(stmpe, STMPE_BLOCK_PWM);
+	if (ret)
+		return ret;
+
+	ret = pwmchip_add(&pwm->chip);
+	if (ret) {
+		stmpe_disable(stmpe, STMPE_BLOCK_PWM);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, pwm);
+
+	return 0;
+}
+
+static struct platform_driver stmpe_pwm_driver = {
+	.driver = {
+		.name = "stmpe-pwm",
+	},
+};
+builtin_platform_driver_probe(stmpe_pwm_driver, stmpe_pwm_probe);

+ 52 - 17
drivers/pwm/pwm-tegra.c

@@ -26,9 +26,11 @@
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/pwm.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
+#include <linux/reset.h>
 
 #define PWM_ENABLE	(1 << 31)
 #define PWM_DUTY_WIDTH	8
@@ -36,15 +38,20 @@
 #define PWM_SCALE_WIDTH	13
 #define PWM_SCALE_SHIFT	0
 
-#define NUM_PWM 4
+struct tegra_pwm_soc {
+	unsigned int num_channels;
+};
 
 struct tegra_pwm_chip {
-	struct pwm_chip		chip;
-	struct device		*dev;
+	struct pwm_chip chip;
+	struct device *dev;
+
+	struct clk *clk;
+	struct reset_control*rst;
 
-	struct clk		*clk;
+	void __iomem *regs;
 
-	void __iomem		*mmio_base;
+	const struct tegra_pwm_soc *soc;
 };
 
 static inline struct tegra_pwm_chip *to_tegra_pwm_chip(struct pwm_chip *chip)
@@ -54,20 +61,20 @@ static inline struct tegra_pwm_chip *to_tegra_pwm_chip(struct pwm_chip *chip)
 
 static inline u32 pwm_readl(struct tegra_pwm_chip *chip, unsigned int num)
 {
-	return readl(chip->mmio_base + (num << 4));
+	return readl(chip->regs + (num << 4));
 }
 
 static inline void pwm_writel(struct tegra_pwm_chip *chip, unsigned int num,
 			     unsigned long val)
 {
-	writel(val, chip->mmio_base + (num << 4));
+	writel(val, chip->regs + (num << 4));
 }
 
 static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 			    int duty_ns, int period_ns)
 {
 	struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
-	unsigned long long c;
+	unsigned long long c = duty_ns;
 	unsigned long rate, hz;
 	u32 val = 0;
 	int err;
@@ -77,7 +84,8 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 	 * per (1 << PWM_DUTY_WIDTH) cycles and make sure to round to the
 	 * nearest integer during division.
 	 */
-	c = duty_ns * ((1 << PWM_DUTY_WIDTH) - 1) + period_ns / 2;
+	c *= (1 << PWM_DUTY_WIDTH);
+	c += period_ns / 2;
 	do_div(c, period_ns);
 
 	val = (u32)c << PWM_DUTY_SHIFT;
@@ -176,12 +184,13 @@ static int tegra_pwm_probe(struct platform_device *pdev)
 	if (!pwm)
 		return -ENOMEM;
 
+	pwm->soc = of_device_get_match_data(&pdev->dev);
 	pwm->dev = &pdev->dev;
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	pwm->mmio_base = devm_ioremap_resource(&pdev->dev, r);
-	if (IS_ERR(pwm->mmio_base))
-		return PTR_ERR(pwm->mmio_base);
+	pwm->regs = devm_ioremap_resource(&pdev->dev, r);
+	if (IS_ERR(pwm->regs))
+		return PTR_ERR(pwm->regs);
 
 	platform_set_drvdata(pdev, pwm);
 
@@ -189,14 +198,24 @@ static int tegra_pwm_probe(struct platform_device *pdev)
 	if (IS_ERR(pwm->clk))
 		return PTR_ERR(pwm->clk);
 
+	pwm->rst = devm_reset_control_get(&pdev->dev, "pwm");
+	if (IS_ERR(pwm->rst)) {
+		ret = PTR_ERR(pwm->rst);
+		dev_err(&pdev->dev, "Reset control is not found: %d\n", ret);
+		return ret;
+	}
+
+	reset_control_deassert(pwm->rst);
+
 	pwm->chip.dev = &pdev->dev;
 	pwm->chip.ops = &tegra_pwm_ops;
 	pwm->chip.base = -1;
-	pwm->chip.npwm = NUM_PWM;
+	pwm->chip.npwm = pwm->soc->num_channels;
 
 	ret = pwmchip_add(&pwm->chip);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
+		reset_control_assert(pwm->rst);
 		return ret;
 	}
 
@@ -206,12 +225,17 @@ static int tegra_pwm_probe(struct platform_device *pdev)
 static int tegra_pwm_remove(struct platform_device *pdev)
 {
 	struct tegra_pwm_chip *pc = platform_get_drvdata(pdev);
-	int i;
+	unsigned int i;
+	int err;
 
 	if (WARN_ON(!pc))
 		return -ENODEV;
 
-	for (i = 0; i < NUM_PWM; i++) {
+	err = clk_prepare_enable(pc->clk);
+	if (err < 0)
+		return err;
+
+	for (i = 0; i < pc->chip.npwm; i++) {
 		struct pwm_device *pwm = &pc->chip.pwms[i];
 
 		if (!pwm_is_enabled(pwm))
@@ -223,12 +247,23 @@ static int tegra_pwm_remove(struct platform_device *pdev)
 		clk_disable_unprepare(pc->clk);
 	}
 
+	reset_control_assert(pc->rst);
+	clk_disable_unprepare(pc->clk);
+
 	return pwmchip_remove(&pc->chip);
 }
 
+static const struct tegra_pwm_soc tegra20_pwm_soc = {
+	.num_channels = 4,
+};
+
+static const struct tegra_pwm_soc tegra186_pwm_soc = {
+	.num_channels = 1,
+};
+
 static const struct of_device_id tegra_pwm_of_match[] = {
-	{ .compatible = "nvidia,tegra20-pwm" },
-	{ .compatible = "nvidia,tegra30-pwm" },
+	{ .compatible = "nvidia,tegra20-pwm", .data = &tegra20_pwm_soc },
+	{ .compatible = "nvidia,tegra186-pwm", .data = &tegra186_pwm_soc },
 	{ }
 };
 

+ 9 - 28
drivers/pwm/pwm-tiecap.c

@@ -27,8 +27,6 @@
 #include <linux/pwm.h>
 #include <linux/of_device.h>
 
-#include "pwm-tipwmss.h"
-
 /* ECAP registers and bits definitions */
 #define CAP1			0x08
 #define CAP2			0x0C
@@ -195,6 +193,7 @@ static const struct pwm_ops ecap_pwm_ops = {
 };
 
 static const struct of_device_id ecap_of_match[] = {
+	{ .compatible	= "ti,am3352-ecap" },
 	{ .compatible	= "ti,am33xx-ecap" },
 	{},
 };
@@ -202,17 +201,24 @@ MODULE_DEVICE_TABLE(of, ecap_of_match);
 
 static int ecap_pwm_probe(struct platform_device *pdev)
 {
+	struct device_node *np = pdev->dev.of_node;
 	int ret;
 	struct resource *r;
 	struct clk *clk;
 	struct ecap_pwm_chip *pc;
-	u16 status;
 
 	pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
 	if (!pc)
 		return -ENOMEM;
 
 	clk = devm_clk_get(&pdev->dev, "fck");
+	if (IS_ERR(clk)) {
+		if (of_device_is_compatible(np, "ti,am33xx-ecap")) {
+			dev_warn(&pdev->dev, "Binding is obsolete.\n");
+			clk = devm_clk_get(pdev->dev.parent, "fck");
+		}
+	}
+
 	if (IS_ERR(clk)) {
 		dev_err(&pdev->dev, "failed to get clock\n");
 		return PTR_ERR(clk);
@@ -243,40 +249,15 @@ static int ecap_pwm_probe(struct platform_device *pdev)
 	}
 
 	pm_runtime_enable(&pdev->dev);
-	pm_runtime_get_sync(&pdev->dev);
-
-	status = pwmss_submodule_state_change(pdev->dev.parent,
-			PWMSS_ECAPCLK_EN);
-	if (!(status & PWMSS_ECAPCLK_EN_ACK)) {
-		dev_err(&pdev->dev, "PWMSS config space clock enable failed\n");
-		ret = -EINVAL;
-		goto pwmss_clk_failure;
-	}
-
-	pm_runtime_put_sync(&pdev->dev);
 
 	platform_set_drvdata(pdev, pc);
 	return 0;
-
-pwmss_clk_failure:
-	pm_runtime_put_sync(&pdev->dev);
-	pm_runtime_disable(&pdev->dev);
-	pwmchip_remove(&pc->chip);
-	return ret;
 }
 
 static int ecap_pwm_remove(struct platform_device *pdev)
 {
 	struct ecap_pwm_chip *pc = platform_get_drvdata(pdev);
 
-	pm_runtime_get_sync(&pdev->dev);
-	/*
-	 * Due to hardware misbehaviour, acknowledge of the stop_req
-	 * is missing. Hence checking of the status bit skipped.
-	 */
-	pwmss_submodule_state_change(pdev->dev.parent, PWMSS_ECAPCLK_STOP_REQ);
-	pm_runtime_put_sync(&pdev->dev);
-
 	pm_runtime_disable(&pdev->dev);
 	return pwmchip_remove(&pc->chip);
 }

+ 9 - 29
drivers/pwm/pwm-tiehrpwm.c

@@ -27,8 +27,6 @@
 #include <linux/pm_runtime.h>
 #include <linux/of_device.h>
 
-#include "pwm-tipwmss.h"
-
 /* EHRPWM registers and bits definitions */
 
 /* Time base module registers */
@@ -426,6 +424,7 @@ static const struct pwm_ops ehrpwm_pwm_ops = {
 };
 
 static const struct of_device_id ehrpwm_of_match[] = {
+	{ .compatible	= "ti,am3352-ehrpwm" },
 	{ .compatible	= "ti,am33xx-ehrpwm" },
 	{},
 };
@@ -433,17 +432,24 @@ MODULE_DEVICE_TABLE(of, ehrpwm_of_match);
 
 static int ehrpwm_pwm_probe(struct platform_device *pdev)
 {
+	struct device_node *np = pdev->dev.of_node;
 	int ret;
 	struct resource *r;
 	struct clk *clk;
 	struct ehrpwm_pwm_chip *pc;
-	u16 status;
 
 	pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
 	if (!pc)
 		return -ENOMEM;
 
 	clk = devm_clk_get(&pdev->dev, "fck");
+	if (IS_ERR(clk)) {
+		if (of_device_is_compatible(np, "ti,am33xx-ecap")) {
+			dev_warn(&pdev->dev, "Binding is obsolete.\n");
+			clk = devm_clk_get(pdev->dev.parent, "fck");
+		}
+	}
+
 	if (IS_ERR(clk)) {
 		dev_err(&pdev->dev, "failed to get clock\n");
 		return PTR_ERR(clk);
@@ -487,27 +493,9 @@ static int ehrpwm_pwm_probe(struct platform_device *pdev)
 	}
 
 	pm_runtime_enable(&pdev->dev);
-	pm_runtime_get_sync(&pdev->dev);
-
-	status = pwmss_submodule_state_change(pdev->dev.parent,
-			PWMSS_EPWMCLK_EN);
-	if (!(status & PWMSS_EPWMCLK_EN_ACK)) {
-		dev_err(&pdev->dev, "PWMSS config space clock enable failed\n");
-		ret = -EINVAL;
-		goto pwmss_clk_failure;
-	}
-
-	pm_runtime_put_sync(&pdev->dev);
 
 	platform_set_drvdata(pdev, pc);
 	return 0;
-
-pwmss_clk_failure:
-	pm_runtime_put_sync(&pdev->dev);
-	pm_runtime_disable(&pdev->dev);
-	pwmchip_remove(&pc->chip);
-	clk_unprepare(pc->tbclk);
-	return ret;
 }
 
 static int ehrpwm_pwm_remove(struct platform_device *pdev)
@@ -516,14 +504,6 @@ static int ehrpwm_pwm_remove(struct platform_device *pdev)
 
 	clk_unprepare(pc->tbclk);
 
-	pm_runtime_get_sync(&pdev->dev);
-	/*
-	 * Due to hardware misbehaviour, acknowledge of the stop_req
-	 * is missing. Hence checking of the status bit skipped.
-	 */
-	pwmss_submodule_state_change(pdev->dev.parent, PWMSS_EPWMCLK_STOP_REQ);
-	pm_runtime_put_sync(&pdev->dev);
-
 	pm_runtime_put_sync(&pdev->dev);
 	pm_runtime_disable(&pdev->dev);
 	return pwmchip_remove(&pc->chip);

+ 0 - 49
drivers/pwm/pwm-tipwmss.c

@@ -22,32 +22,6 @@
 #include <linux/pm_runtime.h>
 #include <linux/of_device.h>
 
-#include "pwm-tipwmss.h"
-
-#define PWMSS_CLKCONFIG		0x8	/* Clock gating reg */
-#define PWMSS_CLKSTATUS		0xc	/* Clock gating status reg */
-
-struct pwmss_info {
-	void __iomem	*mmio_base;
-	struct mutex	pwmss_lock;
-	u16		pwmss_clkconfig;
-};
-
-u16 pwmss_submodule_state_change(struct device *dev, int set)
-{
-	struct pwmss_info *info = dev_get_drvdata(dev);
-	u16 val;
-
-	mutex_lock(&info->pwmss_lock);
-	val = readw(info->mmio_base + PWMSS_CLKCONFIG);
-	val |= set;
-	writew(val , info->mmio_base + PWMSS_CLKCONFIG);
-	mutex_unlock(&info->pwmss_lock);
-
-	return readw(info->mmio_base + PWMSS_CLKSTATUS);
-}
-EXPORT_SYMBOL(pwmss_submodule_state_change);
-
 static const struct of_device_id pwmss_of_match[] = {
 	{ .compatible	= "ti,am33xx-pwmss" },
 	{},
@@ -57,24 +31,10 @@ MODULE_DEVICE_TABLE(of, pwmss_of_match);
 static int pwmss_probe(struct platform_device *pdev)
 {
 	int ret;
-	struct resource *r;
-	struct pwmss_info *info;
 	struct device_node *node = pdev->dev.of_node;
 
-	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
-	if (!info)
-		return -ENOMEM;
-
-	mutex_init(&info->pwmss_lock);
-
-	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	info->mmio_base = devm_ioremap_resource(&pdev->dev, r);
-	if (IS_ERR(info->mmio_base))
-		return PTR_ERR(info->mmio_base);
-
 	pm_runtime_enable(&pdev->dev);
 	pm_runtime_get_sync(&pdev->dev);
-	platform_set_drvdata(pdev, info);
 
 	/* Populate all the child nodes here... */
 	ret = of_platform_populate(node, NULL, NULL, &pdev->dev);
@@ -86,30 +46,21 @@ static int pwmss_probe(struct platform_device *pdev)
 
 static int pwmss_remove(struct platform_device *pdev)
 {
-	struct pwmss_info *info = platform_get_drvdata(pdev);
-
 	pm_runtime_put_sync(&pdev->dev);
 	pm_runtime_disable(&pdev->dev);
-	mutex_destroy(&info->pwmss_lock);
 	return 0;
 }
 
 #ifdef CONFIG_PM_SLEEP
 static int pwmss_suspend(struct device *dev)
 {
-	struct pwmss_info *info = dev_get_drvdata(dev);
-
-	info->pwmss_clkconfig = readw(info->mmio_base + PWMSS_CLKCONFIG);
 	pm_runtime_put_sync(dev);
 	return 0;
 }
 
 static int pwmss_resume(struct device *dev)
 {
-	struct pwmss_info *info = dev_get_drvdata(dev);
-
 	pm_runtime_get_sync(dev);
-	writew(info->pwmss_clkconfig, info->mmio_base + PWMSS_CLKCONFIG);
 	return 0;
 }
 #endif

+ 0 - 39
drivers/pwm/pwm-tipwmss.h

@@ -1,39 +0,0 @@
-/*
- * TI PWM Subsystem driver
- *
- * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#ifndef __TIPWMSS_H
-#define __TIPWMSS_H
-
-/* PWM substem clock gating */
-#define PWMSS_ECAPCLK_EN	BIT(0)
-#define PWMSS_ECAPCLK_STOP_REQ	BIT(1)
-#define PWMSS_EPWMCLK_EN	BIT(8)
-#define PWMSS_EPWMCLK_STOP_REQ	BIT(9)
-
-#define PWMSS_ECAPCLK_EN_ACK	BIT(0)
-#define PWMSS_EPWMCLK_EN_ACK	BIT(8)
-
-#ifdef CONFIG_PWM_TIPWMSS
-extern u16 pwmss_submodule_state_change(struct device *dev, int set);
-#else
-static inline u16 pwmss_submodule_state_change(struct device *dev, int set)
-{
-	/* return success status value */
-	return 0xFFFF;
-}
-#endif
-#endif	/* __TIPWMSS_H */