Before this patch the module doesn't have an easy way to configure it or
is broken directly.
Now it do not need to define in board before compile. It uses configfs.
I just took the updated drivers (Bill Gatliff is the developer) follow
his footsteps looking at his changelog, and apply changes/overwrite files.
Way to probe it:
Ensure configfs, PWM and GPIO_PWM are enabled in kernel_menuconfig.
I used a led, so first removed from my board definition to get the gpio
access.
The once flashed and openwrt running:
make -p /config
mount -t configfs none /config
Now create configurable GPIO PWM, example gpio=10
mkdir /config/gpio_pwm/10
echo 1 > /sys/class/pwm/gpio_pwm\:10/export
Now we configure it (1 seg period, 50% on, 50% off)
echo 1000000000 > /sys/class/pwm/gpio_pwm\:10/period_ns
echo 500000000 > /sys/class/pwm/gpio_pwm\:10/duty_ns
Now run it
echo 1 > /sys/class/pwm/gpio_pwm\:10/run
Done, working.
To disable, stop and unexport it
echo 0 > /sys/class/pwm/gpio_pwm\:10/run
echo 1 > /sys/class/pwm/gpio_pwm\:10/unexport
and delete it
rm -rf /config/gpio_pwm/0
Signed-off-by: Cienti Bordelo <[email protected]>
---
Index: target/linux/generic/files/include/linux/pwm/pwm.h
===================================================================
--- target/linux/generic/files/include/linux/pwm/pwm.h (revision 35501)
+++ target/linux/generic/files/include/linux/pwm/pwm.h (working copy)
@@ -1,8 +1,7 @@
/*
- * include/linux/pwm.h
+ * Copyright (C) 2011 Bill Gatliff <[email protected]>
+ * Copyright (C) 2011 Arun Murthy <[email protected]>
*
- * Copyright (C) 2008 Bill Gatliff < [email protected]>
- *
* 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.
@@ -20,146 +19,125 @@
#ifndef __LINUX_PWM_H
#define __LINUX_PWM_H
-enum {
- PWM_CONFIG_DUTY_TICKS = BIT(0),
- PWM_CONFIG_PERIOD_TICKS = BIT(1),
- PWM_CONFIG_POLARITY = BIT(2),
- PWM_CONFIG_START = BIT(3),
- PWM_CONFIG_STOP = BIT(4),
+#include <linux/device.h>
- PWM_CONFIG_HANDLER = BIT(5),
-
- PWM_CONFIG_DUTY_NS = BIT(6),
- PWM_CONFIG_DUTY_PERCENT = BIT(7),
- PWM_CONFIG_PERIOD_NS = BIT(8),
+enum {
+ PWM_FLAG_REQUESTED = 0,
+ PWM_FLAG_STOP = 1,
+ PWM_FLAG_RUNNING = 2,
+ PWM_FLAG_EXPORTED = 3,
};
-struct pwm_channel;
-struct work_struct;
-
-typedef int (*pwm_handler_t)(struct pwm_channel *p, void *data);
-typedef void (*pwm_callback_t)(struct pwm_channel *p);
-
-struct pwm_channel_config {
- int config_mask;
- unsigned long duty_ticks;
- unsigned long period_ticks;
- int polarity;
-
- pwm_handler_t handler;
-
- unsigned long duty_ns;
- unsigned long period_ns;
- int duty_percent;
+enum {
+ PWM_CONFIG_DUTY_TICKS = 0,
+ PWM_CONFIG_PERIOD_TICKS = 1,
+ PWM_CONFIG_POLARITY = 2,
+ PWM_CONFIG_START = 3,
+ PWM_CONFIG_STOP = 4,
};
-struct pwm_device {
- struct list_head list;
- spinlock_t list_lock;
- struct device *dev;
+struct pwm_config;
+struct pwm_device;
+
+struct pwm_device_ops {
struct module *owner;
- struct pwm_channel *channels;
- const char *bus_id;
- int nchan;
-
- int (*request) (struct pwm_channel *p);
- void (*free) (struct pwm_channel *p);
- int (*config) (struct pwm_channel *p,
- struct pwm_channel_config *c);
- int (*config_nosleep)(struct pwm_channel *p,
- struct pwm_channel_config *c);
- int (*synchronize) (struct pwm_channel *p,
- struct pwm_channel *to_p);
- int (*unsynchronize)(struct pwm_channel *p,
- struct pwm_channel *from_p);
- int (*set_callback) (struct pwm_channel *p,
- pwm_callback_t callback);
+ int (*request) (struct pwm_device *p);
+ void (*release) (struct pwm_device *p);
+ int (*config) (struct pwm_device *p,
+ struct pwm_config *c);
+ int (*config_nosleep) (struct pwm_device *p,
+ struct pwm_config *c);
+ int (*synchronize) (struct pwm_device *p,
+ struct pwm_device *to_p);
+ int (*unsynchronize) (struct pwm_device *p,
+ struct pwm_device *from_p);
};
-int pwm_register(struct pwm_device *pwm);
-int pwm_unregister(struct pwm_device *pwm);
-
-enum {
- FLAG_REQUESTED = 0,
- FLAG_STOP = 1,
+/**
+ * struct pwm_config - configuration data for a PWM device
+ *
+ * @config_mask: which fields are valid
+ * @duty_ticks: requested duty cycle, in ticks
+ * @period_ticks: requested period, in ticks
+ * @polarity: active high (1), or active low (0)
+ */
+struct pwm_config {
+ unsigned long config_mask;
+ unsigned long duty_ticks;
+ unsigned long period_ticks;
+ int polarity;
};
-struct pwm_channel {
- struct list_head list;
- struct pwm_device *pwm;
- const char *requester;
- pid_t pid;
- int chan;
- unsigned long flags;
- unsigned long tick_hz;
-
- spinlock_t lock;
- struct completion complete;
-
- pwm_callback_t callback;
-
- struct work_struct handler_work;
- pwm_handler_t handler;
- void *handler_data;
-
- int active_high;
- unsigned long period_ticks;
- unsigned long duty_ticks;
+/**
+ * struct pwm_device - represents a PWM device
+ *
+ * @dev: device model reference
+ * @ops: operations supported by the PWM device
+ * @label: requestor of the PWM device, or NULL
+ * @flags: PWM device state, see FLAG_*
+ * @tick_hz: base tick rate of PWM device, in HZ
+ * @polarity: active high (1), or active low (0)
+ * @period_ticks: PWM device's current period, in ticks
+ * @duty_ticks: duration of PWM device's active cycle, in ticks
+ */
+struct pwm_device {
+ struct device dev;
+ const struct pwm_device_ops *ops;
+ const char *label;
+ unsigned long flags;
+ unsigned long tick_hz;
+ int polarity;
+ unsigned long period_ticks;
+ unsigned long duty_ticks;
};
-struct gpio_pwm_platform_data {
- int gpio;
-};
+struct pwm_device *pwm_request(const char *name, const char *label);
+void pwm_release(struct pwm_device *p);
-struct pwm_channel *
-pwm_request(const char *bus_id, int chan,
- const char *requester);
+static inline int pwm_is_requested(const struct pwm_device *p)
+{
+ return test_bit(PWM_FLAG_REQUESTED, &p->flags);
+}
-void pwm_free(struct pwm_channel *pwm);
+static inline int pwm_is_running(const struct pwm_device *p)
+{
+ return test_bit(PWM_FLAG_RUNNING, &p->flags);
+}
-int pwm_config_nosleep(struct pwm_channel *pwm,
- struct pwm_channel_config *c);
+static inline int pwm_is_exported(const struct pwm_device *p)
+{
+ return test_bit(PWM_FLAG_EXPORTED, &p->flags);
+}
-int pwm_config(struct pwm_channel *pwm,
- struct pwm_channel_config *c);
+struct pwm_device *pwm_register(const struct pwm_device_ops *ops,
struct device *parent,
+ const char *fmt, ...);
+void pwm_unregister(struct pwm_device *p);
-unsigned long pwm_ns_to_ticks(struct pwm_channel *pwm,
- unsigned long nsecs);
+void pwm_set_drvdata(struct pwm_device *p, void *data);
+void *pwm_get_drvdata(const struct pwm_device *p);
-unsigned long pwm_ticks_to_ns(struct pwm_channel *pwm,
- unsigned long ticks);
+int pwm_set(struct pwm_device *p, unsigned long period_ns,
+ unsigned long duty_ns, int polarity);
-int pwm_set_period_ns(struct pwm_channel *pwm,
- unsigned long period_ns);
+int pwm_set_period_ns(struct pwm_device *p, unsigned long period_ns);
+unsigned long pwm_get_period_ns(struct pwm_device *p);
-unsigned long int pwm_get_period_ns(struct pwm_channel *pwm);
+int pwm_set_duty_ns(struct pwm_device *p, unsigned long duty_ns);
+unsigned long pwm_get_duty_ns(struct pwm_device *p);
-int pwm_set_duty_ns(struct pwm_channel *pwm,
- unsigned long duty_ns);
+int pwm_set_polarity(struct pwm_device *p, int polarity);
-int pwm_set_duty_percent(struct pwm_channel *pwm,
- int percent);
+int pwm_start(struct pwm_device *p);
+int pwm_stop(struct pwm_device *p);
-unsigned long pwm_get_duty_ns(struct pwm_channel *pwm);
+int pwm_config_nosleep(struct pwm_device *p, struct pwm_config *c);
+int pwm_config(struct pwm_device *p, struct pwm_config *c);
-int pwm_set_polarity(struct pwm_channel *pwm,
- int active_high);
+int pwm_synchronize(struct pwm_device *p, struct pwm_device *to_p);
+int pwm_unsynchronize(struct pwm_device *p, struct pwm_device *from_p);
-int pwm_start(struct pwm_channel *pwm);
+struct pwm_device *gpio_pwm_create(int gpio);
+int gpio_pwm_destroy(struct pwm_device *p);
-int pwm_stop(struct pwm_channel *pwm);
-
-int pwm_set_handler(struct pwm_channel *pwm,
- pwm_handler_t handler,
- void *data);
-
-int pwm_synchronize(struct pwm_channel *p,
- struct pwm_channel *to_p);
-
-
-int pwm_unsynchronize(struct pwm_channel *p,
- struct pwm_channel *from_p);
-
-
-#endif /* __LINUX_PWM_H */
+#endif
Index: target/linux/generic/files/drivers/pwm/Kconfig
===================================================================
--- target/linux/generic/files/drivers/pwm/Kconfig (revision 35501)
+++ target/linux/generic/files/drivers/pwm/Kconfig (working copy)
@@ -4,17 +4,26 @@
menuconfig GENERIC_PWM
tristate "PWM Support"
- depends on SYSFS
help
- This enables PWM support through the generic PWM library.
- If unsure, say N.
+ Enables PWM device support implemented via a generic
+ framework. If unsure, say N.
-if GENERIC_PWM
-
config GPIO_PWM
- tristate "PWM emulation using GPIO"
+ tristate "GPIO+hrtimer PWM device emulation"
+ depends on GENERIC_PWM
help
- This option enables a single-channel PWM device using
- a kernel interval timer and a GPIO pin. If unsure, say N.
+ When enabled, this feature emulates single-channel PWM
+ devices using high-resolution timers and GPIO pins. You may
+ create as many of these devices as desired, subject to CPU
+ throughput limitations and GPIO pin availability.
-endif
+ To compile this feature as a module, chose M here; the module
+ will be called gpio-pwm. If unsure, say N.
+
+config ATMEL_PWMC
+ tristate "Atmel AT32/AT91 PWMC support"
+ depends on GENERIC_PWM && (AVR32 || ARCH_AT91SAM9263 ||
ARCH_AT91SAM9RL || ARCH_AT91CAP9)
+ help
+ This option enables support under the generic PWM
+ framework for PWMC peripheral channels found on
+ certain Atmel microcontrollers. If unsure, say N.
Index: target/linux/generic/files/drivers/pwm/gpio-pwm.c
===================================================================
--- target/linux/generic/files/drivers/pwm/gpio-pwm.c (revision 35501)
+++ target/linux/generic/files/drivers/pwm/gpio-pwm.c (working copy)
@@ -1,10 +1,8 @@
/*
- * drivers/pwm/gpio.c
+ * Emulates a PWM device using an hrtimer and GPIO pin
*
- * Models a single-channel PWM device using a timer and a GPIO pin.
+ * Copyright (C) 2011 Bill Gatliff <[email protected]>
*
- * Copyright (C) 2010 Bill Gatliff <[email protected]>
- *
* 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.
@@ -24,48 +22,43 @@
#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/completion.h>
+#include <linux/configfs.h>
#include <linux/pwm/pwm.h>
+#define DRIVER_NAME KBUILD_MODNAME
+
struct gpio_pwm {
- struct pwm_device pwm;
- struct hrtimer timer;
+ struct pwm_device *pwm;
+ struct pwm_device_ops ops;
+ struct hrtimer t;
struct work_struct work;
- pwm_callback_t callback;
+ spinlock_t lock;
+ struct completion complete;
int gpio;
+ int callback;
unsigned long polarity : 1;
unsigned long active : 1;
};
-static inline struct gpio_pwm *to_gpio_pwm(const struct pwm_channel *p)
+static void gpio_pwm_work(struct work_struct *work)
{
- 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);
+ gpio_direction_output(gp->gpio, !(!!gp->polarity ^ !!gp->active));
}
-static enum hrtimer_restart
-gpio_pwm_timeout(struct hrtimer *t)
+static enum hrtimer_restart gpio_pwm_timeout(struct hrtimer *t)
{
- struct gpio_pwm *gp = container_of(t, struct gpio_pwm, timer);
- ktime_t tnew;
+ struct gpio_pwm *gp = container_of(t, struct gpio_pwm, t);
+ struct pwm_device *p = gp->pwm;
- if (unlikely(gp->pwm.channels[0].duty_ticks == 0))
+ if (unlikely(p->duty_ticks == 0))
gp->active = 0;
- else if (unlikely(gp->pwm.channels[0].duty_ticks
- == gp->pwm.channels[0].period_ticks))
+ else if (unlikely(p->duty_ticks == p->period_ticks))
gp->active = 1;
else
gp->active ^= 1;
@@ -75,52 +68,48 @@
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 (unlikely(!gp->active && test_bit(PWM_FLAG_STOP, &p->flags))) {
+ clear_bit(PWM_FLAG_STOP, &p->flags);
+ complete_all(&gp->complete);
+ goto done;
}
if (gp->active)
- tnew = ktime_set(0, gp->pwm.channels[0].duty_ticks);
+ hrtimer_forward_now(&gp->t, ktime_set(0, p->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);
+ hrtimer_forward_now(&gp->t, ktime_set(0, p->period_ticks
+ - p->duty_ticks));
- return HRTIMER_NORESTART;
+done:
+ return HRTIMER_RESTART;
}
-static void gpio_pwm_start(struct pwm_channel *p)
+static void gpio_pwm_start(struct pwm_device *p)
{
- struct gpio_pwm *gp = to_gpio_pwm(p);
+ struct gpio_pwm *gp = pwm_get_drvdata(p);
gp->active = 0;
- gpio_pwm_timeout(&gp->timer);
+ hrtimer_start(&gp->t, ktime_set(0, p->period_ticks - p->duty_ticks),
+ HRTIMER_MODE_REL);
+ set_bit(PWM_FLAG_RUNNING, &p->flags);
}
-static int
-gpio_pwm_config_nosleep(struct pwm_channel *p,
- struct pwm_channel_config *c)
+static int gpio_pwm_config_nosleep(struct pwm_device *p, struct
pwm_config *c)
{
- struct gpio_pwm *gp = to_gpio_pwm(p);
+ struct gpio_pwm *gp = pwm_get_drvdata(p);
int ret = 0;
unsigned long flags;
- spin_lock_irqsave(&p->lock, flags);
+ spin_lock_irqsave(&gp->lock, flags);
switch (c->config_mask) {
- case PWM_CONFIG_DUTY_TICKS:
+ case BIT(PWM_CONFIG_DUTY_TICKS):
p->duty_ticks = c->duty_ticks;
break;
- case PWM_CONFIG_START:
- if (!hrtimer_active(&gp->timer)) {
+ case BIT(PWM_CONFIG_START):
+ if (!hrtimer_active(&gp->t)) {
gpio_pwm_start(p);
}
break;
@@ -129,170 +118,215 @@
break;
}
- spin_unlock_irqrestore(&p->lock, flags);
+ spin_unlock_irqrestore(&gp->lock, flags);
return ret;
}
-static int
-gpio_pwm_stop_sync(struct pwm_channel *p)
+static int gpio_pwm_stop_sync(struct pwm_device *p)
{
- struct gpio_pwm *gp = to_gpio_pwm(p);
+ struct gpio_pwm *gp = pwm_get_drvdata(p);
int ret;
- int was_on = hrtimer_active(&gp->timer);
+ int was_on = hrtimer_active(&gp->t);
if (was_on) {
do {
- init_completion(&p->complete);
- set_bit(FLAG_STOP, &p->flags);
- ret = wait_for_completion_interruptible(&p->complete);
+ init_completion(&gp->complete);
+ set_bit(PWM_FLAG_STOP, &p->flags);
+ ret = wait_for_completion_interruptible(&gp->complete);
if (ret)
return ret;
- } while (p->flags & BIT(FLAG_STOP));
+ } while (test_bit(PWM_FLAG_STOP, &p->flags));
}
+ clear_bit(PWM_FLAG_RUNNING, &p->flags);
+
return was_on;
}
-static int
-gpio_pwm_config(struct pwm_channel *p,
- struct pwm_channel_config *c)
+static int gpio_pwm_config(struct pwm_device *p, struct pwm_config *c)
{
- struct gpio_pwm *gp = to_gpio_pwm(p);
+ struct gpio_pwm *gp = pwm_get_drvdata(p);
int was_on = 0;
- if (p->pwm->config_nosleep) {
- if (!p->pwm->config_nosleep(p, c))
- return 0;
- }
+ if (!gpio_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)
+
+ if (test_bit(PWM_CONFIG_PERIOD_TICKS, &c->config_mask))
p->period_ticks = c->period_ticks;
-
- if (c->config_mask & PWM_CONFIG_DUTY_TICKS)
+ if (test_bit(PWM_CONFIG_DUTY_TICKS, &c->config_mask))
p->duty_ticks = c->duty_ticks;
+ if (test_bit(PWM_CONFIG_POLARITY, &c->config_mask))
+ gp->polarity = !!c->polarity;
- 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)))
+ if (test_bit(PWM_CONFIG_START, &c->config_mask)
+ || (was_on && !test_bit(PWM_CONFIG_STOP, &c->config_mask)))
gpio_pwm_start(p);
return 0;
}
-static int
-gpio_pwm_set_callback(struct pwm_channel *p,
- pwm_callback_t callback)
+static int gpio_pwm_request(struct pwm_device *p)
{
- 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
-gpio_pwm_probe(struct platform_device *pdev)
+static const struct pwm_device_ops gpio_pwm_device_ops = {
+ .owner = THIS_MODULE,
+ .config = gpio_pwm_config,
+ .config_nosleep = gpio_pwm_config_nosleep,
+ .request = gpio_pwm_request,
+};
+
+struct pwm_device *gpio_pwm_create(int gpio)
{
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 (!gpio_is_valid(gpio))
+ return ERR_PTR(-EINVAL);
- if (!gpd || gpio_request(gpd->gpio, dev_name(&pdev->dev)))
- return -EINVAL;
+ if (gpio_request(gpio, DRIVER_NAME))
+ return ERR_PTR(-EBUSY);
gp = kzalloc(sizeof(*gp), GFP_KERNEL);
- if (!gp) {
- ret = -ENOMEM;
+ if (!gp)
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;
-
+ gp->gpio = gpio;
INIT_WORK(&gp->work, gpio_pwm_work);
+ init_completion(&gp->complete);
+ hrtimer_init(&gp->t, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ gp->t.function = gpio_pwm_timeout;
- hrtimer_init(&gp->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- gp->timer.function = gpio_pwm_timeout;
+ gp->pwm = pwm_register(&gpio_pwm_device_ops, NULL, "%s:%d",
DRIVER_NAME, gpio);
+ if (IS_ERR_OR_NULL(gp->pwm))
+ goto err_pwm_register;
- 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;
+ pwm_set_drvdata(gp->pwm, gp);
- ret = pwm_register(&gp->pwm);
- if (ret)
- goto err_pwm_register;
+ return gp->pwm;
- return 0;
-
err_pwm_register:
- platform_set_drvdata(pdev, 0);
kfree(gp);
err_alloc:
- return ret;
+ gpio_free(gpio);
+ return ERR_PTR(ret);
}
+EXPORT_SYMBOL(gpio_pwm_create);
-static int
-gpio_pwm_remove(struct platform_device *pdev)
+int gpio_pwm_destroy(struct pwm_device *p)
{
- struct gpio_pwm *gp = platform_get_drvdata(pdev);
- int ret;
+ struct gpio_pwm *gp = pwm_get_drvdata(p);
- ret = pwm_unregister(&gp->pwm);
- hrtimer_cancel(&gp->timer);
+ if (pwm_is_requested(gp->pwm)) {
+ if (pwm_is_running(gp->pwm))
+ pwm_stop(gp->pwm);
+ pwm_release(gp->pwm);
+ }
+ hrtimer_cancel(&gp->t);
cancel_work_sync(&gp->work);
- platform_set_drvdata(pdev, 0);
+
+ pwm_unregister(gp->pwm);
+ gpio_free(gp->gpio);
kfree(gp);
return 0;
}
+EXPORT_SYMBOL(gpio_pwm_destroy);
-static struct platform_driver gpio_pwm_driver = {
- .driver = {
- .name = "gpio_pwm",
- .owner = THIS_MODULE,
+#ifdef CONFIG_CONFIGFS_FS
+struct gpio_pwm_target {
+ struct config_item item;
+ struct pwm_device *p;
+};
+
+static struct config_item_type gpio_pwm_item_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_item *make_gpio_pwm_target(struct config_group *group,
+ const char *name)
+{
+ struct gpio_pwm_target *t;
+ unsigned long gpio;
+ int ret;
+
+ t = kzalloc(sizeof(*t), GFP_KERNEL);
+ if (!t)
+ return ERR_PTR(-ENOMEM);
+
+ ret = strict_strtoul(name, 10, &gpio);
+ if (ret || !gpio_is_valid(gpio)) {
+ ret = -EINVAL;
+ goto err_invalid_gpio;
+ }
+
+ config_item_init_type_name(&t->item, name, &gpio_pwm_item_type);
+
+ t->p = gpio_pwm_create(gpio);
+ if (IS_ERR_OR_NULL(t->p))
+ goto err_gpio_pwm_create;
+
+ return &t->item;
+
+err_gpio_pwm_create:
+err_invalid_gpio:
+ kfree(t);
+ return ERR_PTR(ret);
+}
+
+static void drop_gpio_pwm_target(struct config_group *group,
+ struct config_item *item)
+{
+ struct gpio_pwm_target *t =
+ container_of(item, struct gpio_pwm_target, item);
+
+ gpio_pwm_destroy(t->p);
+ config_item_put(&t->item);
+ kfree(t);
+}
+
+static struct configfs_group_operations gpio_pwm_subsys_group_ops = {
+ .make_item = make_gpio_pwm_target,
+ .drop_item = drop_gpio_pwm_target,
+};
+
+static struct config_item_type gpio_pwm_subsys_type = {
+ .ct_group_ops = &gpio_pwm_subsys_group_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct configfs_subsystem gpio_pwm_subsys = {
+ .su_group = {
+ .cg_item = {
+ .ci_name = DRIVER_NAME,
+ .ci_type = &gpio_pwm_subsys_type,
+ },
},
- .probe = gpio_pwm_probe,
- .remove = gpio_pwm_remove,
};
static int __init gpio_pwm_init(void)
{
- return platform_driver_register(&gpio_pwm_driver);
+ config_group_init(&gpio_pwm_subsys.su_group);
+ mutex_init(&gpio_pwm_subsys.su_mutex);
+ return configfs_register_subsystem(&gpio_pwm_subsys);
}
module_init(gpio_pwm_init);
static void __exit gpio_pwm_exit(void)
{
- platform_driver_unregister(&gpio_pwm_driver);
+ configfs_unregister_subsystem(&gpio_pwm_subsys);
}
module_exit(gpio_pwm_exit);
+#endif
MODULE_AUTHOR("Bill Gatliff <[email protected]>");
-MODULE_DESCRIPTION("PWM output using GPIO and a high-resolution timer");
+MODULE_DESCRIPTION("PWM channel emulator using GPIO and a
high-resolution timer");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:gpio_pwm");
Index: target/linux/generic/files/drivers/pwm/pwm.c
===================================================================
--- target/linux/generic/files/drivers/pwm/pwm.c (revision 35501)
+++ target/linux/generic/files/drivers/pwm/pwm.c (working copy)
@@ -1,7 +1,8 @@
/*
- * drivers/pwm/pwm.c
+ * PWM API implementation
*
- * Copyright (C) 2010 Bill Gatliff <[email protected]>
+ * Copyright (C) 2011 Bill Gatliff <[email protected]>
+ * Copyright (C) 2011 Arun Murthy <[email protected]>
*
* This program is free software; you may redistribute and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -18,197 +19,132 @@
* USA
*/
-#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/slab.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)
+void pwm_set_drvdata(struct pwm_device *p, void *data)
{
- 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;
+ dev_set_drvdata(&p->dev, data);
}
-EXPORT_SYMBOL(pwm_register);
+EXPORT_SYMBOL(pwm_set_drvdata);
-static int __match_device(struct device *dev, void *data)
+void *pwm_get_drvdata(const struct pwm_device *p)
{
- return dev_get_drvdata(dev) == data;
+ return dev_get_drvdata(&p->dev);
}
+EXPORT_SYMBOL(pwm_get_drvdata);
-int pwm_unregister(struct pwm_device *pwm)
+static inline struct pwm_device *to_pwm_device(struct device *dev)
{
- 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;
+ return container_of(dev, struct pwm_device, dev);
}
-EXPORT_SYMBOL(pwm_unregister);
-static struct pwm_device *
-__pwm_find_device(const char *bus_id)
+static int pwm_match_name(struct device *dev, void *name)
{
- struct pwm_device *p;
-
- list_for_each_entry(p, &pwm_device_list, list) {
- if (!strcmp(bus_id, p->bus_id))
- return p;
- }
- return NULL;
+ return !strcmp(name, dev_name(dev));
}
-static int
-__pwm_request_channel(struct pwm_channel *p,
- const char *requester)
+static int __pwm_request(struct pwm_device *p, const char *label)
{
int ret;
- if (test_and_set_bit(FLAG_REQUESTED, &p->flags))
- return -EBUSY;
+ if (!try_module_get(p->ops->owner))
+ return -ENODEV;
- if (p->pwm->request) {
- ret = p->pwm->request(p);
- if (ret) {
- clear_bit(FLAG_REQUESTED, &p->flags);
- return ret;
- }
+ ret = test_and_set_bit(PWM_FLAG_REQUESTED, &p->flags);
+ if (ret) {
+ ret = -EBUSY;
+ goto err_flag_requested;
}
- p->requester = requester;
- if (!strcmp(requester, REQUEST_SYSFS))
- p->pid = current->pid;
+ p->label = label;
+ if (p->ops->request) {
+ ret = p->ops->request(p);
+ if (ret)
+ goto err_request_ops;
+
+ }
+
return 0;
+
+err_request_ops:
+ clear_bit(PWM_FLAG_REQUESTED, &p->flags);
+
+err_flag_requested:
+ module_put(p->ops->owner);
+ return ret;
}
-struct pwm_channel *
-pwm_request(const char *bus_id,
- int chan,
- const char *requester)
+/**
+ * pwm_request - request a PWM device by name
+ *
+ * @name: name of PWM device
+ * @label: label that identifies requestor
+ *
+ * The @name format is driver-specific, but is typically of the form
+ * "<bus_id>:<chan>". For example, "atmel_pwmc:1" identifies the
+ * second ATMEL PWMC peripheral channel.
+ *
+ * Returns a pointer to the requested PWM device on success, -EINVAL
+ * otherwise.
+ */
+struct pwm_device *pwm_request(const char *name, const char *label)
{
+ struct device *d;
struct pwm_device *p;
int ret;
- mutex_lock(&device_list_mutex);
+ d = class_find_device(&pwm_class, NULL, (char*)name, pwm_match_name);
+ if (!d)
+ return ERR_PTR(-EINVAL);
- p = __pwm_find_device(bus_id);
- if (!p || chan >= p->nchan)
- goto err_no_device;
+ p = to_pwm_device(d);
+ ret = __pwm_request(p, label);
+ if (ret) {
+ put_device(d);
+ return ERR_PTR(ret);
+ }
- 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;
+ return p;
}
EXPORT_SYMBOL(pwm_request);
-void pwm_free(struct pwm_channel *p)
+/**
+ * pwm_release - releases a previously-requested PWM channel
+ *
+ * @p: PWM device to release
+ */
+void pwm_release(struct pwm_device *p)
{
- mutex_lock(&device_list_mutex);
+ if (!test_and_clear_bit(PWM_FLAG_REQUESTED, &p->flags)) {
+ WARN(1, "%s: releasing unrequested PWM device %s\n",
+ __func__, dev_name(&p->dev));
+ return;
+ }
- if (!test_and_clear_bit(FLAG_REQUESTED, &p->flags))
- goto done;
-
pwm_stop(p);
pwm_unsynchronize(p, NULL);
- pwm_set_handler(p, NULL, NULL);
+ p->label = NULL;
- if (p->pwm->free)
- p->pwm->free(p);
- module_put(p->pwm->owner);
-done:
- mutex_unlock(&device_list_mutex);
+ if (p->ops->release)
+ p->ops->release(p);
+
+ put_device(&p->dev);
+ module_put(p->ops->owner);
}
-EXPORT_SYMBOL(pwm_free);
+EXPORT_SYMBOL(pwm_release);
-unsigned long pwm_ns_to_ticks(struct pwm_channel *p,
- unsigned long nsecs)
+static unsigned long pwm_ns_to_ticks(struct pwm_device *p, unsigned
long nsecs)
{
unsigned long long ticks;
@@ -217,10 +153,8 @@
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)
+static unsigned long pwm_ticks_to_ns(struct pwm_device *p, unsigned
long ticks)
{
unsigned long long ns;
@@ -232,412 +166,415 @@
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)
+/**
+ * pwm_config_nosleep - configures a PWM device in an atomic context
+ *
+ * @p: PWM device to configure
+ * @c: configuration to apply to the PWM device
+ *
+ * Returns whatever the PWM device driver's config_nosleep() returns,
+ * or -ENOSYS if the PWM device driver does not have a
+ * config_nosleep() method.
+ */
+int pwm_config_nosleep(struct pwm_device *p, struct pwm_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 (!p->ops->config_nosleep)
+ return -ENOSYS;
- 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;
- }
+ return p->ops->config_nosleep(p, c);
}
-
-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)
+/**
+ * pwm_config - configures a PWM device
+ *
+ * @p: PWM device to configure
+ * @c: configuration to apply to the PWM device
+ *
+ * Performs some basic sanity checking of the parameters, and returns
+ * -EINVAL if they are found to be invalid. Otherwise, returns
+ * whatever the PWM device's config() method returns.
+ */
+int pwm_config(struct pwm_device *p, struct pwm_config *c)
{
int ret = 0;
- if (unlikely(!p->pwm->config))
- return -EINVAL;
+ dev_dbg(&p->dev, "%s: config_mask %lu period_ticks %lu "
+ "duty_ticks %lu polarity %d\n",
+ __func__, c->config_mask, c->period_ticks,
+ c->duty_ticks, c->polarity);
- 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) {
+ switch (c->config_mask & (BIT(PWM_CONFIG_PERIOD_TICKS)
+ | BIT(PWM_CONFIG_DUTY_TICKS))) {
+ case BIT(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) {
+ case BIT(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) {
+ case BIT(PWM_CONFIG_DUTY_TICKS) | BIT(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);
+ return p->ops->config(p, c);
}
EXPORT_SYMBOL(pwm_config);
-int pwm_set_period_ns(struct pwm_channel *p,
- unsigned long period_ns)
+/**
+ * pwm_set - compatibility function to ease migration from older code
+ * @p: the PWM device to configure
+ * @period_ns: period of the desired PWM signal, in nanoseconds
+ * @duty_ns: duration of active portion of desired PWM signal, in
nanoseconds
+ * @polarity: 1 if active period is high, zero otherwise
+ */
+int pwm_set(struct pwm_device *p, unsigned long period_ns,
+ unsigned long duty_ns, int polarity)
{
- struct pwm_channel_config c = {
- .config_mask = PWM_CONFIG_PERIOD_TICKS,
+ struct pwm_config c = {
+ .config_mask = (BIT(PWM_CONFIG_PERIOD_TICKS)
+ | BIT(PWM_CONFIG_DUTY_TICKS)
+ | BIT(PWM_CONFIG_POLARITY)),
.period_ticks = pwm_ns_to_ticks(p, period_ns),
+ .duty_ticks = pwm_ns_to_ticks(p, duty_ns),
+ .polarity = polarity
};
return pwm_config(p, &c);
}
+EXPORT_SYMBOL(pwm_set);
+
+int pwm_set_period_ns(struct pwm_device *p, unsigned long period_ns)
+{
+ struct pwm_config c = {
+ .config_mask = BIT(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)
+unsigned long pwm_get_period_ns(struct pwm_device *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)
+int pwm_set_duty_ns(struct pwm_device *p, unsigned long duty_ns)
{
- struct pwm_channel_config c = {
- .config_mask = PWM_CONFIG_DUTY_TICKS,
+ struct pwm_config c = {
+ .config_mask = BIT(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)
+unsigned long pwm_get_duty_ns(struct pwm_device *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)
+int pwm_set_polarity(struct pwm_device *p, int polarity)
{
- struct pwm_channel_config c = {
- .config_mask = PWM_CONFIG_DUTY_PERCENT,
- .duty_percent = percent,
+ struct pwm_config c = {
+ .config_mask = BIT(PWM_CONFIG_POLARITY),
+ .polarity = polarity,
};
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)
+int pwm_start(struct pwm_device *p)
{
- struct pwm_channel_config c = {
- .config_mask = PWM_CONFIG_START,
+ struct pwm_config c = {
+ .config_mask = BIT(PWM_CONFIG_START),
};
return pwm_config(p, &c);
}
EXPORT_SYMBOL(pwm_start);
-int pwm_stop(struct pwm_channel *p)
+int pwm_stop(struct pwm_device *p)
{
- struct pwm_channel_config c = {
- .config_mask = PWM_CONFIG_STOP,
+ struct pwm_config c = {
+ .config_mask = BIT(PWM_CONFIG_STOP),
};
return pwm_config(p, &c);
}
EXPORT_SYMBOL(pwm_stop);
-int pwm_synchronize(struct pwm_channel *p,
- struct pwm_channel *to_p)
+int pwm_synchronize(struct pwm_device *p, struct pwm_device *to_p)
{
- if (p->pwm != to_p->pwm) {
- /* TODO: support cross-device synchronization */
- return -EINVAL;
- }
+ if (!p->ops->synchronize)
+ return -ENOSYS;
- if (!p->pwm->synchronize)
- return -EINVAL;
-
- return p->pwm->synchronize(p, to_p);
+ return p->ops->synchronize(p, to_p);
}
EXPORT_SYMBOL(pwm_synchronize);
-int pwm_unsynchronize(struct pwm_channel *p,
- struct pwm_channel *from_p)
+int pwm_unsynchronize(struct pwm_device *p, struct pwm_device *from_p)
{
- if (from_p && (p->pwm != from_p->pwm)) {
- /* TODO: support cross-device synchronization */
- return -EINVAL;
- }
+ if (!p->ops->unsynchronize)
+ return -ENOSYS;
- if (!p->pwm->unsynchronize)
- return -EINVAL;
-
- return p->pwm->unsynchronize(p, from_p);
+ return p->ops->unsynchronize(p, from_p);
}
EXPORT_SYMBOL(pwm_unsynchronize);
-static void pwm_handler(struct work_struct *w)
+static ssize_t pwm_run_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
- struct pwm_channel *p = container_of(w, struct pwm_channel,
- handler_work);
- if (p->handler && p->handler(p, p->handler_data))
- pwm_stop(p);
+ struct pwm_device *p = to_pwm_device(dev);
+ return sprintf(buf, "%d\n", pwm_is_running(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)
+ const char *buf, size_t len)
{
- struct pwm_channel *p = dev_get_drvdata(dev);
+ struct pwm_device *p = to_pwm_device(dev);
+
+ if (!pwm_is_exported(p))
+ return -EPERM;
+
if (sysfs_streq(buf, "1"))
pwm_start(p);
else if (sysfs_streq(buf, "0"))
pwm_stop(p);
+ else
+ return -EINVAL;
+
return len;
}
-static DEVICE_ATTR(run, 0200, NULL, pwm_run_store);
+static ssize_t pwm_tick_hz_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pwm_device *p = to_pwm_device(dev);
+ return sprintf(buf, "%lu\n", p->tick_hz);
+}
+
static ssize_t pwm_duty_ns_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct pwm_channel *p = dev_get_drvdata(dev);
+ struct pwm_device *p = to_pwm_device(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)
+ const char *buf, size_t len)
{
unsigned long duty_ns;
- struct pwm_channel *p = dev_get_drvdata(dev);
+ struct pwm_device *p = to_pwm_device(dev);
+ int ret;
- if (1 == sscanf(buf, "%lu", &duty_ns))
- pwm_set_duty_ns(p, duty_ns);
+ if (!pwm_is_exported(p))
+ return -EPERM;
+
+ ret = strict_strtoul(buf, 10, &duty_ns);
+ if (ret)
+ return ret;
+ 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);
+ struct pwm_device *p = to_pwm_device(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)
+ const char *buf, size_t len)
{
unsigned long period_ns;
- struct pwm_channel *p = dev_get_drvdata(dev);
+ struct pwm_device *p = to_pwm_device(dev);
+ int ret;
- if (1 == sscanf(buf, "%lu", &period_ns))
- pwm_set_period_ns(p, period_ns);
+ if (!pwm_is_exported(p))
+ return -EPERM;
+
+ ret = strict_strtoul(buf, 10, &period_ns);
+ if (ret)
+ return ret;
+
+ 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);
+ struct pwm_device *p = to_pwm_device(dev);
+ return sprintf(buf, "%d\n", p->polarity ? 1 : 0);
}
static ssize_t pwm_polarity_store(struct device *dev,
struct device_attribute *attr,
- const char *buf,
- size_t len)
+ const char *buf, size_t len)
{
- int polarity;
- struct pwm_channel *p = dev_get_drvdata(dev);
+ unsigned long polarity;
+ struct pwm_device *p = to_pwm_device(dev);
+ int ret;
- if (1 == sscanf(buf, "%d", &polarity))
- pwm_set_polarity(p, polarity);
+ if (!pwm_is_exported(p))
+ return -EPERM;
+
+ ret = strict_strtoul(buf, 10, &polarity);
+ if (ret)
+ return ret;
+
+ 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,
+static ssize_t pwm_export_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pwm_device *p = to_pwm_device(dev);
+
+ if (pwm_is_requested(p))
+ return sprintf(buf, "%s\n", p->label);
+ return 0;
+}
+
+static ssize_t pwm_export_store(struct device *dev,
struct device_attribute *attr,
- char *buf)
+ const char *buf, size_t len)
{
- struct pwm_channel *p = dev_get_drvdata(dev);
- mutex_lock(&device_list_mutex);
- __pwm_request_channel(p, REQUEST_SYSFS);
- mutex_unlock(&device_list_mutex);
+ struct pwm_device *p = to_pwm_device(dev);
+ int ret;
- if (p->pid)
- return sprintf(buf, "%s %d\n", p->requester, p->pid);
- else
- return sprintf(buf, "%s\n", p->requester);
+ get_device(dev);
+ ret = __pwm_request(p, REQUEST_SYSFS);
+
+ if (!ret)
+ set_bit(PWM_FLAG_EXPORTED, &p->flags);
+ else {
+ put_device(dev);
+ ret = -EBUSY;
+ }
+
+ return ret ? ret : len;
}
-static ssize_t pwm_request_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t len)
+static ssize_t pwm_unexport_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);
+ struct pwm_device *p = to_pwm_device(dev);
+
+ if (!pwm_is_exported(p))
+ return -EINVAL;
+
+ pwm_release(p);
+ clear_bit(PWM_FLAG_EXPORTED, &p->flags);
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 struct device_attribute pwm_dev_attrs[] = {
+ __ATTR(export, S_IRUGO | S_IWUSR, pwm_export_show, pwm_export_store),
+ __ATTR(unexport, S_IWUSR, NULL, pwm_unexport_store),
+ __ATTR(polarity, S_IRUGO | S_IWUSR, pwm_polarity_show,
pwm_polarity_store),
+ __ATTR(period_ns, S_IRUGO | S_IWUSR, pwm_period_ns_show,
pwm_period_ns_store),
+ __ATTR(duty_ns, S_IRUGO | S_IWUSR, pwm_duty_ns_show,
pwm_duty_ns_store),
+ __ATTR(tick_hz, S_IRUGO, pwm_tick_hz_show, NULL),
+ __ATTR(run, S_IRUGO | S_IWUSR, pwm_run_show, pwm_run_store),
+ __ATTR_NULL,
};
-static const struct attribute_group pwm_device_attr_group = {
- .attrs = (struct attribute **)pwm_attrs,
+static struct class pwm_class = {
+ .name = "pwm",
+ .owner = THIS_MODULE,
+ .dev_attrs = pwm_dev_attrs,
};
-static int __pwm_create_sysfs(struct pwm_device *pwm)
+static void __pwm_release(struct device *dev)
{
- int ret = 0;
- struct device *dev;
- int wchan;
+ struct pwm_device *p = container_of(dev, struct pwm_device, dev);
+ kfree(p);
+}
- 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;
- }
+/**
+ * pwm_register - registers a PWM device
+ *
+ * @ops: PWM device operations
+ * @parent: reference to parent device, if any
+ * @fmt: printf-style format specifier for device name
+ */
+struct pwm_device *pwm_register(const struct pwm_device_ops *ops,
+ struct device *parent, const char *fmt, ...)
+{
+ struct pwm_device *p;
+ int ret;
+ va_list vargs;
- return ret;
+ if (!ops || !ops->config)
+ return ERR_PTR(-EINVAL);
-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);
- }
- }
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return ERR_PTR(-ENOMEM);
- return ret;
-}
+ p->ops = ops;
-static struct class_attribute pwm_class_attrs[] = {
- __ATTR_NULL,
-};
+ p->dev.class = &pwm_class;
+ p->dev.parent = parent;
+ p->dev.release = __pwm_release;
-static struct class pwm_class = {
- .name = "pwm",
- .owner = THIS_MODULE,
+ va_start(vargs, fmt);
+ ret = kobject_set_name_vargs(&p->dev.kobj, fmt, vargs);
- .class_attrs = pwm_class_attrs,
-};
+ ret = device_register(&p->dev);
+ if (ret)
+ goto err;
-static int __init pwm_init(void)
-{
- int ret;
+ return p;
- /* 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;
+err:
+ put_device(&p->dev);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(pwm_register);
- pwm_handler_workqueue = create_workqueue("pwmd");
+void pwm_unregister(struct pwm_device *p)
+{
+ device_unregister(&p->dev);
+}
+EXPORT_SYMBOL(pwm_unregister);
- return 0;
+static int __init pwm_init(void)
+{
+ return class_register(&pwm_class);
}
+
+static void __exit pwm_exit(void)
+{
+ class_unregister(&pwm_class);
+}
+
postcore_initcall(pwm_init);
+module_exit(pwm_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Bill Gatliff <[email protected]>");
+MODULE_DESCRIPTION("Generic PWM device API implementation");
Index: target/linux/generic/files/drivers/pwm/Makefile
===================================================================
--- target/linux/generic/files/drivers/pwm/Makefile (revision 35501)
+++ target/linux/generic/files/drivers/pwm/Makefile (working copy)
@@ -2,4 +2,6 @@
# Makefile for pwm devices
#
obj-$(CONFIG_GENERIC_PWM) := pwm.o
+
obj-$(CONFIG_GPIO_PWM) += gpio-pwm.o
+obj-$(CONFIG_ATMEL_PWMC) += atmel-pwmc.o
--
Regards,
Cienti Bordelo
_______________________________________________
openwrt-devel mailing list
[email protected]
https://lists.openwrt.org/mailman/listinfo/openwrt-devel