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@xxxxxxxxxxxxxx> > Acked-by: Linus Walleij <linus.walleij@xxxxxxxxxxxxxx> > --- > 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);