diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-11-16 12:21:40 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-11-16 12:21:40 -0800 |
commit | 0bde7294e2ada03d0f1cc61cec51274081d9a9cf (patch) | |
tree | b1cf62a298b566d9bcc87d9f572f506b98809d66 /drivers | |
parent | 449bf8d03c5b94f00cc014ff601c2fe2eebb5a6e (diff) | |
parent | 1b3f25ce991d528bd0d825b3f14a45904037a382 (diff) |
Merge tag 'pwm/for-3.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm
Pull pwm changes from Thierry Reding:
"Mostly bug fixes and clean up. There is a new driver, which is
actually moving a custom PWM driver from drivers/misc.
The majority of the patches are enhancements to the device tree
support in the pwm-backlight driver. Backlights can now additionally
be powered using a regulator and enabled using a GPIO in addition to
just the PWM input"
* tag 'pwm/for-3.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm: (30 commits)
Documentation/pwm: Update supported SoC name for pwm-samsung
pwm: samsung: Fix kernel warning while unexporting a channel
MAINTAINERS: Move PWM subsystem tree to kernel.org
Documentation/pwm: Fix trivial typos
pwm-backlight: Remove unused variable
pwm_backlight: avoid short blank screen while doing hibernation
pwm-backlight: Fix brightness adjustment
pwm: add ep93xx PWM support
pwm-backlight: Allow for non-increasing brightness levels
pwm-backlight: Add power supply support
pwm-backlight: Use new enable_gpio field
unicore32: Initialize PWM backlight enable_gpio field
ARM: shmobile: Initialize PWM backlight enable_gpio field
ARM: SAMSUNG: Initialize PWM backlight enable_gpio field
ARM: pxa: Initialize PWM backlight enable_gpio field
ARM: OMAP: Initialize PWM backlight enable_gpio field
pwm-backlight: Add optional enable GPIO
pwm-backlight: Track enable state
pwm-backlight: Refactor backlight power on/off
pwm-backlight: Improve readability
...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/misc/Kconfig | 13 | ||||
-rw-r--r-- | drivers/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/misc/ep93xx_pwm.c | 286 | ||||
-rw-r--r-- | drivers/pwm/Kconfig | 9 | ||||
-rw-r--r-- | drivers/pwm/Makefile | 1 | ||||
-rw-r--r-- | drivers/pwm/pwm-atmel-tcb.c | 4 | ||||
-rw-r--r-- | drivers/pwm/pwm-ep93xx.c | 230 | ||||
-rw-r--r-- | drivers/pwm/pwm-imx.c | 3 | ||||
-rw-r--r-- | drivers/pwm/pwm-lpc32xx.c | 2 | ||||
-rw-r--r-- | drivers/pwm/pwm-mxs.c | 2 | ||||
-rw-r--r-- | drivers/pwm/pwm-samsung.c | 3 | ||||
-rw-r--r-- | drivers/pwm/pwm-tiecap.c | 6 | ||||
-rw-r--r-- | drivers/pwm/pwm-tiehrpwm.c | 6 | ||||
-rw-r--r-- | drivers/pwm/pwm-twl-led.c | 1 | ||||
-rw-r--r-- | drivers/pwm/pwm-twl.c | 1 | ||||
-rw-r--r-- | drivers/video/backlight/pwm_bl.c | 166 |
16 files changed, 381 insertions, 353 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index e760715bd9cb..a3e291d0df9a 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -381,19 +381,6 @@ config HMC6352 This driver provides support for the Honeywell HMC6352 compass, providing configuration and heading data via sysfs. -config EP93XX_PWM - tristate "EP93xx PWM support" - depends on ARCH_EP93XX - help - This option enables device driver support for the PWM channels - on the Cirrus EP93xx processors. The EP9307 chip only has one - PWM channel all the others have two, the second channel is an - alternate function of the EGPIO14 pin. A sysfs interface is - provided to control the PWM channels. - - To compile this driver as a module, choose M here: the module will - be called ep93xx_pwm. - config DS1682 tristate "Dallas DS1682 Total Elapsed Time Recorder with Alarm" depends on I2C diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 0b7ea3ea8bb8..f45473e68bf7 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -33,7 +33,6 @@ obj-$(CONFIG_APDS9802ALS) += apds9802als.o obj-$(CONFIG_ISL29003) += isl29003.o obj-$(CONFIG_ISL29020) += isl29020.o obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o -obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o obj-$(CONFIG_DS1682) += ds1682.o obj-$(CONFIG_TI_DAC7512) += ti_dac7512.o obj-$(CONFIG_C2PORT) += c2port/ diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c deleted file mode 100644 index cdb67a9c1959..000000000000 --- a/drivers/misc/ep93xx_pwm.c +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Simple PWM driver for EP93XX - * - * (c) Copyright 2009 Matthieu Crapet <mcrapet@gmail.com> - * (c) Copyright 2009 H Hartley Sweeten <hsweeten@visionengravers.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. - * - * EP9307 has only one channel: - * - PWMOUT - * - * EP9301/02/12/15 have two channels: - * - PWMOUT - * - PWMOUT1 (alternate function for EGPIO14) - */ - -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/clk.h> -#include <linux/err.h> -#include <linux/io.h> - -#include <mach/platform.h> - -#define EP93XX_PWMx_TERM_COUNT 0x00 -#define EP93XX_PWMx_DUTY_CYCLE 0x04 -#define EP93XX_PWMx_ENABLE 0x08 -#define EP93XX_PWMx_INVERT 0x0C - -#define EP93XX_PWM_MAX_COUNT 0xFFFF - -struct ep93xx_pwm { - void __iomem *mmio_base; - struct clk *clk; - u32 duty_percent; -}; - -/* - * /sys/devices/platform/ep93xx-pwm.N - * /min_freq read-only minimum pwm output frequency - * /max_req read-only maximum pwm output frequency - * /freq read-write pwm output frequency (0 = disable output) - * /duty_percent read-write pwm duty cycle percent (1..99) - * /invert read-write invert pwm output - */ - -static ssize_t ep93xx_pwm_get_min_freq(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct platform_device *pdev = to_platform_device(dev); - struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); - unsigned long rate = clk_get_rate(pwm->clk); - - return sprintf(buf, "%ld\n", rate / (EP93XX_PWM_MAX_COUNT + 1)); -} - -static ssize_t ep93xx_pwm_get_max_freq(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct platform_device *pdev = to_platform_device(dev); - struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); - unsigned long rate = clk_get_rate(pwm->clk); - - return sprintf(buf, "%ld\n", rate / 2); -} - -static ssize_t ep93xx_pwm_get_freq(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct platform_device *pdev = to_platform_device(dev); - struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); - - if (readl(pwm->mmio_base + EP93XX_PWMx_ENABLE) & 0x1) { - unsigned long rate = clk_get_rate(pwm->clk); - u16 term = readl(pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); - - return sprintf(buf, "%ld\n", rate / (term + 1)); - } else { - return sprintf(buf, "disabled\n"); - } -} - -static ssize_t ep93xx_pwm_set_freq(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct platform_device *pdev = to_platform_device(dev); - struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); - long val; - int err; - - err = kstrtol(buf, 10, &val); - if (err) - return -EINVAL; - - if (val == 0) { - writel(0x0, pwm->mmio_base + EP93XX_PWMx_ENABLE); - } else if (val <= (clk_get_rate(pwm->clk) / 2)) { - u32 term, duty; - - val = (clk_get_rate(pwm->clk) / val) - 1; - if (val > EP93XX_PWM_MAX_COUNT) - val = EP93XX_PWM_MAX_COUNT; - if (val < 1) - val = 1; - - term = readl(pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); - duty = ((val + 1) * pwm->duty_percent / 100) - 1; - - /* If pwm is running, order is important */ - if (val > term) { - writel(val, pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); - writel(duty, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE); - } else { - writel(duty, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE); - writel(val, pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); - } - - if (!readl(pwm->mmio_base + EP93XX_PWMx_ENABLE) & 0x1) - writel(0x1, pwm->mmio_base + EP93XX_PWMx_ENABLE); - } else { - return -EINVAL; - } - - return count; -} - -static ssize_t ep93xx_pwm_get_duty_percent(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct platform_device *pdev = to_platform_device(dev); - struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); - - return sprintf(buf, "%d\n", pwm->duty_percent); -} - -static ssize_t ep93xx_pwm_set_duty_percent(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct platform_device *pdev = to_platform_device(dev); - struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); - long val; - int err; - - err = kstrtol(buf, 10, &val); - if (err) - return -EINVAL; - - if (val > 0 && val < 100) { - u32 term = readl(pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); - u32 duty = ((term + 1) * val / 100) - 1; - - writel(duty, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE); - pwm->duty_percent = val; - return count; - } - - return -EINVAL; -} - -static ssize_t ep93xx_pwm_get_invert(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct platform_device *pdev = to_platform_device(dev); - struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); - int inverted = readl(pwm->mmio_base + EP93XX_PWMx_INVERT) & 0x1; - - return sprintf(buf, "%d\n", inverted); -} - -static ssize_t ep93xx_pwm_set_invert(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct platform_device *pdev = to_platform_device(dev); - struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); - long val; - int err; - - err = kstrtol(buf, 10, &val); - if (err) - return -EINVAL; - - if (val == 0) - writel(0x0, pwm->mmio_base + EP93XX_PWMx_INVERT); - else if (val == 1) - writel(0x1, pwm->mmio_base + EP93XX_PWMx_INVERT); - else - return -EINVAL; - - return count; -} - -static DEVICE_ATTR(min_freq, S_IRUGO, ep93xx_pwm_get_min_freq, NULL); -static DEVICE_ATTR(max_freq, S_IRUGO, ep93xx_pwm_get_max_freq, NULL); -static DEVICE_ATTR(freq, S_IWUSR | S_IRUGO, - ep93xx_pwm_get_freq, ep93xx_pwm_set_freq); -static DEVICE_ATTR(duty_percent, S_IWUSR | S_IRUGO, - ep93xx_pwm_get_duty_percent, ep93xx_pwm_set_duty_percent); -static DEVICE_ATTR(invert, S_IWUSR | S_IRUGO, - ep93xx_pwm_get_invert, ep93xx_pwm_set_invert); - -static struct attribute *ep93xx_pwm_attrs[] = { - &dev_attr_min_freq.attr, - &dev_attr_max_freq.attr, - &dev_attr_freq.attr, - &dev_attr_duty_percent.attr, - &dev_attr_invert.attr, - NULL -}; - -static const struct attribute_group ep93xx_pwm_sysfs_files = { - .attrs = ep93xx_pwm_attrs, -}; - -static int ep93xx_pwm_probe(struct platform_device *pdev) -{ - struct ep93xx_pwm *pwm; - struct resource *res; - int ret; - - pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); - if (!pwm) - return -ENOMEM; - - pwm->clk = devm_clk_get(&pdev->dev, "pwm_clk"); - if (IS_ERR(pwm->clk)) - return PTR_ERR(pwm->clk); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pwm->mmio_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(pwm->mmio_base)) - return PTR_ERR(pwm->mmio_base); - - ret = ep93xx_pwm_acquire_gpio(pdev); - if (ret) - return ret; - - ret = sysfs_create_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files); - if (ret) { - ep93xx_pwm_release_gpio(pdev); - return ret; - } - - pwm->duty_percent = 50; - - /* disable pwm at startup. Avoids zero value. */ - writel(0x0, pwm->mmio_base + EP93XX_PWMx_ENABLE); - writel(EP93XX_PWM_MAX_COUNT, pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); - writel(EP93XX_PWM_MAX_COUNT/2, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE); - - clk_enable(pwm->clk); - - platform_set_drvdata(pdev, pwm); - return 0; -} - -static int ep93xx_pwm_remove(struct platform_device *pdev) -{ - struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); - - writel(0x0, pwm->mmio_base + EP93XX_PWMx_ENABLE); - clk_disable(pwm->clk); - sysfs_remove_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files); - ep93xx_pwm_release_gpio(pdev); - - return 0; -} - -static struct platform_driver ep93xx_pwm_driver = { - .driver = { - .name = "ep93xx-pwm", - .owner = THIS_MODULE, - }, - .probe = ep93xx_pwm_probe, - .remove = ep93xx_pwm_remove, -}; -module_platform_driver(ep93xx_pwm_driver); - -MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>, " - "H Hartley Sweeten <hsweeten@visionengravers.com>"); -MODULE_DESCRIPTION("EP93xx PWM driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:ep93xx-pwm"); diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 75840b5cea6d..eece329d7872 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -62,6 +62,15 @@ config PWM_BFIN To compile this driver as a module, choose M here: the module will be called pwm-bfin. +config PWM_EP93XX + tristate "Cirrus Logic EP93xx PWM support" + depends on ARCH_EP93XX + help + Generic PWM framework driver for Cirrus Logic EP93xx. + + To compile this driver as a module, choose M here: the module + will be called pwm-ep93xx. + config PWM_IMX tristate "i.MX PWM support" depends on ARCH_MXC diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 77a8c185c5b2..8b754e4dba4a 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_PWM_SYSFS) += sysfs.o obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o +obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o obj-$(CONFIG_PWM_IMX) += pwm-imx.o obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c index ba6ce01035e4..f3dcd02390f1 100644 --- a/drivers/pwm/pwm-atmel-tcb.c +++ b/drivers/pwm/pwm-atmel-tcb.c @@ -249,6 +249,8 @@ static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) } } + cmr |= (tcbpwm->div & ATMEL_TC_TCCLKS); + __raw_writel(cmr, regs + ATMEL_TC_REG(group, CMR)); if (index == 0) @@ -305,7 +307,7 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, i = slowclk; rate = 32768; min = div_u64(NSEC_PER_SEC, rate); - max = min << 16; + max = min << tc->tcb_config->counter_width; /* If period is too big return ERANGE error */ if (max < period_ns) diff --git a/drivers/pwm/pwm-ep93xx.c b/drivers/pwm/pwm-ep93xx.c new file mode 100644 index 000000000000..33aa4461e1ce --- /dev/null +++ b/drivers/pwm/pwm-ep93xx.c @@ -0,0 +1,230 @@ +/* + * PWM framework driver for Cirrus Logic EP93xx + * + * Copyright (c) 2009 Matthieu Crapet <mcrapet@gmail.com> + * Copyright (c) 2009, 2013 H Hartley Sweeten <hsweeten@visionengravers.com> + * + * EP9301/02 have only one channel: + * platform device ep93xx-pwm.1 - PWMOUT1 (EGPIO14) + * + * EP9307 has only one channel: + * platform device ep93xx-pwm.0 - PWMOUT + * + * EP9312/15 have two channels: + * platform device ep93xx-pwm.0 - PWMOUT + * platform device ep93xx-pwm.1 - PWMOUT1 (EGPIO14) + * + * 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. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/pwm.h> + +#include <asm/div64.h> + +#include <mach/platform.h> /* for ep93xx_pwm_{acquire,release}_gpio() */ + +#define EP93XX_PWMx_TERM_COUNT 0x00 +#define EP93XX_PWMx_DUTY_CYCLE 0x04 +#define EP93XX_PWMx_ENABLE 0x08 +#define EP93XX_PWMx_INVERT 0x0c + +struct ep93xx_pwm { + void __iomem *base; + struct clk *clk; + struct pwm_chip chip; +}; + +static inline struct ep93xx_pwm *to_ep93xx_pwm(struct pwm_chip *chip) +{ + return container_of(chip, struct ep93xx_pwm, chip); +} + +static int ep93xx_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct platform_device *pdev = to_platform_device(chip->dev); + + return ep93xx_pwm_acquire_gpio(pdev); +} + +static void ep93xx_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct platform_device *pdev = to_platform_device(chip->dev); + + ep93xx_pwm_release_gpio(pdev); +} + +static int ep93xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip); + void __iomem *base = ep93xx_pwm->base; + unsigned long long c; + unsigned long period_cycles; + unsigned long duty_cycles; + unsigned long term; + int ret = 0; + + /* + * The clock needs to be enabled to access the PWM registers. + * Configuration can be changed at any time. + */ + if (!test_bit(PWMF_ENABLED, &pwm->flags)) { + ret = clk_enable(ep93xx_pwm->clk); + if (ret) + return ret; + } + + c = clk_get_rate(ep93xx_pwm->clk); + c *= period_ns; + do_div(c, 1000000000); + period_cycles = c; + + c = period_cycles; + c *= duty_ns; + do_div(c, period_ns); + duty_cycles = c; + + if (period_cycles < 0x10000 && duty_cycles < 0x10000) { + term = readw(base + EP93XX_PWMx_TERM_COUNT); + + /* Order is important if PWM is running */ + if (period_cycles > term) { + writew(period_cycles, base + EP93XX_PWMx_TERM_COUNT); + writew(duty_cycles, base + EP93XX_PWMx_DUTY_CYCLE); + } else { + writew(duty_cycles, base + EP93XX_PWMx_DUTY_CYCLE); + writew(period_cycles, base + EP93XX_PWMx_TERM_COUNT); + } + } else { + ret = -EINVAL; + } + + if (!test_bit(PWMF_ENABLED, &pwm->flags)) + clk_disable(ep93xx_pwm->clk); + + return ret; +} + +static int ep93xx_pwm_polarity(struct pwm_chip *chip, struct pwm_device *pwm, + enum pwm_polarity polarity) +{ + struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip); + int ret; + + /* + * The clock needs to be enabled to access the PWM registers. + * Polarity can only be changed when the PWM is disabled. + */ + ret = clk_enable(ep93xx_pwm->clk); + if (ret) + return ret; + + if (polarity == PWM_POLARITY_INVERSED) + writew(0x1, ep93xx_pwm->base + EP93XX_PWMx_INVERT); + else + writew(0x0, ep93xx_pwm->base + EP93XX_PWMx_INVERT); + + clk_disable(ep93xx_pwm->clk); + + return 0; +} + +static int ep93xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip); + int ret; + + ret = clk_enable(ep93xx_pwm->clk); + if (ret) + return ret; + + writew(0x1, ep93xx_pwm->base + EP93XX_PWMx_ENABLE); + + return 0; +} + +static void ep93xx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip); + + writew(0x0, ep93xx_pwm->base + EP93XX_PWMx_ENABLE); + clk_disable(ep93xx_pwm->clk); +} + +static const struct pwm_ops ep93xx_pwm_ops = { + .request = ep93xx_pwm_request, + .free = ep93xx_pwm_free, + .config = ep93xx_pwm_config, + .set_polarity = ep93xx_pwm_polarity, + .enable = ep93xx_pwm_enable, + .disable = ep93xx_pwm_disable, + .owner = THIS_MODULE, +}; + +static int ep93xx_pwm_probe(struct platform_device *pdev) +{ + struct ep93xx_pwm *ep93xx_pwm; + struct resource *res; + int ret; + + ep93xx_pwm = devm_kzalloc(&pdev->dev, sizeof(*ep93xx_pwm), GFP_KERNEL); + if (!ep93xx_pwm) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ep93xx_pwm->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ep93xx_pwm->base)) + return PTR_ERR(ep93xx_pwm->base); + + ep93xx_pwm->clk = devm_clk_get(&pdev->dev, "pwm_clk"); + if (IS_ERR(ep93xx_pwm->clk)) + return PTR_ERR(ep93xx_pwm->clk); + + ep93xx_pwm->chip.dev = &pdev->dev; + ep93xx_pwm->chip.ops = &ep93xx_pwm_ops; + ep93xx_pwm->chip.base = -1; + ep93xx_pwm->chip.npwm = 1; + + ret = pwmchip_add(&ep93xx_pwm->chip); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, ep93xx_pwm); + return 0; +} + +static int ep93xx_pwm_remove(struct platform_device *pdev) +{ + struct ep93xx_pwm *ep93xx_pwm = platform_get_drvdata(pdev); + + return pwmchip_remove(&ep93xx_pwm->chip); +} + +static struct platform_driver ep93xx_pwm_driver = { + .driver = { + .name = "ep93xx-pwm", + }, + .probe = ep93xx_pwm_probe, + .remove = ep93xx_pwm_remove, +}; +module_platform_driver(ep93xx_pwm_driver); + +MODULE_DESCRIPTION("Cirrus Logic EP93xx PWM driver"); +MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>, " + "H Hartley Sweeten <hsweeten@visionengravers.com>"); +MODULE_ALIAS("platform:ep93xx-pwm"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index 2b7c4f88b461..cc4773344874 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -16,6 +16,7 @@ #include <linux/clk.h> #include <linux/io.h> #include <linux/pwm.h> +#include <linux/of.h> #include <linux/of_device.h> /* i.MX1 and i.MX21 share the same PWM function block: */ @@ -296,7 +297,7 @@ static struct platform_driver imx_pwm_driver = { .driver = { .name = "imx-pwm", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(imx_pwm_dt_ids), + .of_match_table = imx_pwm_dt_ids, }, .probe = imx_pwm_probe, .remove = imx_pwm_remove, diff --git a/drivers/pwm/pwm-lpc32xx.c b/drivers/pwm/pwm-lpc32xx.c index efac99e03d57..9dc0f9d42bfa 100644 --- a/drivers/pwm/pwm-lpc32xx.c +++ b/drivers/pwm/pwm-lpc32xx.c @@ -169,7 +169,7 @@ static struct platform_driver lpc32xx_pwm_driver = { .driver = { .name = "lpc32xx-pwm", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(lpc32xx_pwm_dt_ids), + .of_match_table = lpc32xx_pwm_dt_ids, }, .probe = lpc32xx_pwm_probe, .remove = lpc32xx_pwm_remove, diff --git a/drivers/pwm/pwm-mxs.c b/drivers/pwm/pwm-mxs.c index c2c5a4fd1b96..9475bc7a6f97 100644 --- a/drivers/pwm/pwm-mxs.c +++ b/drivers/pwm/pwm-mxs.c @@ -189,7 +189,7 @@ static struct platform_driver mxs_pwm_driver = { .driver = { .name = "mxs-pwm", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(mxs_pwm_dt_ids), + .of_match_table = mxs_pwm_dt_ids, }, .probe = mxs_pwm_probe, .remove = mxs_pwm_remove, diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c index fcc8b9adde9f..b59639e0c029 100644 --- a/drivers/pwm/pwm-samsung.c +++ b/drivers/pwm/pwm-samsung.c @@ -18,6 +18,7 @@ #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/pwm.h> #include <linux/slab.h> @@ -224,8 +225,8 @@ static int pwm_samsung_request(struct pwm_chip *chip, struct pwm_device *pwm) static void pwm_samsung_free(struct pwm_chip *chip, struct pwm_device *pwm) { - pwm_set_chip_data(pwm, NULL); devm_kfree(chip->dev, pwm_get_chip_data(pwm)); + pwm_set_chip_data(pwm, NULL); } static int pwm_samsung_enable(struct pwm_chip *chip, struct pwm_device *pwm) diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c index c2e2e5852362..4e5c3d13d4f8 100644 --- a/drivers/pwm/pwm-tiecap.c +++ b/drivers/pwm/pwm-tiecap.c @@ -26,7 +26,6 @@ #include <linux/pm_runtime.h> #include <linux/pwm.h> #include <linux/of_device.h> -#include <linux/pinctrl/consumer.h> #include "pwm-tipwmss.h" @@ -208,11 +207,6 @@ static int ecap_pwm_probe(struct platform_device *pdev) struct clk *clk; struct ecap_pwm_chip *pc; u16 status; - struct pinctrl *pinctrl; - - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) - dev_warn(&pdev->dev, "unable to select pin group\n"); pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); if (!pc) { diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c index 084f55246532..a4d8f519d965 100644 --- a/drivers/pwm/pwm-tiehrpwm.c +++ b/drivers/pwm/pwm-tiehrpwm.c @@ -26,7 +26,6 @@ #include <linux/clk.h> #include <linux/pm_runtime.h> #include <linux/of_device.h> -#include <linux/pinctrl/consumer.h> #include "pwm-tipwmss.h" @@ -439,11 +438,6 @@ static int ehrpwm_pwm_probe(struct platform_device *pdev) struct clk *clk; struct ehrpwm_pwm_chip *pc; u16 status; - struct pinctrl *pinctrl; - - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) - dev_warn(&pdev->dev, "unable to select pin group\n"); pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); if (!pc) { diff --git a/drivers/pwm/pwm-twl-led.c b/drivers/pwm/pwm-twl-led.c index 29d1bba4804e..b964470025c5 100644 --- a/drivers/pwm/pwm-twl-led.c +++ b/drivers/pwm/pwm-twl-led.c @@ -21,6 +21,7 @@ */ #include <linux/module.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/pwm.h> #include <linux/i2c/twl.h> diff --git a/drivers/pwm/pwm-twl.c b/drivers/pwm/pwm-twl.c index eef910580eae..b99a50e626a6 100644 --- a/drivers/pwm/pwm-twl.c +++ b/drivers/pwm/pwm-twl.c @@ -18,6 +18,7 @@ */ #include <linux/module.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/pwm.h> #include <linux/i2c/twl.h> diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index 36db5d98dd2f..fb80d68f4d33 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -10,6 +10,8 @@ * published by the Free Software Foundation. */ +#include <linux/gpio.h> +#include <linux/of_gpio.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> @@ -19,6 +21,7 @@ #include <linux/err.h> #include <linux/pwm.h> #include <linux/pwm_backlight.h> +#include <linux/regulator/consumer.h> #include <linux/slab.h> struct pwm_bl_data { @@ -27,6 +30,11 @@ struct pwm_bl_data { unsigned int period; unsigned int lth_brightness; unsigned int *levels; + bool enabled; + struct regulator *power_supply; + int enable_gpio; + unsigned long enable_gpio_flags; + unsigned int scale; int (*notify)(struct device *, int brightness); void (*notify_after)(struct device *, @@ -35,11 +43,65 @@ struct pwm_bl_data { void (*exit)(struct device *); }; +static void pwm_backlight_power_on(struct pwm_bl_data *pb, int brightness) +{ + int err; + + if (pb->enabled) + return; + + err = regulator_enable(pb->power_supply); + if (err < 0) + dev_err(pb->dev, "failed to enable power supply\n"); + + if (gpio_is_valid(pb->enable_gpio)) { + if (pb->enable_gpio_flags & PWM_BACKLIGHT_GPIO_ACTIVE_LOW) + gpio_set_value(pb->enable_gpio, 0); + else + gpio_set_value(pb->enable_gpio, 1); + } + + pwm_enable(pb->pwm); + pb->enabled = true; +} + +static void pwm_backlight_power_off(struct pwm_bl_data *pb) +{ + if (!pb->enabled) + return; + + pwm_config(pb->pwm, 0, pb->period); + pwm_disable(pb->pwm); + + if (gpio_is_valid(pb->enable_gpio)) { + if (pb->enable_gpio_flags & PWM_BACKLIGHT_GPIO_ACTIVE_LOW) + gpio_set_value(pb->enable_gpio, 1); + else + gpio_set_value(pb->enable_gpio, 0); + } + + regulator_disable(pb->power_supply); + pb->enabled = false; +} + +static int compute_duty_cycle(struct pwm_bl_data *pb, int brightness) +{ + unsigned int lth = pb->lth_brightness; + int duty_cycle; + + if (pb->levels) + duty_cycle = pb->levels[brightness]; + else + duty_cycle = brightness; + + return (duty_cycle * (pb->period - lth) / pb->scale) + lth; +} + static int pwm_backlight_update_status(struct backlight_device *bl) { struct pwm_bl_data *pb = bl_get_data(bl); int brightness = bl->props.brightness; - int max = bl->props.max_brightness; + int duty_cycle; if (bl->props.power != FB_BLANK_UNBLANK || bl->props.fb_blank != FB_BLANK_UNBLANK || @@ -49,24 +111,12 @@ static int pwm_backlight_update_status(struct backlight_device *bl) if (pb->notify) brightness = pb->notify(pb->dev, brightness); - if (brightness == 0) { - pwm_config(pb->pwm, 0, pb->period); - pwm_disable(pb->pwm); - } else { - int duty_cycle; - - if (pb->levels) { - duty_cycle = pb->levels[brightness]; - max = pb->levels[max]; - } else { - duty_cycle = brightness; - } - - duty_cycle = pb->lth_brightness + - (duty_cycle * (pb->period - pb->lth_brightness) / max); + if (brightness > 0) { + duty_cycle = compute_duty_cycle(pb, brightness); pwm_config(pb->pwm, duty_cycle, pb->period); - pwm_enable(pb->pwm); - } + pwm_backlight_power_on(pb, brightness); + } else + pwm_backlight_power_off(pb); if (pb->notify_after) pb->notify_after(pb->dev, brightness); @@ -98,6 +148,7 @@ static int pwm_backlight_parse_dt(struct device *dev, struct platform_pwm_backlight_data *data) { struct device_node *node = dev->of_node; + enum of_gpio_flags flags; struct property *prop; int length; u32 value; @@ -138,11 +189,13 @@ static int pwm_backlight_parse_dt(struct device *dev, data->max_brightness--; } - /* - * TODO: Most users of this driver use a number of GPIOs to control - * backlight power. Support for specifying these needs to be - * added. - */ + data->enable_gpio = of_get_named_gpio_flags(node, "enable-gpios", 0, + &flags); + if (data->enable_gpio == -EPROBE_DEFER) + return -EPROBE_DEFER; + + if (gpio_is_valid(data->enable_gpio) && (flags & OF_GPIO_ACTIVE_LOW)) + data->enable_gpio_flags |= PWM_BACKLIGHT_GPIO_ACTIVE_LOW; return 0; } @@ -168,7 +221,6 @@ static int pwm_backlight_probe(struct platform_device *pdev) struct backlight_properties props; struct backlight_device *bl; struct pwm_bl_data *pb; - unsigned int max; int ret; if (!data) { @@ -195,16 +247,46 @@ static int pwm_backlight_probe(struct platform_device *pdev) } if (data->levels) { - max = data->levels[data->max_brightness]; + unsigned int i; + + for (i = 0; i <= data->max_brightness; i++) + if (data->levels[i] > pb->scale) + pb->scale = data->levels[i]; + pb->levels = data->levels; } else - max = data->max_brightness; + pb->scale = data->max_brightness; + pb->enable_gpio = data->enable_gpio; + pb->enable_gpio_flags = data->enable_gpio_flags; pb->notify = data->notify; pb->notify_after = data->notify_after; pb->check_fb = data->check_fb; pb->exit = data->exit; pb->dev = &pdev->dev; + pb->enabled = false; + + if (gpio_is_valid(pb->enable_gpio)) { + unsigned long flags; + + if (pb->enable_gpio_flags & PWM_BACKLIGHT_GPIO_ACTIVE_LOW) + flags = GPIOF_OUT_INIT_HIGH; + else + flags = GPIOF_OUT_INIT_LOW; + + ret = gpio_request_one(pb->enable_gpio, flags, "enable"); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request GPIO#%d: %d\n", + pb->enable_gpio, ret); + goto err_alloc; + } + } + + pb->power_supply = devm_regulator_get(&pdev->dev, "power"); + if (IS_ERR(pb->power_supply)) { + ret = PTR_ERR(pb->power_supply); + goto err_gpio; + } pb->pwm = devm_pwm_get(&pdev->dev, NULL); if (IS_ERR(pb->pwm)) { @@ -214,7 +296,7 @@ static int pwm_backlight_probe(struct platform_device *pdev) if (IS_ERR(pb->pwm)) { dev_err(&pdev->dev, "unable to request legacy PWM\n"); ret = PTR_ERR(pb->pwm); - goto err_alloc; + goto err_gpio; } } @@ -229,7 +311,7 @@ static int pwm_backlight_probe(struct platform_device *pdev) pwm_set_period(pb->pwm, data->pwm_period_ns); pb->period = pwm_get_period(pb->pwm); - pb->lth_brightness = data->lth_brightness * (pb->period / max); + pb->lth_brightness = data->lth_brightness * (pb->period / pb->scale); memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_RAW; @@ -239,7 +321,7 @@ static int pwm_backlight_probe(struct platform_device *pdev) if (IS_ERR(bl)) { dev_err(&pdev->dev, "failed to register backlight\n"); ret = PTR_ERR(bl); - goto err_alloc; + goto err_gpio; } if (data->dft_brightness > data->max_brightness) { @@ -255,6 +337,9 @@ static int pwm_backlight_probe(struct platform_device *pdev) platform_set_drvdata(pdev, bl); return 0; +err_gpio: + if (gpio_is_valid(pb->enable_gpio)) + gpio_free(pb->enable_gpio); err_alloc: if (data->exit) data->exit(&pdev->dev); @@ -267,10 +352,11 @@ static int pwm_backlight_remove(struct platform_device *pdev) struct pwm_bl_data *pb = bl_get_data(bl); backlight_device_unregister(bl); - pwm_config(pb->pwm, 0, pb->period); - pwm_disable(pb->pwm); + pwm_backlight_power_off(pb); + if (pb->exit) pb->exit(&pdev->dev); + return 0; } @@ -282,10 +368,12 @@ static int pwm_backlight_suspend(struct device *dev) if (pb->notify) pb->notify(pb->dev, 0); - pwm_config(pb->pwm, 0, pb->period); - pwm_disable(pb->pwm); + + pwm_backlight_power_off(pb); + if (pb->notify_after) pb->notify_after(pb->dev, 0); + return 0; } @@ -294,12 +382,19 @@ static int pwm_backlight_resume(struct device *dev) struct backlight_device *bl = dev_get_drvdata(dev); backlight_update_status(bl); + return 0; } #endif -static SIMPLE_DEV_PM_OPS(pwm_backlight_pm_ops, pwm_backlight_suspend, - pwm_backlight_resume); +static const struct dev_pm_ops pwm_backlight_pm_ops = { +#ifdef CONFIG_PM_SLEEP + .suspend = pwm_backlight_suspend, + .resume = pwm_backlight_resume, + .poweroff = pwm_backlight_suspend, + .restore = pwm_backlight_resume, +#endif +}; static struct platform_driver pwm_backlight_driver = { .driver = { @@ -317,4 +412,3 @@ module_platform_driver(pwm_backlight_driver); MODULE_DESCRIPTION("PWM based Backlight Driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:pwm-backlight"); - |