From 0aa0869c3c9b10338dd92a20fa4a9b6959f177b5 Mon Sep 17 00:00:00 2001 From: "Philip, Avinash" Date: Tue, 24 Jul 2012 19:35:32 +0530 Subject: pwm: Add support for configuring the PWM polarity Some hardware supports inverting the polarity of the PWM signal. This commit adds support to the PWM framework to allow users of the PWM API to configure the polarity. Note that in order to reduce complexity, changing the polarity of a PWM signal is only allowed while the PWM is disabled. A practical example where this can prove useful is to simulate inversion of the duty cycle. While inversion of polarity and duty cycle are not exactly the same, the differences for most use-cases are negligible. Signed-off-by: Philip, Avinash Signed-off-by: Thierry Reding --- drivers/pwm/core.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers') diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index c6e05078d3ad..85592e97ef2d 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -378,6 +378,28 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) } EXPORT_SYMBOL_GPL(pwm_config); +/** + * pwm_set_polarity() - configure the polarity of a PWM signal + * @pwm: PWM device + * @polarity: new polarity of the PWM signal + * + * Note that the polarity cannot be configured while the PWM device is enabled + */ +int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity) +{ + if (!pwm || !pwm->chip->ops) + return -EINVAL; + + if (!pwm->chip->ops->set_polarity) + return -ENOSYS; + + if (test_bit(PWMF_ENABLED, &pwm->flags)) + return -EBUSY; + + return pwm->chip->ops->set_polarity(pwm->chip, pwm, polarity); +} +EXPORT_SYMBOL_GPL(pwm_set_polarity); + /** * pwm_enable() - start a PWM output toggling * @pwm: PWM device -- cgit v1.2.3 From 6354316dbe5a13b25bea15d7ffc891be025eb267 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Wed, 1 Aug 2012 19:20:58 +0900 Subject: pwm: add devm_pwm_get() and devm_pwm_put() Add resource managed variants of pwm_get() and pwm_put() for convenience. Code is largely inspired by the equivalent devm functions of the regulator framework. Signed-off-by: Alexandre Courbot Signed-off-by: Thierry Reding --- drivers/pwm/core.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) (limited to 'drivers') diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 85592e97ef2d..92b1782d0d8e 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -646,6 +646,64 @@ out: } EXPORT_SYMBOL_GPL(pwm_put); +static void devm_pwm_release(struct device *dev, void *res) +{ + pwm_put(*(struct pwm_device **)res); +} + +/** + * devm_pwm_get() - resource managed pwm_get() + * @dev: device for PWM consumer + * @con_id: consumer name + * + * This function performs like pwm_get() but the acquired PWM device will + * automatically be released on driver detach. + */ +struct pwm_device *devm_pwm_get(struct device *dev, const char *con_id) +{ + struct pwm_device **ptr, *pwm; + + ptr = devres_alloc(devm_pwm_release, sizeof(**ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + pwm = pwm_get(dev, con_id); + if (!IS_ERR(pwm)) { + *ptr = pwm; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return pwm; +} +EXPORT_SYMBOL_GPL(devm_pwm_get); + +static int devm_pwm_match(struct device *dev, void *res, void *data) +{ + struct pwm_device **p = res; + + if (WARN_ON(!p || !*p)) + return 0; + + return *p == data; +} + +/** + * devm_pwm_put() - resource managed pwm_put() + * @dev: device for PWM consumer + * @pwm: PWM device + * + * Release a PWM previously allocated using devm_pwm_get(). Calling this + * function is usually not needed because devm-allocated resources are + * automatically released on driver detach. + */ +void devm_pwm_put(struct device *dev, struct pwm_device *pwm) +{ + WARN_ON(devres_release(dev, devm_pwm_release, devm_pwm_match, pwm)); +} +EXPORT_SYMBOL_GPL(devm_pwm_put); + #ifdef CONFIG_DEBUG_FS static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s) { -- cgit v1.2.3 From 454870a44b0687675180506b7774fb559d610675 Mon Sep 17 00:00:00 2001 From: "Philip, Avinash" Date: Thu, 6 Sep 2012 10:40:02 +0530 Subject: pwm: pwm-tiecap: Add support for configuring polarity of PWM ECAP APWM hardware supports polarity configuration of PWM output. This commit adds support for polarity configuration of ECAP APWM. Signed-off-by: Philip, Avinash Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tiecap.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers') diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c index 4b6688909fee..081471fbb097 100644 --- a/drivers/pwm/pwm-tiecap.c +++ b/drivers/pwm/pwm-tiecap.c @@ -32,6 +32,7 @@ #define CAP3 0x10 #define CAP4 0x14 #define ECCTL2 0x2A +#define ECCTL2_APWM_POL_LOW BIT(10) #define ECCTL2_APWM_MODE BIT(9) #define ECCTL2_SYNC_SEL_DISA (BIT(7) | BIT(6)) #define ECCTL2_TSCTR_FREERUN BIT(4) @@ -111,6 +112,26 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, return 0; } +static int ecap_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, + enum pwm_polarity polarity) +{ + struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip); + unsigned short reg_val; + + pm_runtime_get_sync(pc->chip.dev); + reg_val = readw(pc->mmio_base + ECCTL2); + if (polarity == PWM_POLARITY_INVERSED) + /* Duty cycle defines LOW period of PWM */ + reg_val |= ECCTL2_APWM_POL_LOW; + else + /* Duty cycle defines HIGH period of PWM */ + reg_val &= ~ECCTL2_APWM_POL_LOW; + + writew(reg_val, pc->mmio_base + ECCTL2); + pm_runtime_put_sync(pc->chip.dev); + return 0; +} + static int ecap_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip); @@ -157,6 +178,7 @@ static void ecap_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) static const struct pwm_ops ecap_pwm_ops = { .free = ecap_pwm_free, .config = ecap_pwm_config, + .set_polarity = ecap_pwm_set_polarity, .enable = ecap_pwm_enable, .disable = ecap_pwm_disable, .owner = THIS_MODULE, -- cgit v1.2.3 From daa5629b21a1e59ed0ef9515a9e791d2f75cc5ca Mon Sep 17 00:00:00 2001 From: "Philip, Avinash" Date: Thu, 6 Sep 2012 10:40:03 +0530 Subject: pwm: pwm-tiehrpwm: Add support for configuring polarity of PWM EHRPWM hardware supports polarity configuration of PWM output. However configuration of polarity done in hardware only in .enable() to ensure PWM output present only after enabling PWM. This commit adds support for polarity configuration for EHRPWM. When being here, remove configuring of polarity during .config() and do it explicitly from .set_polarity(). Signed-off-by: Philip, Avinash Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tiehrpwm.c | 73 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c index b1996bcd5b78..caf00feadc66 100644 --- a/drivers/pwm/pwm-tiehrpwm.c +++ b/drivers/pwm/pwm-tiehrpwm.c @@ -81,6 +81,15 @@ #define AQCTL_ZRO_FRCHIGH BIT(1) #define AQCTL_ZRO_FRCTOGGLE (BIT(1) | BIT(0)) +#define AQCTL_CHANA_POLNORMAL (AQCTL_CAU_FRCLOW | AQCTL_PRD_FRCHIGH | \ + AQCTL_ZRO_FRCHIGH) +#define AQCTL_CHANA_POLINVERSED (AQCTL_CAU_FRCHIGH | AQCTL_PRD_FRCLOW | \ + AQCTL_ZRO_FRCLOW) +#define AQCTL_CHANB_POLNORMAL (AQCTL_CBU_FRCLOW | AQCTL_PRD_FRCHIGH | \ + AQCTL_ZRO_FRCHIGH) +#define AQCTL_CHANB_POLINVERSED (AQCTL_CBU_FRCHIGH | AQCTL_PRD_FRCLOW | \ + AQCTL_ZRO_FRCLOW) + #define AQSFRC_RLDCSF_MASK (BIT(7) | BIT(6)) #define AQSFRC_RLDCSF_ZRO 0 #define AQSFRC_RLDCSF_PRD BIT(6) @@ -105,6 +114,7 @@ struct ehrpwm_pwm_chip { unsigned int clk_rate; void __iomem *mmio_base; unsigned long period_cycles[NUM_PWM_CHANNEL]; + enum pwm_polarity polarity[NUM_PWM_CHANNEL]; }; static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip) @@ -165,39 +175,37 @@ static int set_prescale_div(unsigned long rqst_prescaler, return 1; } -static void configure_chans(struct ehrpwm_pwm_chip *pc, int chan, - unsigned long duty_cycles) +static void configure_polarity(struct ehrpwm_pwm_chip *pc, int chan) { - int cmp_reg, aqctl_reg; + int aqctl_reg; unsigned short aqctl_val, aqctl_mask; /* - * Channels can be configured from action qualifier module. - * Channel 0 configured with compare A register and for - * up-counter mode. - * Channel 1 configured with compare B register and for - * up-counter mode. + * Configure PWM output to HIGH/LOW level on counter + * reaches compare register value and LOW/HIGH level + * on counter value reaches period register value and + * zero value on counter */ if (chan == 1) { aqctl_reg = AQCTLB; - cmp_reg = CMPB; - /* Configure PWM Low from compare B value */ - aqctl_val = AQCTL_CBU_FRCLOW; aqctl_mask = AQCTL_CBU_MASK; + + if (pc->polarity[chan] == PWM_POLARITY_INVERSED) + aqctl_val = AQCTL_CHANB_POLINVERSED; + else + aqctl_val = AQCTL_CHANB_POLNORMAL; } else { - cmp_reg = CMPA; aqctl_reg = AQCTLA; - /* Configure PWM Low from compare A value*/ - aqctl_val = AQCTL_CAU_FRCLOW; aqctl_mask = AQCTL_CAU_MASK; + + if (pc->polarity[chan] == PWM_POLARITY_INVERSED) + aqctl_val = AQCTL_CHANA_POLINVERSED; + else + aqctl_val = AQCTL_CHANA_POLNORMAL; } - /* Configure PWM High from period value and zero value */ - aqctl_val |= AQCTL_PRD_FRCHIGH | AQCTL_ZRO_FRCHIGH; aqctl_mask |= AQCTL_PRD_MASK | AQCTL_ZRO_MASK; - ehrpwm_modify(pc->mmio_base, aqctl_reg, aqctl_mask, aqctl_val); - - ehrpwm_write(pc->mmio_base, cmp_reg, duty_cycles); + ehrpwm_modify(pc->mmio_base, aqctl_reg, aqctl_mask, aqctl_val); } /* @@ -211,7 +219,7 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, unsigned long long c; unsigned long period_cycles, duty_cycles; unsigned short ps_divval, tb_divval; - int i; + int i, cmp_reg; if (period_ns < 0 || duty_ns < 0 || period_ns > NSEC_PER_SEC) return -ERANGE; @@ -278,12 +286,29 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_CTRMODE_MASK, TBCTL_CTRMODE_UP); - /* Configure the channel for duty cycle */ - configure_chans(pc, pwm->hwpwm, duty_cycles); + if (pwm->hwpwm == 1) + /* Channel 1 configured with compare B register */ + cmp_reg = CMPB; + else + /* Channel 0 configured with compare A register */ + cmp_reg = CMPA; + + ehrpwm_write(pc->mmio_base, cmp_reg, duty_cycles); + pm_runtime_put_sync(chip->dev); return 0; } +static int ehrpwm_pwm_set_polarity(struct pwm_chip *chip, + struct pwm_device *pwm, enum pwm_polarity polarity) +{ + struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); + + /* Configuration of polarity in hardware delayed, do at enable */ + pc->polarity[pwm->hwpwm] = polarity; + return 0; +} + static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); @@ -307,6 +332,9 @@ static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val); + /* Channels polarity can be configured from action qualifier module */ + configure_polarity(pc, pwm->hwpwm); + /* Enable time counter for free_run */ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_FREE_RUN); return 0; @@ -358,6 +386,7 @@ static void ehrpwm_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) static const struct pwm_ops ehrpwm_pwm_ops = { .free = ehrpwm_pwm_free, .config = ehrpwm_pwm_config, + .set_polarity = ehrpwm_pwm_set_polarity, .enable = ehrpwm_pwm_enable, .disable = ehrpwm_pwm_disable, .owner = THIS_MODULE, -- cgit v1.2.3 From 19e73333236a6115617f8ffb4cc290bdb6f2865a Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 3 Jul 2012 17:28:14 +0200 Subject: pwm: i.MX: factor out SoC specific functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To cleanup the code and to make it easier to support different SoCs. Signed-off-by: Sascha Hauer Reviewed-by: Shawn Guo Reviewed-by: Benoît Thébaudeau Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx.c | 146 ++++++++++++++++++++++++++++---------------------- 1 file changed, 83 insertions(+), 63 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index 2a0b35333972..8b7f01e0a100 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -46,81 +46,96 @@ struct imx_chip { void __iomem *mmio_base; struct pwm_chip chip; + + int (*config)(struct pwm_chip *chip, + struct pwm_device *pwm, int duty_ns, int period_ns); }; #define to_imx_chip(chip) container_of(chip, struct imx_chip, chip) -static int imx_pwm_config(struct pwm_chip *chip, +static int imx_pwm_config_v1(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { struct imx_chip *imx = to_imx_chip(chip); - if (!(cpu_is_mx1() || cpu_is_mx21())) { - unsigned long long c; - unsigned long period_cycles, duty_cycles, prescale; - u32 cr; - - c = clk_get_rate(imx->clk); - 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_EN; - - if (cpu_is_mx25()) - cr |= MX3_PWMCR_CLKSRC_IPG; - else - cr |= MX3_PWMCR_CLKSRC_IPG_HIGH; - - writel(cr, imx->mmio_base + MX3_PWMCR); - } else if (cpu_is_mx1() || cpu_is_mx21()) { - /* The PWM subsystem allows for exact frequencies. However, - * I cannot connect a scope on my device to the PWM line and - * thus cannot provide the program the PWM controller - * exactly. Instead, I'm relying on the fact that the - * Bootloader (u-boot or WinCE+haret) has programmed the PWM - * function group already. So I'll just modify the PWM sample - * register to follow the ratio of duty_ns vs. period_ns - * accordingly. - * - * This is good enough for programming the brightness of - * the LCD backlight. - * - * The real implementation would divide PERCLK[0] first by - * both the prescaler (/1 .. /128) and then by CLKSEL - * (/2 .. /16). - */ - u32 max = readl(imx->mmio_base + MX1_PWMP); - u32 p = max * duty_ns / period_ns; - writel(max - p, imx->mmio_base + MX1_PWMS); - } else { - BUG(); - } + /* + * The PWM subsystem allows for exact frequencies. However, + * I cannot connect a scope on my device to the PWM line and + * thus cannot provide the program the PWM controller + * exactly. Instead, I'm relying on the fact that the + * Bootloader (u-boot or WinCE+haret) has programmed the PWM + * function group already. So I'll just modify the PWM sample + * register to follow the ratio of duty_ns vs. period_ns + * accordingly. + * + * This is good enough for programming the brightness of + * the LCD backlight. + * + * The real implementation would divide PERCLK[0] first by + * both the prescaler (/1 .. /128) and then by CLKSEL + * (/2 .. /16). + */ + u32 max = readl(imx->mmio_base + MX1_PWMP); + u32 p = max * duty_ns / period_ns; + writel(max - p, imx->mmio_base + MX1_PWMS); + + return 0; +} + +static int imx_pwm_config_v2(struct pwm_chip *chip, + struct pwm_device *pwm, int duty_ns, int period_ns) +{ + struct imx_chip *imx = to_imx_chip(chip); + unsigned long long c; + unsigned long period_cycles, duty_cycles, prescale; + u32 cr; + + c = clk_get_rate(imx->clk); + 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_EN; + + if (cpu_is_mx25()) + cr |= MX3_PWMCR_CLKSRC_IPG; + else + cr |= MX3_PWMCR_CLKSRC_IPG_HIGH; + + writel(cr, imx->mmio_base + MX3_PWMCR); return 0; } +static int imx_pwm_config(struct pwm_chip *chip, + struct pwm_device *pwm, int duty_ns, int period_ns) +{ + struct imx_chip *imx = to_imx_chip(chip); + + return imx->config(chip, pwm, duty_ns, period_ns); +} + static int imx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { struct imx_chip *imx = to_imx_chip(chip); @@ -187,6 +202,11 @@ static int __devinit imx_pwm_probe(struct platform_device *pdev) if (imx->mmio_base == NULL) return -EADDRNOTAVAIL; + if (cpu_is_mx1() || cpu_is_mx21()) + imx->config = imx_pwm_config_v1; + else + imx->config = imx_pwm_config_v2; + ret = pwmchip_add(&imx->chip); if (ret < 0) return ret; -- cgit v1.2.3 From 140827c148f2f3a95fdcec5310f6cd980139b383 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 28 Aug 2012 09:12:01 +0200 Subject: pwm: i.MX: remove unnecessary if in pwm_[en|dis]able MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PWM core makes sure that pwm_enable/disable are called only once. Still keep the enabled state since we will need it in pwm_config. Signed-off-by: Sascha Hauer Reviewed-by: Shawn Guo Reviewed-by: Benoît Thébaudeau Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index 8b7f01e0a100..5d426ffe6a31 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -42,7 +42,7 @@ struct imx_chip { struct clk *clk; - int clk_enabled; + int enabled; void __iomem *mmio_base; struct pwm_chip chip; @@ -139,14 +139,15 @@ static int imx_pwm_config(struct pwm_chip *chip, static int imx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { struct imx_chip *imx = to_imx_chip(chip); - int rc = 0; + int ret; - if (!imx->clk_enabled) { - rc = clk_prepare_enable(imx->clk); - if (!rc) - imx->clk_enabled = 1; - } - return rc; + ret = clk_prepare_enable(imx->clk); + if (ret) + return ret; + + imx->enabled = 1; + + return 0; } static void imx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) @@ -155,10 +156,8 @@ static void imx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) writel(0, imx->mmio_base + MX3_PWMCR); - if (imx->clk_enabled) { - clk_disable_unprepare(imx->clk); - imx->clk_enabled = 0; - } + clk_disable_unprepare(imx->clk); + imx->enabled = 0; } static struct pwm_ops imx_pwm_ops = { @@ -190,8 +189,6 @@ static int __devinit imx_pwm_probe(struct platform_device *pdev) imx->chip.base = -1; imx->chip.npwm = 1; - imx->clk_enabled = 0; - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (r == NULL) { dev_err(&pdev->dev, "no memory resource defined\n"); -- cgit v1.2.3 From 66ad6a613abeeeabc5217a0498fae63205e8ddb8 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 28 Aug 2012 11:39:25 +0200 Subject: pwm: i.MX: add functions to enable/disable pwm. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We used to enable/disable the PWM only by switching the clock on or off. Instead, use the dedicated register bits. These differ on different SoCs, so introduce a SoC specific function for this. Signed-off-by: Sascha Hauer Reviewed-by: Shawn Guo Reviewed-by: Benoît Thébaudeau Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx.c | 48 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index 5d426ffe6a31..13d4d22f12cf 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -25,6 +25,7 @@ #define MX1_PWMS 0x04 /* PWM Sample Register */ #define MX1_PWMP 0x08 /* PWM Period Register */ +#define MX1_PWMC_EN (1 << 4) /* i.MX27, i.MX31, i.MX35 share the same PWM function block: */ @@ -49,6 +50,7 @@ struct imx_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) @@ -82,6 +84,21 @@ 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) +{ + struct imx_chip *imx = to_imx_chip(chip); + u32 val; + + val = readl(imx->mmio_base + MX1_PWMC); + + if (enable) + val |= MX1_PWMC_EN; + else + 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) { @@ -116,7 +133,10 @@ static int imx_pwm_config_v2(struct pwm_chip *chip, cr = MX3_PWMCR_PRESCALER(prescale) | MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN | - MX3_PWMCR_DBGEN | MX3_PWMCR_EN; + MX3_PWMCR_DBGEN; + + if (imx->enabled) + cr |= MX3_PWMCR_EN; if (cpu_is_mx25()) cr |= MX3_PWMCR_CLKSRC_IPG; @@ -128,6 +148,21 @@ static int imx_pwm_config_v2(struct pwm_chip *chip, return 0; } +static void imx_pwm_set_enable_v2(struct pwm_chip *chip, bool enable) +{ + 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; + + writel(val, imx->mmio_base + MX3_PWMCR); +} + static int imx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { @@ -145,6 +180,8 @@ static int imx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) if (ret) return ret; + imx->set_enable(chip, true); + imx->enabled = 1; return 0; @@ -154,7 +191,7 @@ static void imx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) { struct imx_chip *imx = to_imx_chip(chip); - writel(0, imx->mmio_base + MX3_PWMCR); + imx->set_enable(chip, false); clk_disable_unprepare(imx->clk); imx->enabled = 0; @@ -199,10 +236,13 @@ static int __devinit imx_pwm_probe(struct platform_device *pdev) if (imx->mmio_base == NULL) return -EADDRNOTAVAIL; - if (cpu_is_mx1() || cpu_is_mx21()) + if (cpu_is_mx1() || cpu_is_mx21()) { imx->config = imx_pwm_config_v1; - else + imx->set_enable = imx_pwm_set_enable_v1; + } else { imx->config = imx_pwm_config_v2; + imx->set_enable = imx_pwm_set_enable_v2; + } ret = pwmchip_add(&imx->chip); if (ret < 0) -- cgit v1.2.3 From 208d038f4792b5ca0dc76deebbc0ae13b4f1a744 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 28 Aug 2012 08:27:40 +0200 Subject: pwm: i.MX: Use module_platform_driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sascha Hauer Reviewed-by: Shawn Guo Reviewed-by: Benoît Thébaudeau Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index 13d4d22f12cf..a1e799ee07de 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -271,17 +271,7 @@ static struct platform_driver imx_pwm_driver = { .remove = __devexit_p(imx_pwm_remove), }; -static int __init imx_pwm_init(void) -{ - return platform_driver_register(&imx_pwm_driver); -} -arch_initcall(imx_pwm_init); - -static void __exit imx_pwm_exit(void) -{ - platform_driver_unregister(&imx_pwm_driver); -} -module_exit(imx_pwm_exit); +module_platform_driver(imx_pwm_driver); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Sascha Hauer "); -- cgit v1.2.3 From 479e2e301c626cc64fb27b6b1938655eaba8b036 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 25 Jun 2012 16:16:25 +0200 Subject: pwm: i.MX: add devicetree support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At the same time remove platform based support. No user for this driver has made it into mainline so far, so all we break is out of tree stuff. Signed-off-by: Philipp Zabel Signed-off-by: Sascha Hauer Reviewed-by: Shawn Guo Reviewed-by: Benoît Thébaudeau Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx.c | 44 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index a1e799ee07de..0f6c436c0632 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -16,9 +16,9 @@ #include #include #include +#include #include - /* i.MX1 and i.MX21 share the same PWM function block: */ #define MX1_PWMC 0x00 /* PWM Control Register */ @@ -204,12 +204,41 @@ static struct pwm_ops imx_pwm_ops = { .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); +}; + +static struct imx_pwm_data imx_pwm_data_v1 = { + .config = imx_pwm_config_v1, + .set_enable = imx_pwm_set_enable_v1, +}; + +static struct imx_pwm_data imx_pwm_data_v2 = { + .config = imx_pwm_config_v2, + .set_enable = imx_pwm_set_enable_v2, +}; + +static const struct of_device_id imx_pwm_dt_ids[] = { + { .compatible = "fsl,imx1-pwm", .data = &imx_pwm_data_v1, }, + { .compatible = "fsl,imx27-pwm", .data = &imx_pwm_data_v2, }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx_pwm_dt_ids); + static int __devinit imx_pwm_probe(struct platform_device *pdev) { + const struct of_device_id *of_id = + of_match_device(imx_pwm_dt_ids, &pdev->dev); + struct imx_pwm_data *data; struct imx_chip *imx; struct resource *r; int ret = 0; + if (!of_id) + return -ENODEV; + imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL); if (imx == NULL) { dev_err(&pdev->dev, "failed to allocate memory\n"); @@ -236,13 +265,9 @@ static int __devinit imx_pwm_probe(struct platform_device *pdev) if (imx->mmio_base == NULL) return -EADDRNOTAVAIL; - if (cpu_is_mx1() || cpu_is_mx21()) { - imx->config = imx_pwm_config_v1; - imx->set_enable = imx_pwm_set_enable_v1; - } else { - imx->config = imx_pwm_config_v2; - imx->set_enable = imx_pwm_set_enable_v2; - } + data = of_id->data; + imx->config = data->config; + imx->set_enable = data->set_enable; ret = pwmchip_add(&imx->chip); if (ret < 0) @@ -265,7 +290,8 @@ static int __devexit imx_pwm_remove(struct platform_device *pdev) static struct platform_driver imx_pwm_driver = { .driver = { - .name = "mxc_pwm", + .name = "imx-pwm", + .of_match_table = of_match_ptr(imx_pwm_dt_ids), }, .probe = imx_pwm_probe, .remove = __devexit_p(imx_pwm_remove), -- cgit v1.2.3 From 8d1c24bfd20829f5943c76b85c4973db264dd666 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 28 Aug 2012 12:03:29 +0200 Subject: pwm: i.MX: use per clock unconditionally MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The i.MX PWM module has two clocks: The ipg clock and the ipg highfreq (peripheral) clock. The ipg clock has to be enabled for this hardware to work. The actual PWM output can either be driven by the ipg clock or the ipg highfreq. The ipg highfreq has the advantage that it runs even when the SoC is in low power modes. Use the always running clock also on i.MX25. Signed-off-by: Sascha Hauer Reviewed-by: Shawn Guo Reviewed-by: Benoît Thébaudeau Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index 0f6c436c0632..852de6ce6e11 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -17,7 +17,6 @@ #include #include #include -#include /* i.MX1 and i.MX21 share the same PWM function block: */ @@ -133,16 +132,11 @@ static int imx_pwm_config_v2(struct pwm_chip *chip, cr = MX3_PWMCR_PRESCALER(prescale) | MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN | - MX3_PWMCR_DBGEN; + MX3_PWMCR_DBGEN | MX3_PWMCR_CLKSRC_IPG_HIGH; if (imx->enabled) cr |= MX3_PWMCR_EN; - if (cpu_is_mx25()) - cr |= MX3_PWMCR_CLKSRC_IPG; - else - cr |= MX3_PWMCR_CLKSRC_IPG_HIGH; - writel(cr, imx->mmio_base + MX3_PWMCR); return 0; -- cgit v1.2.3 From 7b27c160c68152581c702b9f1fe362338d2a0cad Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 25 Jun 2012 16:15:20 +0200 Subject: pwm: i.MX: fix clock lookup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The i.MX PWM core has two clocks: The ipg clock and the ipg highfreq (peripheral) clock. The ipg clock has to be enabled for this hardware to work. The actual PWM output can either be driven by the ipg clock or the ipg highfreq. The ipg highfreq has the advantage that it runs even when the SoC is in low power modes. This patch requests both clocks and enables the ipg clock for accessing registers and the peripheral clock to actually turn on the PWM. Signed-off-by: Philipp Zabel Signed-off-by: Sascha Hauer Reviewed-by: Shawn Guo Reviewed-by: Benoît Thébaudeau Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx.c | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index 852de6ce6e11..8a5d3ae2946a 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -40,7 +40,8 @@ #define MX3_PWMCR_EN (1 << 0) struct imx_chip { - struct clk *clk; + struct clk *clk_per; + struct clk *clk_ipg; int enabled; void __iomem *mmio_base; @@ -106,7 +107,7 @@ static int imx_pwm_config_v2(struct pwm_chip *chip, unsigned long period_cycles, duty_cycles, prescale; u32 cr; - c = clk_get_rate(imx->clk); + c = clk_get_rate(imx->clk_per); c = c * period_ns; do_div(c, 1000000000); period_cycles = c; @@ -161,8 +162,17 @@ static int imx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { struct imx_chip *imx = to_imx_chip(chip); + int ret; + + ret = clk_prepare_enable(imx->clk_ipg); + if (ret) + return ret; - return imx->config(chip, pwm, duty_ns, period_ns); + ret = imx->config(chip, pwm, duty_ns, period_ns); + + clk_disable_unprepare(imx->clk_ipg); + + return ret; } static int imx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) @@ -170,7 +180,7 @@ static int imx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) struct imx_chip *imx = to_imx_chip(chip); int ret; - ret = clk_prepare_enable(imx->clk); + ret = clk_prepare_enable(imx->clk_per); if (ret) return ret; @@ -187,7 +197,7 @@ static void imx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) imx->set_enable(chip, false); - clk_disable_unprepare(imx->clk); + clk_disable_unprepare(imx->clk_per); imx->enabled = 0; } @@ -239,10 +249,19 @@ static int __devinit imx_pwm_probe(struct platform_device *pdev) return -ENOMEM; } - imx->clk = devm_clk_get(&pdev->dev, "pwm"); + imx->clk_per = devm_clk_get(&pdev->dev, "per"); + if (IS_ERR(imx->clk_per)) { + dev_err(&pdev->dev, "getting per clock failed with %ld\n", + PTR_ERR(imx->clk_per)); + return PTR_ERR(imx->clk_per); + } - if (IS_ERR(imx->clk)) - return PTR_ERR(imx->clk); + 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.dev = &pdev->dev; -- cgit v1.2.3 From 6173f8f4ed9c2bd4184908e35f46755a0d14b0f2 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 31 Aug 2012 11:46:24 +0200 Subject: pwm: Move AB8500 PWM driver to PWM framework This commit moves the driver to drivers/pwm and converts it to the new PWM framework. Signed-off-by: Thierry Reding Acked-by: Linus Walleij Acked-by: Arnd Bergmann Acked-by: Arun Murthy --- drivers/misc/Kconfig | 10 --- drivers/misc/Makefile | 1 - drivers/misc/ab8500-pwm.c | 169 ---------------------------------------------- drivers/pwm/Kconfig | 9 +++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-ab8500.c | 153 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 163 insertions(+), 180 deletions(-) delete mode 100644 drivers/misc/ab8500-pwm.c create mode 100644 drivers/pwm/pwm-ab8500.c (limited to 'drivers') diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 98a442da892a..041b656c4ca4 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -60,16 +60,6 @@ config ATMEL_PWM purposes including software controlled power-efficient backlights on LCD displays, motor control, and waveform generation. -config AB8500_PWM - bool "AB8500 PWM support" - depends on AB8500_CORE && ARCH_U8500 - select HAVE_PWM - depends on !PWM - help - This driver exports functions to enable/disble/config/free Pulse - Width Modulation in the Analog Baseband Chip AB8500. - It is used by led and backlight driver to control the intensity. - config ATMEL_TCLIB bool "Atmel AT32/AT91 Timer/Counter Library" depends on (AVR32 || ARCH_AT91) diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index b88df7a350b8..2129377c0de6 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -44,7 +44,6 @@ obj-$(CONFIG_VMWARE_BALLOON) += vmw_balloon.o obj-$(CONFIG_ARM_CHARLCD) += arm-charlcd.o obj-$(CONFIG_PCH_PHUB) += pch_phub.o obj-y += ti-st/ -obj-$(CONFIG_AB8500_PWM) += ab8500-pwm.o obj-y += lis3lv02d/ obj-y += carma/ obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o diff --git a/drivers/misc/ab8500-pwm.c b/drivers/misc/ab8500-pwm.c deleted file mode 100644 index d7a9aa14e5d5..000000000000 --- a/drivers/misc/ab8500-pwm.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * Author: Arun R Murthy - * License terms: GNU General Public License (GPL) version 2 - */ -#include -#include -#include -#include -#include -#include -#include - -/* - * PWM Out generators - * Bank: 0x10 - */ -#define AB8500_PWM_OUT_CTRL1_REG 0x60 -#define AB8500_PWM_OUT_CTRL2_REG 0x61 -#define AB8500_PWM_OUT_CTRL7_REG 0x66 - -/* backlight driver constants */ -#define ENABLE_PWM 1 -#define DISABLE_PWM 0 - -struct pwm_device { - struct device *dev; - struct list_head node; - const char *label; - unsigned int pwm_id; -}; - -static LIST_HEAD(pwm_list); - -int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) -{ - int ret = 0; - unsigned int higher_val, lower_val; - u8 reg; - - /* - * get the first 8 bits that are be written to - * AB8500_PWM_OUT_CTRL1_REG[0:7] - */ - lower_val = duty_ns & 0x00FF; - /* - * get bits [9:10] that are to be written to - * AB8500_PWM_OUT_CTRL2_REG[0:1] - */ - higher_val = ((duty_ns & 0x0300) >> 8); - - reg = AB8500_PWM_OUT_CTRL1_REG + ((pwm->pwm_id - 1) * 2); - - ret = abx500_set_register_interruptible(pwm->dev, AB8500_MISC, - reg, (u8)lower_val); - if (ret < 0) - return ret; - ret = abx500_set_register_interruptible(pwm->dev, AB8500_MISC, - (reg + 1), (u8)higher_val); - - return ret; -} -EXPORT_SYMBOL(pwm_config); - -int pwm_enable(struct pwm_device *pwm) -{ - int ret; - - ret = abx500_mask_and_set_register_interruptible(pwm->dev, - AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG, - 1 << (pwm->pwm_id-1), ENABLE_PWM); - if (ret < 0) - dev_err(pwm->dev, "%s: Failed to disable PWM, Error %d\n", - pwm->label, ret); - return ret; -} -EXPORT_SYMBOL(pwm_enable); - -void pwm_disable(struct pwm_device *pwm) -{ - int ret; - - ret = abx500_mask_and_set_register_interruptible(pwm->dev, - AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG, - 1 << (pwm->pwm_id-1), DISABLE_PWM); - if (ret < 0) - dev_err(pwm->dev, "%s: Failed to disable PWM, Error %d\n", - pwm->label, ret); - return; -} -EXPORT_SYMBOL(pwm_disable); - -struct pwm_device *pwm_request(int pwm_id, const char *label) -{ - struct pwm_device *pwm; - - list_for_each_entry(pwm, &pwm_list, node) { - if (pwm->pwm_id == pwm_id) { - pwm->label = label; - pwm->pwm_id = pwm_id; - return pwm; - } - } - - return ERR_PTR(-ENOENT); -} -EXPORT_SYMBOL(pwm_request); - -void pwm_free(struct pwm_device *pwm) -{ - pwm_disable(pwm); -} -EXPORT_SYMBOL(pwm_free); - -static int __devinit ab8500_pwm_probe(struct platform_device *pdev) -{ - struct pwm_device *pwm; - /* - * Nothing to be done in probe, this is required to get the - * device which is required for ab8500 read and write - */ - pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL); - if (pwm == NULL) { - dev_err(&pdev->dev, "failed to allocate memory\n"); - return -ENOMEM; - } - pwm->dev = &pdev->dev; - pwm->pwm_id = pdev->id; - list_add_tail(&pwm->node, &pwm_list); - platform_set_drvdata(pdev, pwm); - dev_dbg(pwm->dev, "pwm probe successful\n"); - return 0; -} - -static int __devexit ab8500_pwm_remove(struct platform_device *pdev) -{ - struct pwm_device *pwm = platform_get_drvdata(pdev); - list_del(&pwm->node); - dev_dbg(&pdev->dev, "pwm driver removed\n"); - kfree(pwm); - return 0; -} - -static struct platform_driver ab8500_pwm_driver = { - .driver = { - .name = "ab8500-pwm", - .owner = THIS_MODULE, - }, - .probe = ab8500_pwm_probe, - .remove = __devexit_p(ab8500_pwm_remove), -}; - -static int __init ab8500_pwm_init(void) -{ - return platform_driver_register(&ab8500_pwm_driver); -} - -static void __exit ab8500_pwm_exit(void) -{ - platform_driver_unregister(&ab8500_pwm_driver); -} - -subsys_initcall(ab8500_pwm_init); -module_exit(ab8500_pwm_exit); -MODULE_AUTHOR("Arun MURTHY "); -MODULE_DESCRIPTION("AB8500 Pulse Width Modulation Driver"); -MODULE_ALIAS("platform:ab8500-pwm"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 90c5c7357a50..99a738182bf5 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -29,6 +29,15 @@ menuconfig PWM if PWM +config PWM_AB8500 + tristate "AB8500 PWM support" + depends on AB8500_CORE && ARCH_U8500 + help + Generic PWM framework driver for Analog Baseband AB8500. + + To compile this driver as a module, choose M here: the module + will be called pwm-ab8500. + config PWM_BFIN tristate "Blackfin PWM support" depends on BFIN_GPTIMERS diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index e4b2c898964d..bebc6ff904b9 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_PWM) += core.o +obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o obj-$(CONFIG_PWM_IMX) += pwm-imx.o obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o diff --git a/drivers/pwm/pwm-ab8500.c b/drivers/pwm/pwm-ab8500.c new file mode 100644 index 000000000000..cfb72ca873d1 --- /dev/null +++ b/drivers/pwm/pwm-ab8500.c @@ -0,0 +1,153 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Arun R Murthy + * License terms: GNU General Public License (GPL) version 2 + */ +#include +#include +#include +#include +#include +#include +#include + +/* + * PWM Out generators + * Bank: 0x10 + */ +#define AB8500_PWM_OUT_CTRL1_REG 0x60 +#define AB8500_PWM_OUT_CTRL2_REG 0x61 +#define AB8500_PWM_OUT_CTRL7_REG 0x66 + +/* backlight driver constants */ +#define ENABLE_PWM 1 +#define DISABLE_PWM 0 + +struct ab8500_pwm_chip { + struct pwm_chip chip; +}; + +static int ab8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + int ret = 0; + unsigned int higher_val, lower_val; + u8 reg; + + /* + * get the first 8 bits that are be written to + * AB8500_PWM_OUT_CTRL1_REG[0:7] + */ + lower_val = duty_ns & 0x00FF; + /* + * get bits [9:10] that are to be written to + * AB8500_PWM_OUT_CTRL2_REG[0:1] + */ + higher_val = ((duty_ns & 0x0300) >> 8); + + reg = AB8500_PWM_OUT_CTRL1_REG + ((chip->base - 1) * 2); + + ret = abx500_set_register_interruptible(chip->dev, AB8500_MISC, + reg, (u8)lower_val); + if (ret < 0) + return ret; + ret = abx500_set_register_interruptible(chip->dev, AB8500_MISC, + (reg + 1), (u8)higher_val); + + return ret; +} + +static int ab8500_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + int ret; + + ret = abx500_mask_and_set_register_interruptible(chip->dev, + AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG, + 1 << (chip->base - 1), ENABLE_PWM); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to disable PWM, Error %d\n", + pwm->label, ret); + return ret; +} + +static void ab8500_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + int ret; + + ret = abx500_mask_and_set_register_interruptible(chip->dev, + AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG, + 1 << (chip->base - 1), DISABLE_PWM); + if (ret < 0) + dev_err(chip->dev, "%s: Failed to disable PWM, Error %d\n", + pwm->label, ret); + return; +} + +static const struct pwm_ops ab8500_pwm_ops = { + .config = ab8500_pwm_config, + .enable = ab8500_pwm_enable, + .disable = ab8500_pwm_disable, +}; + +static int __devinit ab8500_pwm_probe(struct platform_device *pdev) +{ + struct ab8500_pwm_chip *ab8500; + int err; + + /* + * Nothing to be done in probe, this is required to get the + * device which is required for ab8500 read and write + */ + ab8500 = kzalloc(sizeof(*ab8500), GFP_KERNEL); + if (ab8500 == NULL) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + ab8500->chip.dev = &pdev->dev; + ab8500->chip.ops = &ab8500_pwm_ops; + ab8500->chip.base = pdev->id; + ab8500->chip.npwm = 1; + + err = pwmchip_add(&ab8500->chip); + if (err < 0) { + kfree(ab8500); + return err; + } + + dev_dbg(&pdev->dev, "pwm probe successful\n"); + platform_set_drvdata(pdev, ab8500); + + return 0; +} + +static int __devexit ab8500_pwm_remove(struct platform_device *pdev) +{ + struct ab8500_pwm_chip *ab8500 = platform_get_drvdata(pdev); + int err; + + err = pwmchip_remove(&ab8500->chip); + if (err < 0) + return err; + + dev_dbg(&pdev->dev, "pwm driver removed\n"); + kfree(ab8500); + + return 0; +} + +static struct platform_driver ab8500_pwm_driver = { + .driver = { + .name = "ab8500-pwm", + .owner = THIS_MODULE, + }, + .probe = ab8500_pwm_probe, + .remove = __devexit_p(ab8500_pwm_remove), +}; +module_platform_driver(ab8500_pwm_driver); + +MODULE_AUTHOR("Arun MURTHY "); +MODULE_DESCRIPTION("AB8500 Pulse Width Modulation Driver"); +MODULE_ALIAS("platform:ab8500-pwm"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 60ce70285b4d3b933eea15e494a9b35fe85b19b7 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 17 Sep 2012 11:50:47 +0530 Subject: pwm-backlight: Use devm_pwm_get() instead of pwm_get() Managed versions of the pwm_get() and pwm_put() functions were recently added to the PWM framework. They can be used to simplify cleanup in the error paths and the device removal callback. Cc: Thierry Reding Signed-off-by: Sachin Kamat Acked-by: Jingoo Han Signed-off-by: Thierry Reding --- drivers/video/backlight/pwm_bl.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index 995f0164c9b0..069983ca49ff 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -213,7 +213,7 @@ static int pwm_backlight_probe(struct platform_device *pdev) pb->exit = data->exit; pb->dev = &pdev->dev; - pb->pwm = pwm_get(&pdev->dev, NULL); + pb->pwm = devm_pwm_get(&pdev->dev, NULL); if (IS_ERR(pb->pwm)) { dev_err(&pdev->dev, "unable to request PWM, trying legacy API\n"); @@ -246,7 +246,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_bl; + goto err_alloc; } bl->props.brightness = data->dft_brightness; @@ -255,8 +255,6 @@ static int pwm_backlight_probe(struct platform_device *pdev) platform_set_drvdata(pdev, bl); return 0; -err_bl: - pwm_put(pb->pwm); err_alloc: if (data->exit) data->exit(&pdev->dev); @@ -271,7 +269,6 @@ static int pwm_backlight_remove(struct platform_device *pdev) backlight_device_unregister(bl); pwm_config(pb->pwm, 0, pb->period); pwm_disable(pb->pwm); - pwm_put(pb->pwm); if (pb->exit) pb->exit(&pdev->dev); return 0; -- cgit v1.2.3 From 79c11b6fa0b225ac165e79e821d50e70f563645f Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 31 Aug 2012 08:29:24 +0200 Subject: pwm: Move PUV3 PWM driver to PWM framework This commit moves the driver to drivers/pwm and converts it to the new PWM framework. Signed-off-by: Thierry Reding Tested-by: Qin Rui Acked-by: Guan Xuetao --- drivers/pwm/Kconfig | 11 +++- drivers/pwm/Makefile | 1 + drivers/pwm/pwm-puv3.c | 161 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 drivers/pwm/pwm-puv3.c (limited to 'drivers') diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 99a738182bf5..d954c72e242e 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -1,6 +1,6 @@ menuconfig PWM bool "Pulse-Width Modulation (PWM) Support" - depends on !MACH_JZ4740 && !PUV3_PWM + depends on !MACH_JZ4740 help Generic Pulse-Width Modulation (PWM) support. @@ -76,6 +76,15 @@ config PWM_MXS To compile this driver as a module, choose M here: the module will be called pwm-mxs. +config PWM_PUV3 + tristate "PKUnity NetBook-0916 PWM support" + depends on ARCH_PUV3 + help + Generic PWM framework driver for PKUnity NetBook-0916. + + To compile this driver as a module, choose M here: the module + will be called pwm-puv3. + config PWM_PXA tristate "PXA PWM support" depends on ARCH_PXA diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index bebc6ff904b9..360aad8fa3ae 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o obj-$(CONFIG_PWM_IMX) += pwm-imx.o obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o obj-$(CONFIG_PWM_MXS) += pwm-mxs.o +obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o obj-$(CONFIG_PWM_PXA) += pwm-pxa.o obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o diff --git a/drivers/pwm/pwm-puv3.c b/drivers/pwm/pwm-puv3.c new file mode 100644 index 000000000000..2a93f37c46ad --- /dev/null +++ b/drivers/pwm/pwm-puv3.c @@ -0,0 +1,161 @@ +/* + * linux/arch/unicore32/kernel/pwm.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Maintained by GUAN Xue-tao + * Copyright (C) 2001-2010 Guan Xuetao + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +struct puv3_pwm_chip { + struct pwm_chip chip; + void __iomem *base; + struct clk *clk; + bool enabled; +}; + +static inline struct puv3_pwm_chip *to_puv3(struct pwm_chip *chip) +{ + return container_of(chip, struct puv3_pwm_chip, chip); +} + +/* + * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE + * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE + */ +static int puv3_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + unsigned long period_cycles, prescale, pv, dc; + struct puv3_pwm_chip *puv3 = to_puv3(chip); + unsigned long long c; + + c = clk_get_rate(puv3->clk); + c = c * period_ns; + do_div(c, 1000000000); + period_cycles = c; + + if (period_cycles < 1) + period_cycles = 1; + + prescale = (period_cycles - 1) / 1024; + pv = period_cycles / (prescale + 1) - 1; + + if (prescale > 63) + return -EINVAL; + + if (duty_ns == period_ns) + dc = OST_PWMDCCR_FDCYCLE; + else + dc = (pv + 1) * duty_ns / period_ns; + + /* + * NOTE: the clock to PWM has to be enabled first + * before writing to the registers + */ + clk_prepare_enable(puv3->clk); + + writel(prescale, puv3->base + OST_PWM_PWCR); + writel(pv - dc, puv3->base + OST_PWM_DCCR); + writel(pv, puv3->base + OST_PWM_PCR); + + clk_disable_unprepare(puv3->clk); + + return 0; +} + +static int puv3_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct puv3_pwm_chip *puv3 = to_puv3(chip); + + return clk_prepare_enable(puv3->clk); +} + +static void puv3_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct puv3_pwm_chip *puv3 = to_puv3(chip); + + clk_disable_unprepare(puv3->clk); +} + +static const struct pwm_ops puv3_pwm_ops = { + .config = puv3_pwm_config, + .enable = puv3_pwm_enable, + .disable = puv3_pwm_disable, + .owner = THIS_MODULE, +}; + +static int __devinit pwm_probe(struct platform_device *pdev) +{ + struct puv3_pwm_chip *puv3; + struct resource *r; + int ret; + + puv3 = devm_kzalloc(&pdev->dev, sizeof(*puv3), GFP_KERNEL); + if (puv3 == NULL) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + puv3->clk = devm_clk_get(&pdev->dev, "OST_CLK"); + if (IS_ERR(puv3->clk)) + return PTR_ERR(puv3->clk); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (r == NULL) { + dev_err(&pdev->dev, "no memory resource defined\n"); + return -ENODEV; + } + + puv3->base = devm_request_and_ioremap(&pdev->dev, r); + if (puv3->base == NULL) + return -EADDRNOTAVAIL; + + puv3->chip.dev = &pdev->dev; + puv3->chip.ops = &puv3_pwm_ops; + puv3->chip.base = -1; + puv3->chip.npwm = 1; + + ret = pwmchip_add(&puv3->chip); + if (ret < 0) { + dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, puv3); + return 0; +} + +static int __devexit pwm_remove(struct platform_device *pdev) +{ + struct puv3_pwm_chip *puv3 = platform_get_drvdata(pdev); + + return pwmchip_remove(&puv3->chip); +} + +static struct platform_driver puv3_pwm_driver = { + .driver = { + .name = "PKUnity-v3-PWM", + }, + .probe = pwm_probe, + .remove = __devexit_p(pwm_remove), +}; +module_platform_driver(puv3_pwm_driver); + +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From f6b8a5700057cc1b531c2f9b7806428a6f83b467 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 22 Aug 2012 10:01:24 +0200 Subject: pwm: Add Ingenic JZ4740 support This commit moves the driver to drivers/pwm and converts it to the new PWM framework. Signed-off-by: Thierry Reding Acked-by: Lars-Peter Clausen Tested-by: Lars-Peter Clausen Acked-by: Ralf Baechle --- drivers/pwm/Kconfig | 11 ++- drivers/pwm/Makefile | 1 + drivers/pwm/pwm-jz4740.c | 221 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 drivers/pwm/pwm-jz4740.c (limited to 'drivers') diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index d954c72e242e..e67800581d60 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -1,6 +1,5 @@ menuconfig PWM bool "Pulse-Width Modulation (PWM) Support" - depends on !MACH_JZ4740 help Generic Pulse-Width Modulation (PWM) support. @@ -56,6 +55,16 @@ config PWM_IMX To compile this driver as a module, choose M here: the module will be called pwm-imx. +config PWM_JZ4740 + tristate "Ingenic JZ4740 PWM support" + depends on MACH_JZ4740 + help + Generic PWM framework driver for Ingenic JZ4740 based + machines. + + To compile this driver as a module, choose M here: the module + will be called pwm-jz4740. + config PWM_LPC32XX tristate "LPC32XX PWM support" depends on ARCH_LPC32XX diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 360aad8fa3ae..29cf57e2c25e 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_PWM) += core.o obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o obj-$(CONFIG_PWM_IMX) += pwm-imx.o +obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o obj-$(CONFIG_PWM_MXS) += pwm-mxs.o obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c new file mode 100644 index 000000000000..10250fcefb98 --- /dev/null +++ b/drivers/pwm/pwm-jz4740.c @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2010, Lars-Peter Clausen + * JZ4740 platform PWM support + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define NUM_PWM 8 + +static const unsigned int jz4740_pwm_gpio_list[NUM_PWM] = { + JZ_GPIO_PWM0, + JZ_GPIO_PWM1, + JZ_GPIO_PWM2, + JZ_GPIO_PWM3, + JZ_GPIO_PWM4, + JZ_GPIO_PWM5, + JZ_GPIO_PWM6, + JZ_GPIO_PWM7, +}; + +struct jz4740_pwm_chip { + struct pwm_chip chip; + struct clk *clk; +}; + +static inline struct jz4740_pwm_chip *to_jz4740(struct pwm_chip *chip) +{ + return container_of(chip, struct jz4740_pwm_chip, chip); +} + +static int jz4740_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) +{ + unsigned int gpio = jz4740_pwm_gpio_list[pwm->hwpwm]; + int ret; + + /* + * Timers 0 and 1 are used for system tasks, so they are unavailable + * for use as PWMs. + */ + if (pwm->hwpwm < 2) + return -EBUSY; + + ret = gpio_request(gpio, pwm->label); + if (ret) { + dev_err(chip->dev, "Failed to request GPIO#%u for PWM: %d\n", + gpio, ret); + return ret; + } + + jz_gpio_set_function(gpio, JZ_GPIO_FUNC_PWM); + + jz4740_timer_start(pwm->hwpwm); + + return 0; +} + +static void jz4740_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + unsigned int gpio = jz4740_pwm_gpio_list[pwm->hwpwm]; + + jz4740_timer_set_ctrl(pwm->hwpwm, 0); + + jz_gpio_set_function(gpio, JZ_GPIO_FUNC_NONE); + gpio_free(gpio); + + jz4740_timer_stop(pwm->hwpwm); +} + +static int jz4740_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + uint32_t ctrl = jz4740_timer_get_ctrl(pwm->pwm); + + ctrl |= JZ_TIMER_CTRL_PWM_ENABLE; + jz4740_timer_set_ctrl(pwm->hwpwm, ctrl); + jz4740_timer_enable(pwm->hwpwm); + + return 0; +} + +static void jz4740_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + uint32_t ctrl = jz4740_timer_get_ctrl(pwm->hwpwm); + + ctrl &= ~JZ_TIMER_CTRL_PWM_ENABLE; + jz4740_timer_disable(pwm->hwpwm); + jz4740_timer_set_ctrl(pwm->hwpwm, ctrl); +} + +static int jz4740_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct jz4740_pwm_chip *jz4740 = to_jz4740(pwm->chip); + unsigned long long tmp; + unsigned long period, duty; + unsigned int prescaler = 0; + uint16_t ctrl; + bool is_enabled; + + tmp = (unsigned long long)clk_get_rate(jz4740->clk) * period_ns; + do_div(tmp, 1000000000); + period = tmp; + + while (period > 0xffff && prescaler < 6) { + period >>= 2; + ++prescaler; + } + + if (prescaler == 6) + return -EINVAL; + + tmp = (unsigned long long)period * duty_ns; + do_div(tmp, period_ns); + duty = period - tmp; + + if (duty >= period) + duty = period - 1; + + is_enabled = jz4740_timer_is_enabled(pwm->hwpwm); + if (is_enabled) + jz4740_pwm_disable(chip, pwm); + + jz4740_timer_set_count(pwm->hwpwm, 0); + jz4740_timer_set_duty(pwm->hwpwm, duty); + jz4740_timer_set_period(pwm->hwpwm, period); + + ctrl = JZ_TIMER_CTRL_PRESCALER(prescaler) | JZ_TIMER_CTRL_SRC_EXT | + JZ_TIMER_CTRL_PWM_ABBRUPT_SHUTDOWN; + + jz4740_timer_set_ctrl(pwm->hwpwm, ctrl); + + if (is_enabled) + jz4740_pwm_enable(chip, pwm); + + return 0; +} + +static const struct pwm_ops jz4740_pwm_ops = { + .request = jz4740_pwm_request, + .free = jz4740_pwm_free, + .config = jz4740_pwm_config, + .enable = jz4740_pwm_enable, + .disable = jz4740_pwm_disable, + .owner = THIS_MODULE, +}; + +static int __devinit jz4740_pwm_probe(struct platform_device *pdev) +{ + struct jz4740_pwm_chip *jz4740; + int ret; + + jz4740 = devm_kzalloc(&pdev->dev, sizeof(*jz4740), GFP_KERNEL); + if (!jz4740) + return -ENOMEM; + + jz4740->clk = clk_get(NULL, "ext"); + if (IS_ERR(jz4740->clk)) + return PTR_ERR(jz4740->clk); + + jz4740->chip.dev = &pdev->dev; + jz4740->chip.ops = &jz4740_pwm_ops; + jz4740->chip.npwm = NUM_PWM; + jz4740->chip.base = -1; + + ret = pwmchip_add(&jz4740->chip); + if (ret < 0) { + clk_put(jz4740->clk); + return ret; + } + + platform_set_drvdata(pdev, jz4740); + + return 0; +} + +static int __devexit jz4740_pwm_remove(struct platform_device *pdev) +{ + struct jz4740_pwm_chip *jz4740 = platform_get_drvdata(pdev); + int ret; + + ret = pwmchip_remove(&jz4740->chip); + if (ret < 0) + return ret; + + clk_put(jz4740->clk); + + return 0; +} + +static struct platform_driver jz4740_pwm_driver = { + .driver = { + .name = "jz4740-pwm", + .owner = THIS_MODULE, + }, + .probe = jz4740_pwm_probe, + .remove = __devexit_p(jz4740_pwm_remove), +}; +module_platform_driver(jz4740_pwm_driver); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Ingenic JZ4740 PWM driver"); +MODULE_ALIAS("platform:jz4740-pwm"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From c2d476a98f71c55e9acdca1d5a1080a22c0622af Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Sun, 2 Sep 2012 22:13:40 +0200 Subject: pwm: Check for negative duty-cycle and period Make sure the duty-cycle and period passed in are not negative. This should eventually be made implicit by making them unsigned. While at it, the drivers' .config() implementations can have the equivalent checks removed. Signed-off-by: Thierry Reding Cc: Shawn Guo Cc: Mark Brown Cc: Arnd Bergmann Cc: Sachin Kamat Cc: Axel Lin Cc: Kukjin Kim Cc: Jingoo Han Cc: Jonghwan Choi Cc: Sascha Hauer Cc: "Philip, Avinash" Cc: Vaibhav Bedia Acked-by: Jingoo Han --- drivers/pwm/core.c | 2 +- drivers/pwm/pwm-bfin.c | 3 --- drivers/pwm/pwm-pxa.c | 3 --- drivers/pwm/pwm-samsung.c | 3 --- drivers/pwm/pwm-tiecap.c | 2 +- drivers/pwm/pwm-tiehrpwm.c | 2 +- 6 files changed, 3 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 92b1782d0d8e..f5acdaa52707 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -371,7 +371,7 @@ EXPORT_SYMBOL_GPL(pwm_free); */ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) { - if (!pwm || period_ns == 0 || duty_ns > period_ns) + if (!pwm || duty_ns < 0 || period_ns <= 0 || duty_ns > period_ns) return -EINVAL; return pwm->chip->ops->config(pwm->chip, pwm, duty_ns, period_ns); diff --git a/drivers/pwm/pwm-bfin.c b/drivers/pwm/pwm-bfin.c index d53c4e7941ef..5da8e185e838 100644 --- a/drivers/pwm/pwm-bfin.c +++ b/drivers/pwm/pwm-bfin.c @@ -69,9 +69,6 @@ static int bfin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, unsigned long period, duty; unsigned long long val; - if (duty_ns < 0 || duty_ns > period_ns) - return -EINVAL; - val = (unsigned long long)get_sclk() * period_ns; do_div(val, NSEC_PER_SEC); period = val; diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c index bd5867a1c700..260c3a88564d 100644 --- a/drivers/pwm/pwm-pxa.c +++ b/drivers/pwm/pwm-pxa.c @@ -70,9 +70,6 @@ static int pxa_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, unsigned long offset; int rc; - if (period_ns == 0 || duty_ns > period_ns) - return -EINVAL; - offset = pwm->hwpwm ? 0x10 : 0; c = clk_get_rate(pc->clk); diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c index e5187c0ade9f..023a3bee76e7 100644 --- a/drivers/pwm/pwm-samsung.c +++ b/drivers/pwm/pwm-samsung.c @@ -126,9 +126,6 @@ static int s3c_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, if (period_ns > NS_IN_HZ || duty_ns > NS_IN_HZ) return -ERANGE; - if (duty_ns > period_ns) - return -EINVAL; - if (period_ns == s3c->period_ns && duty_ns == s3c->duty_ns) return 0; diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c index 081471fbb097..d6d4cf05565e 100644 --- a/drivers/pwm/pwm-tiecap.c +++ b/drivers/pwm/pwm-tiecap.c @@ -60,7 +60,7 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, unsigned long period_cycles, duty_cycles; unsigned int reg_val; - if (period_ns < 0 || duty_ns < 0 || period_ns > NSEC_PER_SEC) + if (period_ns > NSEC_PER_SEC) return -ERANGE; c = pc->clk_rate; diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c index caf00feadc66..d3c1dff0a0dc 100644 --- a/drivers/pwm/pwm-tiehrpwm.c +++ b/drivers/pwm/pwm-tiehrpwm.c @@ -221,7 +221,7 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, unsigned short ps_divval, tb_divval; int i, cmp_reg; - if (period_ns < 0 || duty_ns < 0 || period_ns > NSEC_PER_SEC) + if (period_ns > NSEC_PER_SEC) return -ERANGE; c = pc->clk_rate; -- cgit v1.2.3