Skip to content

Commit

Permalink
gpiolib: get rid of struct poll_desc and worklet
Browse files Browse the repository at this point in the history
As sysfs_notify_dirent has been made irq safe, there is no reason to not
call it directly from irq.  With the work_struct removed, the remaining
element in poll_desc is a sysfs_dirent pointer which may not be NULL.  We
can therefore store it directly in the idr and pass it as context to the
irq handler.

Most part of the patch deals with renaming defines and variables to
reflect their new use without functional change.

I also took the opportunity to initialize the idr statically.

Signed-off-by: Daniel Gl?ckner <dg@emlix.com>
Cc: David Brownell <david-b@pacbell.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Daniel Gl?ckner authored and Linus Torvalds committed Aug 11, 2010
1 parent 9c4ba94 commit 5ba1821
Showing 1 changed file with 27 additions and 53 deletions.
80 changes: 27 additions & 53 deletions drivers/gpio/gpiolib.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ struct gpio_desc {
#define FLAG_TRIG_RISE 6 /* trigger on rising edge */
#define FLAG_ACTIVE_LOW 7 /* sysfs value has active low */

#define PDESC_ID_SHIFT 16 /* add new flags before this one */
#define ID_SHIFT 16 /* add new flags before this one */

#define GPIO_FLAGS_MASK ((1 << PDESC_ID_SHIFT) - 1)
#define GPIO_FLAGS_MASK ((1 << ID_SHIFT) - 1)
#define GPIO_TRIGGER_MASK (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE))

#ifdef CONFIG_DEBUG_FS
Expand All @@ -69,12 +69,7 @@ struct gpio_desc {
static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];

#ifdef CONFIG_GPIO_SYSFS
struct poll_desc {
struct work_struct work;
struct sysfs_dirent *value_sd;
};

static struct idr pdesc_idr;
static DEFINE_IDR(dirent_idr);
#endif

static inline void desc_set_label(struct gpio_desc *d, const char *label)
Expand Down Expand Up @@ -325,24 +320,16 @@ static const DEVICE_ATTR(value, 0644,

static irqreturn_t gpio_sysfs_irq(int irq, void *priv)
{
struct work_struct *work = priv;
struct sysfs_dirent *value_sd = priv;

schedule_work(work);
sysfs_notify_dirent(value_sd);
return IRQ_HANDLED;
}

static void gpio_notify_sysfs(struct work_struct *work)
{
struct poll_desc *pdesc;

pdesc = container_of(work, struct poll_desc, work);
sysfs_notify_dirent(pdesc->value_sd);
}

static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev,
unsigned long gpio_flags)
{
struct poll_desc *pdesc;
struct sysfs_dirent *value_sd;
unsigned long irq_flags;
int ret, irq, id;

Expand All @@ -353,18 +340,16 @@ static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev,
if (irq < 0)
return -EIO;

id = desc->flags >> PDESC_ID_SHIFT;
pdesc = idr_find(&pdesc_idr, id);
if (pdesc) {
free_irq(irq, &pdesc->work);
cancel_work_sync(&pdesc->work);
}
id = desc->flags >> ID_SHIFT;
value_sd = idr_find(&dirent_idr, id);
if (value_sd)
free_irq(irq, value_sd);

desc->flags &= ~GPIO_TRIGGER_MASK;

if (!gpio_flags) {
ret = 0;
goto free_sd;
goto free_id;
}

irq_flags = IRQF_SHARED;
Expand All @@ -375,55 +360,46 @@ static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev,
irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ?
IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;

if (!pdesc) {
pdesc = kmalloc(sizeof(*pdesc), GFP_KERNEL);
if (!pdesc) {
ret = -ENOMEM;
if (!value_sd) {
value_sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value");
if (!value_sd) {
ret = -ENODEV;
goto err_out;
}

do {
ret = -ENOMEM;
if (idr_pre_get(&pdesc_idr, GFP_KERNEL))
ret = idr_get_new_above(&pdesc_idr,
pdesc, 1, &id);
if (idr_pre_get(&dirent_idr, GFP_KERNEL))
ret = idr_get_new_above(&dirent_idr, value_sd,
1, &id);
} while (ret == -EAGAIN);

if (ret)
goto free_mem;
goto free_sd;

desc->flags &= GPIO_FLAGS_MASK;
desc->flags |= (unsigned long)id << PDESC_ID_SHIFT;
desc->flags |= (unsigned long)id << ID_SHIFT;

if (desc->flags >> PDESC_ID_SHIFT != id) {
if (desc->flags >> ID_SHIFT != id) {
ret = -ERANGE;
goto free_id;
}

pdesc->value_sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value");
if (!pdesc->value_sd) {
ret = -ENODEV;
goto free_id;
}
INIT_WORK(&pdesc->work, gpio_notify_sysfs);
}

ret = request_irq(irq, gpio_sysfs_irq, irq_flags,
"gpiolib", &pdesc->work);
"gpiolib", value_sd);
if (ret)
goto free_sd;
goto free_id;

desc->flags |= gpio_flags;
return 0;

free_sd:
if (pdesc)
sysfs_put(pdesc->value_sd);
free_id:
idr_remove(&pdesc_idr, id);
idr_remove(&dirent_idr, id);
desc->flags &= GPIO_FLAGS_MASK;
free_mem:
kfree(pdesc);
free_sd:
if (value_sd)
sysfs_put(value_sd);
err_out:
return ret;
}
Expand Down Expand Up @@ -994,8 +970,6 @@ static int __init gpiolib_sysfs_init(void)
unsigned long flags;
unsigned gpio;

idr_init(&pdesc_idr);

status = class_register(&gpio_class);
if (status < 0)
return status;
Expand Down

0 comments on commit 5ba1821

Please sign in to comment.