summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-03-01 18:46:02 +0100
committerLinus Torvalds <torvalds@linux-foundation.org>2017-03-01 18:46:02 +0100
commit545b2820c4a48245412d97030134cc1d38918726 (patch)
tree78d1b3d0555cd265c3a7830a3dc041b9d1595885 /drivers
parentMerge tag 'drm-ast-2500-for-v4.11' of git://people.freedesktop.org/~airlied/l... (diff)
parentMerge branch 'for-4.11/drivers' into for-next (diff)
downloadlinux-545b2820c4a48245412d97030134cc1d38918726.tar.xz
linux-545b2820c4a48245412d97030134cc1d38918726.zip
Merge tag 'pwm/for-4.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm
Pull pwm updates from Thierry Reding: "This set contains mostly fixes to existing drivers as well as cleanup of code that's not been in active use for a while" * tag 'pwm/for-4.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm: (27 commits) acpi: lpss: call pwm_add_table() for BSW PWM device pwm: Try to load modules during pwm_get() pwm: Don't hold pwm_lookup_lock longer than necessary pwm: Make the PWM_POLARITY flag in DTB optional pwm: Print error messages with pr_err() instead of pr_debug() pwm: imx: Add polarity inversion support to i.MX's PWMv2 pwm: imx: doc: Update imx-pwm.txt documentation entry pwm: imx: Remove redundant i.MX PWMv2 code pwm: imx: Provide atomic PWM support for i.MX PWMv2 pwm: imx: Move PWMv2 wait for fifo slot code to a separate function pwm: imx: Move PWMv2 software reset code to a separate function pwm: imx: Rewrite v1 code to facilitate switch to atomic PWM pwm: imx: Add separate set of PWM ops for v1 and v2 pwm: imx: Remove ipg clock and enable per clock when required pwm: lpss: Add Intel Gemini Lake PCI ID pwm: lpss: Do not export board infos for different PWM types pwm: lpss: Avoid reconfiguring while UPDATE bit is still enabled pwm: lpss: Switch to new atomic API pwm: lpss: Allow duty cycle to be 0 pwm: lpss: Avoid potential overflow of base_unit ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/acpi_lpss.c14
-rw-r--r--drivers/leds/leds-pwm.c16
-rw-r--r--drivers/pwm/Kconfig4
-rw-r--r--drivers/pwm/core.c70
-rw-r--r--drivers/pwm/pwm-atmel-hlcdc.c1
-rw-r--r--drivers/pwm/pwm-atmel.c1
-rw-r--r--drivers/pwm/pwm-bcm-kona.c1
-rw-r--r--drivers/pwm/pwm-berlin.c1
-rw-r--r--drivers/pwm/pwm-bfin.c2
-rw-r--r--drivers/pwm/pwm-brcmstb.c1
-rw-r--r--drivers/pwm/pwm-fsl-ftm.c1
-rw-r--r--drivers/pwm/pwm-imx.c272
-rw-r--r--drivers/pwm/pwm-lp3943.c1
-rw-r--r--drivers/pwm/pwm-lpss-pci.c22
-rw-r--r--drivers/pwm/pwm-lpss-platform.c21
-rw-r--r--drivers/pwm/pwm-lpss.c132
-rw-r--r--drivers/pwm/pwm-lpss.h4
-rw-r--r--drivers/pwm/pwm-mxs.c2
-rw-r--r--drivers/pwm/pwm-pca9685.c176
-rw-r--r--drivers/pwm/pwm-pxa.c2
-rw-r--r--drivers/pwm/pwm-sti.c1
-rw-r--r--drivers/pwm/pwm-sun4i.c1
-rw-r--r--drivers/pwm/pwm-twl-led.c1
-rw-r--r--drivers/pwm/pwm-twl.c1
-rw-r--r--drivers/pwm/pwm-vt8500.c2
-rw-r--r--drivers/staging/greybus/pwm.c1
26 files changed, 462 insertions, 289 deletions
diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c
index 90d112a3063a..5edfd9c49044 100644
--- a/drivers/acpi/acpi_lpss.c
+++ b/drivers/acpi/acpi_lpss.c
@@ -21,6 +21,7 @@
#include <linux/platform_data/x86/pmc_atom.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
+#include <linux/pwm.h>
#include <linux/delay.h>
#include "internal.h"
@@ -154,6 +155,18 @@ static void byt_i2c_setup(struct lpss_private_data *pdata)
writel(0, pdata->mmio_base + LPSS_I2C_ENABLE);
}
+/* BSW PWM used for backlight control by the i915 driver */
+static struct pwm_lookup bsw_pwm_lookup[] = {
+ PWM_LOOKUP_WITH_MODULE("80862288:00", 0, "0000:00:02.0",
+ "pwm_backlight", 0, PWM_POLARITY_NORMAL,
+ "pwm-lpss-platform"),
+};
+
+static void bsw_pwm_setup(struct lpss_private_data *pdata)
+{
+ pwm_add_table(bsw_pwm_lookup, ARRAY_SIZE(bsw_pwm_lookup));
+}
+
static const struct lpss_device_desc lpt_dev_desc = {
.flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR,
.prv_offset = 0x800,
@@ -191,6 +204,7 @@ static const struct lpss_device_desc byt_pwm_dev_desc = {
static const struct lpss_device_desc bsw_pwm_dev_desc = {
.flags = LPSS_SAVE_CTX | LPSS_NO_D3_DELAY,
+ .setup = bsw_pwm_setup,
};
static const struct lpss_device_desc byt_uart_dev_desc = {
diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c
index a9145aa7f36a..8d456dc6c5bf 100644
--- a/drivers/leds/leds-pwm.c
+++ b/drivers/leds/leds-pwm.c
@@ -29,7 +29,6 @@ struct led_pwm_data {
unsigned int active_low;
unsigned int period;
int duty;
- bool can_sleep;
};
struct led_pwm_priv {
@@ -49,8 +48,8 @@ static void __led_pwm_set(struct led_pwm_data *led_dat)
pwm_enable(led_dat->pwm);
}
-static void led_pwm_set(struct led_classdev *led_cdev,
- enum led_brightness brightness)
+static int led_pwm_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
{
struct led_pwm_data *led_dat =
container_of(led_cdev, struct led_pwm_data, cdev);
@@ -66,12 +65,7 @@ static void led_pwm_set(struct led_classdev *led_cdev,
led_dat->duty = duty;
__led_pwm_set(led_dat);
-}
-static int led_pwm_set_blocking(struct led_classdev *led_cdev,
- enum led_brightness brightness)
-{
- led_pwm_set(led_cdev, brightness);
return 0;
}
@@ -112,11 +106,7 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
return ret;
}
- led_data->can_sleep = pwm_can_sleep(led_data->pwm);
- if (!led_data->can_sleep)
- led_data->cdev.brightness_set = led_pwm_set;
- else
- led_data->cdev.brightness_set_blocking = led_pwm_set_blocking;
+ led_data->cdev.brightness_set_blocking = led_pwm_set;
/*
* FIXME: pwm_apply_args() should be removed when switching to the
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 2d0cfaa6d84c..42e37c20b361 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -76,7 +76,9 @@ config PWM_ATMEL_TCB
config PWM_BCM_IPROC
tristate "iProc PWM support"
- depends on ARCH_BCM_IPROC
+ depends on ARCH_BCM_IPROC || COMPILE_TEST
+ depends on COMMON_CLK
+ default ARCH_BCM_IPROC
help
Generic PWM framework driver for Broadcom iProc PWM block. This
block is used in Broadcom iProc SoC's.
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 172ef8245811..a0860b30bd93 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -137,9 +137,14 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args)
{
struct pwm_device *pwm;
+ /* check, whether the driver supports a third cell for flags */
if (pc->of_pwm_n_cells < 3)
return ERR_PTR(-EINVAL);
+ /* flags in the third cell are optional */
+ if (args->args_count < 2)
+ return ERR_PTR(-EINVAL);
+
if (args->args[0] >= pc->npwm)
return ERR_PTR(-EINVAL);
@@ -148,11 +153,10 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args)
return pwm;
pwm->args.period = args->args[1];
+ pwm->args.polarity = PWM_POLARITY_NORMAL;
- if (args->args[2] & PWM_POLARITY_INVERTED)
+ if (args->args_count > 2 && args->args[2] & PWM_POLARITY_INVERTED)
pwm->args.polarity = PWM_POLARITY_INVERSED;
- else
- pwm->args.polarity = PWM_POLARITY_NORMAL;
return pwm;
}
@@ -163,9 +167,14 @@ of_pwm_simple_xlate(struct pwm_chip *pc, const struct of_phandle_args *args)
{
struct pwm_device *pwm;
+ /* sanity check driver support */
if (pc->of_pwm_n_cells < 2)
return ERR_PTR(-EINVAL);
+ /* all cells are required */
+ if (args->args_count != pc->of_pwm_n_cells)
+ return ERR_PTR(-EINVAL);
+
if (args->args[0] >= pc->npwm)
return ERR_PTR(-EINVAL);
@@ -663,24 +672,17 @@ struct pwm_device *of_pwm_get(struct device_node *np, const char *con_id)
err = of_parse_phandle_with_args(np, "pwms", "#pwm-cells", index,
&args);
if (err) {
- pr_debug("%s(): can't parse \"pwms\" property\n", __func__);
+ pr_err("%s(): can't parse \"pwms\" property\n", __func__);
return ERR_PTR(err);
}
pc = of_node_to_pwmchip(args.np);
if (IS_ERR(pc)) {
- pr_debug("%s(): PWM chip not found\n", __func__);
+ pr_err("%s(): PWM chip not found\n", __func__);
pwm = ERR_CAST(pc);
goto put;
}
- if (args.args_count != pc->of_pwm_n_cells) {
- pr_debug("%s: wrong #pwm-cells for %s\n", np->full_name,
- args.np->full_name);
- pwm = ERR_PTR(-EINVAL);
- goto put;
- }
-
pwm = pc->of_xlate(pc, &args);
if (IS_ERR(pwm))
goto put;
@@ -757,12 +759,13 @@ void pwm_remove_table(struct pwm_lookup *table, size_t num)
*/
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;
+ struct pwm_device *pwm;
+ struct pwm_chip *chip;
unsigned int best = 0;
struct pwm_lookup *p, *chosen = NULL;
unsigned int match;
+ int err;
/* look up via DT first */
if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node)
@@ -817,24 +820,35 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)
}
}
- if (!chosen) {
- pwm = ERR_PTR(-ENODEV);
- goto out;
- }
+ mutex_unlock(&pwm_lookup_lock);
+
+ if (!chosen)
+ return ERR_PTR(-ENODEV);
chip = pwmchip_find_by_name(chosen->provider);
+
+ /*
+ * If the lookup entry specifies a module, load the module and retry
+ * the PWM chip lookup. This can be used to work around driver load
+ * ordering issues if driver's can't be made to properly support the
+ * deferred probe mechanism.
+ */
+ if (!chip && chosen->module) {
+ err = request_module(chosen->module);
+ if (err == 0)
+ chip = pwmchip_find_by_name(chosen->provider);
+ }
+
if (!chip)
- goto out;
+ return ERR_PTR(-EPROBE_DEFER);
pwm = pwm_request_from_chip(chip, chosen->index, con_id ?: dev_id);
if (IS_ERR(pwm))
- goto out;
+ return pwm;
pwm->args.period = chosen->period;
pwm->args.polarity = chosen->polarity;
-out:
- mutex_unlock(&pwm_lookup_lock);
return pwm;
}
EXPORT_SYMBOL_GPL(pwm_get);
@@ -960,18 +974,6 @@ void devm_pwm_put(struct device *dev, struct pwm_device *pwm)
}
EXPORT_SYMBOL_GPL(devm_pwm_put);
-/**
- * pwm_can_sleep() - report whether PWM access will sleep
- * @pwm: PWM device
- *
- * Returns: True if accessing the PWM can sleep, false otherwise.
- */
-bool pwm_can_sleep(struct pwm_device *pwm)
-{
- return true;
-}
-EXPORT_SYMBOL_GPL(pwm_can_sleep);
-
#ifdef CONFIG_DEBUG_FS
static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s)
{
diff --git a/drivers/pwm/pwm-atmel-hlcdc.c b/drivers/pwm/pwm-atmel-hlcdc.c
index 14fc011faa32..999187277ea5 100644
--- a/drivers/pwm/pwm-atmel-hlcdc.c
+++ b/drivers/pwm/pwm-atmel-hlcdc.c
@@ -270,7 +270,6 @@ static int atmel_hlcdc_pwm_probe(struct platform_device *pdev)
chip->chip.npwm = 1;
chip->chip.of_xlate = of_pwm_xlate_with_flags;
chip->chip.of_pwm_n_cells = 3;
- chip->chip.can_sleep = 1;
ret = pwmchip_add_with_polarity(&chip->chip, PWM_POLARITY_INVERSED);
if (ret) {
diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c
index e6b8b1b7e6ba..67a7023be5c2 100644
--- a/drivers/pwm/pwm-atmel.c
+++ b/drivers/pwm/pwm-atmel.c
@@ -385,7 +385,6 @@ static int atmel_pwm_probe(struct platform_device *pdev)
atmel_pwm->chip.base = -1;
atmel_pwm->chip.npwm = 4;
- atmel_pwm->chip.can_sleep = true;
atmel_pwm->config = data->config;
atmel_pwm->updated_pwms = 0;
mutex_init(&atmel_pwm->isr_lock);
diff --git a/drivers/pwm/pwm-bcm-kona.c b/drivers/pwm/pwm-bcm-kona.c
index c63418322023..09a95aeb3a70 100644
--- a/drivers/pwm/pwm-bcm-kona.c
+++ b/drivers/pwm/pwm-bcm-kona.c
@@ -276,7 +276,6 @@ static int kona_pwmc_probe(struct platform_device *pdev)
kp->chip.npwm = 6;
kp->chip.of_xlate = of_pwm_xlate_with_flags;
kp->chip.of_pwm_n_cells = 3;
- kp->chip.can_sleep = true;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
kp->base = devm_ioremap_resource(&pdev->dev, res);
diff --git a/drivers/pwm/pwm-berlin.c b/drivers/pwm/pwm-berlin.c
index 01339c152ab0..771859aca4be 100644
--- a/drivers/pwm/pwm-berlin.c
+++ b/drivers/pwm/pwm-berlin.c
@@ -206,7 +206,6 @@ static int berlin_pwm_probe(struct platform_device *pdev)
pwm->chip.ops = &berlin_pwm_ops;
pwm->chip.base = -1;
pwm->chip.npwm = 4;
- pwm->chip.can_sleep = true;
pwm->chip.of_xlate = of_pwm_xlate_with_flags;
pwm->chip.of_pwm_n_cells = 3;
diff --git a/drivers/pwm/pwm-bfin.c b/drivers/pwm/pwm-bfin.c
index 7631ef194de7..d2ed0a2a18e8 100644
--- a/drivers/pwm/pwm-bfin.c
+++ b/drivers/pwm/pwm-bfin.c
@@ -103,7 +103,7 @@ static void bfin_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
disable_gptimer(priv->pin);
}
-static struct pwm_ops bfin_pwm_ops = {
+static const struct pwm_ops bfin_pwm_ops = {
.request = bfin_pwm_request,
.free = bfin_pwm_free,
.config = bfin_pwm_config,
diff --git a/drivers/pwm/pwm-brcmstb.c b/drivers/pwm/pwm-brcmstb.c
index 5d5adee16886..8063cffa1c96 100644
--- a/drivers/pwm/pwm-brcmstb.c
+++ b/drivers/pwm/pwm-brcmstb.c
@@ -270,7 +270,6 @@ static int brcmstb_pwm_probe(struct platform_device *pdev)
p->chip.ops = &brcmstb_pwm_ops;
p->chip.base = -1;
p->chip.npwm = 2;
- p->chip.can_sleep = true;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
p->base = devm_ioremap_resource(&pdev->dev, res);
diff --git a/drivers/pwm/pwm-fsl-ftm.c b/drivers/pwm/pwm-fsl-ftm.c
index fad968eb75f6..557b4ea16796 100644
--- a/drivers/pwm/pwm-fsl-ftm.c
+++ b/drivers/pwm/pwm-fsl-ftm.c
@@ -446,7 +446,6 @@ static int fsl_pwm_probe(struct platform_device *pdev)
fpc->chip.of_pwm_n_cells = 3;
fpc->chip.base = -1;
fpc->chip.npwm = 8;
- fpc->chip.can_sleep = true;
ret = pwmchip_add(&fpc->chip);
if (ret < 0) {
diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c
index d600fd5cd4ba..2ba5c3a398ff 100644
--- a/drivers/pwm/pwm-imx.c
+++ b/drivers/pwm/pwm-imx.c
@@ -38,6 +38,7 @@
#define MX3_PWMCR_DOZEEN (1 << 24)
#define MX3_PWMCR_WAITEN (1 << 23)
#define MX3_PWMCR_DBGEN (1 << 22)
+#define MX3_PWMCR_POUTC (1 << 18)
#define MX3_PWMCR_CLKSRC_IPG_HIGH (2 << 16)
#define MX3_PWMCR_CLKSRC_IPG (1 << 16)
#define MX3_PWMCR_SWR (1 << 3)
@@ -49,15 +50,10 @@
struct imx_chip {
struct clk *clk_per;
- struct clk *clk_ipg;
void __iomem *mmio_base;
struct pwm_chip chip;
-
- int (*config)(struct pwm_chip *chip,
- struct pwm_device *pwm, int duty_ns, int period_ns);
- void (*set_enable)(struct pwm_chip *chip, bool enable);
};
#define to_imx_chip(chip) container_of(chip, struct imx_chip, chip)
@@ -91,176 +87,170 @@ static int imx_pwm_config_v1(struct pwm_chip *chip,
return 0;
}
-static void imx_pwm_set_enable_v1(struct pwm_chip *chip, bool enable)
+static int imx_pwm_enable_v1(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct imx_chip *imx = to_imx_chip(chip);
u32 val;
+ int ret;
- val = readl(imx->mmio_base + MX1_PWMC);
-
- if (enable)
- val |= MX1_PWMC_EN;
- else
- val &= ~MX1_PWMC_EN;
+ ret = clk_prepare_enable(imx->clk_per);
+ if (ret < 0)
+ return ret;
+ val = readl(imx->mmio_base + MX1_PWMC);
+ val |= MX1_PWMC_EN;
writel(val, imx->mmio_base + MX1_PWMC);
-}
-
-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;
- unsigned int period_ms;
- bool enable = pwm_is_enabled(pwm);
- 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_get_period(pwm),
- 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;
- do_div(c, 1000000000);
- period_cycles = c;
-
- prescale = period_cycles / 0x10000 + 1;
-
- period_cycles /= prescale;
- c = (unsigned long long)period_cycles * duty_ns;
- do_div(c, period_ns);
- duty_cycles = c;
-
- /*
- * according to imx pwm RM, the real period value should be
- * PERIOD value in PWMPR plus 2.
- */
- if (period_cycles > 2)
- period_cycles -= 2;
- else
- period_cycles = 0;
-
- writel(duty_cycles, imx->mmio_base + MX3_PWMSAR);
- writel(period_cycles, imx->mmio_base + MX3_PWMPR);
-
- cr = MX3_PWMCR_PRESCALER(prescale) |
- MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN |
- MX3_PWMCR_DBGEN | MX3_PWMCR_CLKSRC_IPG_HIGH;
-
- if (enable)
- cr |= MX3_PWMCR_EN;
-
- writel(cr, imx->mmio_base + MX3_PWMCR);
return 0;
}
-static void imx_pwm_set_enable_v2(struct pwm_chip *chip, bool enable)
+static void imx_pwm_disable_v1(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct imx_chip *imx = to_imx_chip(chip);
u32 val;
- val = readl(imx->mmio_base + MX3_PWMCR);
-
- if (enable)
- val |= MX3_PWMCR_EN;
- else
- val &= ~MX3_PWMCR_EN;
+ val = readl(imx->mmio_base + MX1_PWMC);
+ val &= ~MX1_PWMC_EN;
+ writel(val, imx->mmio_base + MX1_PWMC);
- writel(val, imx->mmio_base + MX3_PWMCR);
+ clk_disable_unprepare(imx->clk_per);
}
-static int imx_pwm_config(struct pwm_chip *chip,
- struct pwm_device *pwm, int duty_ns, int period_ns)
+static void imx_pwm_sw_reset(struct pwm_chip *chip)
{
struct imx_chip *imx = to_imx_chip(chip);
- int ret;
-
- ret = clk_prepare_enable(imx->clk_ipg);
- if (ret)
- return ret;
+ struct device *dev = chip->dev;
+ int wait_count = 0;
+ u32 cr;
+
+ 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");
+}
- ret = imx->config(chip, pwm, duty_ns, period_ns);
+static void imx_pwm_wait_fifo_slot(struct pwm_chip *chip,
+ struct pwm_device *pwm)
+{
+ struct imx_chip *imx = to_imx_chip(chip);
+ struct device *dev = chip->dev;
+ unsigned int period_ms;
+ int fifoav;
+ u32 sr;
- clk_disable_unprepare(imx->clk_ipg);
+ 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_get_period(pwm),
+ NSEC_PER_MSEC);
+ msleep(period_ms);
- return ret;
+ sr = readl(imx->mmio_base + MX3_PWMSR);
+ if (fifoav == (sr & MX3_PWMSR_FIFOAV_MASK))
+ dev_warn(dev, "there is no free FIFO slot\n");
+ }
}
-static int imx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+static int imx_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
{
+ unsigned long period_cycles, duty_cycles, prescale;
struct imx_chip *imx = to_imx_chip(chip);
+ struct pwm_state cstate;
+ unsigned long long c;
int ret;
+ u32 cr;
+
+ pwm_get_state(pwm, &cstate);
+
+ if (state->enabled) {
+ c = clk_get_rate(imx->clk_per);
+ c *= state->period;
+
+ do_div(c, 1000000000);
+ period_cycles = c;
+
+ prescale = period_cycles / 0x10000 + 1;
+
+ period_cycles /= prescale;
+ c = (unsigned long long)period_cycles * state->duty_cycle;
+ do_div(c, state->period);
+ duty_cycles = c;
+
+ /*
+ * according to imx pwm RM, the real period value should be
+ * PERIOD value in PWMPR plus 2.
+ */
+ if (period_cycles > 2)
+ period_cycles -= 2;
+ else
+ period_cycles = 0;
+
+ /*
+ * Wait for a free FIFO slot if the PWM is already enabled, and
+ * flush the FIFO if the PWM was disabled and is about to be
+ * enabled.
+ */
+ if (cstate.enabled) {
+ imx_pwm_wait_fifo_slot(chip, pwm);
+ } else {
+ ret = clk_prepare_enable(imx->clk_per);
+ if (ret)
+ return ret;
+
+ imx_pwm_sw_reset(chip);
+ }
- ret = clk_prepare_enable(imx->clk_per);
- if (ret)
- return ret;
+ writel(duty_cycles, imx->mmio_base + MX3_PWMSAR);
+ writel(period_cycles, imx->mmio_base + MX3_PWMPR);
- imx->set_enable(chip, true);
+ cr = MX3_PWMCR_PRESCALER(prescale) |
+ MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN |
+ MX3_PWMCR_DBGEN | MX3_PWMCR_CLKSRC_IPG_HIGH |
+ MX3_PWMCR_EN;
- return 0;
-}
+ if (state->polarity == PWM_POLARITY_INVERSED)
+ cr |= MX3_PWMCR_POUTC;
-static void imx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
-{
- struct imx_chip *imx = to_imx_chip(chip);
+ writel(cr, imx->mmio_base + MX3_PWMCR);
+ } else if (cstate.enabled) {
+ writel(0, imx->mmio_base + MX3_PWMCR);
- imx->set_enable(chip, false);
+ clk_disable_unprepare(imx->clk_per);
+ }
- clk_disable_unprepare(imx->clk_per);
+ return 0;
}
-static struct pwm_ops imx_pwm_ops = {
- .enable = imx_pwm_enable,
- .disable = imx_pwm_disable,
- .config = imx_pwm_config,
+static const struct pwm_ops imx_pwm_ops_v1 = {
+ .enable = imx_pwm_enable_v1,
+ .disable = imx_pwm_disable_v1,
+ .config = imx_pwm_config_v1,
+ .owner = THIS_MODULE,
+};
+
+static const struct pwm_ops imx_pwm_ops_v2 = {
+ .apply = imx_pwm_apply_v2,
.owner = THIS_MODULE,
};
struct imx_pwm_data {
- int (*config)(struct pwm_chip *chip,
- struct pwm_device *pwm, int duty_ns, int period_ns);
- void (*set_enable)(struct pwm_chip *chip, bool enable);
+ bool polarity_supported;
+ const struct pwm_ops *ops;
};
static struct imx_pwm_data imx_pwm_data_v1 = {
- .config = imx_pwm_config_v1,
- .set_enable = imx_pwm_set_enable_v1,
+ .ops = &imx_pwm_ops_v1,
};
static struct imx_pwm_data imx_pwm_data_v2 = {
- .config = imx_pwm_config_v2,
- .set_enable = imx_pwm_set_enable_v2,
+ .polarity_supported = true,
+ .ops = &imx_pwm_ops_v2,
};
static const struct of_device_id imx_pwm_dt_ids[] = {
@@ -282,6 +272,8 @@ static int imx_pwm_probe(struct platform_device *pdev)
if (!of_id)
return -ENODEV;
+ data = of_id->data;
+
imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL);
if (imx == NULL)
return -ENOMEM;
@@ -293,28 +285,22 @@ static int imx_pwm_probe(struct platform_device *pdev)
return PTR_ERR(imx->clk_per);
}
- imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
- if (IS_ERR(imx->clk_ipg)) {
- dev_err(&pdev->dev, "getting ipg clock failed with %ld\n",
- PTR_ERR(imx->clk_ipg));
- return PTR_ERR(imx->clk_ipg);
- }
-
- imx->chip.ops = &imx_pwm_ops;
+ imx->chip.ops = data->ops;
imx->chip.dev = &pdev->dev;
imx->chip.base = -1;
imx->chip.npwm = 1;
- imx->chip.can_sleep = true;
+
+ if (data->polarity_supported) {
+ dev_dbg(&pdev->dev, "PWM supports output inversion\n");
+ imx->chip.of_xlate = of_pwm_xlate_with_flags;
+ imx->chip.of_pwm_n_cells = 3;
+ }
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
imx->mmio_base = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(imx->mmio_base))
return PTR_ERR(imx->mmio_base);
- data = of_id->data;
- imx->config = data->config;
- imx->set_enable = data->set_enable;
-
ret = pwmchip_add(&imx->chip);
if (ret < 0)
return ret;
diff --git a/drivers/pwm/pwm-lp3943.c b/drivers/pwm/pwm-lp3943.c
index 872ea76a4f19..52584e9962ed 100644
--- a/drivers/pwm/pwm-lp3943.c
+++ b/drivers/pwm/pwm-lp3943.c
@@ -278,7 +278,6 @@ static int lp3943_pwm_probe(struct platform_device *pdev)
lp3943_pwm->chip.dev = &pdev->dev;
lp3943_pwm->chip.ops = &lp3943_pwm_ops;
lp3943_pwm->chip.npwm = LP3943_NUM_PWMS;
- lp3943_pwm->chip.can_sleep = true;
platform_set_drvdata(pdev, lp3943_pwm);
diff --git a/drivers/pwm/pwm-lpss-pci.c b/drivers/pwm/pwm-lpss-pci.c
index 3622f093490e..053088b9b66e 100644
--- a/drivers/pwm/pwm-lpss-pci.c
+++ b/drivers/pwm/pwm-lpss-pci.c
@@ -17,6 +17,27 @@
#include "pwm-lpss.h"
+/* BayTrail */
+static const struct pwm_lpss_boardinfo pwm_lpss_byt_info = {
+ .clk_rate = 25000000,
+ .npwm = 1,
+ .base_unit_bits = 16,
+};
+
+/* Braswell */
+static const struct pwm_lpss_boardinfo pwm_lpss_bsw_info = {
+ .clk_rate = 19200000,
+ .npwm = 1,
+ .base_unit_bits = 16,
+};
+
+/* Broxton */
+static const struct pwm_lpss_boardinfo pwm_lpss_bxt_info = {
+ .clk_rate = 19200000,
+ .npwm = 4,
+ .base_unit_bits = 22,
+};
+
static int pwm_lpss_probe_pci(struct pci_dev *pdev,
const struct pci_device_id *id)
{
@@ -80,6 +101,7 @@ static const struct pci_device_id pwm_lpss_pci_ids[] = {
{ 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},
+ { PCI_VDEVICE(INTEL, 0x31c8), (unsigned long)&pwm_lpss_bxt_info},
{ PCI_VDEVICE(INTEL, 0x5ac8), (unsigned long)&pwm_lpss_bxt_info},
{ },
};
diff --git a/drivers/pwm/pwm-lpss-platform.c b/drivers/pwm/pwm-lpss-platform.c
index 54433fc6d1a4..b22b6fdadb9a 100644
--- a/drivers/pwm/pwm-lpss-platform.c
+++ b/drivers/pwm/pwm-lpss-platform.c
@@ -18,6 +18,27 @@
#include "pwm-lpss.h"
+/* BayTrail */
+static const struct pwm_lpss_boardinfo pwm_lpss_byt_info = {
+ .clk_rate = 25000000,
+ .npwm = 1,
+ .base_unit_bits = 16,
+};
+
+/* Braswell */
+static const struct pwm_lpss_boardinfo pwm_lpss_bsw_info = {
+ .clk_rate = 19200000,
+ .npwm = 1,
+ .base_unit_bits = 16,
+};
+
+/* Broxton */
+static const struct pwm_lpss_boardinfo pwm_lpss_bxt_info = {
+ .clk_rate = 19200000,
+ .npwm = 4,
+ .base_unit_bits = 22,
+};
+
static int pwm_lpss_probe_platform(struct platform_device *pdev)
{
const struct pwm_lpss_boardinfo *info;
diff --git a/drivers/pwm/pwm-lpss.c b/drivers/pwm/pwm-lpss.c
index 72c0bce5a75c..689d2c1cbead 100644
--- a/drivers/pwm/pwm-lpss.c
+++ b/drivers/pwm/pwm-lpss.c
@@ -15,6 +15,7 @@
#include <linux/delay.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
@@ -37,30 +38,6 @@ struct pwm_lpss_chip {
const struct pwm_lpss_boardinfo *info;
};
-/* BayTrail */
-const struct pwm_lpss_boardinfo pwm_lpss_byt_info = {
- .clk_rate = 25000000,
- .npwm = 1,
- .base_unit_bits = 16,
-};
-EXPORT_SYMBOL_GPL(pwm_lpss_byt_info);
-
-/* Braswell */
-const struct pwm_lpss_boardinfo pwm_lpss_bsw_info = {
- .clk_rate = 19200000,
- .npwm = 1,
- .base_unit_bits = 16,
-};
-EXPORT_SYMBOL_GPL(pwm_lpss_bsw_info);
-
-/* Broxton */
-const struct pwm_lpss_boardinfo pwm_lpss_bxt_info = {
- .clk_rate = 19200000,
- .npwm = 4,
- .base_unit_bits = 22,
-};
-EXPORT_SYMBOL_GPL(pwm_lpss_bxt_info);
-
static inline struct pwm_lpss_chip *to_lpwm(struct pwm_chip *chip)
{
return container_of(chip, struct pwm_lpss_chip, chip);
@@ -80,17 +57,42 @@ static inline void pwm_lpss_write(const struct pwm_device *pwm, u32 value)
writel(value, lpwm->regs + pwm->hwpwm * PWM_SIZE + PWM);
}
-static void pwm_lpss_update(struct pwm_device *pwm)
+static int pwm_lpss_update(struct pwm_device *pwm)
{
+ struct pwm_lpss_chip *lpwm = to_lpwm(pwm->chip);
+ const void __iomem *addr = lpwm->regs + pwm->hwpwm * PWM_SIZE + PWM;
+ const unsigned int ms = 500 * USEC_PER_MSEC;
+ u32 val;
+ int err;
+
pwm_lpss_write(pwm, pwm_lpss_read(pwm) | PWM_SW_UPDATE);
- /* Give it some time to propagate */
- usleep_range(10, 50);
+
+ /*
+ * PWM Configuration register has SW_UPDATE bit that is set when a new
+ * configuration is written to the register. The bit is automatically
+ * cleared at the start of the next output cycle by the IP block.
+ *
+ * If one writes a new configuration to the register while it still has
+ * the bit enabled, PWM may freeze. That is, while one can still write
+ * to the register, it won't have an effect. Thus, we try to sleep long
+ * enough that the bit gets cleared and make sure the bit is not
+ * enabled while we update the configuration.
+ */
+ err = readl_poll_timeout(addr, val, !(val & PWM_SW_UPDATE), 40, ms);
+ if (err)
+ dev_err(pwm->chip->dev, "PWM_SW_UPDATE was not cleared\n");
+
+ return err;
}
-static int pwm_lpss_config(struct pwm_chip *chip, struct pwm_device *pwm,
- int duty_ns, int period_ns)
+static inline int pwm_lpss_is_updating(struct pwm_device *pwm)
+{
+ return (pwm_lpss_read(pwm) & PWM_SW_UPDATE) ? -EBUSY : 0;
+}
+
+static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
{
- struct pwm_lpss_chip *lpwm = to_lpwm(chip);
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;
@@ -102,62 +104,62 @@ static int pwm_lpss_config(struct pwm_chip *chip, struct pwm_device *pwm,
* The equation is:
* base_unit = round(base_unit_range * freq / c)
*/
- base_unit_range = BIT(lpwm->info->base_unit_bits);
+ base_unit_range = BIT(lpwm->info->base_unit_bits) - 1;
freq *= base_unit_range;
base_unit = DIV_ROUND_CLOSEST_ULL(freq, c);
- if (duty_ns <= 0)
- duty_ns = 1;
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);
-
ctrl = pwm_lpss_read(pwm);
ctrl &= ~PWM_ON_TIME_DIV_MASK;
- ctrl &= ~((base_unit_range - 1) << PWM_BASE_UNIT_SHIFT);
- base_unit &= (base_unit_range - 1);
+ ctrl &= ~(base_unit_range << PWM_BASE_UNIT_SHIFT);
+ base_unit &= base_unit_range;
ctrl |= (u32) base_unit << PWM_BASE_UNIT_SHIFT;
ctrl |= on_time_div;
pwm_lpss_write(pwm, ctrl);
-
- /*
- * If the PWM is already enabled we need to notify the hardware
- * about the change by setting PWM_SW_UPDATE.
- */
- if (pwm_is_enabled(pwm))
- pwm_lpss_update(pwm);
-
- pm_runtime_put(chip->dev);
-
- return 0;
}
-static int pwm_lpss_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+static int pwm_lpss_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
{
- pm_runtime_get_sync(chip->dev);
+ struct pwm_lpss_chip *lpwm = to_lpwm(chip);
+ int ret;
- /*
- * Hardware must first see PWM_SW_UPDATE before the PWM can be
- * enabled.
- */
- pwm_lpss_update(pwm);
- pwm_lpss_write(pwm, pwm_lpss_read(pwm) | PWM_ENABLE);
- return 0;
-}
+ if (state->enabled) {
+ if (!pwm_is_enabled(pwm)) {
+ pm_runtime_get_sync(chip->dev);
+ ret = pwm_lpss_is_updating(pwm);
+ if (ret) {
+ pm_runtime_put(chip->dev);
+ return ret;
+ }
+ pwm_lpss_prepare(lpwm, pwm, state->duty_cycle, state->period);
+ ret = pwm_lpss_update(pwm);
+ if (ret) {
+ pm_runtime_put(chip->dev);
+ return ret;
+ }
+ pwm_lpss_write(pwm, pwm_lpss_read(pwm) | PWM_ENABLE);
+ } else {
+ ret = pwm_lpss_is_updating(pwm);
+ if (ret)
+ return ret;
+ pwm_lpss_prepare(lpwm, pwm, state->duty_cycle, state->period);
+ return pwm_lpss_update(pwm);
+ }
+ } else if (pwm_is_enabled(pwm)) {
+ pwm_lpss_write(pwm, pwm_lpss_read(pwm) & ~PWM_ENABLE);
+ pm_runtime_put(chip->dev);
+ }
-static void pwm_lpss_disable(struct pwm_chip *chip, struct pwm_device *pwm)
-{
- pwm_lpss_write(pwm, pwm_lpss_read(pwm) & ~PWM_ENABLE);
- pm_runtime_put(chip->dev);
+ return 0;
}
static const struct pwm_ops pwm_lpss_ops = {
- .config = pwm_lpss_config,
- .enable = pwm_lpss_enable,
- .disable = pwm_lpss_disable,
+ .apply = pwm_lpss_apply,
.owner = THIS_MODULE,
};
diff --git a/drivers/pwm/pwm-lpss.h b/drivers/pwm/pwm-lpss.h
index 04766e0d41aa..c94cd7c2695d 100644
--- a/drivers/pwm/pwm-lpss.h
+++ b/drivers/pwm/pwm-lpss.h
@@ -24,10 +24,6 @@ struct pwm_lpss_boardinfo {
unsigned long base_unit_bits;
};
-extern const struct pwm_lpss_boardinfo pwm_lpss_byt_info;
-extern const struct pwm_lpss_boardinfo pwm_lpss_bsw_info;
-extern const struct pwm_lpss_boardinfo pwm_lpss_bxt_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);
diff --git a/drivers/pwm/pwm-mxs.c b/drivers/pwm/pwm-mxs.c
index 9a596324ebef..a6017ad9926c 100644
--- a/drivers/pwm/pwm-mxs.c
+++ b/drivers/pwm/pwm-mxs.c
@@ -151,7 +151,7 @@ static int mxs_pwm_probe(struct platform_device *pdev)
mxs->chip.dev = &pdev->dev;
mxs->chip.ops = &mxs_pwm_ops;
mxs->chip.base = -1;
- mxs->chip.can_sleep = true;
+
ret = of_property_read_u32(np, "fsl,pwm-number", &mxs->chip.npwm);
if (ret < 0) {
dev_err(&pdev->dev, "failed to get pwm number: %d\n", ret);
diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
index 117fccf7934a..0cfb3571a732 100644
--- a/drivers/pwm/pwm-pca9685.c
+++ b/drivers/pwm/pwm-pca9685.c
@@ -20,8 +20,10 @@
*/
#include <linux/acpi.h>
+#include <linux/gpio/driver.h>
#include <linux/i2c.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/pwm.h>
@@ -65,7 +67,6 @@
#define PCA9685_MAXCHAN 0x10
#define LED_FULL (1 << 4)
-#define MODE1_RESTART (1 << 7)
#define MODE1_SLEEP (1 << 4)
#define MODE2_INVRT (1 << 4)
#define MODE2_OUTDRV (1 << 2)
@@ -81,6 +82,10 @@ struct pca9685 {
int active_cnt;
int duty_ns;
int period_ns;
+#if IS_ENABLED(CONFIG_GPIOLIB)
+ struct mutex lock;
+ struct gpio_chip gpio;
+#endif
};
static inline struct pca9685 *to_pca(struct pwm_chip *chip)
@@ -88,6 +93,151 @@ static inline struct pca9685 *to_pca(struct pwm_chip *chip)
return container_of(chip, struct pca9685, chip);
}
+#if IS_ENABLED(CONFIG_GPIOLIB)
+static int pca9685_pwm_gpio_request(struct gpio_chip *gpio, unsigned int offset)
+{
+ struct pca9685 *pca = gpiochip_get_data(gpio);
+ struct pwm_device *pwm;
+
+ mutex_lock(&pca->lock);
+
+ pwm = &pca->chip.pwms[offset];
+
+ if (pwm->flags & (PWMF_REQUESTED | PWMF_EXPORTED)) {
+ mutex_unlock(&pca->lock);
+ return -EBUSY;
+ }
+
+ pwm_set_chip_data(pwm, (void *)1);
+
+ mutex_unlock(&pca->lock);
+ return 0;
+}
+
+static void pca9685_pwm_gpio_free(struct gpio_chip *gpio, unsigned int offset)
+{
+ struct pca9685 *pca = gpiochip_get_data(gpio);
+ struct pwm_device *pwm;
+
+ mutex_lock(&pca->lock);
+ pwm = &pca->chip.pwms[offset];
+ pwm_set_chip_data(pwm, NULL);
+ mutex_unlock(&pca->lock);
+}
+
+static bool pca9685_pwm_is_gpio(struct pca9685 *pca, struct pwm_device *pwm)
+{
+ bool is_gpio = false;
+
+ mutex_lock(&pca->lock);
+
+ if (pwm->hwpwm >= PCA9685_MAXCHAN) {
+ unsigned int i;
+
+ /*
+ * Check if any of the GPIOs are requested and in that case
+ * prevent using the "all LEDs" channel.
+ */
+ for (i = 0; i < pca->gpio.ngpio; i++)
+ if (gpiochip_is_requested(&pca->gpio, i)) {
+ is_gpio = true;
+ break;
+ }
+ } else if (pwm_get_chip_data(pwm)) {
+ is_gpio = true;
+ }
+
+ mutex_unlock(&pca->lock);
+ return is_gpio;
+}
+
+static int pca9685_pwm_gpio_get(struct gpio_chip *gpio, unsigned int offset)
+{
+ struct pca9685 *pca = gpiochip_get_data(gpio);
+ struct pwm_device *pwm = &pca->chip.pwms[offset];
+ unsigned int value;
+
+ regmap_read(pca->regmap, LED_N_ON_H(pwm->hwpwm), &value);
+
+ return value & LED_FULL;
+}
+
+static void pca9685_pwm_gpio_set(struct gpio_chip *gpio, unsigned int offset,
+ int value)
+{
+ struct pca9685 *pca = gpiochip_get_data(gpio);
+ struct pwm_device *pwm = &pca->chip.pwms[offset];
+ unsigned int on = value ? LED_FULL : 0;
+
+ /* Clear both OFF registers */
+ regmap_write(pca->regmap, LED_N_OFF_L(pwm->hwpwm), 0);
+ regmap_write(pca->regmap, LED_N_OFF_H(pwm->hwpwm), 0);
+
+ /* Set the full ON bit */
+ regmap_write(pca->regmap, LED_N_ON_H(pwm->hwpwm), on);
+}
+
+static int pca9685_pwm_gpio_get_direction(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ /* Always out */
+ return 0;
+}
+
+static int pca9685_pwm_gpio_direction_input(struct gpio_chip *gpio,
+ unsigned int offset)
+{
+ return -EINVAL;
+}
+
+static int pca9685_pwm_gpio_direction_output(struct gpio_chip *gpio,
+ unsigned int offset, int value)
+{
+ pca9685_pwm_gpio_set(gpio, offset, value);
+
+ return 0;
+}
+
+/*
+ * The PCA9685 has a bit for turning the PWM output full off or on. Some
+ * boards like Intel Galileo actually uses these as normal GPIOs so we
+ * expose a GPIO chip here which can exclusively take over the underlying
+ * PWM channel.
+ */
+static int pca9685_pwm_gpio_probe(struct pca9685 *pca)
+{
+ struct device *dev = pca->chip.dev;
+
+ mutex_init(&pca->lock);
+
+ pca->gpio.label = dev_name(dev);
+ pca->gpio.parent = dev;
+ pca->gpio.request = pca9685_pwm_gpio_request;
+ pca->gpio.free = pca9685_pwm_gpio_free;
+ pca->gpio.get_direction = pca9685_pwm_gpio_get_direction;
+ pca->gpio.direction_input = pca9685_pwm_gpio_direction_input;
+ pca->gpio.direction_output = pca9685_pwm_gpio_direction_output;
+ pca->gpio.get = pca9685_pwm_gpio_get;
+ pca->gpio.set = pca9685_pwm_gpio_set;
+ pca->gpio.base = -1;
+ pca->gpio.ngpio = PCA9685_MAXCHAN;
+ pca->gpio.can_sleep = true;
+
+ return devm_gpiochip_add_data(dev, &pca->gpio, pca);
+}
+#else
+static inline bool pca9685_pwm_is_gpio(struct pca9685 *pca,
+ struct pwm_device *pwm)
+{
+ return false;
+}
+
+static inline int pca9685_pwm_gpio_probe(struct pca9685 *pca)
+{
+ return 0;
+}
+#endif
+
static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_ns, int period_ns)
{
@@ -117,16 +267,6 @@ static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
udelay(500);
pca->period_ns = period_ns;
-
- /*
- * If the duty cycle did not change, restart PWM with
- * the same duty cycle to period ratio and return.
- */
- if (duty_ns == pca->duty_ns) {
- regmap_update_bits(pca->regmap, PCA9685_MODE1,
- MODE1_RESTART, 0x1);
- return 0;
- }
} else {
dev_err(chip->dev,
"prescaler not set: period out of bounds!\n");
@@ -264,6 +404,9 @@ static int pca9685_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct pca9685 *pca = to_pca(chip);
+ if (pca9685_pwm_is_gpio(pca, pwm))
+ return -EBUSY;
+
if (pca->active_cnt++ == 0)
return regmap_update_bits(pca->regmap, PCA9685_MODE1,
MODE1_SLEEP, 0x0);
@@ -343,9 +486,16 @@ static int pca9685_pwm_probe(struct i2c_client *client,
pca->chip.dev = &client->dev;
pca->chip.base = -1;
- pca->chip.can_sleep = true;
- return pwmchip_add(&pca->chip);
+ ret = pwmchip_add(&pca->chip);
+ if (ret < 0)
+ return ret;
+
+ ret = pca9685_pwm_gpio_probe(pca);
+ if (ret < 0)
+ pwmchip_remove(&pca->chip);
+
+ return ret;
}
static int pca9685_pwm_remove(struct i2c_client *client)
diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c
index 58b709f29130..4143a46684d2 100644
--- a/drivers/pwm/pwm-pxa.c
+++ b/drivers/pwm/pwm-pxa.c
@@ -118,7 +118,7 @@ static void pxa_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
clk_disable_unprepare(pc->clk);
}
-static struct pwm_ops pxa_pwm_ops = {
+static const struct pwm_ops pxa_pwm_ops = {
.config = pxa_pwm_config,
.enable = pxa_pwm_enable,
.disable = pxa_pwm_disable,
diff --git a/drivers/pwm/pwm-sti.c b/drivers/pwm/pwm-sti.c
index dd82dc840af9..2b7c31c9d1ab 100644
--- a/drivers/pwm/pwm-sti.c
+++ b/drivers/pwm/pwm-sti.c
@@ -635,7 +635,6 @@ skip_cpt:
pc->chip.ops = &sti_pwm_ops;
pc->chip.base = -1;
pc->chip.npwm = pc->cdata->pwm_num_devs;
- pc->chip.can_sleep = true;
ret = pwmchip_add(&pc->chip);
if (ret < 0) {
diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c
index b0803f6c64d9..1284ffa05921 100644
--- a/drivers/pwm/pwm-sun4i.c
+++ b/drivers/pwm/pwm-sun4i.c
@@ -340,7 +340,6 @@ static int sun4i_pwm_probe(struct platform_device *pdev)
pwm->chip.ops = &sun4i_pwm_ops;
pwm->chip.base = -1;
pwm->chip.npwm = pwm->data->npwm;
- pwm->chip.can_sleep = true;
pwm->chip.of_xlate = of_pwm_xlate_with_flags;
pwm->chip.of_pwm_n_cells = 3;
diff --git a/drivers/pwm/pwm-twl-led.c b/drivers/pwm/pwm-twl-led.c
index b964470025c5..21eff991d0e3 100644
--- a/drivers/pwm/pwm-twl-led.c
+++ b/drivers/pwm/pwm-twl-led.c
@@ -303,7 +303,6 @@ static int twl_pwmled_probe(struct platform_device *pdev)
twl->chip.dev = &pdev->dev;
twl->chip.base = -1;
- twl->chip.can_sleep = true;
mutex_init(&twl->mutex);
diff --git a/drivers/pwm/pwm-twl.c b/drivers/pwm/pwm-twl.c
index 7a993b056638..9de617b76680 100644
--- a/drivers/pwm/pwm-twl.c
+++ b/drivers/pwm/pwm-twl.c
@@ -323,7 +323,6 @@ static int twl_pwm_probe(struct platform_device *pdev)
twl->chip.dev = &pdev->dev;
twl->chip.base = -1;
twl->chip.npwm = 2;
- twl->chip.can_sleep = true;
mutex_init(&twl->mutex);
diff --git a/drivers/pwm/pwm-vt8500.c b/drivers/pwm/pwm-vt8500.c
index cdb58fd4619d..8141a4984126 100644
--- a/drivers/pwm/pwm-vt8500.c
+++ b/drivers/pwm/pwm-vt8500.c
@@ -184,7 +184,7 @@ static int vt8500_pwm_set_polarity(struct pwm_chip *chip,
return 0;
}
-static struct pwm_ops vt8500_pwm_ops = {
+static const struct pwm_ops vt8500_pwm_ops = {
.enable = vt8500_pwm_enable,
.disable = vt8500_pwm_disable,
.config = vt8500_pwm_config,
diff --git a/drivers/staging/greybus/pwm.c b/drivers/staging/greybus/pwm.c
index c4bf3298ba07..f0404bc37123 100644
--- a/drivers/staging/greybus/pwm.c
+++ b/drivers/staging/greybus/pwm.c
@@ -284,7 +284,6 @@ static int gb_pwm_probe(struct gbphy_device *gbphy_dev,
pwm->ops = &gb_pwm_ops;
pwm->base = -1; /* Allocate base dynamically */
pwm->npwm = pwmc->pwm_max + 1;
- pwm->can_sleep = true; /* FIXME */
ret = pwmchip_add(pwm);
if (ret) {