* [PATCH 0/7] PWM core driver for pwm based led and backlight driver
@ 2010-09-28 10:35 Arun Murthy
2010-09-28 10:35 ` [PATCH 1/7] pwm: Add pwm core driver Arun Murthy
` (6 more replies)
0 siblings, 7 replies; 16+ messages in thread
From: Arun Murthy @ 2010-09-28 10:35 UTC (permalink / raw)
To: linux-arm-kernel
The series of patch add a new pwm core driver.
Align the existing pwm drivers to make use of the pwm core driver.
Arun Murthy (7):
pwm: Add pwm core driver
backlight:pwm: add an element 'name' to platform data
leds: pwm: add a new element 'name' to platform data
pwm: Align existing pwm drivers with pwm-core driver
platform: Update the pwm based led and backlight platform data
pwm: move existing pwm driver to drivers/pwm
pwm: Modify backlight and led Kconfig aligning to pwm core
arch/arm/mach-pxa/cm-x300.c | 1 +
arch/arm/mach-pxa/colibri-pxa270-income.c | 1 +
arch/arm/mach-pxa/ezx.c | 1 +
arch/arm/mach-pxa/hx4700.c | 1 +
arch/arm/mach-pxa/lpd270.c | 1 +
arch/arm/mach-pxa/magician.c | 1 +
arch/arm/mach-pxa/mainstone.c | 1 +
arch/arm/mach-pxa/mioa701.c | 1 +
arch/arm/mach-pxa/palm27x.c | 1 +
arch/arm/mach-pxa/palmtc.c | 1 +
arch/arm/mach-pxa/palmte2.c | 1 +
arch/arm/mach-pxa/pcm990-baseboard.c | 1 +
arch/arm/mach-pxa/raumfeld.c | 1 +
arch/arm/mach-pxa/tavorevb.c | 2 +
arch/arm/mach-pxa/viper.c | 1 +
arch/arm/mach-pxa/z2.c | 2 +
arch/arm/mach-pxa/zylonite.c | 1 +
arch/arm/mach-s3c2410/mach-h1940.c | 1 +
arch/arm/mach-s3c2440/mach-rx1950.c | 1 +
arch/arm/mach-s3c64xx/mach-hmt.c | 1 +
arch/arm/mach-s3c64xx/mach-smartq.c | 1 +
arch/arm/plat-mxc/pwm.c | 166 +++++++++------------
arch/arm/plat-pxa/pwm.c | 210 ++++++++++++--------------
arch/arm/plat-samsung/pwm.c | 235 +++++++++++++----------------
arch/mips/jz4740/pwm.c | 2 +-
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/leds/Kconfig | 2 +-
drivers/leds/leds-pwm.c | 4 +-
drivers/mfd/Kconfig | 9 -
drivers/mfd/Makefile | 1 -
drivers/mfd/twl-core.c | 13 ++
drivers/mfd/twl6030-pwm.c | 163 --------------------
drivers/misc/Kconfig | 9 -
drivers/misc/Makefile | 1 -
drivers/misc/ab8500-pwm.c | 168 --------------------
drivers/pwm/Kconfig | 33 ++++
drivers/pwm/Makefile | 4 +
drivers/pwm/pwm-ab8500.c | 157 +++++++++++++++++++
drivers/pwm/pwm-core.c | 124 +++++++++++++++
drivers/pwm/pwm-twl6040.c | 196 ++++++++++++++++++++++++
drivers/video/backlight/Kconfig | 2 +-
drivers/video/backlight/pwm_bl.c | 4 +-
include/linux/leds_pwm.h | 1 +
include/linux/pwm.h | 29 ++++-
include/linux/pwm_backlight.h | 1 +
46 files changed, 864 insertions(+), 696 deletions(-)
delete mode 100644 drivers/mfd/twl6030-pwm.c
delete mode 100644 drivers/misc/ab8500-pwm.c
create mode 100644 drivers/pwm/Kconfig
create mode 100644 drivers/pwm/Makefile
create mode 100644 drivers/pwm/pwm-ab8500.c
create mode 100644 drivers/pwm/pwm-core.c
create mode 100644 drivers/pwm/pwm-twl6040.c
--
1.7.2.dirty
^ permalink raw reply [flat|nested] 16+ messages in thread* [PATCH 1/7] pwm: Add pwm core driver 2010-09-28 10:35 [PATCH 0/7] PWM core driver for pwm based led and backlight driver Arun Murthy @ 2010-09-28 10:35 ` Arun Murthy 2010-09-28 12:53 ` Hemanth V 2010-09-28 10:35 ` [PATCH 2/7] backlight:pwm: add an element 'name' to platform data Arun Murthy ` (5 subsequent siblings) 6 siblings, 1 reply; 16+ messages in thread From: Arun Murthy @ 2010-09-28 10:35 UTC (permalink / raw) To: linux-arm-kernel The existing pwm based led and backlight driver makes use of the pwm(include/linux/pwm.h). So all the board specific pwm drivers will be exposing the same set of function name as in include/linux/pwm.h. As a result build fails in case of multi soc environments where each soc has a pwm device in it. In order to overcome this issue all the pwm drivers must register to some core pwm driver with function pointers for pwm operations (i.e pwm_config, pwm_enable, pwm_disable). The clients of pwm device will have to call pwm_request, wherein they will get the pointer to struct pwm_ops. This structure include function pointers for pwm_config, pwm_enable and pwm_disable. Signed-off-by: Arun Murthy <arun.murthy@stericsson.com> Acked-by: Linus Walleij <linus.walleij@stericsson.com> --- drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/pwm/Kconfig | 16 ++++++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-core.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pwm.h | 12 ++++- 6 files changed, 160 insertions(+), 1 deletions(-) create mode 100644 drivers/pwm/Kconfig create mode 100644 drivers/pwm/Makefile create mode 100644 drivers/pwm/pwm-core.c diff --git a/drivers/Kconfig b/drivers/Kconfig index a2b902f..e042f27 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -111,4 +111,6 @@ source "drivers/xen/Kconfig" source "drivers/staging/Kconfig" source "drivers/platform/Kconfig" + +source "drivers/pwm/Kconfig" endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 4ca727d..0061ec4 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -116,3 +116,4 @@ obj-$(CONFIG_STAGING) += staging/ obj-y += platform/ obj-y += ieee802154/ obj-y += vbus/ +obj-$(CONFIG_PWM_DEVICES) += pwm/ diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig new file mode 100644 index 0000000..5d10106 --- /dev/null +++ b/drivers/pwm/Kconfig @@ -0,0 +1,16 @@ +# +# PWM devices +# + +menuconfig PWM_DEVICES + bool "PWM devices" + default y + ---help--- + Say Y here to get to see options for device drivers from various + different categories. This option alone does not add any kernel code. + + If you say N, all options in this submenu will be skipped and disabled. + +if PWM_DEVICES + +endif # PWM_DEVICES diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile new file mode 100644 index 0000000..552f969 --- /dev/null +++ b/drivers/pwm/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_PWM_DEVICES) += pwm-core.o diff --git a/drivers/pwm/pwm-core.c b/drivers/pwm/pwm-core.c new file mode 100644 index 0000000..b84027a --- /dev/null +++ b/drivers/pwm/pwm-core.c @@ -0,0 +1,129 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License v2 + * Author: Arun R Murthy <arun.murthy@stericsson.com> + */ +#include <linux/init.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/rwsem.h> +#include <linux/err.h> +#include <linux/pwm.h> + +struct pwm_device { + struct pwm_ops *pops; + int pwm_id; +}; + +struct pwm_dev_info { + struct pwm_device *pwm_dev; + struct list_head list; +}; +static struct pwm_dev_info *di; + +DECLARE_RWSEM(pwm_list_lock); + +void __deprecated pwm_free(struct pwm_device *pwm) +{ +} + +int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) +{ + return pwm->pops->pwm_config(pwm, duty_ns, period_ns); +} +EXPORT_SYMBOL(pwm_config); + +int pwm_enable(struct pwm_device *pwm) +{ + return pwm->pops->pwm_enable(pwm); +} +EXPORT_SYMBOL(pwm_enable); + +void pwm_disable(struct pwm_device *pwm) +{ + pwm->pops->pwm_disable(pwm); +} +EXPORT_SYMBOL(pwm_disable); + +int pwm_device_register(struct pwm_device *pwm_dev) +{ + struct pwm_dev_info *pwm; + + down_write(&pwm_list_lock); + pwm = kzalloc(sizeof(struct pwm_dev_info), GFP_KERNEL); + if (!pwm) { + up_write(&pwm_list_lock); + return -ENOMEM; + } + pwm->pwm_dev = pwm_dev; + list_add_tail(&pwm->list, &di->list); + up_write(&pwm_list_lock); + + return 0; +} +EXPORT_SYMBOL(pwm_device_register); + +int pwm_device_unregister(struct pwm_device *pwm_dev) +{ + struct pwm_dev_info *tmp; + struct list_head *pos, *tmp_lst; + + down_write(&pwm_list_lock); + list_for_each_safe(pos, tmp_lst, &di->list) { + tmp = list_entry(pos, struct pwm_dev_info, list); + if (tmp->pwm_dev == pwm_dev) { + list_del(pos); + kfree(tmp); + up_write(&pwm_list_lock); + return 0; + } + } + up_write(&pwm_list_lock); + return -ENOENT; +} +EXPORT_SYMBOL(pwm_device_unregister); + +struct pwm_device *pwm_request(int pwm_id, const char *name) +{ + struct pwm_dev_info *pwm; + struct list_head *pos; + + down_read(&pwm_list_lock); + list_for_each(pos, &di->list) { + pwm = list_entry(pos, struct pwm_dev_info, list); + if ((!strcmp(pwm->pwm_dev->pops->name, name)) && + (pwm->pwm_dev->pwm_id == pwm_id)) { + up_read(&pwm_list_lock); + return pwm->pwm_dev; + } + } + up_read(&pwm_list_lock); + return ERR_PTR(-ENOENT); +} +EXPORT_SYMBOL(pwm_request); + +static int __init pwm_init(void) +{ + struct pwm_dev_info *pwm; + + pwm = kzalloc(sizeof(struct pwm_dev_info), GFP_KERNEL); + if (!pwm) + return -ENOMEM; + INIT_LIST_HEAD(&pwm->list); + di = pwm; + return 0; +} + +static void __exit pwm_exit(void) +{ + kfree(di); +} + +subsys_initcall(pwm_init); +module_exit(pwm_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Arun R Murthy"); +MODULE_ALIAS("core:pwm"); +MODULE_DESCRIPTION("Core pwm driver"); diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 7c77575..6e7da1f 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -3,6 +3,13 @@ struct pwm_device; +struct pwm_ops { + int (*pwm_config)(struct pwm_device *pwm, int duty_ns, int period_ns); + int (*pwm_enable)(struct pwm_device *pwm); + int (*pwm_disable)(struct pwm_device *pwm); + char *name; +}; + /* * pwm_request - request a PWM device */ @@ -11,7 +18,7 @@ struct pwm_device *pwm_request(int pwm_id, const char *label); /* * pwm_free - free a PWM device */ -void pwm_free(struct pwm_device *pwm); +void __deprecated pwm_free(struct pwm_device *pwm); /* * pwm_config - change a PWM device configuration @@ -28,4 +35,7 @@ int pwm_enable(struct pwm_device *pwm); */ void pwm_disable(struct pwm_device *pwm); +int pwm_device_register(struct pwm_device *pwm_dev); +int pwm_device_unregister(struct pwm_device *pwm_dev); + #endif /* __LINUX_PWM_H */ -- 1.7.2.dirty ^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 1/7] pwm: Add pwm core driver 2010-09-28 10:35 ` [PATCH 1/7] pwm: Add pwm core driver Arun Murthy @ 2010-09-28 12:53 ` Hemanth V 2010-09-28 13:06 ` Samuel Ortiz 0 siblings, 1 reply; 16+ messages in thread From: Hemanth V @ 2010-09-28 12:53 UTC (permalink / raw) To: linux-arm-kernel ----- Original Message ----- From: "Arun Murthy" <arun.murthy@stericsson.com> > The existing pwm based led and backlight driver makes use of the > pwm(include/linux/pwm.h). So all the board specific pwm drivers will > be exposing the same set of function name as in include/linux/pwm.h. > As a result build fails in case of multi soc environments where each soc > has a pwm device in it. This seems very specific to ST environment, looking at the driver list from ( [PATCH 4/7] pwm: Align existing pwm drivers with pwm-core ) it seems most multi SOC environments might support PWM in either one of the SOC. arch/arm/plat-mxc/pwm.c arch/arm/plat-pxa/pwm.c arch/arm/plat-samsung/pwm.c arch/mips/jz4740/pwm.c drivers/mfd/twl6030-pwm.c Unless people have examples of other SOCs which might use this, the better approach might be to go for a custom driver rather than changing the framework. Thanks Hemanth ^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 1/7] pwm: Add pwm core driver 2010-09-28 12:53 ` Hemanth V @ 2010-09-28 13:06 ` Samuel Ortiz 2010-09-28 13:35 ` Felipe Balbi 0 siblings, 1 reply; 16+ messages in thread From: Samuel Ortiz @ 2010-09-28 13:06 UTC (permalink / raw) To: linux-arm-kernel On Tue, Sep 28, 2010 at 06:23:24PM +0530, Hemanth V wrote: > ----- Original Message ----- From: "Arun Murthy" > <arun.murthy@stericsson.com> > > > >The existing pwm based led and backlight driver makes use of the > >pwm(include/linux/pwm.h). So all the board specific pwm drivers will > >be exposing the same set of function name as in include/linux/pwm.h. > >As a result build fails in case of multi soc environments where each soc > >has a pwm device in it. > > This seems very specific to ST environment, No it's not. It's an issue Arun has hit while enabling one of the ST MFD chip, but he's tackling a generic issue. > looking at the driver list from > ( [PATCH 4/7] pwm: Align existing pwm drivers with pwm-core ) it seems > most multi SOC environments might support PWM in either one of the SOC. > > arch/arm/plat-mxc/pwm.c > arch/arm/plat-pxa/pwm.c > arch/arm/plat-samsung/pwm.c > arch/mips/jz4740/pwm.c > drivers/mfd/twl6030-pwm.c > > Unless people have examples of other SOCs which might use this, > the better approach might be to go for a custom driver rather than changing > the framework. I wouldn't call the current pwm code a framework. It's a bunch of header definitions that happens to work in the specific case of 1 pwm per sub architecture. What Arun is proposing is an actual framework. And it seems to be clean and simple enough. Cheers, Samuel. -- Intel Open Source Technology Centre http://oss.intel.com/ ^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 1/7] pwm: Add pwm core driver 2010-09-28 13:06 ` Samuel Ortiz @ 2010-09-28 13:35 ` Felipe Balbi 0 siblings, 0 replies; 16+ messages in thread From: Felipe Balbi @ 2010-09-28 13:35 UTC (permalink / raw) To: linux-arm-kernel On Tue, Sep 28, 2010 at 08:06:11AM -0500, Samuel Ortiz wrote: >On Tue, Sep 28, 2010 at 06:23:24PM +0530, Hemanth V wrote: >> ----- Original Message ----- From: "Arun Murthy" >> <arun.murthy@stericsson.com> >> >> >> >The existing pwm based led and backlight driver makes use of the >> >pwm(include/linux/pwm.h). So all the board specific pwm drivers will >> >be exposing the same set of function name as in include/linux/pwm.h. >> >As a result build fails in case of multi soc environments where each soc >> >has a pwm device in it. >> >> This seems very specific to ST environment, >No it's not. It's an issue Arun has hit while enabling one of the ST MFD chip, >but he's tackling a generic issue. > >> looking at the driver list from >> ( [PATCH 4/7] pwm: Align existing pwm drivers with pwm-core ) it seems >> most multi SOC environments might support PWM in either one of the SOC. >> >> arch/arm/plat-mxc/pwm.c >> arch/arm/plat-pxa/pwm.c >> arch/arm/plat-samsung/pwm.c >> arch/mips/jz4740/pwm.c >> drivers/mfd/twl6030-pwm.c >> >> Unless people have examples of other SOCs which might use this, >> the better approach might be to go for a custom driver rather than changing >> the framework. >I wouldn't call the current pwm code a framework. It's a bunch of header >definitions that happens to work in the specific case of 1 pwm per >sub architecture. >What Arun is proposing is an actual framework. And it seems to be clean and >simple enough. FWIW, I agree with you Sam. Sooner or later, this will hit other SoCs. -- balbi ^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 2/7] backlight:pwm: add an element 'name' to platform data 2010-09-28 10:35 [PATCH 0/7] PWM core driver for pwm based led and backlight driver Arun Murthy 2010-09-28 10:35 ` [PATCH 1/7] pwm: Add pwm core driver Arun Murthy @ 2010-09-28 10:35 ` Arun Murthy 2010-09-28 10:35 ` [PATCH 3/7] leds: pwm: add a new " Arun Murthy ` (4 subsequent siblings) 6 siblings, 0 replies; 16+ messages in thread From: Arun Murthy @ 2010-09-28 10:35 UTC (permalink / raw) To: linux-arm-kernel A new element 'name' is added to pwm backlight platform data structure. This is required to identify the pwm device. Signed-off-by: Arun Murthy <arun.murthy@stericsson.com> Acked-by: Linus Walleij <linus.walleij@stericsson.com> --- drivers/video/backlight/pwm_bl.c | 4 +++- include/linux/pwm_backlight.h | 1 + 2 files changed, 4 insertions(+), 1 deletions(-) diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index 5504435..b0978a8 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -94,7 +94,9 @@ static int pwm_backlight_probe(struct platform_device *pdev) pb->notify = data->notify; pb->dev = &pdev->dev; - pb->pwm = pwm_request(data->pwm_id, "backlight"); + if (!data->name) + data->name = "backlight"; + pb->pwm = pwm_request(data->pwm_id, data->name); if (IS_ERR(pb->pwm)) { dev_err(&pdev->dev, "unable to request PWM for backlight\n"); ret = PTR_ERR(pb->pwm); diff --git a/include/linux/pwm_backlight.h b/include/linux/pwm_backlight.h index 01b3d75..c2ce8f8 100644 --- a/include/linux/pwm_backlight.h +++ b/include/linux/pwm_backlight.h @@ -6,6 +6,7 @@ struct platform_pwm_backlight_data { int pwm_id; + char *name; unsigned int max_brightness; unsigned int dft_brightness; unsigned int pwm_period_ns; -- 1.7.2.dirty ^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 3/7] leds: pwm: add a new element 'name' to platform data 2010-09-28 10:35 [PATCH 0/7] PWM core driver for pwm based led and backlight driver Arun Murthy 2010-09-28 10:35 ` [PATCH 1/7] pwm: Add pwm core driver Arun Murthy 2010-09-28 10:35 ` [PATCH 2/7] backlight:pwm: add an element 'name' to platform data Arun Murthy @ 2010-09-28 10:35 ` Arun Murthy 2010-09-28 10:43 ` Sergei Shtylyov 2010-09-28 10:35 ` [PATCH 4/7] pwm: Align existing pwm drivers with pwm-core driver Arun Murthy ` (3 subsequent siblings) 6 siblings, 1 reply; 16+ messages in thread From: Arun Murthy @ 2010-09-28 10:35 UTC (permalink / raw) To: linux-arm-kernel A new element 'name' is added to pwm led platform data structure. This is required to identify the pwm device. Signed-off-by: Arun Murthy <arun.murthy@stericsson.com> Acked-by: Linus Walleij <linus.walleij@stericsson.com> --- drivers/leds/leds-pwm.c | 4 +++- include/linux/leds_pwm.h | 1 + 2 files changed, 4 insertions(+), 1 deletions(-) diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index da3fa8d..8da2be6 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c @@ -66,8 +66,10 @@ static int led_pwm_probe(struct platform_device *pdev) cur_led = &pdata->leds[i]; led_dat = &leds_data[i]; + if (!pdata->name) + pdata->name = cur_led->name; led_dat->pwm = pwm_request(cur_led->pwm_id, - cur_led->name); + pdata->name); if (IS_ERR(led_dat->pwm)) { dev_err(&pdev->dev, "unable to request PWM %d\n", cur_led->pwm_id); diff --git a/include/linux/leds_pwm.h b/include/linux/leds_pwm.h index 33a0711..7a847a0 100644 --- a/include/linux/leds_pwm.h +++ b/include/linux/leds_pwm.h @@ -16,6 +16,7 @@ struct led_pwm { struct led_pwm_platform_data { int num_leds; struct led_pwm *leds; + char *name; }; #endif -- 1.7.2.dirty ^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 3/7] leds: pwm: add a new element 'name' to platform data 2010-09-28 10:35 ` [PATCH 3/7] leds: pwm: add a new " Arun Murthy @ 2010-09-28 10:43 ` Sergei Shtylyov 2010-09-28 10:48 ` Arun MURTHY 0 siblings, 1 reply; 16+ messages in thread From: Sergei Shtylyov @ 2010-09-28 10:43 UTC (permalink / raw) To: linux-arm-kernel Hello. On 28-09-2010 14:35, Arun Murthy wrote: > A new element 'name' is added to pwm led platform data structure. > This is required to identify the pwm device. > Signed-off-by: Arun Murthy<arun.murthy@stericsson.com> > Acked-by: Linus Walleij<linus.walleij@stericsson.com> [...] > diff --git a/include/linux/leds_pwm.h b/include/linux/leds_pwm.h > index 33a0711..7a847a0 100644 > --- a/include/linux/leds_pwm.h > +++ b/include/linux/leds_pwm.h > @@ -16,6 +16,7 @@ struct led_pwm { > struct led_pwm_platform_data { > int num_leds; > struct led_pwm *leds; > + char *name; > }; Shouldn't '*name'be aligned, at least with '*leds'? WBR, Sergei ^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 3/7] leds: pwm: add a new element 'name' to platform data 2010-09-28 10:43 ` Sergei Shtylyov @ 2010-09-28 10:48 ` Arun MURTHY 0 siblings, 0 replies; 16+ messages in thread From: Arun MURTHY @ 2010-09-28 10:48 UTC (permalink / raw) To: linux-arm-kernel > > @@ -16,6 +16,7 @@ struct led_pwm { > > struct led_pwm_platform_data { > > int num_leds; > > struct led_pwm *leds; > > + char *name; > > }; > > Shouldn't '*name'be aligned, at least with '*leds'? Sure, will take care of this in the v2 patch. Thanks and Regards, Arun R Murthy ------------- ^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 4/7] pwm: Align existing pwm drivers with pwm-core driver 2010-09-28 10:35 [PATCH 0/7] PWM core driver for pwm based led and backlight driver Arun Murthy ` (2 preceding siblings ...) 2010-09-28 10:35 ` [PATCH 3/7] leds: pwm: add a new " Arun Murthy @ 2010-09-28 10:35 ` Arun Murthy 2010-09-28 10:35 ` [PATCH 5/7] platform: Update the pwm based led and backlight platform data Arun Murthy ` (2 subsequent siblings) 6 siblings, 0 replies; 16+ messages in thread From: Arun Murthy @ 2010-09-28 10:35 UTC (permalink / raw) To: linux-arm-kernel pwm-core: make the driver visible for ARM only Align ab8500 pwm with the pwm core driver Align twl6030 pwm driver with pwm core driver Align Freescale mxc pwm driver with pwm core driver Align pxa pwm driver with pwm core driver Align samsung(s3c) pwm driver with pwm core driver mips-jz4740: pwm: Align with new pwm core driver PWM core driver has been added and has been enabled only for ARM platform. The same can be utilised for mips also. Please align with the pwm core driver(drivers/pwm-core.c). Signed-off-by: Arun Murthy <arun.murthy@stericsson.com> Acked-by: Linus Walleij <linus.walleij@stericsson.com> --- arch/arm/plat-mxc/pwm.c | 166 +++++++++++++----------------- arch/arm/plat-pxa/pwm.c | 210 ++++++++++++++++++-------------------- arch/arm/plat-samsung/pwm.c | 235 +++++++++++++++++++------------------------ arch/mips/jz4740/pwm.c | 2 +- drivers/mfd/twl-core.c | 13 +++ drivers/mfd/twl6030-pwm.c | 111 +++++++++++++------- drivers/misc/ab8500-pwm.c | 87 +++++++--------- drivers/pwm/Kconfig | 1 + drivers/pwm/pwm-core.c | 9 +-- include/linux/pwm.h | 21 ++++- 10 files changed, 418 insertions(+), 437 deletions(-) diff --git a/arch/arm/plat-mxc/pwm.c b/arch/arm/plat-mxc/pwm.c index c36f263..b259ba9 100644 --- a/arch/arm/plat-mxc/pwm.c +++ b/arch/arm/plat-mxc/pwm.c @@ -38,22 +38,16 @@ -struct pwm_device { - struct list_head node; - struct platform_device *pdev; - - const char *label; +struct mxc_pwm_device { struct clk *clk; - int clk_enabled; void __iomem *mmio_base; - - unsigned int use_count; - unsigned int pwm_id; }; -int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) +static int mxc_pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) { + struct mxc_pwm_device *mxc_pwm = pwm->data; + if (pwm == NULL || period_ns == 0 || duty_ns > period_ns) return -EINVAL; @@ -62,7 +56,7 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) unsigned long period_cycles, duty_cycles, prescale; u32 cr; - c = clk_get_rate(pwm->clk); + c = clk_get_rate(mxc_pwm->clk); c = c * period_ns; do_div(c, 1000000000); period_cycles = c; @@ -74,8 +68,8 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) do_div(c, period_ns); duty_cycles = c; - writel(duty_cycles, pwm->mmio_base + MX3_PWMSAR); - writel(period_cycles, pwm->mmio_base + MX3_PWMPR); + writel(duty_cycles, mxc_pwm->mmio_base + MX3_PWMSAR); + writel(period_cycles, mxc_pwm->mmio_base + MX3_PWMPR); cr = MX3_PWMCR_PRESCALER(prescale) | MX3_PWMCR_EN; @@ -84,7 +78,7 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) else cr |= MX3_PWMCR_CLKSRC_IPG_HIGH; - writel(cr, pwm->mmio_base + MX3_PWMCR); + writel(cr, mxc_pwm->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 @@ -102,110 +96,76 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) * both the prescaler (/1 .. /128) and then by CLKSEL * (/2 .. /16). */ - u32 max = readl(pwm->mmio_base + MX1_PWMP); + u32 max = readl(mxc_pwm->mmio_base + MX1_PWMP); u32 p = max * duty_ns / period_ns; - writel(max - p, pwm->mmio_base + MX1_PWMS); + writel(max - p, mxc_pwm->mmio_base + MX1_PWMS); } else { BUG(); } return 0; } -EXPORT_SYMBOL(pwm_config); -int pwm_enable(struct pwm_device *pwm) +static int mxc_pwm_enable(struct pwm_device *pwm) { + struct mxc_pwm_device *mxc_pwm = pwm->data; int rc = 0; - if (!pwm->clk_enabled) { - rc = clk_enable(pwm->clk); + if (!mxc_pwm->clk_enabled) { + rc = clk_enable(mxc_pwm->clk); if (!rc) - pwm->clk_enabled = 1; + mxc_pwm->clk_enabled = 1; } return rc; } -EXPORT_SYMBOL(pwm_enable); - -void pwm_disable(struct pwm_device *pwm) -{ - writel(0, pwm->mmio_base + MX3_PWMCR); - - if (pwm->clk_enabled) { - clk_disable(pwm->clk); - pwm->clk_enabled = 0; - } -} -EXPORT_SYMBOL(pwm_disable); - -static DEFINE_MUTEX(pwm_lock); -static LIST_HEAD(pwm_list); -struct pwm_device *pwm_request(int pwm_id, const char *label) +static int mxc_pwm_disable(struct pwm_device *pwm) { - struct pwm_device *pwm; - int found = 0; + struct mxc_pwm_device *mxc_pwm = pwm->data; - mutex_lock(&pwm_lock); + writel(0, mxc_pwm->mmio_base + MX3_PWMCR); - list_for_each_entry(pwm, &pwm_list, node) { - if (pwm->pwm_id == pwm_id) { - found = 1; - break; - } + if (mxc_pwm->clk_enabled) { + clk_disable(mxc_pwm->clk); + mxc_pwm->clk_enabled = 0; } - - if (found) { - if (pwm->use_count == 0) { - pwm->use_count++; - pwm->label = label; - } else - pwm = ERR_PTR(-EBUSY); - } else - pwm = ERR_PTR(-ENOENT); - - mutex_unlock(&pwm_lock); - return pwm; -} -EXPORT_SYMBOL(pwm_request); - -void pwm_free(struct pwm_device *pwm) -{ - mutex_lock(&pwm_lock); - - if (pwm->use_count) { - pwm->use_count--; - pwm->label = NULL; - } else - pr_warning("PWM device already freed\n"); - - mutex_unlock(&pwm_lock); + return 0; } -EXPORT_SYMBOL(pwm_free); static int __devinit mxc_pwm_probe(struct platform_device *pdev) { + struct mxc_pwm_device *mxc_pwm; struct pwm_device *pwm; + struct pwm_ops *pops; struct resource *r; int ret = 0; + mxc_pwm = kzalloc(sizeof(struct mxc_pwm_device), GFP_KERNEL); + if (mxc_pwm == NULL) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL); if (pwm == NULL) { dev_err(&pdev->dev, "failed to allocate memory\n"); - return -ENOMEM; + ret = -ENOMEM; + goto err_free1; + } + pops = kzalloc(sizeof(struct pwm_ops), GFP_KERNEL); + if (pops == NULL) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + ret = -ENOMEM; + goto err_free2; } - pwm->clk = clk_get(&pdev->dev, "pwm"); + mxc_pwm->clk = clk_get(&pdev->dev, "pwm"); - if (IS_ERR(pwm->clk)) { - ret = PTR_ERR(pwm->clk); - goto err_free; + if (IS_ERR(mxc_pwm->clk)) { + ret = PTR_ERR(mxc_pwm->clk); + goto err_free3; } - pwm->clk_enabled = 0; - - pwm->use_count = 0; - pwm->pwm_id = pdev->id; - pwm->pdev = pdev; + mxc_pwm->clk_enabled = 0; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (r == NULL) { @@ -221,16 +181,27 @@ static int __devinit mxc_pwm_probe(struct platform_device *pdev) goto err_free_clk; } - pwm->mmio_base = ioremap(r->start, r->end - r->start + 1); - if (pwm->mmio_base == NULL) { + mxc_pwm->mmio_base = ioremap(r->start, r->end - r->start + 1); + if (mxc_pwm->mmio_base == NULL) { dev_err(&pdev->dev, "failed to ioremap() registers\n"); ret = -ENODEV; goto err_free_mem; } - mutex_lock(&pwm_lock); - list_add_tail(&pwm->node, &pwm_list); - mutex_unlock(&pwm_lock); + pops->pwm_config = mxc_pwm_config; + pops->pwm_enable = mxc_pwm_enable; + pops->pwm_disable = mxc_pwm_disable; + pops->name = pdev->name; + + pwm->pwm_id = pdev->id; + pwm->dev = &pdev->dev; + pwm->pops = pops; + pwm->data = mxc_pwm; + ret = pwm_device_register(pwm); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register pwm device\n"); + goto err_free_mem; + } platform_set_drvdata(pdev, pwm); return 0; @@ -238,33 +209,38 @@ static int __devinit mxc_pwm_probe(struct platform_device *pdev) err_free_mem: release_mem_region(r->start, r->end - r->start + 1); err_free_clk: - clk_put(pwm->clk); -err_free: + clk_put(mxc_pwm->clk); +err_free3: + kfree(pops); +err_free2: kfree(pwm); +err_free1: + kfree(mxc_pwm); return ret; } static int __devexit mxc_pwm_remove(struct platform_device *pdev) { struct pwm_device *pwm; + struct mxc_pwm_device *mxc_pwm; struct resource *r; pwm = platform_get_drvdata(pdev); if (pwm == NULL) return -ENODEV; + mxc_pwm = pwm->data; - mutex_lock(&pwm_lock); - list_del(&pwm->node); - mutex_unlock(&pwm_lock); - - iounmap(pwm->mmio_base); + pwm_device_unregister(pwm); + iounmap(mxc_pwm->mmio_base); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(r->start, r->end - r->start + 1); - clk_put(pwm->clk); + clk_put(mxc_pwm->clk); + kfree(pwm->pops); kfree(pwm); + kfree(mxc_pwm); return 0; } diff --git a/arch/arm/plat-pxa/pwm.c b/arch/arm/plat-pxa/pwm.c index ef32686..1de902a 100644 --- a/arch/arm/plat-pxa/pwm.c +++ b/arch/arm/plat-pxa/pwm.c @@ -43,33 +43,27 @@ MODULE_DEVICE_TABLE(platform, pwm_id_table); #define PWMCR_SD (1 << 6) #define PWMDCR_FD (1 << 10) -struct pwm_device { - struct list_head node; - struct pwm_device *secondary; - struct platform_device *pdev; - - const char *label; +struct pxa_pwm_device { + struct pxa_pwm_device *sec; struct clk *clk; int clk_enabled; void __iomem *mmio_base; - - unsigned int use_count; - unsigned int pwm_id; }; /* * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE */ -int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) +int pxa_pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) { unsigned long long c; unsigned long period_cycles, prescale, pv, dc; + struct pxa_pwm_device *pxa_pwm = pwm->data; if (pwm == NULL || period_ns == 0 || duty_ns > period_ns) return -EINVAL; - c = clk_get_rate(pwm->clk); + c = clk_get_rate(pxa_pwm->clk); c = c * period_ns; do_div(c, 1000000000); period_cycles = c; @@ -90,94 +84,45 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) /* NOTE: the clock to PWM has to be enabled first * before writing to the registers */ - clk_enable(pwm->clk); - __raw_writel(prescale, pwm->mmio_base + PWMCR); - __raw_writel(dc, pwm->mmio_base + PWMDCR); - __raw_writel(pv, pwm->mmio_base + PWMPCR); - clk_disable(pwm->clk); + clk_enable(pxa_pwm->clk); + __raw_writel(prescale, pxa_pwm->mmio_base + PWMCR); + __raw_writel(dc, pxa_pwm->mmio_base + PWMDCR); + __raw_writel(pv, pxa_pwm->mmio_base + PWMPCR); + clk_disable(pxa_pwm->clk); return 0; } -EXPORT_SYMBOL(pwm_config); -int pwm_enable(struct pwm_device *pwm) +int pxa_pwm_enable(struct pwm_device *pwm) { + struct pxa_pwm_device *pxa_pwm = pwm->data; int rc = 0; - if (!pwm->clk_enabled) { - rc = clk_enable(pwm->clk); + if (!pxa_pwm->clk_enabled) { + rc = clk_enable(pxa_pwm->clk); if (!rc) - pwm->clk_enabled = 1; + pxa_pwm->clk_enabled = 1; } return rc; } -EXPORT_SYMBOL(pwm_enable); -void pwm_disable(struct pwm_device *pwm) +int pxa_pwm_disable(struct pwm_device *pwm) { - if (pwm->clk_enabled) { - clk_disable(pwm->clk); - pwm->clk_enabled = 0; - } -} -EXPORT_SYMBOL(pwm_disable); - -static DEFINE_MUTEX(pwm_lock); -static LIST_HEAD(pwm_list); + struct pxa_pwm_device *pxa_pwm = pwm->data; -struct pwm_device *pwm_request(int pwm_id, const char *label) -{ - struct pwm_device *pwm; - int found = 0; - - mutex_lock(&pwm_lock); - - list_for_each_entry(pwm, &pwm_list, node) { - if (pwm->pwm_id == pwm_id) { - found = 1; - break; - } + if (pxa_pwm->clk_enabled) { + clk_disable(pxa_pwm->clk); + pxa_pwm->clk_enabled = 0; } - - if (found) { - if (pwm->use_count == 0) { - pwm->use_count++; - pwm->label = label; - } else - pwm = ERR_PTR(-EBUSY); - } else - pwm = ERR_PTR(-ENOENT); - - mutex_unlock(&pwm_lock); - return pwm; -} -EXPORT_SYMBOL(pwm_request); - -void pwm_free(struct pwm_device *pwm) -{ - mutex_lock(&pwm_lock); - - if (pwm->use_count) { - pwm->use_count--; - pwm->label = NULL; - } else - pr_warning("PWM device already freed\n"); - - mutex_unlock(&pwm_lock); -} -EXPORT_SYMBOL(pwm_free); - -static inline void __add_pwm(struct pwm_device *pwm) -{ - mutex_lock(&pwm_lock); - list_add_tail(&pwm->node, &pwm_list); - mutex_unlock(&pwm_lock); + return 0; } static int __devinit pwm_probe(struct platform_device *pdev) { const struct platform_device_id *id = platform_get_device_id(pdev); + struct pxa_pwm_device *pxa_pwm, *pxa_pwm_sec; struct pwm_device *pwm, *secondary = NULL; + struct pwm_ops *pops; struct resource *r; int ret = 0; @@ -186,17 +131,26 @@ static int __devinit pwm_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to allocate memory\n"); return -ENOMEM; } + pops = kzalloc(sizeof(struct pwm_ops), GFP_KERNEL); + if (pops == NULL) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + kfree(pwm); + return -ENOMEM; + } + pxa_pwm = kzalloc(sizeof(struct pxa_pwm_device), GFP_KERNEL); + if (pxa_pwm == NULL) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + kfree(pops); + kfree(pwm); + return -ENOMEM; + } - pwm->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(pwm->clk)) { - ret = PTR_ERR(pwm->clk); + pxa_pwm->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(pxa_pwm->clk)) { + ret = PTR_ERR(pxa_pwm->clk); goto err_free; } - pwm->clk_enabled = 0; - - pwm->use_count = 0; - pwm->pwm_id = PWM_ID_BASE(id->driver_data) + pdev->id; - pwm->pdev = pdev; + pxa_pwm->clk_enabled = 0; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (r == NULL) { @@ -212,69 +166,105 @@ static int __devinit pwm_probe(struct platform_device *pdev) goto err_free_clk; } - pwm->mmio_base = ioremap(r->start, resource_size(r)); - if (pwm->mmio_base == NULL) { + pxa_pwm->mmio_base = ioremap(r->start, resource_size(r)); + if (pxa_pwm->mmio_base == NULL) { dev_err(&pdev->dev, "failed to ioremap() registers\n"); ret = -ENODEV; goto err_free_mem; } + pops->pwm_config = pxa_pwm_config; + pops->pwm_enable = pxa_pwm_enable; + pops->pwm_disable = pxa_pwm_disable; + pops->name = pdev->name; + + pwm->pwm_id = PWM_ID_BASE(id->driver_data) + pdev->id; + pwm->dev = &pdev->dev; + pwm->pops = pops; + pwm->data = pxa_pwm; + + ret = pwm_device_register(pwm); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register pwm device\n"); + goto err_free_mem; + } + if (id->driver_data & HAS_SECONDARY_PWM) { secondary = kzalloc(sizeof(struct pwm_device), GFP_KERNEL); if (secondary == NULL) { ret = -ENOMEM; - goto err_free_mem; + goto err_pwm; + } + pxa_pwm_sec = kzalloc(sizeof(struct pxa_pwm_device), + GFP_KERNEL); + if (pxa_pwm_sec == NULL) { + ret = -ENOMEM; + goto err_free_mem2; } *secondary = *pwm; - pwm->secondary = secondary; + *pxa_pwm_sec = *pxa_pwm; + pxa_pwm->sec = pxa_pwm_sec; /* registers for the second PWM has offset of 0x10 */ - secondary->mmio_base = pwm->mmio_base + 0x10; + pxa_pwm_sec->mmio_base = pxa_pwm->mmio_base + 0x10; secondary->pwm_id = pdev->id + 2; - } + secondary->data = pxa_pwm_sec; - __add_pwm(pwm); - if (secondary) - __add_pwm(secondary); + ret = pwm_device_register(secondary); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register pwm device\n"); + goto err_free_mem3; + } + } platform_set_drvdata(pdev, pwm); return 0; - +err_free_mem3: + kfree(pxa_pwm_sec); +err_free_mem2: + kfree(secondary); +err_pwm: + pwm_device_unregister(pwm); err_free_mem: release_mem_region(r->start, resource_size(r)); err_free_clk: - clk_put(pwm->clk); + clk_put(pxa_pwm->clk); err_free: + kfree(pxa_pwm); + kfree(pops); kfree(pwm); return ret; } static int __devexit pwm_remove(struct platform_device *pdev) { - struct pwm_device *pwm; + struct pwm_device *pwm, *secondary; + struct pxa_pwm_device *pxa_pwm, *pxa_pwm_sec; struct resource *r; pwm = platform_get_drvdata(pdev); if (pwm == NULL) return -ENODEV; - - mutex_lock(&pwm_lock); - - if (pwm->secondary) { - list_del(&pwm->secondary->node); - kfree(pwm->secondary); + pxa_pwm = pwm->data; + secondary = pwm_request((pdev->id + 2), pdev->name); + pxa_pwm_sec = secondary->data; + + pwm_device_unregister(pwm); + iounmap(pxa_pwm->mmio_base); + if (secondary) { + pwm_device_unregister(secondary); + iounmap(pxa_pwm->mmio_base); + kfree(pxa_pwm_sec); + kfree(secondary); } - list_del(&pwm->node); - mutex_unlock(&pwm_lock); - - iounmap(pwm->mmio_base); - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(r->start, resource_size(r)); - clk_put(pwm->clk); + clk_put(pxa_pwm->clk); + kfree(pxa_pwm); + kfree(pwm->pops); kfree(pwm); return 0; } diff --git a/arch/arm/plat-samsung/pwm.c b/arch/arm/plat-samsung/pwm.c index 2eeb49f..63fba01 100644 --- a/arch/arm/plat-samsung/pwm.c +++ b/arch/arm/plat-samsung/pwm.c @@ -26,25 +26,19 @@ #include <plat/devs.h> #include <plat/regs-timer.h> -struct pwm_device { - struct list_head list; +struct s3c_pwm_device { struct platform_device *pdev; struct clk *clk_div; struct clk *clk; - const char *label; unsigned int period_ns; unsigned int duty_ns; unsigned char tcon_base; unsigned char running; - unsigned char use_count; - unsigned char pwm_id; }; -#define pwm_dbg(_pwm, msg...) dev_dbg(&(_pwm)->pdev->dev, msg) - static struct clk *clk_scaler[2]; /* Standard setup for a timer block. */ @@ -78,108 +72,61 @@ struct platform_device s3c_device_timer[] = { [4] = { DEFINE_S3C_TIMER(4, IRQ_TIMER4) }, }; -static inline int pwm_is_tdiv(struct pwm_device *pwm) +static inline int pwm_is_tdiv(struct s3c_pwm_device *s3c_pwm) { - return clk_get_parent(pwm->clk) == pwm->clk_div; + return clk_get_parent(s3c_pwm->clk) == s3c_pwm->clk_div; } -static DEFINE_MUTEX(pwm_lock); -static LIST_HEAD(pwm_list); +#define pwm_tcon_start(s3c_pwm) (1 << (s3c_pwm->tcon_base + 0)) +#define pwm_tcon_invert(s3c_pwm) (1 << (s3c_pwm->tcon_base + 2)) +#define pwm_tcon_autoreload(s3c_pwm) (1 << (s3c_pwm->tcon_base + 3)) +#define pwm_tcon_manulupdate(s3c_pwm) (1 << (s3c_pwm->tcon_base + 1)) -struct pwm_device *pwm_request(int pwm_id, const char *label) -{ - struct pwm_device *pwm; - int found = 0; - - mutex_lock(&pwm_lock); - - list_for_each_entry(pwm, &pwm_list, list) { - if (pwm->pwm_id == pwm_id) { - found = 1; - break; - } - } - - if (found) { - if (pwm->use_count == 0) { - pwm->use_count = 1; - pwm->label = label; - } else - pwm = ERR_PTR(-EBUSY); - } else - pwm = ERR_PTR(-ENOENT); - - mutex_unlock(&pwm_lock); - return pwm; -} - -EXPORT_SYMBOL(pwm_request); - - -void pwm_free(struct pwm_device *pwm) -{ - mutex_lock(&pwm_lock); - - if (pwm->use_count) { - pwm->use_count--; - pwm->label = NULL; - } else - printk(KERN_ERR "PWM%d device already freed\n", pwm->pwm_id); - - mutex_unlock(&pwm_lock); -} - -EXPORT_SYMBOL(pwm_free); - -#define pwm_tcon_start(pwm) (1 << (pwm->tcon_base + 0)) -#define pwm_tcon_invert(pwm) (1 << (pwm->tcon_base + 2)) -#define pwm_tcon_autoreload(pwm) (1 << (pwm->tcon_base + 3)) -#define pwm_tcon_manulupdate(pwm) (1 << (pwm->tcon_base + 1)) - -int pwm_enable(struct pwm_device *pwm) +int s3c_pwm_enable(struct pwm_device *pwm) { unsigned long flags; unsigned long tcon; + struct s3c_pwm_device *s3c_pwm = pwm->data; local_irq_save(flags); tcon = __raw_readl(S3C2410_TCON); - tcon |= pwm_tcon_start(pwm); + tcon |= pwm_tcon_start(s3c_pwm); __raw_writel(tcon, S3C2410_TCON); local_irq_restore(flags); - pwm->running = 1; + s3c_pwm->running = 1; return 0; } -EXPORT_SYMBOL(pwm_enable); - -void pwm_disable(struct pwm_device *pwm) +int s3c_pwm_disable(struct pwm_device *pwm) { unsigned long flags; unsigned long tcon; + struct s3c_pwm_device *s3c_pwm = pwm->data; local_irq_save(flags); tcon = __raw_readl(S3C2410_TCON); - tcon &= ~pwm_tcon_start(pwm); + tcon &= ~pwm_tcon_start(s3c_pwm); __raw_writel(tcon, S3C2410_TCON); local_irq_restore(flags); - pwm->running = 0; + s3c_pwm->running = 0; + return 0; } -EXPORT_SYMBOL(pwm_disable); - -static unsigned long pwm_calc_tin(struct pwm_device *pwm, unsigned long freq) +static unsigned long pwm_calc_tin(struct pwm_device *pwm, + unsigned long freq) { unsigned long tin_parent_rate; unsigned int div; + struct s3c_pwm_device *s3c_pwm = pwm->data; - tin_parent_rate = clk_get_rate(clk_get_parent(pwm->clk_div)); - pwm_dbg(pwm, "tin parent at %lu\n", tin_parent_rate); + tin_parent_rate = clk_get_rate(clk_get_parent(s3c_pwm->clk_div)); + dev_dbg(pwm->dev, "tin parent at %lu\n", tin_parent_rate); for (div = 2; div <= 16; div *= 2) { if ((tin_parent_rate / (div << 16)) < freq) @@ -191,7 +138,7 @@ static unsigned long pwm_calc_tin(struct pwm_device *pwm, unsigned long freq) #define NS_IN_HZ (1000000000UL) -int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) +int s3c_pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) { unsigned long tin_rate; unsigned long tin_ns; @@ -200,6 +147,7 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) unsigned long tcon; unsigned long tcnt; long tcmp; + struct s3c_pwm_device *s3c_pwm = pwm->data; /* We currently avoid using 64bit arithmetic by using the * fact that anything faster than 1Hz is easily representable @@ -211,8 +159,8 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) if (duty_ns > period_ns) return -EINVAL; - if (period_ns == pwm->period_ns && - duty_ns == pwm->duty_ns) + if (period_ns == s3c_pwm->period_ns && + duty_ns == s3c_pwm->duty_ns) return 0; /* The TCMP and TCNT can be read without a lock, they're not @@ -223,26 +171,26 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) period = NS_IN_HZ / period_ns; - pwm_dbg(pwm, "duty_ns=%d, period_ns=%d (%lu)\n", + dev_dbg(pwm->dev, "duty_ns=%d, period_ns=%d (%lu)\n", duty_ns, period_ns, period); /* Check to see if we are changing the clock rate of the PWM */ - if (pwm->period_ns != period_ns) { - if (pwm_is_tdiv(pwm)) { + if (s3c_pwm->period_ns != period_ns) { + if (pwm_is_tdiv(s3c_pwm)) { tin_rate = pwm_calc_tin(pwm, period); - clk_set_rate(pwm->clk_div, tin_rate); + clk_set_rate(s3c_pwm->clk_div, tin_rate); } else - tin_rate = clk_get_rate(pwm->clk); + tin_rate = clk_get_rate(s3c_pwm->clk); - pwm->period_ns = period_ns; + s3c_pwm->period_ns = period_ns; - pwm_dbg(pwm, "tin_rate=%lu\n", tin_rate); + dev_dbg(pwm->dev, "tin_rate=%lu\n", tin_rate); tin_ns = NS_IN_HZ / tin_rate; tcnt = period_ns / tin_ns; } else - tin_ns = NS_IN_HZ / clk_get_rate(pwm->clk); + tin_ns = NS_IN_HZ / clk_get_rate(s3c_pwm->clk); /* Note, counters count down */ @@ -253,7 +201,7 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) if (tcmp == tcnt) tcmp--; - pwm_dbg(pwm, "tin_ns=%lu, tcmp=%ld/%lu\n", tin_ns, tcmp, tcnt); + dev_dbg(pwm->dev, "tin_ns=%lu, tcmp=%ld/%lu\n", tin_ns, tcmp, tcnt); if (tcmp < 0) tcmp = 0; @@ -266,11 +214,11 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) __raw_writel(tcnt, S3C2410_TCNTB(pwm->pwm_id)); tcon = __raw_readl(S3C2410_TCON); - tcon |= pwm_tcon_manulupdate(pwm); - tcon |= pwm_tcon_autoreload(pwm); + tcon |= pwm_tcon_manulupdate(s3c_pwm); + tcon |= pwm_tcon_autoreload(s3c_pwm); __raw_writel(tcon, S3C2410_TCON); - tcon &= ~pwm_tcon_manulupdate(pwm); + tcon &= ~pwm_tcon_manulupdate(s3c_pwm); __raw_writel(tcon, S3C2410_TCON); local_irq_restore(flags); @@ -278,103 +226,122 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) return 0; } -EXPORT_SYMBOL(pwm_config); - -static int pwm_register(struct pwm_device *pwm) -{ - pwm->duty_ns = -1; - pwm->period_ns = -1; - - mutex_lock(&pwm_lock); - list_add_tail(&pwm->list, &pwm_list); - mutex_unlock(&pwm_lock); - - return 0; -} - static int s3c_pwm_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct s3c_pwm_device *s3c_pwm; struct pwm_device *pwm; + struct pwm_ops *pops; unsigned long flags; unsigned long tcon; unsigned int id = pdev->id; - int ret; + int ret = 0; if (id == 4) { dev_err(dev, "TIMER4 is currently not supported\n"); return -ENXIO; } + s3c_pwm = kzalloc(sizeof(struct s3c_pwm_device), GFP_KERNEL); + if (s3c_pwm == NULL) { + dev_err(dev, "failed to allocate pwm_device\n"); + return -ENOMEM; + } + s3c_pwm->pdev = pdev; pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL); if (pwm == NULL) { dev_err(dev, "failed to allocate pwm_device\n"); - return -ENOMEM; + goto err_alloc; + ret = -ENOMEM; + } + pops = kzalloc(sizeof(struct pwm_ops), GFP_KERNEL); + if (pops == NULL) { + dev_err(dev, "failed to allocate memory\n"); + goto err_alloc1; + ret = -ENOMEM; } - - pwm->pdev = pdev; - pwm->pwm_id = id; /* calculate base of control bits in TCON */ - pwm->tcon_base = id == 0 ? 0 : (id * 4) + 4; + s3c_pwm->tcon_base = id == 0 ? 0 : (id * 4) + 4; - pwm->clk = clk_get(dev, "pwm-tin"); - if (IS_ERR(pwm->clk)) { + s3c_pwm->clk = clk_get(dev, "pwm-tin"); + if (IS_ERR(s3c_pwm->clk)) { dev_err(dev, "failed to get pwm tin clk\n"); - ret = PTR_ERR(pwm->clk); - goto err_alloc; + ret = PTR_ERR(s3c_pwm->clk); + goto err_alloc2; } - pwm->clk_div = clk_get(dev, "pwm-tdiv"); - if (IS_ERR(pwm->clk_div)) { + s3c_pwm->clk_div = clk_get(dev, "pwm-tdiv"); + if (IS_ERR(s3c_pwm->clk_div)) { dev_err(dev, "failed to get pwm tdiv clk\n"); - ret = PTR_ERR(pwm->clk_div); + ret = PTR_ERR(s3c_pwm->clk_div); goto err_clk_tin; } local_irq_save(flags); tcon = __raw_readl(S3C2410_TCON); - tcon |= pwm_tcon_invert(pwm); + tcon |= pwm_tcon_invert(s3c_pwm); __raw_writel(tcon, S3C2410_TCON); local_irq_restore(flags); + pops->pwm_config = s3c_pwm_config; + pops->pwm_enable = s3c_pwm_enable; + pops->pwm_disable = s3c_pwm_disable; + pops->name = pdev->name; + + pwm->dev = dev; + pwm->pwm_id = id; + pwm->pops = pops; + pwm->data = s3c_pwm; - ret = pwm_register(pwm); + s3c_pwm->duty_ns = -1; + s3c_pwm->period_ns = -1; + ret = pwm_device_register(pwm); if (ret) { dev_err(dev, "failed to register pwm\n"); goto err_clk_tdiv; } - pwm_dbg(pwm, "config bits %02x\n", - (__raw_readl(S3C2410_TCON) >> pwm->tcon_base) & 0x0f); + dev_dbg(dev, "config bits %02x\n", + (__raw_readl(S3C2410_TCON) >> s3c_pwm->tcon_base) & 0x0f); dev_info(dev, "tin at %lu, tdiv at %lu, tin=%sclk, base %d\n", - clk_get_rate(pwm->clk), - clk_get_rate(pwm->clk_div), - pwm_is_tdiv(pwm) ? "div" : "ext", pwm->tcon_base); + clk_get_rate(s3c_pwm->clk), + clk_get_rate(s3c_pwm->clk_div), + pwm_is_tdiv(s3c_pwm) ? "div" : "ext", s3c_pwm->tcon_base); platform_set_drvdata(pdev, pwm); return 0; - err_clk_tdiv: - clk_put(pwm->clk_div); +err_clk_tdiv: + clk_put(s3c_pwm->clk_div); - err_clk_tin: - clk_put(pwm->clk); +err_clk_tin: + clk_put(s3c_pwm->clk); - err_alloc: +err_alloc2: + kfree(pops); + +err_alloc1: kfree(pwm); + +err_alloc: + kfree(s3c_pwm); return ret; } static int __devexit s3c_pwm_remove(struct platform_device *pdev) { struct pwm_device *pwm = platform_get_drvdata(pdev); + struct s3c_pwm_device *s3c_pwm = pwm->data; - clk_put(pwm->clk_div); - clk_put(pwm->clk); + pwm_device_unregister(pwm); + clk_put(s3c_pwm->clk_div); + clk_put(s3c_pwm->clk); + kfree(s3c_pwm); + kfree(pwm->pops); kfree(pwm); return 0; @@ -384,13 +351,14 @@ static int __devexit s3c_pwm_remove(struct platform_device *pdev) static int s3c_pwm_suspend(struct platform_device *pdev, pm_message_t state) { struct pwm_device *pwm = platform_get_drvdata(pdev); + struct s3c_pwm_device *s3c_pwm = pwm->data; /* No one preserve these values during suspend so reset them * Otherwise driver leaves PWM unconfigured if same values * passed to pwm_config */ - pwm->period_ns = 0; - pwm->duty_ns = 0; + s3c_pwm->period_ns = 0; + s3c_pwm->duty_ns = 0; return 0; } @@ -398,11 +366,12 @@ static int s3c_pwm_suspend(struct platform_device *pdev, pm_message_t state) static int s3c_pwm_resume(struct platform_device *pdev) { struct pwm_device *pwm = platform_get_drvdata(pdev); + struct s3c_pwm_device *s3c_pwm = pwm->data; unsigned long tcon; /* Restore invertion */ tcon = __raw_readl(S3C2410_TCON); - tcon |= pwm_tcon_invert(pwm); + tcon |= pwm_tcon_invert(s3c_pwm); __raw_writel(tcon, S3C2410_TCON); return 0; diff --git a/arch/mips/jz4740/pwm.c b/arch/mips/jz4740/pwm.c index a26a6fa..9f46767 100644 --- a/arch/mips/jz4740/pwm.c +++ b/arch/mips/jz4740/pwm.c @@ -152,7 +152,7 @@ int pwm_enable(struct pwm_device *pwm) return 0; } -void pwm_disable(struct pwm_device *pwm) +int pwm_disable(struct pwm_device *pwm) { uint32_t ctrl = jz4740_timer_get_ctrl(pwm->id); diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index b0f2c00..6a6ea41 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -129,6 +129,12 @@ #define twl_has_pwrbutton() false #endif +#if defined CONFIG_TWL6030_PWM +#define twl_has_pwm() true +#else +#define twl_has_pwm() false +#endif + #define SUB_CHIP_ID0 0 #define SUB_CHIP_ID1 1 #define SUB_CHIP_ID2 2 @@ -825,6 +831,13 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) if (IS_ERR(child)) return PTR_ERR(child); } + if (twl_has_pwm()) { + child = add_child(SUB_CHIP_ID2, "twl6030_pwm", + NULL, 0, + false, 0, 0); + if (IS_ERR(child)) + return PTR_ERR(child); + } return 0; } diff --git a/drivers/mfd/twl6030-pwm.c b/drivers/mfd/twl6030-pwm.c index 5d25bdc..b78324b 100644 --- a/drivers/mfd/twl6030-pwm.c +++ b/drivers/mfd/twl6030-pwm.c @@ -20,8 +20,10 @@ #include <linux/module.h> #include <linux/platform_device.h> -#include <linux/i2c/twl.h> #include <linux/slab.h> +#include <linux/pwm.h> +#include <linux/err.h> +#include <linux/i2c/twl.h> #define LED_PWM_CTRL1 0xF4 #define LED_PWM_CTRL2 0xF5 @@ -45,15 +47,10 @@ #define PWM_CTRL2_MODE_MASK 0x3 -struct pwm_device { - const char *label; - unsigned int pwm_id; -}; - -int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) +int twl6030_pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) { u8 duty_cycle; - int ret; + int ret = 0; if (pwm == NULL || period_ns == 0 || duty_ns > period_ns) return -EINVAL; @@ -69,12 +66,11 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) } return 0; } -EXPORT_SYMBOL(pwm_config); -int pwm_enable(struct pwm_device *pwm) +int twl6030_pwm_enable(struct pwm_device *pwm) { u8 val; - int ret; + int ret = 0; ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2); if (ret < 0) { @@ -95,18 +91,17 @@ int pwm_enable(struct pwm_device *pwm) twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2); return 0; } -EXPORT_SYMBOL(pwm_enable); -void pwm_disable(struct pwm_device *pwm) +int twl6030_pwm_disable(struct pwm_device *pwm) { u8 val; - int ret; + int ret = 0; ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2); if (ret < 0) { pr_err("%s: Failed to disable PWM, Error %d\n", pwm->label, ret); - return; + return ret; } val &= ~PWM_CTRL2_MODE_MASK; @@ -116,48 +111,86 @@ void pwm_disable(struct pwm_device *pwm) if (ret < 0) { pr_err("%s: Failed to disable PWM, Error %d\n", pwm->label, ret); - return; } - return; + return ret; } -EXPORT_SYMBOL(pwm_disable); -struct pwm_device *pwm_request(int pwm_id, const char *label) +static int __devinit twl6030_pwm_probe(struct platform_device *pdev) { - u8 val; - int ret; struct pwm_device *pwm; + struct pwm_ops *pops; + int ret; + u8 val; pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL); if (pwm == NULL) { - pr_err("%s: failed to allocate memory\n", label); - return NULL; + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + pops = kzalloc(sizeof(struct pwm_ops), GFP_KERNEL); + if (pops == NULL) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + kfree(pwm); + return -ENOMEM; } - pwm->label = label; - pwm->pwm_id = pwm_id; - + pops->pwm_config = twl6030_pwm_config; + pops->pwm_enable = twl6030_pwm_enable; + pops->pwm_disable = twl6030_pwm_disable; + pops->name = &pdev->name; + pwm->dev = &pdev->dev; + pwm->pwm_id = pdev->id; + pwm->pops = pops; + ret = pwm_device_register(pwm); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register pwm device\n"); + kfree(pwm); + kfree(pops); + return ret; + } + platform_set_drvdata(pdev, pwm); /* Configure PWM */ val = PWM_CTRL2_DIS_PD | PWM_CTRL2_CURR_02 | PWM_CTRL2_SRC_VAC | - PWM_CTRL2_MODE_HW; + PWM_CTRL2_MODE_HW; ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2); - if (ret < 0) { - pr_err("%s: Failed to configure PWM, Error %d\n", - pwm->label, ret); - - kfree(pwm); - return NULL; + dev_err(&pdev->dev, "Failed to configure PWM, Error %d\n", ret); + return ret; } - - return pwm; + dev_dbg(&pdev->dev, "pwm probe successful\n"); + return ret; } -EXPORT_SYMBOL(pwm_request); -void pwm_free(struct pwm_device *pwm) +static int __devexit twl6030_pwm_remove(struct platform_device *pdev) { - pwm_disable(pwm); + struct pwm_device *pwm = platform_get_drvdata(pdev); + + pwm_device_unregister(pwm); + kfree(pwm->pops); kfree(pwm); + dev_dbg(&pdev->dev, "pwm driver removed\n"); + return 0; } -EXPORT_SYMBOL(pwm_free); + +static struct platform_driver twl6030_pwm_driver = { + .driver = { + .name = "twl6030_pwm", + .owner = THIS_MODULE, + }, + .probe = twl6030_pwm_probe, + .remove = __devexit_p(twl6030_pwm_remove), +}; + +static int __init twl6030_pwm_init(void) +{ + return platform_driver_register(&twl6030_pwm_driver); +} + +static void __exit twl6030_pwm_deinit(void) +{ + platform_driver_unregister(&twl6030_pwm_driver); +} + +subsys_initcall(twl6030_pwm_init); +module_exit(twl6030_pwm_deinit); diff --git a/drivers/misc/ab8500-pwm.c b/drivers/misc/ab8500-pwm.c index 54e3d05..d2b23b6 100644 --- a/drivers/misc/ab8500-pwm.c +++ b/drivers/misc/ab8500-pwm.c @@ -23,16 +23,9 @@ #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 ab8500_pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) { int ret = 0; unsigned int higher_val, lower_val; @@ -60,23 +53,21 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) return ret; } -EXPORT_SYMBOL(pwm_config); -int pwm_enable(struct pwm_device *pwm) +int ab8500_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); + 1 << (pwm->pwm_id-1), 1 << (pwm->pwm_id-1)); 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 ab8500_pwm_disable(struct pwm_device *pwm) { int ret; @@ -86,58 +77,56 @@ void pwm_disable(struct pwm_device *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); + return ret; } -EXPORT_SYMBOL(pwm_free); static int __devinit ab8500_pwm_probe(struct platform_device *pdev) { - struct pwm_device *pwm; + int ret = 0; + struct pwm_ops *pops; + struct pwm_device *pwm_dev; /* * 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) { + pops = kzalloc(sizeof(struct pwm_ops), GFP_KERNEL); + if (pops == 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; + pwm_dev = kzalloc(sizeof(struct pwm_device), GFP_KERNEL); + if (pwm_dev == NULL) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + kfree(pops); + return -ENOMEM; + } + pops->pwm_config = ab8500_pwm_config; + pops->pwm_enable = ab8500_pwm_enable; + pops->pwm_disable = ab8500_pwm_disable; + pops->name = "ab8500"; + pwm_dev->dev = &pdev->dev; + pwm_dev->pwm_id = pdev->id; + pwm_dev->pops = pops; + ret = pwm_device_register(pwm_dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register pwm device\n"); + kfree(pwm_dev); + kfree(pops); + return ret; + } + platform_set_drvdata(pdev, pwm_dev); + dev_dbg(&pdev->dev, "pwm probe successful\n"); + return ret; } static int __devexit ab8500_pwm_remove(struct platform_device *pdev) { - struct pwm_device *pwm = platform_get_drvdata(pdev); - list_del(&pwm->node); + struct pwm_device *pwm_dev = platform_get_drvdata(pdev); + + pwm_device_unregister(pwm_dev); dev_dbg(&pdev->dev, "pwm driver removed\n"); - kfree(pwm); + kfree(pwm_dev->pops); + kfree(pwm_dev); return 0; } diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 5d10106..a88640c 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -4,6 +4,7 @@ menuconfig PWM_DEVICES bool "PWM devices" + depends on ARM default y ---help--- Say Y here to get to see options for device drivers from various diff --git a/drivers/pwm/pwm-core.c b/drivers/pwm/pwm-core.c index b84027a..3a0d426 100644 --- a/drivers/pwm/pwm-core.c +++ b/drivers/pwm/pwm-core.c @@ -11,11 +11,6 @@ #include <linux/err.h> #include <linux/pwm.h> -struct pwm_device { - struct pwm_ops *pops; - int pwm_id; -}; - struct pwm_dev_info { struct pwm_device *pwm_dev; struct list_head list; @@ -40,9 +35,9 @@ int pwm_enable(struct pwm_device *pwm) } EXPORT_SYMBOL(pwm_enable); -void pwm_disable(struct pwm_device *pwm) +int pwm_disable(struct pwm_device *pwm) { - pwm->pops->pwm_disable(pwm); + return pwm->pops->pwm_disable(pwm); } EXPORT_SYMBOL(pwm_disable); diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 6e7da1f..4344c0b 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -1,14 +1,29 @@ #ifndef __LINUX_PWM_H #define __LINUX_PWM_H -struct pwm_device; +/* + * TODO: #if defined CONFIG_PWM_CORE has to be removed after mips jz4740 + * pwm driver aligning with pwm-core.c driver. + */ +#if defined CONFIG_PWM_CORE +struct pwm_device { + struct pwm_ops *pops; + struct device *dev; + struct list_head node; + const char *label; + unsigned int pwm_id; + void *data; +}; struct pwm_ops { int (*pwm_config)(struct pwm_device *pwm, int duty_ns, int period_ns); int (*pwm_enable)(struct pwm_device *pwm); int (*pwm_disable)(struct pwm_device *pwm); - char *name; + const char *name; }; +#else +struct pwm_device; +#endif /* * pwm_request - request a PWM device @@ -33,7 +48,7 @@ int pwm_enable(struct pwm_device *pwm); /* * pwm_disable - stop a PWM output toggling */ -void pwm_disable(struct pwm_device *pwm); +int pwm_disable(struct pwm_device *pwm); int pwm_device_register(struct pwm_device *pwm_dev); int pwm_device_unregister(struct pwm_device *pwm_dev); -- 1.7.2.dirty ^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 5/7] platform: Update the pwm based led and backlight platform data 2010-09-28 10:35 [PATCH 0/7] PWM core driver for pwm based led and backlight driver Arun Murthy ` (3 preceding siblings ...) 2010-09-28 10:35 ` [PATCH 4/7] pwm: Align existing pwm drivers with pwm-core driver Arun Murthy @ 2010-09-28 10:35 ` Arun Murthy 2010-09-28 10:35 ` [PATCH 6/7] pwm: move existing pwm driver to drivers/pwm Arun Murthy 2010-09-28 10:35 ` [PATCH 7/7] pwm: Modify backlight and led Kconfig aligning to pwm core Arun Murthy 6 siblings, 0 replies; 16+ messages in thread From: Arun Murthy @ 2010-09-28 10:35 UTC (permalink / raw) To: linux-arm-kernel mxc-pwm: Update the platform data with pwm name for backlight s3c24xx-pwm: update platform data for backlight with pwm name Signed-off-by: Arun Murthy <arun.murthy@stericsson.com> Acked-by: Linus Walleij <linus.walleij@stericsson.com> --- arch/arm/mach-pxa/cm-x300.c | 1 + arch/arm/mach-pxa/colibri-pxa270-income.c | 1 + arch/arm/mach-pxa/ezx.c | 1 + arch/arm/mach-pxa/hx4700.c | 1 + arch/arm/mach-pxa/lpd270.c | 1 + arch/arm/mach-pxa/magician.c | 1 + arch/arm/mach-pxa/mainstone.c | 1 + arch/arm/mach-pxa/mioa701.c | 1 + arch/arm/mach-pxa/palm27x.c | 1 + arch/arm/mach-pxa/palmtc.c | 1 + arch/arm/mach-pxa/palmte2.c | 1 + arch/arm/mach-pxa/pcm990-baseboard.c | 1 + arch/arm/mach-pxa/raumfeld.c | 1 + arch/arm/mach-pxa/tavorevb.c | 2 ++ arch/arm/mach-pxa/viper.c | 1 + arch/arm/mach-pxa/z2.c | 2 ++ arch/arm/mach-pxa/zylonite.c | 1 + arch/arm/mach-s3c2410/mach-h1940.c | 1 + arch/arm/mach-s3c2440/mach-rx1950.c | 1 + arch/arm/mach-s3c64xx/mach-hmt.c | 1 + arch/arm/mach-s3c64xx/mach-smartq.c | 1 + 21 files changed, 23 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-pxa/cm-x300.c b/arch/arm/mach-pxa/cm-x300.c index c70e6c2..ddf763b 100644 --- a/arch/arm/mach-pxa/cm-x300.c +++ b/arch/arm/mach-pxa/cm-x300.c @@ -301,6 +301,7 @@ static inline void cm_x300_init_lcd(void) {} #if defined(CONFIG_BACKLIGHT_PWM) || defined(CONFIG_BACKLIGHT_PWM_MODULE) static struct platform_pwm_backlight_data cm_x300_backlight_data = { .pwm_id = 2, + .name = "pxa25x-pwm", .max_brightness = 100, .dft_brightness = 100, .pwm_period_ns = 10000, diff --git a/arch/arm/mach-pxa/colibri-pxa270-income.c b/arch/arm/mach-pxa/colibri-pxa270-income.c index 37f0f3e..d5b5874 100644 --- a/arch/arm/mach-pxa/colibri-pxa270-income.c +++ b/arch/arm/mach-pxa/colibri-pxa270-income.c @@ -234,6 +234,7 @@ static inline void income_lcd_init(void) {} #if defined(CONFIG_BACKLIGHT_PWM) || defined(CONFIG_BACKLIGHT_PWM__MODULE) static struct platform_pwm_backlight_data income_backlight_data = { .pwm_id = 0, + .name = "pxa25x-pwm", .max_brightness = 0x3ff, .dft_brightness = 0x1ff, .pwm_period_ns = 1000000, diff --git a/arch/arm/mach-pxa/ezx.c b/arch/arm/mach-pxa/ezx.c index 626c82b..747f217 100644 --- a/arch/arm/mach-pxa/ezx.c +++ b/arch/arm/mach-pxa/ezx.c @@ -49,6 +49,7 @@ static struct platform_pwm_backlight_data ezx_backlight_data = { .pwm_id = 0, + .name = "pxa25x-pwm", .max_brightness = 1023, .dft_brightness = 1023, .pwm_period_ns = 78770, diff --git a/arch/arm/mach-pxa/hx4700.c b/arch/arm/mach-pxa/hx4700.c index 848c861..8e4905a 100644 --- a/arch/arm/mach-pxa/hx4700.c +++ b/arch/arm/mach-pxa/hx4700.c @@ -565,6 +565,7 @@ static struct platform_device hx4700_lcd = { static struct platform_pwm_backlight_data backlight_data = { .pwm_id = 1, + .name = "pxa25x-pwm", .max_brightness = 200, .dft_brightness = 100, .pwm_period_ns = 30923, diff --git a/arch/arm/mach-pxa/lpd270.c b/arch/arm/mach-pxa/lpd270.c index d279507..91efade 100644 --- a/arch/arm/mach-pxa/lpd270.c +++ b/arch/arm/mach-pxa/lpd270.c @@ -273,6 +273,7 @@ static struct platform_device lpd270_flash_device[2] = { static struct platform_pwm_backlight_data lpd270_backlight_data = { .pwm_id = 0, + .name = "pxa25x-pwm", .max_brightness = 1, .dft_brightness = 1, .pwm_period_ns = 78770, diff --git a/arch/arm/mach-pxa/magician.c b/arch/arm/mach-pxa/magician.c index e81dd0c..bb657a4 100644 --- a/arch/arm/mach-pxa/magician.c +++ b/arch/arm/mach-pxa/magician.c @@ -382,6 +382,7 @@ static void magician_backlight_exit(struct device *dev) static struct platform_pwm_backlight_data backlight_data = { .pwm_id = 0, + .name = "pxa25x-pwm", .max_brightness = 272, .dft_brightness = 100, .pwm_period_ns = 30923, diff --git a/arch/arm/mach-pxa/mainstone.c b/arch/arm/mach-pxa/mainstone.c index 5543c64..cbd359c 100644 --- a/arch/arm/mach-pxa/mainstone.c +++ b/arch/arm/mach-pxa/mainstone.c @@ -342,6 +342,7 @@ static struct platform_device mst_flash_device[2] = { #if defined(CONFIG_FB_PXA) || defined(CONFIG_FB_PXA_MODULE) static struct platform_pwm_backlight_data mainstone_backlight_data = { .pwm_id = 0, + .name = "pxa25x-pwm", .max_brightness = 1023, .dft_brightness = 1023, .pwm_period_ns = 78770, diff --git a/arch/arm/mach-pxa/mioa701.c b/arch/arm/mach-pxa/mioa701.c index dc66942..e442088 100644 --- a/arch/arm/mach-pxa/mioa701.c +++ b/arch/arm/mach-pxa/mioa701.c @@ -224,6 +224,7 @@ static void mio_gpio_free(struct gpio_ress *gpios, int size) /* LCD Screen and Backlight */ static struct platform_pwm_backlight_data mioa701_backlight_data = { .pwm_id = 0, + .name = "pxa25x-pwm", .max_brightness = 100, .dft_brightness = 50, .pwm_period_ns = 4000 * 1024, /* Fl = 250kHz */ diff --git a/arch/arm/mach-pxa/palm27x.c b/arch/arm/mach-pxa/palm27x.c index 77ad6d3..46677a4 100644 --- a/arch/arm/mach-pxa/palm27x.c +++ b/arch/arm/mach-pxa/palm27x.c @@ -321,6 +321,7 @@ static void palm27x_backlight_exit(struct device *dev) static struct platform_pwm_backlight_data palm27x_backlight_data = { .pwm_id = 0, + .name = "pxa25x-pwm", .max_brightness = 0xfe, .dft_brightness = 0x7e, .pwm_period_ns = 3500, diff --git a/arch/arm/mach-pxa/palmtc.c b/arch/arm/mach-pxa/palmtc.c index ce1104d..385a0b5 100644 --- a/arch/arm/mach-pxa/palmtc.c +++ b/arch/arm/mach-pxa/palmtc.c @@ -180,6 +180,7 @@ static void palmtc_backlight_exit(struct device *dev) static struct platform_pwm_backlight_data palmtc_backlight_data = { .pwm_id = 1, + .name = "pxa25x-pwm", .max_brightness = PALMTC_MAX_INTENSITY, .dft_brightness = PALMTC_MAX_INTENSITY, .pwm_period_ns = PALMTC_PERIOD_NS, diff --git a/arch/arm/mach-pxa/palmte2.c b/arch/arm/mach-pxa/palmte2.c index 93c11a0..b7e95f4 100644 --- a/arch/arm/mach-pxa/palmte2.c +++ b/arch/arm/mach-pxa/palmte2.c @@ -177,6 +177,7 @@ static void palmte2_backlight_exit(struct device *dev) static struct platform_pwm_backlight_data palmte2_backlight_data = { .pwm_id = 0, + .name = "pxa25x-pwm", .max_brightness = PALMTE2_MAX_INTENSITY, .dft_brightness = PALMTE2_MAX_INTENSITY, .pwm_period_ns = PALMTE2_PERIOD_NS, diff --git a/arch/arm/mach-pxa/pcm990-baseboard.c b/arch/arm/mach-pxa/pcm990-baseboard.c index f56ae10..29c7e88 100644 --- a/arch/arm/mach-pxa/pcm990-baseboard.c +++ b/arch/arm/mach-pxa/pcm990-baseboard.c @@ -138,6 +138,7 @@ static struct pxafb_mach_info pcm990_fbinfo __initdata = { static struct platform_pwm_backlight_data pcm990_backlight_data = { .pwm_id = 0, + .name = "pxa25x-pwm", .max_brightness = 1023, .dft_brightness = 1023, .pwm_period_ns = 78770, diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c index 67e04f4..98dc2e3 100644 --- a/arch/arm/mach-pxa/raumfeld.c +++ b/arch/arm/mach-pxa/raumfeld.c @@ -535,6 +535,7 @@ static void __init raumfeld_w1_init(void) /* PWM controlled backlight */ static struct platform_pwm_backlight_data raumfeld_pwm_backlight_data = { .pwm_id = 0, + .name = "pxa25x-pwm", .max_brightness = 100, .dft_brightness = 100, /* 10000 ns = 10 ms ^= 100 kHz */ diff --git a/arch/arm/mach-pxa/tavorevb.c b/arch/arm/mach-pxa/tavorevb.c index f02dcb5..3164de8 100644 --- a/arch/arm/mach-pxa/tavorevb.c +++ b/arch/arm/mach-pxa/tavorevb.c @@ -168,6 +168,7 @@ static struct platform_pwm_backlight_data tavorevb_backlight_data[] = { [0] = { /* primary backlight */ .pwm_id = 2, + .name = "pxa25x-pwm", .max_brightness = 100, .dft_brightness = 100, .pwm_period_ns = 100000, @@ -175,6 +176,7 @@ static struct platform_pwm_backlight_data tavorevb_backlight_data[] = { [1] = { /* secondary backlight */ .pwm_id = 0, + .name = "pxa25x-pwm", .max_brightness = 100, .dft_brightness = 100, .pwm_period_ns = 100000, diff --git a/arch/arm/mach-pxa/viper.c b/arch/arm/mach-pxa/viper.c index e90114a..fdb768c 100644 --- a/arch/arm/mach-pxa/viper.c +++ b/arch/arm/mach-pxa/viper.c @@ -397,6 +397,7 @@ static void viper_backlight_exit(struct device *dev) static struct platform_pwm_backlight_data viper_backlight_data = { .pwm_id = 0, + .name = "pxa25x-pwm", .max_brightness = 100, .dft_brightness = 100, .pwm_period_ns = 1000000, diff --git a/arch/arm/mach-pxa/z2.c b/arch/arm/mach-pxa/z2.c index f0d0228..bb3d821 100644 --- a/arch/arm/mach-pxa/z2.c +++ b/arch/arm/mach-pxa/z2.c @@ -204,6 +204,7 @@ static struct platform_pwm_backlight_data z2_backlight_data[] = { [0] = { /* Keypad Backlight */ .pwm_id = 1, + .name = "pxa25x-pwm", .max_brightness = 1023, .dft_brightness = 512, .pwm_period_ns = 1260320, @@ -211,6 +212,7 @@ static struct platform_pwm_backlight_data z2_backlight_data[] = { [1] = { /* LCD Backlight */ .pwm_id = 2, + .name = "pxa25x-pwm", .max_brightness = 1023, .dft_brightness = 512, .pwm_period_ns = 1260320, diff --git a/arch/arm/mach-pxa/zylonite.c b/arch/arm/mach-pxa/zylonite.c index 5ba9d99..29492bf 100644 --- a/arch/arm/mach-pxa/zylonite.c +++ b/arch/arm/mach-pxa/zylonite.c @@ -122,6 +122,7 @@ static inline void zylonite_init_leds(void) {} #if defined(CONFIG_FB_PXA) || defined(CONFIG_FB_PXA_MODULE) static struct platform_pwm_backlight_data zylonite_backlight_data = { .pwm_id = 3, + .name = "pxa25x-pwm", .max_brightness = 100, .dft_brightness = 100, .pwm_period_ns = 10000, diff --git a/arch/arm/mach-s3c2410/mach-h1940.c b/arch/arm/mach-s3c2410/mach-h1940.c index 3ba3bab..357342f 100644 --- a/arch/arm/mach-s3c2410/mach-h1940.c +++ b/arch/arm/mach-s3c2410/mach-h1940.c @@ -224,6 +224,7 @@ static void h1940_backlight_exit(struct device *dev) static struct platform_pwm_backlight_data backlight_data = { .pwm_id = 0, + .name = "s3c24xx-pwm", .max_brightness = 100, .dft_brightness = 50, /* tcnt = 0x31 */ diff --git a/arch/arm/mach-s3c2440/mach-rx1950.c b/arch/arm/mach-s3c2440/mach-rx1950.c index 142d1f9..6d993de 100644 --- a/arch/arm/mach-s3c2440/mach-rx1950.c +++ b/arch/arm/mach-s3c2440/mach-rx1950.c @@ -291,6 +291,7 @@ static int rx1950_backlight_notify(struct device *dev, int brightness) static struct platform_pwm_backlight_data rx1950_backlight_data = { .pwm_id = 0, + .name = "s3c24xx-pwm", .max_brightness = 24, .dft_brightness = 4, .pwm_period_ns = 48000, diff --git a/arch/arm/mach-s3c64xx/mach-hmt.c b/arch/arm/mach-s3c64xx/mach-hmt.c index fba9022..14e9011 100644 --- a/arch/arm/mach-s3c64xx/mach-hmt.c +++ b/arch/arm/mach-s3c64xx/mach-hmt.c @@ -109,6 +109,7 @@ static void hmt_bl_exit(struct device *dev) static struct platform_pwm_backlight_data hmt_backlight_data = { .pwm_id = 1, + .name = "s3c24xx-pwm", .max_brightness = 100 * 256, .dft_brightness = 40 * 256, .pwm_period_ns = 1000000000 / (100 * 256 * 20), diff --git a/arch/arm/mach-s3c64xx/mach-smartq.c b/arch/arm/mach-s3c64xx/mach-smartq.c index cb1ebeb..20999d5 100644 --- a/arch/arm/mach-s3c64xx/mach-smartq.c +++ b/arch/arm/mach-s3c64xx/mach-smartq.c @@ -145,6 +145,7 @@ static int smartq_bl_init(struct device *dev) static struct platform_pwm_backlight_data smartq_backlight_data = { .pwm_id = 1, + .name = "s3c24xx-pwm", .max_brightness = 1000, .dft_brightness = 600, .pwm_period_ns = 1000000000 / (1000 * 20), -- 1.7.2.dirty ^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 6/7] pwm: move existing pwm driver to drivers/pwm 2010-09-28 10:35 [PATCH 0/7] PWM core driver for pwm based led and backlight driver Arun Murthy ` (4 preceding siblings ...) 2010-09-28 10:35 ` [PATCH 5/7] platform: Update the pwm based led and backlight platform data Arun Murthy @ 2010-09-28 10:35 ` Arun Murthy 2010-09-28 10:35 ` [PATCH 7/7] pwm: Modify backlight and led Kconfig aligning to pwm core Arun Murthy 6 siblings, 0 replies; 16+ messages in thread From: Arun Murthy @ 2010-09-28 10:35 UTC (permalink / raw) To: linux-arm-kernel As of now only ab8500 and twl6030 are moved. Signed-off-by: Arun Murthy <arun.murthy@stericsson.com> Acked-by: Linus Walleij <linus.walleij@stericsson.com> --- drivers/mfd/Kconfig | 9 --------- drivers/mfd/Makefile | 1 - drivers/misc/Kconfig | 9 --------- drivers/misc/Makefile | 1 - drivers/pwm/Kconfig | 18 ++++++++++++++++++ drivers/pwm/Makefile | 3 +++ drivers/{misc/ab8500-pwm.c => pwm/pwm-ab8500.c} | 0 drivers/{mfd/twl6030-pwm.c => pwm/pwm-twl6040.c} | 0 8 files changed, 21 insertions(+), 20 deletions(-) rename drivers/{misc/ab8500-pwm.c => pwm/pwm-ab8500.c} (100%) rename drivers/{mfd/twl6030-pwm.c => pwm/pwm-twl6040.c} (100%) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 256fabd..ab1d376 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -186,15 +186,6 @@ config TWL4030_CODEC select MFD_CORE default n -config TWL6030_PWM - tristate "TWL6030 PWM (Pulse Width Modulator) Support" - depends on TWL4030_CORE - select HAVE_PWM - default n - help - Say yes here if you want support for TWL6030 PWM. - This is used to control charging LED brightness. - config MFD_STMPE bool "Support STMicroelectronics STMPE" depends on I2C=y && GENERIC_HARDIRQS diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index d5968cd..1a89dbf 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -37,7 +37,6 @@ obj-$(CONFIG_MENELAUS) += menelaus.o obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o -obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o obj-$(CONFIG_MFD_MC13783) += mc13783-core.o diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index ff8ea55..2c38d4e 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -62,15 +62,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 - select HAVE_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 5da82965..21b4761 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -35,5 +35,4 @@ obj-y += eeprom/ obj-y += cb710/ obj-$(CONFIG_VMWARE_BALLOON) += vmware_balloon.o obj-$(CONFIG_ARM_CHARLCD) += arm-charlcd.o -obj-$(CONFIG_AB8500_PWM) += ab8500-pwm.o obj-$(CONFIG_PCH_PHUB) += pch_phub.o diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index a88640c..e347365 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -14,4 +14,22 @@ menuconfig PWM_DEVICES if PWM_DEVICES +config AB8500_PWM + bool "AB8500 PWM support" + depends on AB8500_CORE + select HAVE_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 TWL6030_PWM + tristate "TWL6030 PWM (Pulse Width Modulator) Support" + depends on TWL4030_CORE + select HAVE_PWM + default n + help + Say yes here if you want support for TWL6030 PWM. + This is used to control charging LED brightness. + endif # PWM_DEVICES diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 552f969..f35afb4 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -1 +1,4 @@ obj-$(CONFIG_PWM_DEVICES) += pwm-core.o + +obj-$(CONFIG_AB8500_PWM) += pwm-ab8500.o +obj-$(CONFIG_TWL6030_PWM) += pwm-twl6030.o diff --git a/drivers/misc/ab8500-pwm.c b/drivers/pwm/pwm-ab8500.c similarity index 100% rename from drivers/misc/ab8500-pwm.c rename to drivers/pwm/pwm-ab8500.c diff --git a/drivers/mfd/twl6030-pwm.c b/drivers/pwm/pwm-twl6040.c similarity index 100% rename from drivers/mfd/twl6030-pwm.c rename to drivers/pwm/pwm-twl6040.c -- 1.7.2.dirty ^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 7/7] pwm: Modify backlight and led Kconfig aligning to pwm core 2010-09-28 10:35 [PATCH 0/7] PWM core driver for pwm based led and backlight driver Arun Murthy ` (5 preceding siblings ...) 2010-09-28 10:35 ` [PATCH 6/7] pwm: move existing pwm driver to drivers/pwm Arun Murthy @ 2010-09-28 10:35 ` Arun Murthy 6 siblings, 0 replies; 16+ messages in thread From: Arun Murthy @ 2010-09-28 10:35 UTC (permalink / raw) To: linux-arm-kernel PWM based backlight and led driver will not be calling the pwm drivers through the pwm core driver and hence adding dependancy on the same. Signed-off-by: Arun Murthy <arun.murthy@stericsson.com> Acked-by: Linus Walleij <linus.walleij@stericsson.com> --- drivers/leds/Kconfig | 2 +- drivers/pwm/Kconfig | 2 -- drivers/video/backlight/Kconfig | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index e411262..eba388f 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -244,7 +244,7 @@ config LEDS_DAC124S085 config LEDS_PWM tristate "PWM driven LED Support" - depends on HAVE_PWM + depends on HAVE_PWM || PWM_CORE help This option enables support for pwm driven LEDs diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index e347365..1f8cbc0 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -17,7 +17,6 @@ if PWM_DEVICES config AB8500_PWM bool "AB8500 PWM support" depends on AB8500_CORE - select HAVE_PWM help This driver exports functions to enable/disble/config/free Pulse Width Modulation in the Analog Baseband Chip AB8500. @@ -26,7 +25,6 @@ config AB8500_PWM config TWL6030_PWM tristate "TWL6030 PWM (Pulse Width Modulator) Support" depends on TWL4030_CORE - select HAVE_PWM default n help Say yes here if you want support for TWL6030 PWM. diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index e54a337..73fc17b 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -217,7 +217,7 @@ config BACKLIGHT_CARILLO_RANCH config BACKLIGHT_PWM tristate "Generic PWM based Backlight Driver" - depends on HAVE_PWM + depends on HAVE_PWM || PWM_CORE help If you have a LCD backlight adjustable by PWM, say Y to enable this driver. -- 1.7.2.dirty ^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCHv2 0/7] PWM core driver for pwm based led and backlight driver @ 2010-09-28 7:40 Arun Murthy 2010-09-28 7:40 ` [PATCH 4/7] pwm: Align existing pwm drivers with pwm-core driver Arun Murthy 0 siblings, 1 reply; 16+ messages in thread From: Arun Murthy @ 2010-09-28 7:40 UTC (permalink / raw) To: linux-arm-kernel The series of patch add a new pwm core driver. Align the existing pwm drivers to make use of the pwm core driver. Arun Murthy (7): pwm: Add pwm core driver backlight:pwm: add an element 'name' to platform data leds: pwm: add a new element 'name' to platform data pwm: Align existing pwm drivers with pwm-core driver platform: Update the pwm based led and backlight platform data pwm: move existing pwm driver to drivers/pwm pwm: Modify backlight and led Kconfig aligning to pwm core arch/arm/mach-pxa/cm-x300.c | 1 + arch/arm/mach-pxa/colibri-pxa270-income.c | 1 + arch/arm/mach-pxa/ezx.c | 1 + arch/arm/mach-pxa/hx4700.c | 1 + arch/arm/mach-pxa/lpd270.c | 1 + arch/arm/mach-pxa/magician.c | 1 + arch/arm/mach-pxa/mainstone.c | 1 + arch/arm/mach-pxa/mioa701.c | 1 + arch/arm/mach-pxa/palm27x.c | 1 + arch/arm/mach-pxa/palmtc.c | 1 + arch/arm/mach-pxa/palmte2.c | 1 + arch/arm/mach-pxa/pcm990-baseboard.c | 1 + arch/arm/mach-pxa/raumfeld.c | 1 + arch/arm/mach-pxa/tavorevb.c | 2 + arch/arm/mach-pxa/viper.c | 1 + arch/arm/mach-pxa/z2.c | 2 + arch/arm/mach-pxa/zylonite.c | 1 + arch/arm/mach-s3c2410/mach-h1940.c | 1 + arch/arm/mach-s3c2440/mach-rx1950.c | 1 + arch/arm/mach-s3c64xx/mach-hmt.c | 1 + arch/arm/mach-s3c64xx/mach-smartq.c | 1 + arch/arm/plat-mxc/pwm.c | 166 +++++++++------------ arch/arm/plat-pxa/pwm.c | 210 ++++++++++++-------------- arch/arm/plat-samsung/pwm.c | 235 +++++++++++++---------------- arch/mips/jz4740/pwm.c | 2 +- drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/leds/Kconfig | 2 +- drivers/leds/leds-pwm.c | 4 +- drivers/mfd/Kconfig | 9 - drivers/mfd/Makefile | 1 - drivers/mfd/twl-core.c | 13 ++ drivers/mfd/twl6030-pwm.c | 163 -------------------- drivers/misc/Kconfig | 9 - drivers/misc/Makefile | 1 - drivers/misc/ab8500-pwm.c | 168 -------------------- drivers/pwm/Kconfig | 33 ++++ drivers/pwm/Makefile | 4 + drivers/pwm/pwm-ab8500.c | 157 +++++++++++++++++++ drivers/pwm/pwm-core.c | 124 +++++++++++++++ drivers/pwm/pwm-twl6040.c | 196 ++++++++++++++++++++++++ drivers/video/backlight/Kconfig | 2 +- drivers/video/backlight/pwm_bl.c | 4 +- include/linux/leds_pwm.h | 1 + include/linux/pwm.h | 29 ++++- include/linux/pwm_backlight.h | 1 + 46 files changed, 864 insertions(+), 696 deletions(-) delete mode 100644 drivers/mfd/twl6030-pwm.c delete mode 100644 drivers/misc/ab8500-pwm.c create mode 100644 drivers/pwm/Kconfig create mode 100644 drivers/pwm/Makefile create mode 100644 drivers/pwm/pwm-ab8500.c create mode 100644 drivers/pwm/pwm-core.c create mode 100644 drivers/pwm/pwm-twl6040.c -- 1.7.2.dirty ^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 4/7] pwm: Align existing pwm drivers with pwm-core driver 2010-09-28 7:40 [PATCHv2 0/7] PWM core driver for pwm based led and backlight driver Arun Murthy @ 2010-09-28 7:40 ` Arun Murthy 2010-09-28 8:58 ` Lars-Peter Clausen 0 siblings, 1 reply; 16+ messages in thread From: Arun Murthy @ 2010-09-28 7:40 UTC (permalink / raw) To: linux-arm-kernel pwm-core: make the driver visible for ARM only Align ab8500 pwm with the pwm core driver Align twl6030 pwm driver with pwm core driver Align Freescale mxc pwm driver with pwm core driver Align pxa pwm driver with pwm core driver Align samsung(s3c) pwm driver with pwm core driver mips-jz4740: pwm: Align with new pwm core driver PWM core driver has been added and has been enabled only for ARM platform. The same can be utilised for mips also. Please align with the pwm core driver(drivers/pwm-core.c). Signed-off-by: Arun Murthy <arun.murthy@stericsson.com> Acked-by: Linus Walleij <linus.walleij@stericsson.com> --- arch/arm/plat-mxc/pwm.c | 166 +++++++++++++----------------- arch/arm/plat-pxa/pwm.c | 210 ++++++++++++++++++-------------------- arch/arm/plat-samsung/pwm.c | 235 +++++++++++++++++++------------------------ arch/mips/jz4740/pwm.c | 2 +- drivers/mfd/twl-core.c | 13 +++ drivers/mfd/twl6030-pwm.c | 111 +++++++++++++------- drivers/misc/ab8500-pwm.c | 87 +++++++--------- drivers/pwm/Kconfig | 1 + drivers/pwm/pwm-core.c | 9 +-- include/linux/pwm.h | 21 ++++- 10 files changed, 418 insertions(+), 437 deletions(-) diff --git a/arch/arm/plat-mxc/pwm.c b/arch/arm/plat-mxc/pwm.c index c36f263..b259ba9 100644 --- a/arch/arm/plat-mxc/pwm.c +++ b/arch/arm/plat-mxc/pwm.c @@ -38,22 +38,16 @@ -struct pwm_device { - struct list_head node; - struct platform_device *pdev; - - const char *label; +struct mxc_pwm_device { struct clk *clk; - int clk_enabled; void __iomem *mmio_base; - - unsigned int use_count; - unsigned int pwm_id; }; -int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) +static int mxc_pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) { + struct mxc_pwm_device *mxc_pwm = pwm->data; + if (pwm == NULL || period_ns == 0 || duty_ns > period_ns) return -EINVAL; @@ -62,7 +56,7 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) unsigned long period_cycles, duty_cycles, prescale; u32 cr; - c = clk_get_rate(pwm->clk); + c = clk_get_rate(mxc_pwm->clk); c = c * period_ns; do_div(c, 1000000000); period_cycles = c; @@ -74,8 +68,8 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) do_div(c, period_ns); duty_cycles = c; - writel(duty_cycles, pwm->mmio_base + MX3_PWMSAR); - writel(period_cycles, pwm->mmio_base + MX3_PWMPR); + writel(duty_cycles, mxc_pwm->mmio_base + MX3_PWMSAR); + writel(period_cycles, mxc_pwm->mmio_base + MX3_PWMPR); cr = MX3_PWMCR_PRESCALER(prescale) | MX3_PWMCR_EN; @@ -84,7 +78,7 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) else cr |= MX3_PWMCR_CLKSRC_IPG_HIGH; - writel(cr, pwm->mmio_base + MX3_PWMCR); + writel(cr, mxc_pwm->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 @@ -102,110 +96,76 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) * both the prescaler (/1 .. /128) and then by CLKSEL * (/2 .. /16). */ - u32 max = readl(pwm->mmio_base + MX1_PWMP); + u32 max = readl(mxc_pwm->mmio_base + MX1_PWMP); u32 p = max * duty_ns / period_ns; - writel(max - p, pwm->mmio_base + MX1_PWMS); + writel(max - p, mxc_pwm->mmio_base + MX1_PWMS); } else { BUG(); } return 0; } -EXPORT_SYMBOL(pwm_config); -int pwm_enable(struct pwm_device *pwm) +static int mxc_pwm_enable(struct pwm_device *pwm) { + struct mxc_pwm_device *mxc_pwm = pwm->data; int rc = 0; - if (!pwm->clk_enabled) { - rc = clk_enable(pwm->clk); + if (!mxc_pwm->clk_enabled) { + rc = clk_enable(mxc_pwm->clk); if (!rc) - pwm->clk_enabled = 1; + mxc_pwm->clk_enabled = 1; } return rc; } -EXPORT_SYMBOL(pwm_enable); - -void pwm_disable(struct pwm_device *pwm) -{ - writel(0, pwm->mmio_base + MX3_PWMCR); - - if (pwm->clk_enabled) { - clk_disable(pwm->clk); - pwm->clk_enabled = 0; - } -} -EXPORT_SYMBOL(pwm_disable); - -static DEFINE_MUTEX(pwm_lock); -static LIST_HEAD(pwm_list); -struct pwm_device *pwm_request(int pwm_id, const char *label) +static int mxc_pwm_disable(struct pwm_device *pwm) { - struct pwm_device *pwm; - int found = 0; + struct mxc_pwm_device *mxc_pwm = pwm->data; - mutex_lock(&pwm_lock); + writel(0, mxc_pwm->mmio_base + MX3_PWMCR); - list_for_each_entry(pwm, &pwm_list, node) { - if (pwm->pwm_id == pwm_id) { - found = 1; - break; - } + if (mxc_pwm->clk_enabled) { + clk_disable(mxc_pwm->clk); + mxc_pwm->clk_enabled = 0; } - - if (found) { - if (pwm->use_count == 0) { - pwm->use_count++; - pwm->label = label; - } else - pwm = ERR_PTR(-EBUSY); - } else - pwm = ERR_PTR(-ENOENT); - - mutex_unlock(&pwm_lock); - return pwm; -} -EXPORT_SYMBOL(pwm_request); - -void pwm_free(struct pwm_device *pwm) -{ - mutex_lock(&pwm_lock); - - if (pwm->use_count) { - pwm->use_count--; - pwm->label = NULL; - } else - pr_warning("PWM device already freed\n"); - - mutex_unlock(&pwm_lock); + return 0; } -EXPORT_SYMBOL(pwm_free); static int __devinit mxc_pwm_probe(struct platform_device *pdev) { + struct mxc_pwm_device *mxc_pwm; struct pwm_device *pwm; + struct pwm_ops *pops; struct resource *r; int ret = 0; + mxc_pwm = kzalloc(sizeof(struct mxc_pwm_device), GFP_KERNEL); + if (mxc_pwm == NULL) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL); if (pwm == NULL) { dev_err(&pdev->dev, "failed to allocate memory\n"); - return -ENOMEM; + ret = -ENOMEM; + goto err_free1; + } + pops = kzalloc(sizeof(struct pwm_ops), GFP_KERNEL); + if (pops == NULL) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + ret = -ENOMEM; + goto err_free2; } - pwm->clk = clk_get(&pdev->dev, "pwm"); + mxc_pwm->clk = clk_get(&pdev->dev, "pwm"); - if (IS_ERR(pwm->clk)) { - ret = PTR_ERR(pwm->clk); - goto err_free; + if (IS_ERR(mxc_pwm->clk)) { + ret = PTR_ERR(mxc_pwm->clk); + goto err_free3; } - pwm->clk_enabled = 0; - - pwm->use_count = 0; - pwm->pwm_id = pdev->id; - pwm->pdev = pdev; + mxc_pwm->clk_enabled = 0; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (r == NULL) { @@ -221,16 +181,27 @@ static int __devinit mxc_pwm_probe(struct platform_device *pdev) goto err_free_clk; } - pwm->mmio_base = ioremap(r->start, r->end - r->start + 1); - if (pwm->mmio_base == NULL) { + mxc_pwm->mmio_base = ioremap(r->start, r->end - r->start + 1); + if (mxc_pwm->mmio_base == NULL) { dev_err(&pdev->dev, "failed to ioremap() registers\n"); ret = -ENODEV; goto err_free_mem; } - mutex_lock(&pwm_lock); - list_add_tail(&pwm->node, &pwm_list); - mutex_unlock(&pwm_lock); + pops->pwm_config = mxc_pwm_config; + pops->pwm_enable = mxc_pwm_enable; + pops->pwm_disable = mxc_pwm_disable; + pops->name = pdev->name; + + pwm->pwm_id = pdev->id; + pwm->dev = &pdev->dev; + pwm->pops = pops; + pwm->data = mxc_pwm; + ret = pwm_device_register(pwm); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register pwm device\n"); + goto err_free_mem; + } platform_set_drvdata(pdev, pwm); return 0; @@ -238,33 +209,38 @@ static int __devinit mxc_pwm_probe(struct platform_device *pdev) err_free_mem: release_mem_region(r->start, r->end - r->start + 1); err_free_clk: - clk_put(pwm->clk); -err_free: + clk_put(mxc_pwm->clk); +err_free3: + kfree(pops); +err_free2: kfree(pwm); +err_free1: + kfree(mxc_pwm); return ret; } static int __devexit mxc_pwm_remove(struct platform_device *pdev) { struct pwm_device *pwm; + struct mxc_pwm_device *mxc_pwm; struct resource *r; pwm = platform_get_drvdata(pdev); if (pwm == NULL) return -ENODEV; + mxc_pwm = pwm->data; - mutex_lock(&pwm_lock); - list_del(&pwm->node); - mutex_unlock(&pwm_lock); - - iounmap(pwm->mmio_base); + pwm_device_unregister(pwm); + iounmap(mxc_pwm->mmio_base); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(r->start, r->end - r->start + 1); - clk_put(pwm->clk); + clk_put(mxc_pwm->clk); + kfree(pwm->pops); kfree(pwm); + kfree(mxc_pwm); return 0; } diff --git a/arch/arm/plat-pxa/pwm.c b/arch/arm/plat-pxa/pwm.c index ef32686..1de902a 100644 --- a/arch/arm/plat-pxa/pwm.c +++ b/arch/arm/plat-pxa/pwm.c @@ -43,33 +43,27 @@ MODULE_DEVICE_TABLE(platform, pwm_id_table); #define PWMCR_SD (1 << 6) #define PWMDCR_FD (1 << 10) -struct pwm_device { - struct list_head node; - struct pwm_device *secondary; - struct platform_device *pdev; - - const char *label; +struct pxa_pwm_device { + struct pxa_pwm_device *sec; struct clk *clk; int clk_enabled; void __iomem *mmio_base; - - unsigned int use_count; - unsigned int pwm_id; }; /* * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE */ -int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) +int pxa_pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) { unsigned long long c; unsigned long period_cycles, prescale, pv, dc; + struct pxa_pwm_device *pxa_pwm = pwm->data; if (pwm == NULL || period_ns == 0 || duty_ns > period_ns) return -EINVAL; - c = clk_get_rate(pwm->clk); + c = clk_get_rate(pxa_pwm->clk); c = c * period_ns; do_div(c, 1000000000); period_cycles = c; @@ -90,94 +84,45 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) /* NOTE: the clock to PWM has to be enabled first * before writing to the registers */ - clk_enable(pwm->clk); - __raw_writel(prescale, pwm->mmio_base + PWMCR); - __raw_writel(dc, pwm->mmio_base + PWMDCR); - __raw_writel(pv, pwm->mmio_base + PWMPCR); - clk_disable(pwm->clk); + clk_enable(pxa_pwm->clk); + __raw_writel(prescale, pxa_pwm->mmio_base + PWMCR); + __raw_writel(dc, pxa_pwm->mmio_base + PWMDCR); + __raw_writel(pv, pxa_pwm->mmio_base + PWMPCR); + clk_disable(pxa_pwm->clk); return 0; } -EXPORT_SYMBOL(pwm_config); -int pwm_enable(struct pwm_device *pwm) +int pxa_pwm_enable(struct pwm_device *pwm) { + struct pxa_pwm_device *pxa_pwm = pwm->data; int rc = 0; - if (!pwm->clk_enabled) { - rc = clk_enable(pwm->clk); + if (!pxa_pwm->clk_enabled) { + rc = clk_enable(pxa_pwm->clk); if (!rc) - pwm->clk_enabled = 1; + pxa_pwm->clk_enabled = 1; } return rc; } -EXPORT_SYMBOL(pwm_enable); -void pwm_disable(struct pwm_device *pwm) +int pxa_pwm_disable(struct pwm_device *pwm) { - if (pwm->clk_enabled) { - clk_disable(pwm->clk); - pwm->clk_enabled = 0; - } -} -EXPORT_SYMBOL(pwm_disable); - -static DEFINE_MUTEX(pwm_lock); -static LIST_HEAD(pwm_list); + struct pxa_pwm_device *pxa_pwm = pwm->data; -struct pwm_device *pwm_request(int pwm_id, const char *label) -{ - struct pwm_device *pwm; - int found = 0; - - mutex_lock(&pwm_lock); - - list_for_each_entry(pwm, &pwm_list, node) { - if (pwm->pwm_id == pwm_id) { - found = 1; - break; - } + if (pxa_pwm->clk_enabled) { + clk_disable(pxa_pwm->clk); + pxa_pwm->clk_enabled = 0; } - - if (found) { - if (pwm->use_count == 0) { - pwm->use_count++; - pwm->label = label; - } else - pwm = ERR_PTR(-EBUSY); - } else - pwm = ERR_PTR(-ENOENT); - - mutex_unlock(&pwm_lock); - return pwm; -} -EXPORT_SYMBOL(pwm_request); - -void pwm_free(struct pwm_device *pwm) -{ - mutex_lock(&pwm_lock); - - if (pwm->use_count) { - pwm->use_count--; - pwm->label = NULL; - } else - pr_warning("PWM device already freed\n"); - - mutex_unlock(&pwm_lock); -} -EXPORT_SYMBOL(pwm_free); - -static inline void __add_pwm(struct pwm_device *pwm) -{ - mutex_lock(&pwm_lock); - list_add_tail(&pwm->node, &pwm_list); - mutex_unlock(&pwm_lock); + return 0; } static int __devinit pwm_probe(struct platform_device *pdev) { const struct platform_device_id *id = platform_get_device_id(pdev); + struct pxa_pwm_device *pxa_pwm, *pxa_pwm_sec; struct pwm_device *pwm, *secondary = NULL; + struct pwm_ops *pops; struct resource *r; int ret = 0; @@ -186,17 +131,26 @@ static int __devinit pwm_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to allocate memory\n"); return -ENOMEM; } + pops = kzalloc(sizeof(struct pwm_ops), GFP_KERNEL); + if (pops == NULL) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + kfree(pwm); + return -ENOMEM; + } + pxa_pwm = kzalloc(sizeof(struct pxa_pwm_device), GFP_KERNEL); + if (pxa_pwm == NULL) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + kfree(pops); + kfree(pwm); + return -ENOMEM; + } - pwm->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(pwm->clk)) { - ret = PTR_ERR(pwm->clk); + pxa_pwm->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(pxa_pwm->clk)) { + ret = PTR_ERR(pxa_pwm->clk); goto err_free; } - pwm->clk_enabled = 0; - - pwm->use_count = 0; - pwm->pwm_id = PWM_ID_BASE(id->driver_data) + pdev->id; - pwm->pdev = pdev; + pxa_pwm->clk_enabled = 0; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (r == NULL) { @@ -212,69 +166,105 @@ static int __devinit pwm_probe(struct platform_device *pdev) goto err_free_clk; } - pwm->mmio_base = ioremap(r->start, resource_size(r)); - if (pwm->mmio_base == NULL) { + pxa_pwm->mmio_base = ioremap(r->start, resource_size(r)); + if (pxa_pwm->mmio_base == NULL) { dev_err(&pdev->dev, "failed to ioremap() registers\n"); ret = -ENODEV; goto err_free_mem; } + pops->pwm_config = pxa_pwm_config; + pops->pwm_enable = pxa_pwm_enable; + pops->pwm_disable = pxa_pwm_disable; + pops->name = pdev->name; + + pwm->pwm_id = PWM_ID_BASE(id->driver_data) + pdev->id; + pwm->dev = &pdev->dev; + pwm->pops = pops; + pwm->data = pxa_pwm; + + ret = pwm_device_register(pwm); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register pwm device\n"); + goto err_free_mem; + } + if (id->driver_data & HAS_SECONDARY_PWM) { secondary = kzalloc(sizeof(struct pwm_device), GFP_KERNEL); if (secondary == NULL) { ret = -ENOMEM; - goto err_free_mem; + goto err_pwm; + } + pxa_pwm_sec = kzalloc(sizeof(struct pxa_pwm_device), + GFP_KERNEL); + if (pxa_pwm_sec == NULL) { + ret = -ENOMEM; + goto err_free_mem2; } *secondary = *pwm; - pwm->secondary = secondary; + *pxa_pwm_sec = *pxa_pwm; + pxa_pwm->sec = pxa_pwm_sec; /* registers for the second PWM has offset of 0x10 */ - secondary->mmio_base = pwm->mmio_base + 0x10; + pxa_pwm_sec->mmio_base = pxa_pwm->mmio_base + 0x10; secondary->pwm_id = pdev->id + 2; - } + secondary->data = pxa_pwm_sec; - __add_pwm(pwm); - if (secondary) - __add_pwm(secondary); + ret = pwm_device_register(secondary); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register pwm device\n"); + goto err_free_mem3; + } + } platform_set_drvdata(pdev, pwm); return 0; - +err_free_mem3: + kfree(pxa_pwm_sec); +err_free_mem2: + kfree(secondary); +err_pwm: + pwm_device_unregister(pwm); err_free_mem: release_mem_region(r->start, resource_size(r)); err_free_clk: - clk_put(pwm->clk); + clk_put(pxa_pwm->clk); err_free: + kfree(pxa_pwm); + kfree(pops); kfree(pwm); return ret; } static int __devexit pwm_remove(struct platform_device *pdev) { - struct pwm_device *pwm; + struct pwm_device *pwm, *secondary; + struct pxa_pwm_device *pxa_pwm, *pxa_pwm_sec; struct resource *r; pwm = platform_get_drvdata(pdev); if (pwm == NULL) return -ENODEV; - - mutex_lock(&pwm_lock); - - if (pwm->secondary) { - list_del(&pwm->secondary->node); - kfree(pwm->secondary); + pxa_pwm = pwm->data; + secondary = pwm_request((pdev->id + 2), pdev->name); + pxa_pwm_sec = secondary->data; + + pwm_device_unregister(pwm); + iounmap(pxa_pwm->mmio_base); + if (secondary) { + pwm_device_unregister(secondary); + iounmap(pxa_pwm->mmio_base); + kfree(pxa_pwm_sec); + kfree(secondary); } - list_del(&pwm->node); - mutex_unlock(&pwm_lock); - - iounmap(pwm->mmio_base); - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(r->start, resource_size(r)); - clk_put(pwm->clk); + clk_put(pxa_pwm->clk); + kfree(pxa_pwm); + kfree(pwm->pops); kfree(pwm); return 0; } diff --git a/arch/arm/plat-samsung/pwm.c b/arch/arm/plat-samsung/pwm.c index 2eeb49f..63fba01 100644 --- a/arch/arm/plat-samsung/pwm.c +++ b/arch/arm/plat-samsung/pwm.c @@ -26,25 +26,19 @@ #include <plat/devs.h> #include <plat/regs-timer.h> -struct pwm_device { - struct list_head list; +struct s3c_pwm_device { struct platform_device *pdev; struct clk *clk_div; struct clk *clk; - const char *label; unsigned int period_ns; unsigned int duty_ns; unsigned char tcon_base; unsigned char running; - unsigned char use_count; - unsigned char pwm_id; }; -#define pwm_dbg(_pwm, msg...) dev_dbg(&(_pwm)->pdev->dev, msg) - static struct clk *clk_scaler[2]; /* Standard setup for a timer block. */ @@ -78,108 +72,61 @@ struct platform_device s3c_device_timer[] = { [4] = { DEFINE_S3C_TIMER(4, IRQ_TIMER4) }, }; -static inline int pwm_is_tdiv(struct pwm_device *pwm) +static inline int pwm_is_tdiv(struct s3c_pwm_device *s3c_pwm) { - return clk_get_parent(pwm->clk) == pwm->clk_div; + return clk_get_parent(s3c_pwm->clk) == s3c_pwm->clk_div; } -static DEFINE_MUTEX(pwm_lock); -static LIST_HEAD(pwm_list); +#define pwm_tcon_start(s3c_pwm) (1 << (s3c_pwm->tcon_base + 0)) +#define pwm_tcon_invert(s3c_pwm) (1 << (s3c_pwm->tcon_base + 2)) +#define pwm_tcon_autoreload(s3c_pwm) (1 << (s3c_pwm->tcon_base + 3)) +#define pwm_tcon_manulupdate(s3c_pwm) (1 << (s3c_pwm->tcon_base + 1)) -struct pwm_device *pwm_request(int pwm_id, const char *label) -{ - struct pwm_device *pwm; - int found = 0; - - mutex_lock(&pwm_lock); - - list_for_each_entry(pwm, &pwm_list, list) { - if (pwm->pwm_id == pwm_id) { - found = 1; - break; - } - } - - if (found) { - if (pwm->use_count == 0) { - pwm->use_count = 1; - pwm->label = label; - } else - pwm = ERR_PTR(-EBUSY); - } else - pwm = ERR_PTR(-ENOENT); - - mutex_unlock(&pwm_lock); - return pwm; -} - -EXPORT_SYMBOL(pwm_request); - - -void pwm_free(struct pwm_device *pwm) -{ - mutex_lock(&pwm_lock); - - if (pwm->use_count) { - pwm->use_count--; - pwm->label = NULL; - } else - printk(KERN_ERR "PWM%d device already freed\n", pwm->pwm_id); - - mutex_unlock(&pwm_lock); -} - -EXPORT_SYMBOL(pwm_free); - -#define pwm_tcon_start(pwm) (1 << (pwm->tcon_base + 0)) -#define pwm_tcon_invert(pwm) (1 << (pwm->tcon_base + 2)) -#define pwm_tcon_autoreload(pwm) (1 << (pwm->tcon_base + 3)) -#define pwm_tcon_manulupdate(pwm) (1 << (pwm->tcon_base + 1)) - -int pwm_enable(struct pwm_device *pwm) +int s3c_pwm_enable(struct pwm_device *pwm) { unsigned long flags; unsigned long tcon; + struct s3c_pwm_device *s3c_pwm = pwm->data; local_irq_save(flags); tcon = __raw_readl(S3C2410_TCON); - tcon |= pwm_tcon_start(pwm); + tcon |= pwm_tcon_start(s3c_pwm); __raw_writel(tcon, S3C2410_TCON); local_irq_restore(flags); - pwm->running = 1; + s3c_pwm->running = 1; return 0; } -EXPORT_SYMBOL(pwm_enable); - -void pwm_disable(struct pwm_device *pwm) +int s3c_pwm_disable(struct pwm_device *pwm) { unsigned long flags; unsigned long tcon; + struct s3c_pwm_device *s3c_pwm = pwm->data; local_irq_save(flags); tcon = __raw_readl(S3C2410_TCON); - tcon &= ~pwm_tcon_start(pwm); + tcon &= ~pwm_tcon_start(s3c_pwm); __raw_writel(tcon, S3C2410_TCON); local_irq_restore(flags); - pwm->running = 0; + s3c_pwm->running = 0; + return 0; } -EXPORT_SYMBOL(pwm_disable); - -static unsigned long pwm_calc_tin(struct pwm_device *pwm, unsigned long freq) +static unsigned long pwm_calc_tin(struct pwm_device *pwm, + unsigned long freq) { unsigned long tin_parent_rate; unsigned int div; + struct s3c_pwm_device *s3c_pwm = pwm->data; - tin_parent_rate = clk_get_rate(clk_get_parent(pwm->clk_div)); - pwm_dbg(pwm, "tin parent at %lu\n", tin_parent_rate); + tin_parent_rate = clk_get_rate(clk_get_parent(s3c_pwm->clk_div)); + dev_dbg(pwm->dev, "tin parent at %lu\n", tin_parent_rate); for (div = 2; div <= 16; div *= 2) { if ((tin_parent_rate / (div << 16)) < freq) @@ -191,7 +138,7 @@ static unsigned long pwm_calc_tin(struct pwm_device *pwm, unsigned long freq) #define NS_IN_HZ (1000000000UL) -int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) +int s3c_pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) { unsigned long tin_rate; unsigned long tin_ns; @@ -200,6 +147,7 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) unsigned long tcon; unsigned long tcnt; long tcmp; + struct s3c_pwm_device *s3c_pwm = pwm->data; /* We currently avoid using 64bit arithmetic by using the * fact that anything faster than 1Hz is easily representable @@ -211,8 +159,8 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) if (duty_ns > period_ns) return -EINVAL; - if (period_ns == pwm->period_ns && - duty_ns == pwm->duty_ns) + if (period_ns == s3c_pwm->period_ns && + duty_ns == s3c_pwm->duty_ns) return 0; /* The TCMP and TCNT can be read without a lock, they're not @@ -223,26 +171,26 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) period = NS_IN_HZ / period_ns; - pwm_dbg(pwm, "duty_ns=%d, period_ns=%d (%lu)\n", + dev_dbg(pwm->dev, "duty_ns=%d, period_ns=%d (%lu)\n", duty_ns, period_ns, period); /* Check to see if we are changing the clock rate of the PWM */ - if (pwm->period_ns != period_ns) { - if (pwm_is_tdiv(pwm)) { + if (s3c_pwm->period_ns != period_ns) { + if (pwm_is_tdiv(s3c_pwm)) { tin_rate = pwm_calc_tin(pwm, period); - clk_set_rate(pwm->clk_div, tin_rate); + clk_set_rate(s3c_pwm->clk_div, tin_rate); } else - tin_rate = clk_get_rate(pwm->clk); + tin_rate = clk_get_rate(s3c_pwm->clk); - pwm->period_ns = period_ns; + s3c_pwm->period_ns = period_ns; - pwm_dbg(pwm, "tin_rate=%lu\n", tin_rate); + dev_dbg(pwm->dev, "tin_rate=%lu\n", tin_rate); tin_ns = NS_IN_HZ / tin_rate; tcnt = period_ns / tin_ns; } else - tin_ns = NS_IN_HZ / clk_get_rate(pwm->clk); + tin_ns = NS_IN_HZ / clk_get_rate(s3c_pwm->clk); /* Note, counters count down */ @@ -253,7 +201,7 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) if (tcmp == tcnt) tcmp--; - pwm_dbg(pwm, "tin_ns=%lu, tcmp=%ld/%lu\n", tin_ns, tcmp, tcnt); + dev_dbg(pwm->dev, "tin_ns=%lu, tcmp=%ld/%lu\n", tin_ns, tcmp, tcnt); if (tcmp < 0) tcmp = 0; @@ -266,11 +214,11 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) __raw_writel(tcnt, S3C2410_TCNTB(pwm->pwm_id)); tcon = __raw_readl(S3C2410_TCON); - tcon |= pwm_tcon_manulupdate(pwm); - tcon |= pwm_tcon_autoreload(pwm); + tcon |= pwm_tcon_manulupdate(s3c_pwm); + tcon |= pwm_tcon_autoreload(s3c_pwm); __raw_writel(tcon, S3C2410_TCON); - tcon &= ~pwm_tcon_manulupdate(pwm); + tcon &= ~pwm_tcon_manulupdate(s3c_pwm); __raw_writel(tcon, S3C2410_TCON); local_irq_restore(flags); @@ -278,103 +226,122 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) return 0; } -EXPORT_SYMBOL(pwm_config); - -static int pwm_register(struct pwm_device *pwm) -{ - pwm->duty_ns = -1; - pwm->period_ns = -1; - - mutex_lock(&pwm_lock); - list_add_tail(&pwm->list, &pwm_list); - mutex_unlock(&pwm_lock); - - return 0; -} - static int s3c_pwm_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct s3c_pwm_device *s3c_pwm; struct pwm_device *pwm; + struct pwm_ops *pops; unsigned long flags; unsigned long tcon; unsigned int id = pdev->id; - int ret; + int ret = 0; if (id == 4) { dev_err(dev, "TIMER4 is currently not supported\n"); return -ENXIO; } + s3c_pwm = kzalloc(sizeof(struct s3c_pwm_device), GFP_KERNEL); + if (s3c_pwm == NULL) { + dev_err(dev, "failed to allocate pwm_device\n"); + return -ENOMEM; + } + s3c_pwm->pdev = pdev; pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL); if (pwm == NULL) { dev_err(dev, "failed to allocate pwm_device\n"); - return -ENOMEM; + goto err_alloc; + ret = -ENOMEM; + } + pops = kzalloc(sizeof(struct pwm_ops), GFP_KERNEL); + if (pops == NULL) { + dev_err(dev, "failed to allocate memory\n"); + goto err_alloc1; + ret = -ENOMEM; } - - pwm->pdev = pdev; - pwm->pwm_id = id; /* calculate base of control bits in TCON */ - pwm->tcon_base = id == 0 ? 0 : (id * 4) + 4; + s3c_pwm->tcon_base = id == 0 ? 0 : (id * 4) + 4; - pwm->clk = clk_get(dev, "pwm-tin"); - if (IS_ERR(pwm->clk)) { + s3c_pwm->clk = clk_get(dev, "pwm-tin"); + if (IS_ERR(s3c_pwm->clk)) { dev_err(dev, "failed to get pwm tin clk\n"); - ret = PTR_ERR(pwm->clk); - goto err_alloc; + ret = PTR_ERR(s3c_pwm->clk); + goto err_alloc2; } - pwm->clk_div = clk_get(dev, "pwm-tdiv"); - if (IS_ERR(pwm->clk_div)) { + s3c_pwm->clk_div = clk_get(dev, "pwm-tdiv"); + if (IS_ERR(s3c_pwm->clk_div)) { dev_err(dev, "failed to get pwm tdiv clk\n"); - ret = PTR_ERR(pwm->clk_div); + ret = PTR_ERR(s3c_pwm->clk_div); goto err_clk_tin; } local_irq_save(flags); tcon = __raw_readl(S3C2410_TCON); - tcon |= pwm_tcon_invert(pwm); + tcon |= pwm_tcon_invert(s3c_pwm); __raw_writel(tcon, S3C2410_TCON); local_irq_restore(flags); + pops->pwm_config = s3c_pwm_config; + pops->pwm_enable = s3c_pwm_enable; + pops->pwm_disable = s3c_pwm_disable; + pops->name = pdev->name; + + pwm->dev = dev; + pwm->pwm_id = id; + pwm->pops = pops; + pwm->data = s3c_pwm; - ret = pwm_register(pwm); + s3c_pwm->duty_ns = -1; + s3c_pwm->period_ns = -1; + ret = pwm_device_register(pwm); if (ret) { dev_err(dev, "failed to register pwm\n"); goto err_clk_tdiv; } - pwm_dbg(pwm, "config bits %02x\n", - (__raw_readl(S3C2410_TCON) >> pwm->tcon_base) & 0x0f); + dev_dbg(dev, "config bits %02x\n", + (__raw_readl(S3C2410_TCON) >> s3c_pwm->tcon_base) & 0x0f); dev_info(dev, "tin at %lu, tdiv at %lu, tin=%sclk, base %d\n", - clk_get_rate(pwm->clk), - clk_get_rate(pwm->clk_div), - pwm_is_tdiv(pwm) ? "div" : "ext", pwm->tcon_base); + clk_get_rate(s3c_pwm->clk), + clk_get_rate(s3c_pwm->clk_div), + pwm_is_tdiv(s3c_pwm) ? "div" : "ext", s3c_pwm->tcon_base); platform_set_drvdata(pdev, pwm); return 0; - err_clk_tdiv: - clk_put(pwm->clk_div); +err_clk_tdiv: + clk_put(s3c_pwm->clk_div); - err_clk_tin: - clk_put(pwm->clk); +err_clk_tin: + clk_put(s3c_pwm->clk); - err_alloc: +err_alloc2: + kfree(pops); + +err_alloc1: kfree(pwm); + +err_alloc: + kfree(s3c_pwm); return ret; } static int __devexit s3c_pwm_remove(struct platform_device *pdev) { struct pwm_device *pwm = platform_get_drvdata(pdev); + struct s3c_pwm_device *s3c_pwm = pwm->data; - clk_put(pwm->clk_div); - clk_put(pwm->clk); + pwm_device_unregister(pwm); + clk_put(s3c_pwm->clk_div); + clk_put(s3c_pwm->clk); + kfree(s3c_pwm); + kfree(pwm->pops); kfree(pwm); return 0; @@ -384,13 +351,14 @@ static int __devexit s3c_pwm_remove(struct platform_device *pdev) static int s3c_pwm_suspend(struct platform_device *pdev, pm_message_t state) { struct pwm_device *pwm = platform_get_drvdata(pdev); + struct s3c_pwm_device *s3c_pwm = pwm->data; /* No one preserve these values during suspend so reset them * Otherwise driver leaves PWM unconfigured if same values * passed to pwm_config */ - pwm->period_ns = 0; - pwm->duty_ns = 0; + s3c_pwm->period_ns = 0; + s3c_pwm->duty_ns = 0; return 0; } @@ -398,11 +366,12 @@ static int s3c_pwm_suspend(struct platform_device *pdev, pm_message_t state) static int s3c_pwm_resume(struct platform_device *pdev) { struct pwm_device *pwm = platform_get_drvdata(pdev); + struct s3c_pwm_device *s3c_pwm = pwm->data; unsigned long tcon; /* Restore invertion */ tcon = __raw_readl(S3C2410_TCON); - tcon |= pwm_tcon_invert(pwm); + tcon |= pwm_tcon_invert(s3c_pwm); __raw_writel(tcon, S3C2410_TCON); return 0; diff --git a/arch/mips/jz4740/pwm.c b/arch/mips/jz4740/pwm.c index a26a6fa..9f46767 100644 --- a/arch/mips/jz4740/pwm.c +++ b/arch/mips/jz4740/pwm.c @@ -152,7 +152,7 @@ int pwm_enable(struct pwm_device *pwm) return 0; } -void pwm_disable(struct pwm_device *pwm) +int pwm_disable(struct pwm_device *pwm) { uint32_t ctrl = jz4740_timer_get_ctrl(pwm->id); diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index b0f2c00..6a6ea41 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -129,6 +129,12 @@ #define twl_has_pwrbutton() false #endif +#if defined CONFIG_TWL6030_PWM +#define twl_has_pwm() true +#else +#define twl_has_pwm() false +#endif + #define SUB_CHIP_ID0 0 #define SUB_CHIP_ID1 1 #define SUB_CHIP_ID2 2 @@ -825,6 +831,13 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) if (IS_ERR(child)) return PTR_ERR(child); } + if (twl_has_pwm()) { + child = add_child(SUB_CHIP_ID2, "twl6030_pwm", + NULL, 0, + false, 0, 0); + if (IS_ERR(child)) + return PTR_ERR(child); + } return 0; } diff --git a/drivers/mfd/twl6030-pwm.c b/drivers/mfd/twl6030-pwm.c index 5d25bdc..b78324b 100644 --- a/drivers/mfd/twl6030-pwm.c +++ b/drivers/mfd/twl6030-pwm.c @@ -20,8 +20,10 @@ #include <linux/module.h> #include <linux/platform_device.h> -#include <linux/i2c/twl.h> #include <linux/slab.h> +#include <linux/pwm.h> +#include <linux/err.h> +#include <linux/i2c/twl.h> #define LED_PWM_CTRL1 0xF4 #define LED_PWM_CTRL2 0xF5 @@ -45,15 +47,10 @@ #define PWM_CTRL2_MODE_MASK 0x3 -struct pwm_device { - const char *label; - unsigned int pwm_id; -}; - -int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) +int twl6030_pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) { u8 duty_cycle; - int ret; + int ret = 0; if (pwm == NULL || period_ns == 0 || duty_ns > period_ns) return -EINVAL; @@ -69,12 +66,11 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) } return 0; } -EXPORT_SYMBOL(pwm_config); -int pwm_enable(struct pwm_device *pwm) +int twl6030_pwm_enable(struct pwm_device *pwm) { u8 val; - int ret; + int ret = 0; ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2); if (ret < 0) { @@ -95,18 +91,17 @@ int pwm_enable(struct pwm_device *pwm) twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2); return 0; } -EXPORT_SYMBOL(pwm_enable); -void pwm_disable(struct pwm_device *pwm) +int twl6030_pwm_disable(struct pwm_device *pwm) { u8 val; - int ret; + int ret = 0; ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2); if (ret < 0) { pr_err("%s: Failed to disable PWM, Error %d\n", pwm->label, ret); - return; + return ret; } val &= ~PWM_CTRL2_MODE_MASK; @@ -116,48 +111,86 @@ void pwm_disable(struct pwm_device *pwm) if (ret < 0) { pr_err("%s: Failed to disable PWM, Error %d\n", pwm->label, ret); - return; } - return; + return ret; } -EXPORT_SYMBOL(pwm_disable); -struct pwm_device *pwm_request(int pwm_id, const char *label) +static int __devinit twl6030_pwm_probe(struct platform_device *pdev) { - u8 val; - int ret; struct pwm_device *pwm; + struct pwm_ops *pops; + int ret; + u8 val; pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL); if (pwm == NULL) { - pr_err("%s: failed to allocate memory\n", label); - return NULL; + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + pops = kzalloc(sizeof(struct pwm_ops), GFP_KERNEL); + if (pops == NULL) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + kfree(pwm); + return -ENOMEM; } - pwm->label = label; - pwm->pwm_id = pwm_id; - + pops->pwm_config = twl6030_pwm_config; + pops->pwm_enable = twl6030_pwm_enable; + pops->pwm_disable = twl6030_pwm_disable; + pops->name = &pdev->name; + pwm->dev = &pdev->dev; + pwm->pwm_id = pdev->id; + pwm->pops = pops; + ret = pwm_device_register(pwm); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register pwm device\n"); + kfree(pwm); + kfree(pops); + return ret; + } + platform_set_drvdata(pdev, pwm); /* Configure PWM */ val = PWM_CTRL2_DIS_PD | PWM_CTRL2_CURR_02 | PWM_CTRL2_SRC_VAC | - PWM_CTRL2_MODE_HW; + PWM_CTRL2_MODE_HW; ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2); - if (ret < 0) { - pr_err("%s: Failed to configure PWM, Error %d\n", - pwm->label, ret); - - kfree(pwm); - return NULL; + dev_err(&pdev->dev, "Failed to configure PWM, Error %d\n", ret); + return ret; } - - return pwm; + dev_dbg(&pdev->dev, "pwm probe successful\n"); + return ret; } -EXPORT_SYMBOL(pwm_request); -void pwm_free(struct pwm_device *pwm) +static int __devexit twl6030_pwm_remove(struct platform_device *pdev) { - pwm_disable(pwm); + struct pwm_device *pwm = platform_get_drvdata(pdev); + + pwm_device_unregister(pwm); + kfree(pwm->pops); kfree(pwm); + dev_dbg(&pdev->dev, "pwm driver removed\n"); + return 0; } -EXPORT_SYMBOL(pwm_free); + +static struct platform_driver twl6030_pwm_driver = { + .driver = { + .name = "twl6030_pwm", + .owner = THIS_MODULE, + }, + .probe = twl6030_pwm_probe, + .remove = __devexit_p(twl6030_pwm_remove), +}; + +static int __init twl6030_pwm_init(void) +{ + return platform_driver_register(&twl6030_pwm_driver); +} + +static void __exit twl6030_pwm_deinit(void) +{ + platform_driver_unregister(&twl6030_pwm_driver); +} + +subsys_initcall(twl6030_pwm_init); +module_exit(twl6030_pwm_deinit); diff --git a/drivers/misc/ab8500-pwm.c b/drivers/misc/ab8500-pwm.c index 54e3d05..d2b23b6 100644 --- a/drivers/misc/ab8500-pwm.c +++ b/drivers/misc/ab8500-pwm.c @@ -23,16 +23,9 @@ #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 ab8500_pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) { int ret = 0; unsigned int higher_val, lower_val; @@ -60,23 +53,21 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) return ret; } -EXPORT_SYMBOL(pwm_config); -int pwm_enable(struct pwm_device *pwm) +int ab8500_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); + 1 << (pwm->pwm_id-1), 1 << (pwm->pwm_id-1)); 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 ab8500_pwm_disable(struct pwm_device *pwm) { int ret; @@ -86,58 +77,56 @@ void pwm_disable(struct pwm_device *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); + return ret; } -EXPORT_SYMBOL(pwm_free); static int __devinit ab8500_pwm_probe(struct platform_device *pdev) { - struct pwm_device *pwm; + int ret = 0; + struct pwm_ops *pops; + struct pwm_device *pwm_dev; /* * 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) { + pops = kzalloc(sizeof(struct pwm_ops), GFP_KERNEL); + if (pops == 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; + pwm_dev = kzalloc(sizeof(struct pwm_device), GFP_KERNEL); + if (pwm_dev == NULL) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + kfree(pops); + return -ENOMEM; + } + pops->pwm_config = ab8500_pwm_config; + pops->pwm_enable = ab8500_pwm_enable; + pops->pwm_disable = ab8500_pwm_disable; + pops->name = "ab8500"; + pwm_dev->dev = &pdev->dev; + pwm_dev->pwm_id = pdev->id; + pwm_dev->pops = pops; + ret = pwm_device_register(pwm_dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register pwm device\n"); + kfree(pwm_dev); + kfree(pops); + return ret; + } + platform_set_drvdata(pdev, pwm_dev); + dev_dbg(&pdev->dev, "pwm probe successful\n"); + return ret; } static int __devexit ab8500_pwm_remove(struct platform_device *pdev) { - struct pwm_device *pwm = platform_get_drvdata(pdev); - list_del(&pwm->node); + struct pwm_device *pwm_dev = platform_get_drvdata(pdev); + + pwm_device_unregister(pwm_dev); dev_dbg(&pdev->dev, "pwm driver removed\n"); - kfree(pwm); + kfree(pwm_dev->pops); + kfree(pwm_dev); return 0; } diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 5d10106..a88640c 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -4,6 +4,7 @@ menuconfig PWM_DEVICES bool "PWM devices" + depends on ARM default y ---help--- Say Y here to get to see options for device drivers from various diff --git a/drivers/pwm/pwm-core.c b/drivers/pwm/pwm-core.c index b84027a..3a0d426 100644 --- a/drivers/pwm/pwm-core.c +++ b/drivers/pwm/pwm-core.c @@ -11,11 +11,6 @@ #include <linux/err.h> #include <linux/pwm.h> -struct pwm_device { - struct pwm_ops *pops; - int pwm_id; -}; - struct pwm_dev_info { struct pwm_device *pwm_dev; struct list_head list; @@ -40,9 +35,9 @@ int pwm_enable(struct pwm_device *pwm) } EXPORT_SYMBOL(pwm_enable); -void pwm_disable(struct pwm_device *pwm) +int pwm_disable(struct pwm_device *pwm) { - pwm->pops->pwm_disable(pwm); + return pwm->pops->pwm_disable(pwm); } EXPORT_SYMBOL(pwm_disable); diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 6e7da1f..4344c0b 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -1,14 +1,29 @@ #ifndef __LINUX_PWM_H #define __LINUX_PWM_H -struct pwm_device; +/* + * TODO: #if defined CONFIG_PWM_CORE has to be removed after mips jz4740 + * pwm driver aligning with pwm-core.c driver. + */ +#if defined CONFIG_PWM_CORE +struct pwm_device { + struct pwm_ops *pops; + struct device *dev; + struct list_head node; + const char *label; + unsigned int pwm_id; + void *data; +}; struct pwm_ops { int (*pwm_config)(struct pwm_device *pwm, int duty_ns, int period_ns); int (*pwm_enable)(struct pwm_device *pwm); int (*pwm_disable)(struct pwm_device *pwm); - char *name; + const char *name; }; +#else +struct pwm_device; +#endif /* * pwm_request - request a PWM device @@ -33,7 +48,7 @@ int pwm_enable(struct pwm_device *pwm); /* * pwm_disable - stop a PWM output toggling */ -void pwm_disable(struct pwm_device *pwm); +int pwm_disable(struct pwm_device *pwm); int pwm_device_register(struct pwm_device *pwm_dev); int pwm_device_unregister(struct pwm_device *pwm_dev); -- 1.7.2.dirty ^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 4/7] pwm: Align existing pwm drivers with pwm-core driver 2010-09-28 7:40 ` [PATCH 4/7] pwm: Align existing pwm drivers with pwm-core driver Arun Murthy @ 2010-09-28 8:58 ` Lars-Peter Clausen [not found] ` <F45880696056844FA6A73F415B568C69532DC2FB98@EXDCVYMBSTM006.EQ1STM.local> 0 siblings, 1 reply; 16+ messages in thread From: Lars-Peter Clausen @ 2010-09-28 8:58 UTC (permalink / raw) To: linux-arm-kernel Arun Murthy wrote: > pwm-core: make the driver visible for ARM only > > Align ab8500 pwm with the pwm core driver > Align twl6030 pwm driver with pwm core driver > Align Freescale mxc pwm driver with pwm core driver > Align pxa pwm driver with pwm core driver > Align samsung(s3c) pwm driver with pwm core driver > > mips-jz4740: pwm: Align with new pwm core driver > > PWM core driver has been added and has been enabled only for ARM > platform. The same can be utilised for mips also. > Please align with the pwm core driver(drivers/pwm-core.c). Is there any reason for artificially limiting it to ARM? > > Signed-off-by: Arun Murthy <arun.murthy@stericsson.com> > Acked-by: Linus Walleij <linus.walleij@stericsson.com> > --- > arch/arm/plat-mxc/pwm.c | 166 +++++++++++++----------------- > arch/arm/plat-pxa/pwm.c | 210 ++++++++++++++++++-------------------- > arch/arm/plat-samsung/pwm.c | 235 +++++++++++++++++++------------------------ > arch/mips/jz4740/pwm.c | 2 +- > drivers/mfd/twl-core.c | 13 +++ > drivers/mfd/twl6030-pwm.c | 111 +++++++++++++------- > drivers/misc/ab8500-pwm.c | 87 +++++++--------- > drivers/pwm/Kconfig | 1 + > drivers/pwm/pwm-core.c | 9 +-- > include/linux/pwm.h | 21 ++++- > 10 files changed, 418 insertions(+), 437 deletions(-) > > diff --git a/arch/arm/plat-mxc/pwm.c b/arch/arm/plat-mxc/pwm.c > index c36f263..b259ba9 100644 > --- a/arch/arm/plat-mxc/pwm.c > +++ b/arch/arm/plat-mxc/pwm.c > @@ -38,22 +38,16 @@ > > > > -struct pwm_device { > - struct list_head node; > - struct platform_device *pdev; > - > - const char *label; > +struct mxc_pwm_device { > struct clk *clk; > - > int clk_enabled; > void __iomem *mmio_base; > - > - unsigned int use_count; > - unsigned int pwm_id; > }; > > -int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) > +static int mxc_pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) > { > + struct mxc_pwm_device *mxc_pwm = pwm->data; > + > if (pwm == NULL || period_ns == 0 || duty_ns > period_ns) > return -EINVAL; > > @@ -62,7 +56,7 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) > unsigned long period_cycles, duty_cycles, prescale; > u32 cr; > > - c = clk_get_rate(pwm->clk); > + c = clk_get_rate(mxc_pwm->clk); > c = c * period_ns; > do_div(c, 1000000000); > period_cycles = c; > @@ -74,8 +68,8 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) > do_div(c, period_ns); > duty_cycles = c; > > - writel(duty_cycles, pwm->mmio_base + MX3_PWMSAR); > - writel(period_cycles, pwm->mmio_base + MX3_PWMPR); > + writel(duty_cycles, mxc_pwm->mmio_base + MX3_PWMSAR); > + writel(period_cycles, mxc_pwm->mmio_base + MX3_PWMPR); > > cr = MX3_PWMCR_PRESCALER(prescale) | MX3_PWMCR_EN; > > @@ -84,7 +78,7 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) > else > cr |= MX3_PWMCR_CLKSRC_IPG_HIGH; > > - writel(cr, pwm->mmio_base + MX3_PWMCR); > + writel(cr, mxc_pwm->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 > @@ -102,110 +96,76 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) > * both the prescaler (/1 .. /128) and then by CLKSEL > * (/2 .. /16). > */ > - u32 max = readl(pwm->mmio_base + MX1_PWMP); > + u32 max = readl(mxc_pwm->mmio_base + MX1_PWMP); > u32 p = max * duty_ns / period_ns; > - writel(max - p, pwm->mmio_base + MX1_PWMS); > + writel(max - p, mxc_pwm->mmio_base + MX1_PWMS); > } else { > BUG(); > } > > return 0; > } > -EXPORT_SYMBOL(pwm_config); > > -int pwm_enable(struct pwm_device *pwm) > +static int mxc_pwm_enable(struct pwm_device *pwm) > { > + struct mxc_pwm_device *mxc_pwm = pwm->data; > int rc = 0; > > - if (!pwm->clk_enabled) { > - rc = clk_enable(pwm->clk); > + if (!mxc_pwm->clk_enabled) { > + rc = clk_enable(mxc_pwm->clk); > if (!rc) > - pwm->clk_enabled = 1; > + mxc_pwm->clk_enabled = 1; > } > return rc; > } > -EXPORT_SYMBOL(pwm_enable); > - > -void pwm_disable(struct pwm_device *pwm) > -{ > - writel(0, pwm->mmio_base + MX3_PWMCR); > - > - if (pwm->clk_enabled) { > - clk_disable(pwm->clk); > - pwm->clk_enabled = 0; > - } > -} > -EXPORT_SYMBOL(pwm_disable); > - > -static DEFINE_MUTEX(pwm_lock); > -static LIST_HEAD(pwm_list); > > -struct pwm_device *pwm_request(int pwm_id, const char *label) > +static int mxc_pwm_disable(struct pwm_device *pwm) > { > - struct pwm_device *pwm; > - int found = 0; > + struct mxc_pwm_device *mxc_pwm = pwm->data; > > - mutex_lock(&pwm_lock); > + writel(0, mxc_pwm->mmio_base + MX3_PWMCR); > > - list_for_each_entry(pwm, &pwm_list, node) { > - if (pwm->pwm_id == pwm_id) { > - found = 1; > - break; > - } > + if (mxc_pwm->clk_enabled) { > + clk_disable(mxc_pwm->clk); > + mxc_pwm->clk_enabled = 0; > } > - > - if (found) { > - if (pwm->use_count == 0) { > - pwm->use_count++; > - pwm->label = label; > - } else > - pwm = ERR_PTR(-EBUSY); > - } else > - pwm = ERR_PTR(-ENOENT); > - > - mutex_unlock(&pwm_lock); > - return pwm; > -} > -EXPORT_SYMBOL(pwm_request); > - > -void pwm_free(struct pwm_device *pwm) > -{ > - mutex_lock(&pwm_lock); > - > - if (pwm->use_count) { > - pwm->use_count--; > - pwm->label = NULL; > - } else > - pr_warning("PWM device already freed\n"); > - > - mutex_unlock(&pwm_lock); > + return 0; > } > -EXPORT_SYMBOL(pwm_free); > > static int __devinit mxc_pwm_probe(struct platform_device *pdev) > { > + struct mxc_pwm_device *mxc_pwm; > struct pwm_device *pwm; > + struct pwm_ops *pops; > struct resource *r; > int ret = 0; > > + mxc_pwm = kzalloc(sizeof(struct mxc_pwm_device), GFP_KERNEL); > + if (mxc_pwm == NULL) { > + dev_err(&pdev->dev, "failed to allocate memory\n"); > + return -ENOMEM; > + } > pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL); > if (pwm == NULL) { > dev_err(&pdev->dev, "failed to allocate memory\n"); > - return -ENOMEM; > + ret = -ENOMEM; > + goto err_free1; > + } > + pops = kzalloc(sizeof(struct pwm_ops), GFP_KERNEL); > + if (pops == NULL) { > + dev_err(&pdev->dev, "failed to allocate memory\n"); > + ret = -ENOMEM; > + goto err_free2; > } > > - pwm->clk = clk_get(&pdev->dev, "pwm"); > + mxc_pwm->clk = clk_get(&pdev->dev, "pwm"); > > - if (IS_ERR(pwm->clk)) { > - ret = PTR_ERR(pwm->clk); > - goto err_free; > + if (IS_ERR(mxc_pwm->clk)) { > + ret = PTR_ERR(mxc_pwm->clk); > + goto err_free3; > } > > - pwm->clk_enabled = 0; > - > - pwm->use_count = 0; > - pwm->pwm_id = pdev->id; > - pwm->pdev = pdev; > + mxc_pwm->clk_enabled = 0; > > r = platform_get_resource(pdev, IORESOURCE_MEM, 0); > if (r == NULL) { > @@ -221,16 +181,27 @@ static int __devinit mxc_pwm_probe(struct platform_device *pdev) > goto err_free_clk; > } > > - pwm->mmio_base = ioremap(r->start, r->end - r->start + 1); > - if (pwm->mmio_base == NULL) { > + mxc_pwm->mmio_base = ioremap(r->start, r->end - r->start + 1); > + if (mxc_pwm->mmio_base == NULL) { > dev_err(&pdev->dev, "failed to ioremap() registers\n"); > ret = -ENODEV; > goto err_free_mem; > } > > - mutex_lock(&pwm_lock); > - list_add_tail(&pwm->node, &pwm_list); > - mutex_unlock(&pwm_lock); > + pops->pwm_config = mxc_pwm_config; > + pops->pwm_enable = mxc_pwm_enable; > + pops->pwm_disable = mxc_pwm_disable; > + pops->name = pdev->name; > + > + pwm->pwm_id = pdev->id; > + pwm->dev = &pdev->dev; > + pwm->pops = pops; > + pwm->data = mxc_pwm; > + ret = pwm_device_register(pwm); > + if (ret < 0) { > + dev_err(&pdev->dev, "failed to register pwm device\n"); > + goto err_free_mem; > + } > > platform_set_drvdata(pdev, pwm); > return 0; > @@ -238,33 +209,38 @@ static int __devinit mxc_pwm_probe(struct platform_device *pdev) > err_free_mem: > release_mem_region(r->start, r->end - r->start + 1); > err_free_clk: > - clk_put(pwm->clk); > -err_free: > + clk_put(mxc_pwm->clk); > +err_free3: > + kfree(pops); > +err_free2: > kfree(pwm); > +err_free1: > + kfree(mxc_pwm); > return ret; > } > > static int __devexit mxc_pwm_remove(struct platform_device *pdev) > { > struct pwm_device *pwm; > + struct mxc_pwm_device *mxc_pwm; > struct resource *r; > > pwm = platform_get_drvdata(pdev); > if (pwm == NULL) > return -ENODEV; > + mxc_pwm = pwm->data; > > - mutex_lock(&pwm_lock); > - list_del(&pwm->node); > - mutex_unlock(&pwm_lock); > - > - iounmap(pwm->mmio_base); > + pwm_device_unregister(pwm); > + iounmap(mxc_pwm->mmio_base); > > r = platform_get_resource(pdev, IORESOURCE_MEM, 0); > release_mem_region(r->start, r->end - r->start + 1); > > - clk_put(pwm->clk); > + clk_put(mxc_pwm->clk); > > + kfree(pwm->pops); > kfree(pwm); > + kfree(mxc_pwm); > return 0; > } > > diff --git a/arch/arm/plat-pxa/pwm.c b/arch/arm/plat-pxa/pwm.c > index ef32686..1de902a 100644 > --- a/arch/arm/plat-pxa/pwm.c > +++ b/arch/arm/plat-pxa/pwm.c > @@ -43,33 +43,27 @@ MODULE_DEVICE_TABLE(platform, pwm_id_table); > #define PWMCR_SD (1 << 6) > #define PWMDCR_FD (1 << 10) > > -struct pwm_device { > - struct list_head node; > - struct pwm_device *secondary; > - struct platform_device *pdev; > - > - const char *label; > +struct pxa_pwm_device { > + struct pxa_pwm_device *sec; > struct clk *clk; > int clk_enabled; > void __iomem *mmio_base; > - > - unsigned int use_count; > - unsigned int pwm_id; > }; > > /* > * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE > * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE > */ > -int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) > +int pxa_pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) > { > unsigned long long c; > unsigned long period_cycles, prescale, pv, dc; > + struct pxa_pwm_device *pxa_pwm = pwm->data; > > if (pwm == NULL || period_ns == 0 || duty_ns > period_ns) > return -EINVAL; > > - c = clk_get_rate(pwm->clk); > + c = clk_get_rate(pxa_pwm->clk); > c = c * period_ns; > do_div(c, 1000000000); > period_cycles = c; > @@ -90,94 +84,45 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) > /* NOTE: the clock to PWM has to be enabled first > * before writing to the registers > */ > - clk_enable(pwm->clk); > - __raw_writel(prescale, pwm->mmio_base + PWMCR); > - __raw_writel(dc, pwm->mmio_base + PWMDCR); > - __raw_writel(pv, pwm->mmio_base + PWMPCR); > - clk_disable(pwm->clk); > + clk_enable(pxa_pwm->clk); > + __raw_writel(prescale, pxa_pwm->mmio_base + PWMCR); > + __raw_writel(dc, pxa_pwm->mmio_base + PWMDCR); > + __raw_writel(pv, pxa_pwm->mmio_base + PWMPCR); > + clk_disable(pxa_pwm->clk); > > return 0; > } > -EXPORT_SYMBOL(pwm_config); > > -int pwm_enable(struct pwm_device *pwm) > +int pxa_pwm_enable(struct pwm_device *pwm) > { > + struct pxa_pwm_device *pxa_pwm = pwm->data; > int rc = 0; > > - if (!pwm->clk_enabled) { > - rc = clk_enable(pwm->clk); > + if (!pxa_pwm->clk_enabled) { > + rc = clk_enable(pxa_pwm->clk); > if (!rc) > - pwm->clk_enabled = 1; > + pxa_pwm->clk_enabled = 1; > } > return rc; > } > -EXPORT_SYMBOL(pwm_enable); > > -void pwm_disable(struct pwm_device *pwm) > +int pxa_pwm_disable(struct pwm_device *pwm) > { > - if (pwm->clk_enabled) { > - clk_disable(pwm->clk); > - pwm->clk_enabled = 0; > - } > -} > -EXPORT_SYMBOL(pwm_disable); > - > -static DEFINE_MUTEX(pwm_lock); > -static LIST_HEAD(pwm_list); > + struct pxa_pwm_device *pxa_pwm = pwm->data; > > -struct pwm_device *pwm_request(int pwm_id, const char *label) > -{ > - struct pwm_device *pwm; > - int found = 0; > - > - mutex_lock(&pwm_lock); > - > - list_for_each_entry(pwm, &pwm_list, node) { > - if (pwm->pwm_id == pwm_id) { > - found = 1; > - break; > - } > + if (pxa_pwm->clk_enabled) { > + clk_disable(pxa_pwm->clk); > + pxa_pwm->clk_enabled = 0; > } > - > - if (found) { > - if (pwm->use_count == 0) { > - pwm->use_count++; > - pwm->label = label; > - } else > - pwm = ERR_PTR(-EBUSY); > - } else > - pwm = ERR_PTR(-ENOENT); > - > - mutex_unlock(&pwm_lock); > - return pwm; > -} > -EXPORT_SYMBOL(pwm_request); > - > -void pwm_free(struct pwm_device *pwm) > -{ > - mutex_lock(&pwm_lock); > - > - if (pwm->use_count) { > - pwm->use_count--; > - pwm->label = NULL; > - } else > - pr_warning("PWM device already freed\n"); > - > - mutex_unlock(&pwm_lock); > -} > -EXPORT_SYMBOL(pwm_free); > - > -static inline void __add_pwm(struct pwm_device *pwm) > -{ > - mutex_lock(&pwm_lock); > - list_add_tail(&pwm->node, &pwm_list); > - mutex_unlock(&pwm_lock); > + return 0; > } > > static int __devinit pwm_probe(struct platform_device *pdev) > { > const struct platform_device_id *id = platform_get_device_id(pdev); > + struct pxa_pwm_device *pxa_pwm, *pxa_pwm_sec; > struct pwm_device *pwm, *secondary = NULL; > + struct pwm_ops *pops; > struct resource *r; > int ret = 0; > > @@ -186,17 +131,26 @@ static int __devinit pwm_probe(struct platform_device *pdev) > dev_err(&pdev->dev, "failed to allocate memory\n"); > return -ENOMEM; > } > + pops = kzalloc(sizeof(struct pwm_ops), GFP_KERNEL); > + if (pops == NULL) { > + dev_err(&pdev->dev, "failed to allocate memory\n"); > + kfree(pwm); > + return -ENOMEM; > + } > + pxa_pwm = kzalloc(sizeof(struct pxa_pwm_device), GFP_KERNEL); > + if (pxa_pwm == NULL) { > + dev_err(&pdev->dev, "failed to allocate memory\n"); > + kfree(pops); > + kfree(pwm); > + return -ENOMEM; > + } > > - pwm->clk = clk_get(&pdev->dev, NULL); > - if (IS_ERR(pwm->clk)) { > - ret = PTR_ERR(pwm->clk); > + pxa_pwm->clk = clk_get(&pdev->dev, NULL); > + if (IS_ERR(pxa_pwm->clk)) { > + ret = PTR_ERR(pxa_pwm->clk); > goto err_free; > } > - pwm->clk_enabled = 0; > - > - pwm->use_count = 0; > - pwm->pwm_id = PWM_ID_BASE(id->driver_data) + pdev->id; > - pwm->pdev = pdev; > + pxa_pwm->clk_enabled = 0; > > r = platform_get_resource(pdev, IORESOURCE_MEM, 0); > if (r == NULL) { > @@ -212,69 +166,105 @@ static int __devinit pwm_probe(struct platform_device *pdev) > goto err_free_clk; > } > > - pwm->mmio_base = ioremap(r->start, resource_size(r)); > - if (pwm->mmio_base == NULL) { > + pxa_pwm->mmio_base = ioremap(r->start, resource_size(r)); > + if (pxa_pwm->mmio_base == NULL) { > dev_err(&pdev->dev, "failed to ioremap() registers\n"); > ret = -ENODEV; > goto err_free_mem; > } > > + pops->pwm_config = pxa_pwm_config; > + pops->pwm_enable = pxa_pwm_enable; > + pops->pwm_disable = pxa_pwm_disable; > + pops->name = pdev->name; > + > + pwm->pwm_id = PWM_ID_BASE(id->driver_data) + pdev->id; > + pwm->dev = &pdev->dev; > + pwm->pops = pops; > + pwm->data = pxa_pwm; > + > + ret = pwm_device_register(pwm); > + if (ret < 0) { > + dev_err(&pdev->dev, "failed to register pwm device\n"); > + goto err_free_mem; > + } > + > if (id->driver_data & HAS_SECONDARY_PWM) { > secondary = kzalloc(sizeof(struct pwm_device), GFP_KERNEL); > if (secondary == NULL) { > ret = -ENOMEM; > - goto err_free_mem; > + goto err_pwm; > + } > + pxa_pwm_sec = kzalloc(sizeof(struct pxa_pwm_device), > + GFP_KERNEL); > + if (pxa_pwm_sec == NULL) { > + ret = -ENOMEM; > + goto err_free_mem2; > } > > *secondary = *pwm; > - pwm->secondary = secondary; > + *pxa_pwm_sec = *pxa_pwm; > + pxa_pwm->sec = pxa_pwm_sec; > > /* registers for the second PWM has offset of 0x10 */ > - secondary->mmio_base = pwm->mmio_base + 0x10; > + pxa_pwm_sec->mmio_base = pxa_pwm->mmio_base + 0x10; > secondary->pwm_id = pdev->id + 2; > - } > + secondary->data = pxa_pwm_sec; > > - __add_pwm(pwm); > - if (secondary) > - __add_pwm(secondary); > + ret = pwm_device_register(secondary); > + if (ret < 0) { > + dev_err(&pdev->dev, "failed to register pwm device\n"); > + goto err_free_mem3; > + } > + } > > platform_set_drvdata(pdev, pwm); > return 0; > - > +err_free_mem3: > + kfree(pxa_pwm_sec); > +err_free_mem2: > + kfree(secondary); > +err_pwm: > + pwm_device_unregister(pwm); > err_free_mem: > release_mem_region(r->start, resource_size(r)); > err_free_clk: > - clk_put(pwm->clk); > + clk_put(pxa_pwm->clk); > err_free: > + kfree(pxa_pwm); > + kfree(pops); > kfree(pwm); > return ret; > } > > static int __devexit pwm_remove(struct platform_device *pdev) > { > - struct pwm_device *pwm; > + struct pwm_device *pwm, *secondary; > + struct pxa_pwm_device *pxa_pwm, *pxa_pwm_sec; > struct resource *r; > > pwm = platform_get_drvdata(pdev); > if (pwm == NULL) > return -ENODEV; > - > - mutex_lock(&pwm_lock); > - > - if (pwm->secondary) { > - list_del(&pwm->secondary->node); > - kfree(pwm->secondary); > + pxa_pwm = pwm->data; > + secondary = pwm_request((pdev->id + 2), pdev->name); > + pxa_pwm_sec = secondary->data; > + > + pwm_device_unregister(pwm); > + iounmap(pxa_pwm->mmio_base); > + if (secondary) { > + pwm_device_unregister(secondary); > + iounmap(pxa_pwm->mmio_base); > + kfree(pxa_pwm_sec); > + kfree(secondary); > } > > - list_del(&pwm->node); > - mutex_unlock(&pwm_lock); > - > - iounmap(pwm->mmio_base); > - > r = platform_get_resource(pdev, IORESOURCE_MEM, 0); > release_mem_region(r->start, resource_size(r)); > > - clk_put(pwm->clk); > + clk_put(pxa_pwm->clk); > + kfree(pxa_pwm); > + kfree(pwm->pops); > kfree(pwm); > return 0; > } > diff --git a/arch/arm/plat-samsung/pwm.c b/arch/arm/plat-samsung/pwm.c > index 2eeb49f..63fba01 100644 > --- a/arch/arm/plat-samsung/pwm.c > +++ b/arch/arm/plat-samsung/pwm.c > @@ -26,25 +26,19 @@ > #include <plat/devs.h> > #include <plat/regs-timer.h> > > -struct pwm_device { > - struct list_head list; > +struct s3c_pwm_device { > struct platform_device *pdev; > > struct clk *clk_div; > struct clk *clk; > - const char *label; > > unsigned int period_ns; > unsigned int duty_ns; > > unsigned char tcon_base; > unsigned char running; > - unsigned char use_count; > - unsigned char pwm_id; > }; > > -#define pwm_dbg(_pwm, msg...) dev_dbg(&(_pwm)->pdev->dev, msg) > - > static struct clk *clk_scaler[2]; > > /* Standard setup for a timer block. */ > @@ -78,108 +72,61 @@ struct platform_device s3c_device_timer[] = { > [4] = { DEFINE_S3C_TIMER(4, IRQ_TIMER4) }, > }; > > -static inline int pwm_is_tdiv(struct pwm_device *pwm) > +static inline int pwm_is_tdiv(struct s3c_pwm_device *s3c_pwm) > { > - return clk_get_parent(pwm->clk) == pwm->clk_div; > + return clk_get_parent(s3c_pwm->clk) == s3c_pwm->clk_div; > } > > -static DEFINE_MUTEX(pwm_lock); > -static LIST_HEAD(pwm_list); > +#define pwm_tcon_start(s3c_pwm) (1 << (s3c_pwm->tcon_base + 0)) > +#define pwm_tcon_invert(s3c_pwm) (1 << (s3c_pwm->tcon_base + 2)) > +#define pwm_tcon_autoreload(s3c_pwm) (1 << (s3c_pwm->tcon_base + 3)) > +#define pwm_tcon_manulupdate(s3c_pwm) (1 << (s3c_pwm->tcon_base + 1)) > > -struct pwm_device *pwm_request(int pwm_id, const char *label) > -{ > - struct pwm_device *pwm; > - int found = 0; > - > - mutex_lock(&pwm_lock); > - > - list_for_each_entry(pwm, &pwm_list, list) { > - if (pwm->pwm_id == pwm_id) { > - found = 1; > - break; > - } > - } > - > - if (found) { > - if (pwm->use_count == 0) { > - pwm->use_count = 1; > - pwm->label = label; > - } else > - pwm = ERR_PTR(-EBUSY); > - } else > - pwm = ERR_PTR(-ENOENT); > - > - mutex_unlock(&pwm_lock); > - return pwm; > -} > - > -EXPORT_SYMBOL(pwm_request); > - > - > -void pwm_free(struct pwm_device *pwm) > -{ > - mutex_lock(&pwm_lock); > - > - if (pwm->use_count) { > - pwm->use_count--; > - pwm->label = NULL; > - } else > - printk(KERN_ERR "PWM%d device already freed\n", pwm->pwm_id); > - > - mutex_unlock(&pwm_lock); > -} > - > -EXPORT_SYMBOL(pwm_free); > - > -#define pwm_tcon_start(pwm) (1 << (pwm->tcon_base + 0)) > -#define pwm_tcon_invert(pwm) (1 << (pwm->tcon_base + 2)) > -#define pwm_tcon_autoreload(pwm) (1 << (pwm->tcon_base + 3)) > -#define pwm_tcon_manulupdate(pwm) (1 << (pwm->tcon_base + 1)) > - > -int pwm_enable(struct pwm_device *pwm) > +int s3c_pwm_enable(struct pwm_device *pwm) > { > unsigned long flags; > unsigned long tcon; > + struct s3c_pwm_device *s3c_pwm = pwm->data; > > local_irq_save(flags); > > tcon = __raw_readl(S3C2410_TCON); > - tcon |= pwm_tcon_start(pwm); > + tcon |= pwm_tcon_start(s3c_pwm); > __raw_writel(tcon, S3C2410_TCON); > > local_irq_restore(flags); > > - pwm->running = 1; > + s3c_pwm->running = 1; > return 0; > } > > -EXPORT_SYMBOL(pwm_enable); > - > -void pwm_disable(struct pwm_device *pwm) > +int s3c_pwm_disable(struct pwm_device *pwm) > { > unsigned long flags; > unsigned long tcon; > + struct s3c_pwm_device *s3c_pwm = pwm->data; > > local_irq_save(flags); > > tcon = __raw_readl(S3C2410_TCON); > - tcon &= ~pwm_tcon_start(pwm); > + tcon &= ~pwm_tcon_start(s3c_pwm); > __raw_writel(tcon, S3C2410_TCON); > > local_irq_restore(flags); > > - pwm->running = 0; > + s3c_pwm->running = 0; > + return 0; > } > > -EXPORT_SYMBOL(pwm_disable); > - > -static unsigned long pwm_calc_tin(struct pwm_device *pwm, unsigned long freq) > +static unsigned long pwm_calc_tin(struct pwm_device *pwm, > + unsigned long freq) > { > unsigned long tin_parent_rate; > unsigned int div; > + struct s3c_pwm_device *s3c_pwm = pwm->data; > > - tin_parent_rate = clk_get_rate(clk_get_parent(pwm->clk_div)); > - pwm_dbg(pwm, "tin parent at %lu\n", tin_parent_rate); > + tin_parent_rate = clk_get_rate(clk_get_parent(s3c_pwm->clk_div)); > + dev_dbg(pwm->dev, "tin parent at %lu\n", tin_parent_rate); > > for (div = 2; div <= 16; div *= 2) { > if ((tin_parent_rate / (div << 16)) < freq) > @@ -191,7 +138,7 @@ static unsigned long pwm_calc_tin(struct pwm_device *pwm, unsigned long freq) > > #define NS_IN_HZ (1000000000UL) > > -int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) > +int s3c_pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) > { > unsigned long tin_rate; > unsigned long tin_ns; > @@ -200,6 +147,7 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) > unsigned long tcon; > unsigned long tcnt; > long tcmp; > + struct s3c_pwm_device *s3c_pwm = pwm->data; > > /* We currently avoid using 64bit arithmetic by using the > * fact that anything faster than 1Hz is easily representable > @@ -211,8 +159,8 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) > if (duty_ns > period_ns) > return -EINVAL; > > - if (period_ns == pwm->period_ns && > - duty_ns == pwm->duty_ns) > + if (period_ns == s3c_pwm->period_ns && > + duty_ns == s3c_pwm->duty_ns) > return 0; > > /* The TCMP and TCNT can be read without a lock, they're not > @@ -223,26 +171,26 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) > > period = NS_IN_HZ / period_ns; > > - pwm_dbg(pwm, "duty_ns=%d, period_ns=%d (%lu)\n", > + dev_dbg(pwm->dev, "duty_ns=%d, period_ns=%d (%lu)\n", > duty_ns, period_ns, period); > > /* Check to see if we are changing the clock rate of the PWM */ > > - if (pwm->period_ns != period_ns) { > - if (pwm_is_tdiv(pwm)) { > + if (s3c_pwm->period_ns != period_ns) { > + if (pwm_is_tdiv(s3c_pwm)) { > tin_rate = pwm_calc_tin(pwm, period); > - clk_set_rate(pwm->clk_div, tin_rate); > + clk_set_rate(s3c_pwm->clk_div, tin_rate); > } else > - tin_rate = clk_get_rate(pwm->clk); > + tin_rate = clk_get_rate(s3c_pwm->clk); > > - pwm->period_ns = period_ns; > + s3c_pwm->period_ns = period_ns; > > - pwm_dbg(pwm, "tin_rate=%lu\n", tin_rate); > + dev_dbg(pwm->dev, "tin_rate=%lu\n", tin_rate); > > tin_ns = NS_IN_HZ / tin_rate; > tcnt = period_ns / tin_ns; > } else > - tin_ns = NS_IN_HZ / clk_get_rate(pwm->clk); > + tin_ns = NS_IN_HZ / clk_get_rate(s3c_pwm->clk); > > /* Note, counters count down */ > > @@ -253,7 +201,7 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) > if (tcmp == tcnt) > tcmp--; > > - pwm_dbg(pwm, "tin_ns=%lu, tcmp=%ld/%lu\n", tin_ns, tcmp, tcnt); > + dev_dbg(pwm->dev, "tin_ns=%lu, tcmp=%ld/%lu\n", tin_ns, tcmp, tcnt); > > if (tcmp < 0) > tcmp = 0; > @@ -266,11 +214,11 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) > __raw_writel(tcnt, S3C2410_TCNTB(pwm->pwm_id)); > > tcon = __raw_readl(S3C2410_TCON); > - tcon |= pwm_tcon_manulupdate(pwm); > - tcon |= pwm_tcon_autoreload(pwm); > + tcon |= pwm_tcon_manulupdate(s3c_pwm); > + tcon |= pwm_tcon_autoreload(s3c_pwm); > __raw_writel(tcon, S3C2410_TCON); > > - tcon &= ~pwm_tcon_manulupdate(pwm); > + tcon &= ~pwm_tcon_manulupdate(s3c_pwm); > __raw_writel(tcon, S3C2410_TCON); > > local_irq_restore(flags); > @@ -278,103 +226,122 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) > return 0; > } > > -EXPORT_SYMBOL(pwm_config); > - > -static int pwm_register(struct pwm_device *pwm) > -{ > - pwm->duty_ns = -1; > - pwm->period_ns = -1; > - > - mutex_lock(&pwm_lock); > - list_add_tail(&pwm->list, &pwm_list); > - mutex_unlock(&pwm_lock); > - > - return 0; > -} > - > static int s3c_pwm_probe(struct platform_device *pdev) > { > struct device *dev = &pdev->dev; > + struct s3c_pwm_device *s3c_pwm; > struct pwm_device *pwm; > + struct pwm_ops *pops; > unsigned long flags; > unsigned long tcon; > unsigned int id = pdev->id; > - int ret; > + int ret = 0; > > if (id == 4) { > dev_err(dev, "TIMER4 is currently not supported\n"); > return -ENXIO; > } > > + s3c_pwm = kzalloc(sizeof(struct s3c_pwm_device), GFP_KERNEL); > + if (s3c_pwm == NULL) { > + dev_err(dev, "failed to allocate pwm_device\n"); > + return -ENOMEM; > + } > + s3c_pwm->pdev = pdev; > pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL); > if (pwm == NULL) { > dev_err(dev, "failed to allocate pwm_device\n"); > - return -ENOMEM; > + goto err_alloc; > + ret = -ENOMEM; > + } > + pops = kzalloc(sizeof(struct pwm_ops), GFP_KERNEL); > + if (pops == NULL) { > + dev_err(dev, "failed to allocate memory\n"); > + goto err_alloc1; > + ret = -ENOMEM; > } > - > - pwm->pdev = pdev; > - pwm->pwm_id = id; > > /* calculate base of control bits in TCON */ > - pwm->tcon_base = id == 0 ? 0 : (id * 4) + 4; > + s3c_pwm->tcon_base = id == 0 ? 0 : (id * 4) + 4; > > - pwm->clk = clk_get(dev, "pwm-tin"); > - if (IS_ERR(pwm->clk)) { > + s3c_pwm->clk = clk_get(dev, "pwm-tin"); > + if (IS_ERR(s3c_pwm->clk)) { > dev_err(dev, "failed to get pwm tin clk\n"); > - ret = PTR_ERR(pwm->clk); > - goto err_alloc; > + ret = PTR_ERR(s3c_pwm->clk); > + goto err_alloc2; > } > > - pwm->clk_div = clk_get(dev, "pwm-tdiv"); > - if (IS_ERR(pwm->clk_div)) { > + s3c_pwm->clk_div = clk_get(dev, "pwm-tdiv"); > + if (IS_ERR(s3c_pwm->clk_div)) { > dev_err(dev, "failed to get pwm tdiv clk\n"); > - ret = PTR_ERR(pwm->clk_div); > + ret = PTR_ERR(s3c_pwm->clk_div); > goto err_clk_tin; > } > > local_irq_save(flags); > > tcon = __raw_readl(S3C2410_TCON); > - tcon |= pwm_tcon_invert(pwm); > + tcon |= pwm_tcon_invert(s3c_pwm); > __raw_writel(tcon, S3C2410_TCON); > > local_irq_restore(flags); > > + pops->pwm_config = s3c_pwm_config; > + pops->pwm_enable = s3c_pwm_enable; > + pops->pwm_disable = s3c_pwm_disable; > + pops->name = pdev->name; > + > + pwm->dev = dev; > + pwm->pwm_id = id; > + pwm->pops = pops; > + pwm->data = s3c_pwm; > > - ret = pwm_register(pwm); > + s3c_pwm->duty_ns = -1; > + s3c_pwm->period_ns = -1; > + ret = pwm_device_register(pwm); > if (ret) { > dev_err(dev, "failed to register pwm\n"); > goto err_clk_tdiv; > } > > - pwm_dbg(pwm, "config bits %02x\n", > - (__raw_readl(S3C2410_TCON) >> pwm->tcon_base) & 0x0f); > + dev_dbg(dev, "config bits %02x\n", > + (__raw_readl(S3C2410_TCON) >> s3c_pwm->tcon_base) & 0x0f); > > dev_info(dev, "tin at %lu, tdiv at %lu, tin=%sclk, base %d\n", > - clk_get_rate(pwm->clk), > - clk_get_rate(pwm->clk_div), > - pwm_is_tdiv(pwm) ? "div" : "ext", pwm->tcon_base); > + clk_get_rate(s3c_pwm->clk), > + clk_get_rate(s3c_pwm->clk_div), > + pwm_is_tdiv(s3c_pwm) ? "div" : "ext", s3c_pwm->tcon_base); > > platform_set_drvdata(pdev, pwm); > return 0; > > - err_clk_tdiv: > - clk_put(pwm->clk_div); > +err_clk_tdiv: > + clk_put(s3c_pwm->clk_div); > > - err_clk_tin: > - clk_put(pwm->clk); > +err_clk_tin: > + clk_put(s3c_pwm->clk); > > - err_alloc: > +err_alloc2: > + kfree(pops); > + > +err_alloc1: > kfree(pwm); > + > +err_alloc: > + kfree(s3c_pwm); > return ret; > } > > static int __devexit s3c_pwm_remove(struct platform_device *pdev) > { > struct pwm_device *pwm = platform_get_drvdata(pdev); > + struct s3c_pwm_device *s3c_pwm = pwm->data; > > - clk_put(pwm->clk_div); > - clk_put(pwm->clk); > + pwm_device_unregister(pwm); > + clk_put(s3c_pwm->clk_div); > + clk_put(s3c_pwm->clk); > + kfree(s3c_pwm); > + kfree(pwm->pops); > kfree(pwm); > > return 0; > @@ -384,13 +351,14 @@ static int __devexit s3c_pwm_remove(struct platform_device *pdev) > static int s3c_pwm_suspend(struct platform_device *pdev, pm_message_t state) > { > struct pwm_device *pwm = platform_get_drvdata(pdev); > + struct s3c_pwm_device *s3c_pwm = pwm->data; > > /* No one preserve these values during suspend so reset them > * Otherwise driver leaves PWM unconfigured if same values > * passed to pwm_config > */ > - pwm->period_ns = 0; > - pwm->duty_ns = 0; > + s3c_pwm->period_ns = 0; > + s3c_pwm->duty_ns = 0; > > return 0; > } > @@ -398,11 +366,12 @@ static int s3c_pwm_suspend(struct platform_device *pdev, pm_message_t state) > static int s3c_pwm_resume(struct platform_device *pdev) > { > struct pwm_device *pwm = platform_get_drvdata(pdev); > + struct s3c_pwm_device *s3c_pwm = pwm->data; > unsigned long tcon; > > /* Restore invertion */ > tcon = __raw_readl(S3C2410_TCON); > - tcon |= pwm_tcon_invert(pwm); > + tcon |= pwm_tcon_invert(s3c_pwm); > __raw_writel(tcon, S3C2410_TCON); > > return 0; > diff --git a/arch/mips/jz4740/pwm.c b/arch/mips/jz4740/pwm.c > index a26a6fa..9f46767 100644 > --- a/arch/mips/jz4740/pwm.c > +++ b/arch/mips/jz4740/pwm.c > @@ -152,7 +152,7 @@ int pwm_enable(struct pwm_device *pwm) > return 0; > } > > -void pwm_disable(struct pwm_device *pwm) > +int pwm_disable(struct pwm_device *pwm) > { > uint32_t ctrl = jz4740_timer_get_ctrl(pwm->id); > > diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c > index b0f2c00..6a6ea41 100644 > --- a/drivers/mfd/twl-core.c > +++ b/drivers/mfd/twl-core.c > @@ -129,6 +129,12 @@ > #define twl_has_pwrbutton() false > #endif > > +#if defined CONFIG_TWL6030_PWM > +#define twl_has_pwm() true > +#else > +#define twl_has_pwm() false > +#endif > + > #define SUB_CHIP_ID0 0 > #define SUB_CHIP_ID1 1 > #define SUB_CHIP_ID2 2 > @@ -825,6 +831,13 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) > if (IS_ERR(child)) > return PTR_ERR(child); > } > + if (twl_has_pwm()) { > + child = add_child(SUB_CHIP_ID2, "twl6030_pwm", > + NULL, 0, > + false, 0, 0); > + if (IS_ERR(child)) > + return PTR_ERR(child); > + } > > return 0; > } > diff --git a/drivers/mfd/twl6030-pwm.c b/drivers/mfd/twl6030-pwm.c > index 5d25bdc..b78324b 100644 > --- a/drivers/mfd/twl6030-pwm.c > +++ b/drivers/mfd/twl6030-pwm.c > @@ -20,8 +20,10 @@ > > #include <linux/module.h> > #include <linux/platform_device.h> > -#include <linux/i2c/twl.h> > #include <linux/slab.h> > +#include <linux/pwm.h> > +#include <linux/err.h> > +#include <linux/i2c/twl.h> > > #define LED_PWM_CTRL1 0xF4 > #define LED_PWM_CTRL2 0xF5 > @@ -45,15 +47,10 @@ > > #define PWM_CTRL2_MODE_MASK 0x3 > > -struct pwm_device { > - const char *label; > - unsigned int pwm_id; > -}; > - > -int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) > +int twl6030_pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) > { > u8 duty_cycle; > - int ret; > + int ret = 0; > > if (pwm == NULL || period_ns == 0 || duty_ns > period_ns) > return -EINVAL; > @@ -69,12 +66,11 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) > } > return 0; > } > -EXPORT_SYMBOL(pwm_config); > > -int pwm_enable(struct pwm_device *pwm) > +int twl6030_pwm_enable(struct pwm_device *pwm) > { > u8 val; > - int ret; > + int ret = 0; > > ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2); > if (ret < 0) { > @@ -95,18 +91,17 @@ int pwm_enable(struct pwm_device *pwm) > twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2); > return 0; > } > -EXPORT_SYMBOL(pwm_enable); > > -void pwm_disable(struct pwm_device *pwm) > +int twl6030_pwm_disable(struct pwm_device *pwm) > { > u8 val; > - int ret; > + int ret = 0; > > ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2); > if (ret < 0) { > pr_err("%s: Failed to disable PWM, Error %d\n", > pwm->label, ret); > - return; > + return ret; > } > > val &= ~PWM_CTRL2_MODE_MASK; > @@ -116,48 +111,86 @@ void pwm_disable(struct pwm_device *pwm) > if (ret < 0) { > pr_err("%s: Failed to disable PWM, Error %d\n", > pwm->label, ret); > - return; > } > - return; > + return ret; > } > -EXPORT_SYMBOL(pwm_disable); > > -struct pwm_device *pwm_request(int pwm_id, const char *label) > +static int __devinit twl6030_pwm_probe(struct platform_device *pdev) > { > - u8 val; > - int ret; > struct pwm_device *pwm; > + struct pwm_ops *pops; > + int ret; > + u8 val; > > pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL); > if (pwm == NULL) { > - pr_err("%s: failed to allocate memory\n", label); > - return NULL; > + dev_err(&pdev->dev, "failed to allocate memory\n"); > + return -ENOMEM; > + } > + pops = kzalloc(sizeof(struct pwm_ops), GFP_KERNEL); > + if (pops == NULL) { > + dev_err(&pdev->dev, "failed to allocate memory\n"); > + kfree(pwm); > + return -ENOMEM; > } > > - pwm->label = label; > - pwm->pwm_id = pwm_id; > - > + pops->pwm_config = twl6030_pwm_config; > + pops->pwm_enable = twl6030_pwm_enable; > + pops->pwm_disable = twl6030_pwm_disable; > + pops->name = &pdev->name; > + pwm->dev = &pdev->dev; > + pwm->pwm_id = pdev->id; > + pwm->pops = pops; > + ret = pwm_device_register(pwm); > + if (ret < 0) { > + dev_err(&pdev->dev, "failed to register pwm device\n"); > + kfree(pwm); > + kfree(pops); > + return ret; > + } > + platform_set_drvdata(pdev, pwm); > /* Configure PWM */ > val = PWM_CTRL2_DIS_PD | PWM_CTRL2_CURR_02 | PWM_CTRL2_SRC_VAC | > - PWM_CTRL2_MODE_HW; > + PWM_CTRL2_MODE_HW; > > ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2); > - > if (ret < 0) { > - pr_err("%s: Failed to configure PWM, Error %d\n", > - pwm->label, ret); > - > - kfree(pwm); > - return NULL; > + dev_err(&pdev->dev, "Failed to configure PWM, Error %d\n", ret); > + return ret; > } > - > - return pwm; > + dev_dbg(&pdev->dev, "pwm probe successful\n"); > + return ret; > } > -EXPORT_SYMBOL(pwm_request); > > -void pwm_free(struct pwm_device *pwm) > +static int __devexit twl6030_pwm_remove(struct platform_device *pdev) > { > - pwm_disable(pwm); > + struct pwm_device *pwm = platform_get_drvdata(pdev); > + > + pwm_device_unregister(pwm); > + kfree(pwm->pops); > kfree(pwm); > + dev_dbg(&pdev->dev, "pwm driver removed\n"); > + return 0; > } > -EXPORT_SYMBOL(pwm_free); > + > +static struct platform_driver twl6030_pwm_driver = { > + .driver = { > + .name = "twl6030_pwm", > + .owner = THIS_MODULE, > + }, > + .probe = twl6030_pwm_probe, > + .remove = __devexit_p(twl6030_pwm_remove), > +}; > + > +static int __init twl6030_pwm_init(void) > +{ > + return platform_driver_register(&twl6030_pwm_driver); > +} > + > +static void __exit twl6030_pwm_deinit(void) > +{ > + platform_driver_unregister(&twl6030_pwm_driver); > +} > + > +subsys_initcall(twl6030_pwm_init); > +module_exit(twl6030_pwm_deinit); > diff --git a/drivers/misc/ab8500-pwm.c b/drivers/misc/ab8500-pwm.c > index 54e3d05..d2b23b6 100644 > --- a/drivers/misc/ab8500-pwm.c > +++ b/drivers/misc/ab8500-pwm.c > @@ -23,16 +23,9 @@ > #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 ab8500_pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) > { > int ret = 0; > unsigned int higher_val, lower_val; > @@ -60,23 +53,21 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) > > return ret; > } > -EXPORT_SYMBOL(pwm_config); > > -int pwm_enable(struct pwm_device *pwm) > +int ab8500_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); > + 1 << (pwm->pwm_id-1), 1 << (pwm->pwm_id-1)); > 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 ab8500_pwm_disable(struct pwm_device *pwm) > { > int ret; > > @@ -86,58 +77,56 @@ void pwm_disable(struct pwm_device *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); > + return ret; > } > -EXPORT_SYMBOL(pwm_free); > > static int __devinit ab8500_pwm_probe(struct platform_device *pdev) > { > - struct pwm_device *pwm; > + int ret = 0; > + struct pwm_ops *pops; > + struct pwm_device *pwm_dev; > /* > * 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) { > + pops = kzalloc(sizeof(struct pwm_ops), GFP_KERNEL); > + if (pops == 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; > + pwm_dev = kzalloc(sizeof(struct pwm_device), GFP_KERNEL); > + if (pwm_dev == NULL) { > + dev_err(&pdev->dev, "failed to allocate memory\n"); > + kfree(pops); > + return -ENOMEM; > + } > + pops->pwm_config = ab8500_pwm_config; > + pops->pwm_enable = ab8500_pwm_enable; > + pops->pwm_disable = ab8500_pwm_disable; > + pops->name = "ab8500"; > + pwm_dev->dev = &pdev->dev; > + pwm_dev->pwm_id = pdev->id; > + pwm_dev->pops = pops; > + ret = pwm_device_register(pwm_dev); > + if (ret < 0) { > + dev_err(&pdev->dev, "failed to register pwm device\n"); > + kfree(pwm_dev); > + kfree(pops); > + return ret; > + } > + platform_set_drvdata(pdev, pwm_dev); > + dev_dbg(&pdev->dev, "pwm probe successful\n"); > + return ret; > } > > static int __devexit ab8500_pwm_remove(struct platform_device *pdev) > { > - struct pwm_device *pwm = platform_get_drvdata(pdev); > - list_del(&pwm->node); > + struct pwm_device *pwm_dev = platform_get_drvdata(pdev); > + > + pwm_device_unregister(pwm_dev); > dev_dbg(&pdev->dev, "pwm driver removed\n"); > - kfree(pwm); > + kfree(pwm_dev->pops); > + kfree(pwm_dev); > return 0; > } > > diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig > index 5d10106..a88640c 100644 > --- a/drivers/pwm/Kconfig > +++ b/drivers/pwm/Kconfig > @@ -4,6 +4,7 @@ > > menuconfig PWM_DEVICES > bool "PWM devices" > + depends on ARM > default y > ---help--- > Say Y here to get to see options for device drivers from various > diff --git a/drivers/pwm/pwm-core.c b/drivers/pwm/pwm-core.c > index b84027a..3a0d426 100644 Why can't these changes be in the initial patch which adds pwm-core? > --- a/drivers/pwm/pwm-core.c > +++ b/drivers/pwm/pwm-core.c > @@ -11,11 +11,6 @@ > #include <linux/err.h> > #include <linux/pwm.h> > > -struct pwm_device { > - struct pwm_ops *pops; > - int pwm_id; > -}; > - > struct pwm_dev_info { > struct pwm_device *pwm_dev; > struct list_head list; > @@ -40,9 +35,9 @@ int pwm_enable(struct pwm_device *pwm) > } > EXPORT_SYMBOL(pwm_enable); > > -void pwm_disable(struct pwm_device *pwm) > +int pwm_disable(struct pwm_device *pwm) > { > - pwm->pops->pwm_disable(pwm); > + return pwm->pops->pwm_disable(pwm); > } > EXPORT_SYMBOL(pwm_disable); > > diff --git a/include/linux/pwm.h b/include/linux/pwm.h > index 6e7da1f..4344c0b 100644 > --- a/include/linux/pwm.h > +++ b/include/linux/pwm.h > @@ -1,14 +1,29 @@ > #ifndef __LINUX_PWM_H > #define __LINUX_PWM_H > > -struct pwm_device; > +/* > + * TODO: #if defined CONFIG_PWM_CORE has to be removed after mips jz4740 > + * pwm driver aligning with pwm-core.c driver. > + */ > +#if defined CONFIG_PWM_CORE > +struct pwm_device { > + struct pwm_ops *pops; > + struct device *dev; > + struct list_head node; > + const char *label; > + unsigned int pwm_id; > + void *data; > +}; > > struct pwm_ops { > int (*pwm_config)(struct pwm_device *pwm, int duty_ns, int period_ns); > int (*pwm_enable)(struct pwm_device *pwm); > int (*pwm_disable)(struct pwm_device *pwm); > - char *name; > + const char *name; > }; > +#else > +struct pwm_device; > +#endif > > /* > * pwm_request - request a PWM device > @@ -33,7 +48,7 @@ int pwm_enable(struct pwm_device *pwm); > /* > * pwm_disable - stop a PWM output toggling > */ > -void pwm_disable(struct pwm_device *pwm); > +int pwm_disable(struct pwm_device *pwm); > > int pwm_device_register(struct pwm_device *pwm_dev); > int pwm_device_unregister(struct pwm_device *pwm_dev); ^ permalink raw reply [flat|nested] 16+ messages in thread
[parent not found: <F45880696056844FA6A73F415B568C69532DC2FB98@EXDCVYMBSTM006.EQ1STM.local>]
* [PATCH 4/7] pwm: Align existing pwm drivers with pwm-core driver [not found] ` <F45880696056844FA6A73F415B568C69532DC2FB98@EXDCVYMBSTM006.EQ1STM.local> @ 2010-09-28 10:10 ` Lars-Peter Clausen 0 siblings, 0 replies; 16+ messages in thread From: Lars-Peter Clausen @ 2010-09-28 10:10 UTC (permalink / raw) To: linux-arm-kernel Arun MURTHY wrote: >>> mips-jz4740: pwm: Align with new pwm core driver >>> >>> PWM core driver has been added and has been enabled only for ARM >>> platform. The same can be utilised for mips also. >>> Please align with the pwm core driver(drivers/pwm-core.c). >> >> Is there any reason for artificially limiting it to ARM? > > No not at all, right now I have aligned all existing pwm drivers in ARM to make use of the pwm core driver. > But faced difficulty in aligning the mips-jz4740 pwm driver, without having much knowledge about the device/data sheet. > Hence I have let it to the maintainer of that driver to align and thereafter this limitation will be removed. > Have also comments the same as TODO in the driver. > Ok, I'll take care of adjusting the jz4740 pwm driver once the pwm-core is in proper shape. But I still think it would be better to have a config symbol which would be selected by SoC code and on which PWM_CORE would depend. Then it would be possible for SoC implementation to device whether it wants to provide it's own PWM API implementation or use pwm-core. >>> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig >>> index 5d10106..a88640c 100644 >>> --- a/drivers/pwm/Kconfig >>> +++ b/drivers/pwm/Kconfig >>> @@ -4,6 +4,7 @@ >>> >>> menuconfig PWM_DEVICES >>> bool "PWM devices" >>> + depends on ARM >>> default y >>> ---help--- >>> Say Y here to get to see options for device drivers from >> various >>> diff --git a/drivers/pwm/pwm-core.c b/drivers/pwm/pwm-core.c >>> index b84027a..3a0d426 100644 >> >> >> Why can't these changes be in the initial patch which adds pwm-core? >> > Since by default this driver is enabled, and if there is some other pwm driver enabled, both happen to export the same function(pwm_enable/pwm_disable,..) After applying the first patch build may fail. > I would understand that if you were just moving code around, but the pwm_device struct looks completly different now. > Thanks and Regards, > Arun R Murthy > ------------- > ^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2010-09-28 13:35 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-09-28 10:35 [PATCH 0/7] PWM core driver for pwm based led and backlight driver Arun Murthy
2010-09-28 10:35 ` [PATCH 1/7] pwm: Add pwm core driver Arun Murthy
2010-09-28 12:53 ` Hemanth V
2010-09-28 13:06 ` Samuel Ortiz
2010-09-28 13:35 ` Felipe Balbi
2010-09-28 10:35 ` [PATCH 2/7] backlight:pwm: add an element 'name' to platform data Arun Murthy
2010-09-28 10:35 ` [PATCH 3/7] leds: pwm: add a new " Arun Murthy
2010-09-28 10:43 ` Sergei Shtylyov
2010-09-28 10:48 ` Arun MURTHY
2010-09-28 10:35 ` [PATCH 4/7] pwm: Align existing pwm drivers with pwm-core driver Arun Murthy
2010-09-28 10:35 ` [PATCH 5/7] platform: Update the pwm based led and backlight platform data Arun Murthy
2010-09-28 10:35 ` [PATCH 6/7] pwm: move existing pwm driver to drivers/pwm Arun Murthy
2010-09-28 10:35 ` [PATCH 7/7] pwm: Modify backlight and led Kconfig aligning to pwm core Arun Murthy
-- strict thread matches above, loose matches on Subject: below --
2010-09-28 7:40 [PATCHv2 0/7] PWM core driver for pwm based led and backlight driver Arun Murthy
2010-09-28 7:40 ` [PATCH 4/7] pwm: Align existing pwm drivers with pwm-core driver Arun Murthy
2010-09-28 8:58 ` Lars-Peter Clausen
[not found] ` <F45880696056844FA6A73F415B568C69532DC2FB98@EXDCVYMBSTM006.EQ1STM.local>
2010-09-28 10:10 ` Lars-Peter Clausen
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).