diff options
Diffstat (limited to 'target/linux/generic-2.6/files/drivers/pwm')
| -rw-r--r-- | target/linux/generic-2.6/files/drivers/pwm/Kconfig | 31 | ||||
| -rw-r--r-- | target/linux/generic-2.6/files/drivers/pwm/Makefile | 6 | ||||
| -rw-r--r-- | target/linux/generic-2.6/files/drivers/pwm/atmel-pwm.c | 592 | ||||
| -rw-r--r-- | target/linux/generic-2.6/files/drivers/pwm/gpio.c | 298 | ||||
| -rw-r--r-- | target/linux/generic-2.6/files/drivers/pwm/pwm.c | 643 | 
5 files changed, 0 insertions, 1570 deletions
diff --git a/target/linux/generic-2.6/files/drivers/pwm/Kconfig b/target/linux/generic-2.6/files/drivers/pwm/Kconfig deleted file mode 100644 index 1c24e1107..000000000 --- a/target/linux/generic-2.6/files/drivers/pwm/Kconfig +++ /dev/null @@ -1,31 +0,0 @@ -# -# PWM infrastructure and devices -# - -menuconfig GENERIC_PWM -	tristate "PWM Support" -	depends on SYSFS -	help -	  This enables PWM support through the generic PWM library. -	  If unsure, say N. - -if GENERIC_PWM - -config ATMEL_PWM -	tristate "Atmel AT32/AT91 PWM support" -	depends on AVR32 || ARCH_AT91 -	help -	  This option enables device driver support for the PWMC -	  peripheral channels found on certain Atmel processors. -	  Pulse Width Modulation is used many for purposes, including -	  software controlled power-efficient backlights on LCD -	  displays, motor control, and waveform generation.  If -	  unsure, say N. - -config GPIO_PWM -       tristate "PWM emulation using GPIO" -       help -         This option enables a single-channel PWM device using -	 a kernel interval timer and a GPIO pin.  If unsure, say N. - -endif diff --git a/target/linux/generic-2.6/files/drivers/pwm/Makefile b/target/linux/generic-2.6/files/drivers/pwm/Makefile deleted file mode 100644 index e8cacc574..000000000 --- a/target/linux/generic-2.6/files/drivers/pwm/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# -# Makefile for pwm devices -# -obj-y := pwm.o -obj-$(CONFIG_ATMEL_PWM)		+= atmel-pwm.o -obj-$(CONFIG_GPIO_PWM)		+= gpio.o diff --git a/target/linux/generic-2.6/files/drivers/pwm/atmel-pwm.c b/target/linux/generic-2.6/files/drivers/pwm/atmel-pwm.c deleted file mode 100644 index 158bb922c..000000000 --- a/target/linux/generic-2.6/files/drivers/pwm/atmel-pwm.c +++ /dev/null @@ -1,592 +0,0 @@ -/* - * drivers/pwm/atmel-pwm.c - * - * Copyright (C) 2010 Bill Gatliff <bgat@billgatliff.com> - * Copyright (C) 2007 David Brownell - * - * This program is free software; you may redistribute and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/clk.h> -#include <linux/err.h> -#include <linux/io.h> -#include <linux/interrupt.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/pwm/pwm.h> - -enum { -	/* registers common to the PWMC peripheral */ -	PWMC_MR = 0, -	PWMC_ENA = 4, -	PWMC_DIS = 8, -	PWMC_SR = 0xc, -	PWMC_IER = 0x10, -	PWMC_IDR = 0x14, -	PWMC_IMR = 0x18, -	PWMC_ISR = 0x1c, - -	/* registers per each PWMC channel */ -	PWMC_CMR = 0, -	PWMC_CDTY = 4, -	PWMC_CPRD = 8, -	PWMC_CCNT = 0xc, -	PWMC_CUPD = 0x10, - -	/* how to find each channel */ -	PWMC_CHAN_BASE = 0x200, -	PWMC_CHAN_STRIDE = 0x20, - -	/* CMR bits of interest */ -	PWMC_CMR_CPD = 10, -	PWMC_CMR_CPOL = 9, -	PWMC_CMR_CALG = 8, -	PWMC_CMR_CPRE_MASK = 0xf, -}; - -struct atmel_pwm { -	struct pwm_device pwm; -	spinlock_t lock; -	void __iomem *iobase; -	struct clk *clk; -	u32 *sync_mask; -	int irq; -	u32 ccnt_mask; -}; - -static inline struct atmel_pwm *to_atmel_pwm(const struct pwm_channel *p) -{ -	return container_of(p->pwm, struct atmel_pwm, pwm); -} - -static inline void -pwmc_writel(const struct atmel_pwm *p, -	    unsigned offset, u32 val) -{ -	__raw_writel(val, p->iobase + offset); -} - -static inline u32 -pwmc_readl(const struct atmel_pwm *p, -	   unsigned offset) -{ -	return __raw_readl(p->iobase + offset); -} - -static inline void -pwmc_chan_writel(const struct pwm_channel *p, -		 u32 offset, u32 val) -{ -	const struct atmel_pwm *ap = to_atmel_pwm(p); - -	if (PWMC_CMR == offset) -		val &= ((1 << PWMC_CMR_CPD) -			| (1 << PWMC_CMR_CPOL) -			| (1 << PWMC_CMR_CALG) -			| (PWMC_CMR_CPRE_MASK)); -	else -		val &= ap->ccnt_mask; - -	pwmc_writel(ap, offset + PWMC_CHAN_BASE -		    + (p->chan * PWMC_CHAN_STRIDE), val); -} - -static inline u32 -pwmc_chan_readl(const struct pwm_channel *p, -		u32 offset) -{ -	const struct atmel_pwm *ap = to_atmel_pwm(p); - -	return pwmc_readl(ap, offset + PWMC_CHAN_BASE -			  + (p->chan * PWMC_CHAN_STRIDE)); -} - -static inline int -__atmel_pwm_is_on(struct pwm_channel *p) -{ -	struct atmel_pwm *ap = to_atmel_pwm(p); -	return (pwmc_readl(ap, PWMC_SR) & (1 << p->chan)) ? 1 : 0; -} - -static inline void -__atmel_pwm_unsynchronize(struct pwm_channel *p, -			  struct pwm_channel *to_p) -{ -	const struct atmel_pwm *ap = to_atmel_pwm(p); -	int wchan; - -	if (to_p) { -		ap->sync_mask[p->chan] &= ~(1 << to_p->chan); -		ap->sync_mask[to_p->chan] &= ~(1 << p->chan); -		goto done; -	} - -	ap->sync_mask[p->chan] = 0; -	for (wchan = 0; wchan < ap->pwm.nchan; wchan++) -		ap->sync_mask[wchan] &= ~(1 << p->chan); -done: -	dev_dbg(p->pwm->dev, "sync_mask %x\n", ap->sync_mask[p->chan]); -} - -static inline void -__atmel_pwm_synchronize(struct pwm_channel *p, -			struct pwm_channel *to_p) -{ -	const struct atmel_pwm *ap = to_atmel_pwm(p); - -	if (!to_p) -		return; - -	ap->sync_mask[p->chan] |= (1 << to_p->chan); -	ap->sync_mask[to_p->chan] |= (1 << p->chan); - -	dev_dbg(p->pwm->dev, "sync_mask %x\n", ap->sync_mask[p->chan]); -} - -static inline void -__atmel_pwm_stop(struct pwm_channel *p) -{ -	struct atmel_pwm *ap = to_atmel_pwm(p); -	u32 chid = 1 << p->chan; - -	pwmc_writel(ap, PWMC_DIS, ap->sync_mask[p->chan] | chid); -} - -static inline void -__atmel_pwm_start(struct pwm_channel *p) -{ -	struct atmel_pwm *ap = to_atmel_pwm(p); -	u32 chid = 1 << p->chan; - -	pwmc_writel(ap, PWMC_ENA, ap->sync_mask[p->chan] | chid); -} - -static int -atmel_pwm_synchronize(struct pwm_channel *p, -		      struct pwm_channel *to_p) -{ -	unsigned long flags; -	spin_lock_irqsave(&p->lock, flags); -	__atmel_pwm_synchronize(p, to_p); -	spin_unlock_irqrestore(&p->lock, flags); -	return 0; -} - -static int -atmel_pwm_unsynchronize(struct pwm_channel *p, -			struct pwm_channel *from_p) -{ -	unsigned long flags; -	spin_lock_irqsave(&p->lock, flags); -	__atmel_pwm_unsynchronize(p, from_p); -	spin_unlock_irqrestore(&p->lock, flags); -	return 0; -} - -static inline int -__atmel_pwm_config_polarity(struct pwm_channel *p, -			    struct pwm_channel_config *c) -{ -	u32 cmr = pwmc_chan_readl(p, PWMC_CMR); - -	if (c->polarity) -		cmr &= ~BIT(PWMC_CMR_CPOL); -	else -		cmr |= BIT(PWMC_CMR_CPOL); -	pwmc_chan_writel(p, PWMC_CMR, cmr); -	p->active_high = c->polarity ? 1 : 0; - -	dev_dbg(p->pwm->dev, "polarity %d\n", c->polarity); -	return 0; -} - -static inline int -__atmel_pwm_config_duty_ticks(struct pwm_channel *p, -			      struct pwm_channel_config *c) -{ -	u32 cmr, cprd, cpre, cdty; - -	cmr = pwmc_chan_readl(p, PWMC_CMR); -	cprd = pwmc_chan_readl(p, PWMC_CPRD); - -	cpre = cmr & PWMC_CMR_CPRE_MASK; -	cmr &= ~BIT(PWMC_CMR_CPD); - -	cdty = cprd - (c->duty_ticks >> cpre); - -	p->duty_ticks = c->duty_ticks; - -	if (__atmel_pwm_is_on(p)) { -		pwmc_chan_writel(p, PWMC_CMR, cmr); -		pwmc_chan_writel(p, PWMC_CUPD, cdty); -	} else -		pwmc_chan_writel(p, PWMC_CDTY, cdty); - -	dev_dbg(p->pwm->dev, "duty_ticks = %lu cprd = %x" -		" cdty = %x cpre = %x\n", p->duty_ticks, -		cprd, cdty, cpre); - -	return 0; -} - -static inline int -__atmel_pwm_config_period_ticks(struct pwm_channel *p, -				struct pwm_channel_config *c) -{ -	u32 cmr, cprd, cpre; - -	cpre = fls(c->period_ticks); -	if (cpre < 16) -		cpre = 0; -	else { -		cpre -= 15; -		if (cpre > 10) -			return -EINVAL; -	} - -	cmr = pwmc_chan_readl(p, PWMC_CMR); -	cmr &= ~PWMC_CMR_CPRE_MASK; -	cmr |= cpre; - -	cprd = c->period_ticks >> cpre; - -	pwmc_chan_writel(p, PWMC_CMR, cmr); -	pwmc_chan_writel(p, PWMC_CPRD, cprd); -	p->period_ticks = c->period_ticks; - -	dev_dbg(p->pwm->dev, "period_ticks = %lu cprd = %x cpre = %x\n", -		 p->period_ticks, cprd, cpre); - -	return 0; -} - -static int -atmel_pwm_config_nosleep(struct pwm_channel *p, -			 struct pwm_channel_config *c) -{ -	int ret = 0; -	unsigned long flags; - -	spin_lock_irqsave(&p->lock, flags); - -	switch (c->config_mask) { - -	case PWM_CONFIG_DUTY_TICKS: -		__atmel_pwm_config_duty_ticks(p, c); -		break; - -	case PWM_CONFIG_STOP: -		__atmel_pwm_stop(p); -		break; - -	case PWM_CONFIG_START: -		__atmel_pwm_start(p); -		break; - -	case PWM_CONFIG_POLARITY: -		__atmel_pwm_config_polarity(p, c); -		break; - -	default: -		ret = -EINVAL; -		break; -	} - -	spin_unlock_irqrestore(&p->lock, flags); -	return ret; -} - -static int -atmel_pwm_stop_sync(struct pwm_channel *p) -{ -	struct atmel_pwm *ap = container_of(p->pwm, struct atmel_pwm, pwm); -	int ret; -	int was_on = __atmel_pwm_is_on(p); - -	if (was_on) { -		do { -			init_completion(&p->complete); -			set_bit(FLAG_STOP, &p->flags); -			pwmc_writel(ap, PWMC_IER, 1 << p->chan); - -			dev_dbg(p->pwm->dev, "waiting on stop_sync completion...\n"); - -			ret = wait_for_completion_interruptible(&p->complete); - -			dev_dbg(p->pwm->dev, "stop_sync complete (%d)\n", ret); - -			if (ret) -				return ret; -		} while (p->flags & BIT(FLAG_STOP)); -	} - -	return was_on; -} - -static int -atmel_pwm_config(struct pwm_channel *p, -		 struct pwm_channel_config *c) -{ -	int was_on = 0; - -	if (p->pwm->config_nosleep) { -		if (!p->pwm->config_nosleep(p, c)) -			return 0; -	} - -	might_sleep(); - -	dev_dbg(p->pwm->dev, "config_mask %x\n", c->config_mask); - -	was_on = atmel_pwm_stop_sync(p); -	if (was_on < 0) -		return was_on; - -	if (c->config_mask & PWM_CONFIG_PERIOD_TICKS) { -		__atmel_pwm_config_period_ticks(p, c); -		if (!(c->config_mask & PWM_CONFIG_DUTY_TICKS)) { -			struct pwm_channel_config d = { -				.config_mask = PWM_CONFIG_DUTY_TICKS, -				.duty_ticks = p->duty_ticks, -			}; -			__atmel_pwm_config_duty_ticks(p, &d); -		} -	} - -	if (c->config_mask & PWM_CONFIG_DUTY_TICKS) -		__atmel_pwm_config_duty_ticks(p, c); - -	if (c->config_mask & PWM_CONFIG_POLARITY) -		__atmel_pwm_config_polarity(p, c); - -	if ((c->config_mask & PWM_CONFIG_START) -	    || (was_on && !(c->config_mask & PWM_CONFIG_STOP))) -		__atmel_pwm_start(p); - -	return 0; -} - -static void -__atmel_pwm_set_callback(struct pwm_channel *p, -			 pwm_callback_t callback) -{ -	struct atmel_pwm *ap = container_of(p->pwm, struct atmel_pwm, pwm); - -	p->callback = callback; -	pwmc_writel(ap, p->callback ? PWMC_IER : PWMC_IDR, 1 << p->chan); -} - -static int -atmel_pwm_set_callback(struct pwm_channel *p, -		       pwm_callback_t callback) -{ -	struct atmel_pwm *ap = to_atmel_pwm(p); -	unsigned long flags; - -	spin_lock_irqsave(&ap->lock, flags); -	__atmel_pwm_set_callback(p, callback); -	spin_unlock_irqrestore(&ap->lock, flags); - -	return 0; -} - -static int -atmel_pwm_request(struct pwm_channel *p) -{ -	struct atmel_pwm *ap = to_atmel_pwm(p); -	unsigned long flags; - -	spin_lock_irqsave(&p->lock, flags); -	clk_enable(ap->clk); -	p->tick_hz = clk_get_rate(ap->clk); -	__atmel_pwm_unsynchronize(p, NULL); -	__atmel_pwm_stop(p); -	spin_unlock_irqrestore(&p->lock, flags); - -	return 0; -} - -static void -atmel_pwm_free(struct pwm_channel *p) -{ -	struct atmel_pwm *ap = to_atmel_pwm(p); -	clk_disable(ap->clk); -} - -static irqreturn_t -atmel_pwmc_irq(int irq, void *data) -{ -	struct atmel_pwm *ap = data; -	struct pwm_channel *p; -	u32 isr; -	int chid; -	unsigned long flags; - -	spin_lock_irqsave(&ap->lock, flags); - -	isr = pwmc_readl(ap, PWMC_ISR); -	for (chid = 0; isr; chid++, isr >>= 1) { -		p = &ap->pwm.channels[chid]; -		if (isr & 1) { -			if (p->callback) -				p->callback(p); -			if (p->flags & BIT(FLAG_STOP)) { -				__atmel_pwm_stop(p); -				clear_bit(FLAG_STOP, &p->flags); -			} -			complete_all(&p->complete); -		} -	} - -	spin_unlock_irqrestore(&ap->lock, flags); - -	return IRQ_HANDLED; -} - -static int __devinit -atmel_pwmc_probe(struct platform_device *pdev) -{ -	struct atmel_pwm *ap; -	struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	int ret = 0; - -	ap = kzalloc(sizeof(*ap), GFP_KERNEL); -	if (!ap) { -		ret = -ENOMEM; -		goto err_atmel_pwm_alloc; -	} - -	spin_lock_init(&ap->lock); -	platform_set_drvdata(pdev, ap); - -	ap->pwm.dev = &pdev->dev; -	ap->pwm.bus_id = dev_name(&pdev->dev); - -	ap->pwm.nchan = 4; /* TODO: true only for SAM9263 and AP7000 */ -	ap->ccnt_mask = 0xffffUL; /* TODO: true only for SAM9263 */ - -	ap->sync_mask = kzalloc(ap->pwm.nchan * sizeof(u32), GFP_KERNEL); -	if (!ap->sync_mask) { -		ret = -ENOMEM; -		goto err_alloc_sync_masks; -	} - -	ap->pwm.owner = THIS_MODULE; -	ap->pwm.request = atmel_pwm_request; -	ap->pwm.free = atmel_pwm_free; -	ap->pwm.config_nosleep = atmel_pwm_config_nosleep; -	ap->pwm.config = atmel_pwm_config; -	ap->pwm.synchronize = atmel_pwm_synchronize; -	ap->pwm.unsynchronize = atmel_pwm_unsynchronize; -	ap->pwm.set_callback = atmel_pwm_set_callback; - -	ap->clk = clk_get(&pdev->dev, "pwm_clk"); -	if (PTR_ERR(ap->clk)) { -		ret = -ENODEV; -		goto err_clk_get; -	} - -	ap->iobase = ioremap_nocache(r->start, r->end - r->start + 1); -	if (!ap->iobase) { -		ret = -ENODEV; -		goto err_ioremap; -	} - -	clk_enable(ap->clk); -	pwmc_writel(ap, PWMC_DIS, -1); -	pwmc_writel(ap, PWMC_IDR, -1); -	clk_disable(ap->clk); - -	ap->irq = platform_get_irq(pdev, 0); -	if (ap->irq != -ENXIO) { -		ret = request_irq(ap->irq, atmel_pwmc_irq, 0, -				  ap->pwm.bus_id, ap); -		if (ret) -			goto err_request_irq; -	} - -	ret = pwm_register(&ap->pwm); -	if (ret) -		goto err_pwm_register; - -	return 0; - -err_pwm_register: -	if (ap->irq != -ENXIO) -		free_irq(ap->irq, ap); -err_request_irq: -	iounmap(ap->iobase); -err_ioremap: -	clk_put(ap->clk); -err_clk_get: -	platform_set_drvdata(pdev, NULL); -err_alloc_sync_masks: -	kfree(ap); -err_atmel_pwm_alloc: -	return ret; -} - -static int __devexit -atmel_pwmc_remove(struct platform_device *pdev) -{ -	struct atmel_pwm *ap = platform_get_drvdata(pdev); -	int ret; - -	/* TODO: what can we do if this fails? */ -	ret = pwm_unregister(&ap->pwm); - -	clk_enable(ap->clk); -	pwmc_writel(ap, PWMC_IDR, -1); -	pwmc_writel(ap, PWMC_DIS, -1); -	clk_disable(ap->clk); - -	if (ap->irq != -ENXIO) -		free_irq(ap->irq, ap); - -	clk_put(ap->clk); -	iounmap(ap->iobase); - -	kfree(ap); - -	return 0; -} - -static struct platform_driver atmel_pwm_driver = { -	.driver = { -		.name = "atmel_pwmc", -		.owner = THIS_MODULE, -	}, -	.probe = atmel_pwmc_probe, -	.remove = __devexit_p(atmel_pwmc_remove), -}; - -static int __init atmel_pwm_init(void) -{ -	return platform_driver_register(&atmel_pwm_driver); -} -module_init(atmel_pwm_init); - -static void __exit atmel_pwm_exit(void) -{ -	platform_driver_unregister(&atmel_pwm_driver); -} -module_exit(atmel_pwm_exit); - -MODULE_AUTHOR("Bill Gatliff <bgat@billgatliff.com>"); -MODULE_DESCRIPTION("Driver for Atmel PWMC peripheral"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:atmel_pwmc"); diff --git a/target/linux/generic-2.6/files/drivers/pwm/gpio.c b/target/linux/generic-2.6/files/drivers/pwm/gpio.c deleted file mode 100644 index dff5d1d62..000000000 --- a/target/linux/generic-2.6/files/drivers/pwm/gpio.c +++ /dev/null @@ -1,298 +0,0 @@ -/* - * drivers/pwm/gpio.c - * - * Models a single-channel PWM device using a timer and a GPIO pin. - * - * Copyright (C) 2010 Bill Gatliff <bgat@billgatliff.com> - * - * This program is free software; you may redistribute and/or modify - * it under the terms of the GNU General Public License Version 2, as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/hrtimer.h> -#include <linux/err.h> -#include <linux/platform_device.h> -#include <linux/workqueue.h> -#include <linux/gpio.h> -#include <linux/slab.h> -#include <linux/pwm/pwm.h> - -struct gpio_pwm { -	struct pwm_device pwm; -	struct hrtimer timer; -	struct work_struct work; -	pwm_callback_t callback; -	int gpio; -	unsigned long polarity : 1; -	unsigned long active : 1; -}; - -static inline struct gpio_pwm *to_gpio_pwm(const struct pwm_channel *p) -{ -	return container_of(p->pwm, struct gpio_pwm, pwm); -} - -static void -gpio_pwm_work (struct work_struct *work) -{ -	struct gpio_pwm *gp = container_of(work, struct gpio_pwm, work); - -	if (gp->active) -		gpio_direction_output(gp->gpio, gp->polarity ? 1 : 0); -	else -		gpio_direction_output(gp->gpio, gp->polarity ? 0 : 1); -} - -static enum hrtimer_restart -gpio_pwm_timeout(struct hrtimer *t) -{ -	struct gpio_pwm *gp = container_of(t, struct gpio_pwm, timer); -	ktime_t tnew; - -	if (unlikely(gp->pwm.channels[0].duty_ticks == 0)) -		gp->active = 0; -	else if (unlikely(gp->pwm.channels[0].duty_ticks -			  == gp->pwm.channels[0].period_ticks)) -		gp->active = 1; -	else -		gp->active ^= 1; - -	if (gpio_cansleep(gp->gpio)) -		schedule_work(&gp->work); -	else -		gpio_pwm_work(&gp->work); - -	if (!gp->active && gp->pwm.channels[0].callback) -		gp->pwm.channels[0].callback(&gp->pwm.channels[0]); - -	if (unlikely(!gp->active && -		     (gp->pwm.channels[0].flags & BIT(FLAG_STOP)))) { -		clear_bit(FLAG_STOP, &gp->pwm.channels[0].flags); -		complete_all(&gp->pwm.channels[0].complete); -		return HRTIMER_NORESTART; -	} - -	if (gp->active) -		tnew = ktime_set(0, gp->pwm.channels[0].duty_ticks); -	else -		tnew = ktime_set(0, gp->pwm.channels[0].period_ticks -				 - gp->pwm.channels[0].duty_ticks); -	hrtimer_start(&gp->timer, tnew, HRTIMER_MODE_REL); - -	return HRTIMER_NORESTART; -} - -static void gpio_pwm_start(struct pwm_channel *p) -{ -	struct gpio_pwm *gp = to_gpio_pwm(p); - -	gp->active = 0; -	gpio_pwm_timeout(&gp->timer); -} - -static int -gpio_pwm_config_nosleep(struct pwm_channel *p, -			struct pwm_channel_config *c) -{ -	struct gpio_pwm *gp = to_gpio_pwm(p); -	int ret = 0; -	unsigned long flags; - -	spin_lock_irqsave(&p->lock, flags); - -	switch (c->config_mask) { - -	case PWM_CONFIG_DUTY_TICKS: -		p->duty_ticks = c->duty_ticks; -		break; - -	case PWM_CONFIG_START: -		if (!hrtimer_active(&gp->timer)) { -			gpio_pwm_start(p); -		} -		break; -	default: -		ret = -EINVAL; -		break; -	} - -	spin_unlock_irqrestore(&p->lock, flags); -	return ret; -} - -static int -gpio_pwm_stop_sync(struct pwm_channel *p) -{ -	struct gpio_pwm *gp = to_gpio_pwm(p); -	int ret; -	int was_on = hrtimer_active(&gp->timer); - -	if (was_on) { -		do { -			init_completion(&p->complete); -			set_bit(FLAG_STOP, &p->flags); -			ret = wait_for_completion_interruptible(&p->complete); -			if (ret) -				return ret; -		} while (p->flags & BIT(FLAG_STOP)); -	} - -	return was_on; -} - -static int -gpio_pwm_config(struct pwm_channel *p, -		struct pwm_channel_config *c) -{ -	struct gpio_pwm *gp = to_gpio_pwm(p); -	int was_on = 0; - -	if (p->pwm->config_nosleep) { -		if (!p->pwm->config_nosleep(p, c)) -			return 0; -	} - -	might_sleep(); - -	was_on = gpio_pwm_stop_sync(p); -	if (was_on < 0) -		return was_on; - -	if (c->config_mask & PWM_CONFIG_PERIOD_TICKS) -		p->period_ticks = c->period_ticks; - -	if (c->config_mask & PWM_CONFIG_DUTY_TICKS) -		p->duty_ticks = c->duty_ticks; - -	if (c->config_mask & PWM_CONFIG_POLARITY) { -		gp->polarity = c->polarity ? 1 : 0; -		p->active_high = gp->polarity; -	} - -	if ((c->config_mask & PWM_CONFIG_START) -	    || (was_on && !(c->config_mask & PWM_CONFIG_STOP))) -		gpio_pwm_start(p); - -	return 0; -} - -static int -gpio_pwm_set_callback(struct pwm_channel *p, -		      pwm_callback_t callback) -{ -	struct gpio_pwm *gp = to_gpio_pwm(p); -	gp->callback = callback; -	return 0; -} - -static int -gpio_pwm_request(struct pwm_channel *p) -{ -	p->tick_hz = 1000000000UL; -	return 0; -} - -static int __devinit -gpio_pwm_probe(struct platform_device *pdev) -{ -	struct gpio_pwm *gp; -	struct gpio_pwm_platform_data *gpd = pdev->dev.platform_data; -	int ret = 0; - -	/* TODO: create configfs entries, so users can assign GPIOs to -	 * PWMs at runtime instead of creating a platform_device -	 * specification and rebuilding their kernel */ - -	if (!gpd || gpio_request(gpd->gpio, dev_name(&pdev->dev))) -		return -EINVAL; - -	gp = kzalloc(sizeof(*gp), GFP_KERNEL); -	if (!gp) { -		ret = -ENOMEM; -		goto err_alloc; -	} - -	platform_set_drvdata(pdev, gp); - -	gp->pwm.dev = &pdev->dev; -	gp->pwm.bus_id = dev_name(&pdev->dev); -	gp->pwm.nchan = 1; -	gp->gpio = gpd->gpio; - -	INIT_WORK(&gp->work, gpio_pwm_work); - -	hrtimer_init(&gp->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); -	gp->timer.function = gpio_pwm_timeout; - -	gp->pwm.owner = THIS_MODULE; -	gp->pwm.config_nosleep = gpio_pwm_config_nosleep; -	gp->pwm.config = gpio_pwm_config; -	gp->pwm.request = gpio_pwm_request; -	gp->pwm.set_callback = gpio_pwm_set_callback; - -	ret = pwm_register(&gp->pwm); -	if (ret) -		goto err_pwm_register; - -	return 0; - -err_pwm_register: -	platform_set_drvdata(pdev, 0); -	kfree(gp); -err_alloc: -	return ret; -} - -static int __devexit -gpio_pwm_remove(struct platform_device *pdev) -{ -	struct gpio_pwm *gp = platform_get_drvdata(pdev); -	int ret; - -	ret = pwm_unregister(&gp->pwm); -	hrtimer_cancel(&gp->timer); -	cancel_work_sync(&gp->work); -	platform_set_drvdata(pdev, 0); -	kfree(gp); - -	return 0; -} - -static struct platform_driver gpio_pwm_driver = { -	.driver = { -		.name = "gpio_pwm", -		.owner = THIS_MODULE, -	}, -	.probe = gpio_pwm_probe, -	.remove = __devexit_p(gpio_pwm_remove), -}; - -static int __init gpio_pwm_init(void) -{ -	return platform_driver_register(&gpio_pwm_driver); -} -module_init(gpio_pwm_init); - -static void __exit gpio_pwm_exit(void) -{ -	platform_driver_unregister(&gpio_pwm_driver); -} -module_exit(gpio_pwm_exit); - -MODULE_AUTHOR("Bill Gatliff <bgat@billgatliff.com>"); -MODULE_DESCRIPTION("PWM output using GPIO and a high-resolution timer"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:gpio_pwm"); diff --git a/target/linux/generic-2.6/files/drivers/pwm/pwm.c b/target/linux/generic-2.6/files/drivers/pwm/pwm.c deleted file mode 100644 index c1596e9e7..000000000 --- a/target/linux/generic-2.6/files/drivers/pwm/pwm.c +++ /dev/null @@ -1,643 +0,0 @@ -/* - * drivers/pwm/pwm.c - * - * Copyright (C) 2010 Bill Gatliff <bgat@billgatliff.com> - * - * This program is free software; you may redistribute and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/device.h> -#include <linux/spinlock.h> -#include <linux/fs.h> -#include <linux/completion.h> -#include <linux/workqueue.h> -#include <linux/list.h> -#include <linux/sched.h> -#include <linux/slab.h>    /*kcalloc, kfree since 2.6.34 */ -#include <linux/pwm/pwm.h> - -static int __pwm_create_sysfs(struct pwm_device *pwm); - -static const char *REQUEST_SYSFS = "sysfs"; -static LIST_HEAD(pwm_device_list); -static DEFINE_MUTEX(device_list_mutex); -static struct class pwm_class; -static struct workqueue_struct *pwm_handler_workqueue; - -int pwm_register(struct pwm_device *pwm) -{ -	struct pwm_channel *p; -	int wchan; -	int ret; - -	spin_lock_init(&pwm->list_lock); - -	p = kcalloc(pwm->nchan, sizeof(*p), GFP_KERNEL); -	if (!p) -		return -ENOMEM; - -	for (wchan = 0; wchan < pwm->nchan; wchan++) { -		spin_lock_init(&p[wchan].lock); -		init_completion(&p[wchan].complete); -		p[wchan].chan = wchan; -		p[wchan].pwm = pwm; -	} - -	pwm->channels = p; - -	mutex_lock(&device_list_mutex); - -	list_add_tail(&pwm->list, &pwm_device_list); -	ret = __pwm_create_sysfs(pwm); -	if (ret) { -		mutex_unlock(&device_list_mutex); -		goto err_create_sysfs; -	} - -	mutex_unlock(&device_list_mutex); - -	dev_info(pwm->dev, "%d channel%s\n", pwm->nchan, -		 pwm->nchan > 1 ? "s" : ""); -	return 0; - -err_create_sysfs: -	kfree(p); - -	return ret; -} -EXPORT_SYMBOL(pwm_register); - -static int __match_device(struct device *dev, void *data) -{ -	return dev_get_drvdata(dev) == data; -} - -int pwm_unregister(struct pwm_device *pwm) -{ -	int wchan; -	struct device *dev; - -	mutex_lock(&device_list_mutex); - -	for (wchan = 0; wchan < pwm->nchan; wchan++) { -	  if (pwm->channels[wchan].flags & BIT(FLAG_REQUESTED)) { -			mutex_unlock(&device_list_mutex); -			return -EBUSY; -		} -	} - -	for (wchan = 0; wchan < pwm->nchan; wchan++) { -		dev = class_find_device(&pwm_class, NULL, -					&pwm->channels[wchan], -					__match_device); -		if (dev) { -			put_device(dev); -			device_unregister(dev); -		} -	} - -	kfree(pwm->channels); -	list_del(&pwm->list); -	mutex_unlock(&device_list_mutex); - -	return 0; -} -EXPORT_SYMBOL(pwm_unregister); - -static struct pwm_device * -__pwm_find_device(const char *bus_id) -{ -	struct pwm_device *p; - -	list_for_each_entry(p, &pwm_device_list, list) { -		if (!strcmp(bus_id, p->bus_id)) -			return p; -	} -	return NULL; -} - -static int -__pwm_request_channel(struct pwm_channel *p, -		      const char *requester) -{ -	int ret; - -	if (test_and_set_bit(FLAG_REQUESTED, &p->flags)) -		return -EBUSY; - -	if (p->pwm->request) { -		ret = p->pwm->request(p); -		if (ret) { -			clear_bit(FLAG_REQUESTED, &p->flags); -			return ret; -		} -	} - -	p->requester = requester; -	if (!strcmp(requester, REQUEST_SYSFS)) -		p->pid = current->pid; - -	return 0; -} - -struct pwm_channel * -pwm_request(const char *bus_id, -	    int chan, -	    const char *requester) -{ -	struct pwm_device *p; -	int ret; - -	mutex_lock(&device_list_mutex); - -	p = __pwm_find_device(bus_id); -	if (!p || chan >= p->nchan) -		goto err_no_device; - -	if (!try_module_get(p->owner)) -		goto err_module_get_failed; - -	ret = __pwm_request_channel(&p->channels[chan], requester); -	if (ret) -		goto err_request_failed; - -	mutex_unlock(&device_list_mutex); -	return &p->channels[chan]; - -err_request_failed: -	module_put(p->owner); -err_module_get_failed: -err_no_device: -	mutex_unlock(&device_list_mutex); -	return NULL; -} -EXPORT_SYMBOL(pwm_request); - -void pwm_free(struct pwm_channel *p) -{ -	mutex_lock(&device_list_mutex); - -	if (!test_and_clear_bit(FLAG_REQUESTED, &p->flags)) -		goto done; - -	pwm_stop(p); -	pwm_unsynchronize(p, NULL); -	pwm_set_handler(p, NULL, NULL); - -	if (p->pwm->free) -		p->pwm->free(p); -	module_put(p->pwm->owner); -done: -	mutex_unlock(&device_list_mutex); -} -EXPORT_SYMBOL(pwm_free); - -unsigned long pwm_ns_to_ticks(struct pwm_channel *p, -			      unsigned long nsecs) -{ -	unsigned long long ticks; - -	ticks = nsecs; -	ticks *= p->tick_hz; -	do_div(ticks, 1000000000); -	return ticks; -} -EXPORT_SYMBOL(pwm_ns_to_ticks); - -unsigned long pwm_ticks_to_ns(struct pwm_channel *p, -			      unsigned long ticks) -{ -	unsigned long long ns; - -	if (!p->tick_hz) -		return 0; - -	ns = ticks; -	ns *= 1000000000UL; -	do_div(ns, p->tick_hz); -	return ns; -} -EXPORT_SYMBOL(pwm_ticks_to_ns); - -static void -pwm_config_ns_to_ticks(struct pwm_channel *p, -		       struct pwm_channel_config *c) -{ -	if (c->config_mask & PWM_CONFIG_PERIOD_NS) { -		c->period_ticks = pwm_ns_to_ticks(p, c->period_ns); -		c->config_mask &= ~PWM_CONFIG_PERIOD_NS; -		c->config_mask |= PWM_CONFIG_PERIOD_TICKS; -	} - -	if (c->config_mask & PWM_CONFIG_DUTY_NS) { -		c->duty_ticks = pwm_ns_to_ticks(p, c->duty_ns); -		c->config_mask &= ~PWM_CONFIG_DUTY_NS; -		c->config_mask |= PWM_CONFIG_DUTY_TICKS; -	} -} - -static void -pwm_config_percent_to_ticks(struct pwm_channel *p, -			    struct pwm_channel_config *c) -{ -	if (c->config_mask & PWM_CONFIG_DUTY_PERCENT) { -		if (c->config_mask & PWM_CONFIG_PERIOD_TICKS) -			c->duty_ticks = c->period_ticks; -		else -			c->duty_ticks = p->period_ticks; - -		c->duty_ticks *= c->duty_percent; -		c->duty_ticks /= 100; -		c->config_mask &= ~PWM_CONFIG_DUTY_PERCENT; -		c->config_mask |= PWM_CONFIG_DUTY_TICKS; -	} -} - -int pwm_config_nosleep(struct pwm_channel *p, -		       struct pwm_channel_config *c) -{ -	if (!p->pwm->config_nosleep) -		return -EINVAL; - -	pwm_config_ns_to_ticks(p, c); -	pwm_config_percent_to_ticks(p, c); - -	return p->pwm->config_nosleep(p, c); -} -EXPORT_SYMBOL(pwm_config_nosleep); - -int pwm_config(struct pwm_channel *p, -	       struct pwm_channel_config *c) -{ -	int ret = 0; - -	if (unlikely(!p->pwm->config)) -		return -EINVAL; - -	pwm_config_ns_to_ticks(p, c); -	pwm_config_percent_to_ticks(p, c); - -	switch (c->config_mask & (PWM_CONFIG_PERIOD_TICKS -				  | PWM_CONFIG_DUTY_TICKS)) { -	case PWM_CONFIG_PERIOD_TICKS: -		if (p->duty_ticks > c->period_ticks) { -			ret = -EINVAL; -			goto err; -		} -		break; -	case PWM_CONFIG_DUTY_TICKS: -		if (p->period_ticks < c->duty_ticks) { -			ret = -EINVAL; -			goto err; -		} -		break; -	case PWM_CONFIG_DUTY_TICKS | PWM_CONFIG_PERIOD_TICKS: -		if (c->duty_ticks > c->period_ticks) { -			ret = -EINVAL; -			goto err; -		} -		break; -	default: -		break; -	} - -err: -	dev_dbg(p->pwm->dev, "%s: config_mask %d period_ticks %lu duty_ticks %lu" -		" polarity %d duty_ns %lu period_ns %lu duty_percent %d\n", -		__func__, c->config_mask, c->period_ticks, c->duty_ticks, -		c->polarity, c->duty_ns, c->period_ns, c->duty_percent); - -	if (ret) -		return ret; -	return p->pwm->config(p, c); -} -EXPORT_SYMBOL(pwm_config); - -int pwm_set_period_ns(struct pwm_channel *p, -		      unsigned long period_ns) -{ -	struct pwm_channel_config c = { -		.config_mask = PWM_CONFIG_PERIOD_TICKS, -		.period_ticks = pwm_ns_to_ticks(p, period_ns), -	}; - -	return pwm_config(p, &c); -} -EXPORT_SYMBOL(pwm_set_period_ns); - -unsigned long pwm_get_period_ns(struct pwm_channel *p) -{ -	return pwm_ticks_to_ns(p, p->period_ticks); -} -EXPORT_SYMBOL(pwm_get_period_ns); - -int pwm_set_duty_ns(struct pwm_channel *p, -		    unsigned long duty_ns) -{ -	struct pwm_channel_config c = { -		.config_mask = PWM_CONFIG_DUTY_TICKS, -		.duty_ticks = pwm_ns_to_ticks(p, duty_ns), -	}; -	return pwm_config(p, &c); -} -EXPORT_SYMBOL(pwm_set_duty_ns); - -unsigned long pwm_get_duty_ns(struct pwm_channel *p) -{ -	return pwm_ticks_to_ns(p, p->duty_ticks); -} -EXPORT_SYMBOL(pwm_get_duty_ns); - -int pwm_set_duty_percent(struct pwm_channel *p, -			 int percent) -{ -	struct pwm_channel_config c = { -		.config_mask = PWM_CONFIG_DUTY_PERCENT, -		.duty_percent = percent, -	}; -	return pwm_config(p, &c); -} -EXPORT_SYMBOL(pwm_set_duty_percent); - -int pwm_set_polarity(struct pwm_channel *p, -		     int active_high) -{ -	struct pwm_channel_config c = { -		.config_mask = PWM_CONFIG_POLARITY, -		.polarity = active_high, -	}; -	return pwm_config(p, &c); -} -EXPORT_SYMBOL(pwm_set_polarity); - -int pwm_start(struct pwm_channel *p) -{ -	struct pwm_channel_config c = { -		.config_mask = PWM_CONFIG_START, -	}; -	return pwm_config(p, &c); -} -EXPORT_SYMBOL(pwm_start); - -int pwm_stop(struct pwm_channel *p) -{ -	struct pwm_channel_config c = { -		.config_mask = PWM_CONFIG_STOP, -	}; -	return pwm_config(p, &c); -} -EXPORT_SYMBOL(pwm_stop); - -int pwm_synchronize(struct pwm_channel *p, -		    struct pwm_channel *to_p) -{ -	if (p->pwm != to_p->pwm) { -		/* TODO: support cross-device synchronization */ -		return -EINVAL; -	} - -	if (!p->pwm->synchronize) -		return -EINVAL; - -	return p->pwm->synchronize(p, to_p); -} -EXPORT_SYMBOL(pwm_synchronize); - -int pwm_unsynchronize(struct pwm_channel *p, -		      struct pwm_channel *from_p) -{ -	if (from_p && (p->pwm != from_p->pwm)) { -		/* TODO: support cross-device synchronization */ -		return -EINVAL; -	} - -	if (!p->pwm->unsynchronize) -		return -EINVAL; - -	return p->pwm->unsynchronize(p, from_p); -} -EXPORT_SYMBOL(pwm_unsynchronize); - -static void pwm_handler(struct work_struct *w) -{ -	struct pwm_channel *p = container_of(w, struct pwm_channel, -					     handler_work); -	if (p->handler && p->handler(p, p->handler_data)) -		pwm_stop(p); -} - -static void __pwm_callback(struct pwm_channel *p) -{ -	queue_work(pwm_handler_workqueue, &p->handler_work); -	dev_dbg(p->pwm->dev, "handler %p scheduled with data %p\n", -		p->handler, p->handler_data); -} - -int pwm_set_handler(struct pwm_channel *p, -		    pwm_handler_t handler, -		    void *data) -{ -	if (p->pwm->set_callback) { -		p->handler_data = data; -		p->handler = handler; -		INIT_WORK(&p->handler_work, pwm_handler); -		return p->pwm->set_callback(p, handler ? __pwm_callback : NULL); -	} -	return -EINVAL; -} -EXPORT_SYMBOL(pwm_set_handler); - -static ssize_t pwm_run_store(struct device *dev, -			     struct device_attribute *attr, -			     const char *buf, -			     size_t len) -{ -	struct pwm_channel *p = dev_get_drvdata(dev); -	if (sysfs_streq(buf, "1")) -		pwm_start(p); -	else if (sysfs_streq(buf, "0")) -		pwm_stop(p); -	return len; -} -static DEVICE_ATTR(run, 0200, NULL, pwm_run_store); - -static ssize_t pwm_duty_ns_show(struct device *dev, -				struct device_attribute *attr, -				char *buf) -{ -	struct pwm_channel *p = dev_get_drvdata(dev); -	return sprintf(buf, "%lu\n", pwm_get_duty_ns(p)); -} - -static ssize_t pwm_duty_ns_store(struct device *dev, -				 struct device_attribute *attr, -				 const char *buf, -				 size_t len) -{ -	unsigned long duty_ns; -	struct pwm_channel *p = dev_get_drvdata(dev); - -	if (1 == sscanf(buf, "%lu", &duty_ns)) -		pwm_set_duty_ns(p, duty_ns); -	return len; -} -static DEVICE_ATTR(duty_ns, 0644, pwm_duty_ns_show, pwm_duty_ns_store); - -static ssize_t pwm_period_ns_show(struct device *dev, -				  struct device_attribute *attr, -				  char *buf) -{ -	struct pwm_channel *p = dev_get_drvdata(dev); -	return sprintf(buf, "%lu\n", pwm_get_period_ns(p)); -} - -static ssize_t pwm_period_ns_store(struct device *dev, -				   struct device_attribute *attr, -				   const char *buf, -				   size_t len) -{ -	unsigned long period_ns; -	struct pwm_channel *p = dev_get_drvdata(dev); - -	if (1 == sscanf(buf, "%lu", &period_ns)) -		pwm_set_period_ns(p, period_ns); -	return len; -} -static DEVICE_ATTR(period_ns, 0644, pwm_period_ns_show, pwm_period_ns_store); - -static ssize_t pwm_polarity_show(struct device *dev, -				 struct device_attribute *attr, -				 char *buf) -{ -	struct pwm_channel *p = dev_get_drvdata(dev); -	return sprintf(buf, "%d\n", p->active_high ? 1 : 0); -} - -static ssize_t pwm_polarity_store(struct device *dev, -				  struct device_attribute *attr, -				  const char *buf, -				  size_t len) -{ -	int polarity; -	struct pwm_channel *p = dev_get_drvdata(dev); - -	if (1 == sscanf(buf, "%d", &polarity)) -		pwm_set_polarity(p, polarity); -	return len; -} -static DEVICE_ATTR(polarity, 0644, pwm_polarity_show, pwm_polarity_store); - -static ssize_t pwm_request_show(struct device *dev, -				struct device_attribute *attr, -				char *buf) -{ -	struct pwm_channel *p = dev_get_drvdata(dev); -	mutex_lock(&device_list_mutex); -	__pwm_request_channel(p, REQUEST_SYSFS); -	mutex_unlock(&device_list_mutex); - -	if (p->pid) -		return sprintf(buf, "%s %d\n", p->requester, p->pid); -	else -		return sprintf(buf, "%s\n", p->requester); -} - -static ssize_t pwm_request_store(struct device *dev, -				 struct device_attribute *attr, -				 const char *buf, -				 size_t len) -{ -	struct pwm_channel *p = dev_get_drvdata(dev); -	pwm_free(p); -	return len; -} -static DEVICE_ATTR(request, 0644, pwm_request_show, pwm_request_store); - -static const struct attribute *pwm_attrs[] = -{ -	&dev_attr_run.attr, -	&dev_attr_polarity.attr, -	&dev_attr_duty_ns.attr, -	&dev_attr_period_ns.attr, -	&dev_attr_request.attr, -	NULL, -}; - -static const struct attribute_group pwm_device_attr_group = { -	.attrs = (struct attribute **)pwm_attrs, -}; - -static int __pwm_create_sysfs(struct pwm_device *pwm) -{ -	int ret = 0; -	struct device *dev; -	int wchan; - -	for (wchan = 0; wchan < pwm->nchan; wchan++) { -		dev = device_create(&pwm_class, pwm->dev, MKDEV(0, 0), -				    pwm->channels + wchan, -				    "%s:%d", pwm->bus_id, wchan); -		if (!dev) -			goto err_dev_create; -		ret = sysfs_create_group(&dev->kobj, &pwm_device_attr_group); -		if (ret) -			goto err_dev_create; -	} - -	return ret; - -err_dev_create: -	for (wchan = 0; wchan < pwm->nchan; wchan++) { -		dev = class_find_device(&pwm_class, NULL, -					&pwm->channels[wchan], -					__match_device); -		if (dev) { -			put_device(dev); -			device_unregister(dev); -		} -	} - -	return ret; -} - -static struct class_attribute pwm_class_attrs[] = { -	__ATTR_NULL, -}; - -static struct class pwm_class = { -	.name = "pwm", -	.owner = THIS_MODULE, - -	.class_attrs = pwm_class_attrs, -}; - -static int __init pwm_init(void) -{ -	int ret; - -	/* TODO: how to deal with devices that register very early? */ -	pr_err("%s\n", __func__); -	ret = class_register(&pwm_class); -	if (ret < 0) -		return ret; - -	pwm_handler_workqueue = create_workqueue("pwmd"); - -	return 0; -} -postcore_initcall(pwm_init);  | 
