diff options
Diffstat (limited to 'drivers/gpio')
-rw-r--r-- | drivers/gpio/gpiolib.c | 208 |
1 files changed, 203 insertions, 5 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index aef6b3d8e2cf..bb11a429394a 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c | |||
@@ -1,5 +1,6 @@ | |||
1 | #include <linux/kernel.h> | 1 | #include <linux/kernel.h> |
2 | #include <linux/module.h> | 2 | #include <linux/module.h> |
3 | #include <linux/interrupt.h> | ||
3 | #include <linux/irq.h> | 4 | #include <linux/irq.h> |
4 | #include <linux/spinlock.h> | 5 | #include <linux/spinlock.h> |
5 | #include <linux/device.h> | 6 | #include <linux/device.h> |
@@ -7,6 +8,7 @@ | |||
7 | #include <linux/debugfs.h> | 8 | #include <linux/debugfs.h> |
8 | #include <linux/seq_file.h> | 9 | #include <linux/seq_file.h> |
9 | #include <linux/gpio.h> | 10 | #include <linux/gpio.h> |
11 | #include <linux/idr.h> | ||
10 | 12 | ||
11 | 13 | ||
12 | /* Optional implementation infrastructure for GPIO interfaces. | 14 | /* Optional implementation infrastructure for GPIO interfaces. |
@@ -49,6 +51,13 @@ struct gpio_desc { | |||
49 | #define FLAG_RESERVED 2 | 51 | #define FLAG_RESERVED 2 |
50 | #define FLAG_EXPORT 3 /* protected by sysfs_lock */ | 52 | #define FLAG_EXPORT 3 /* protected by sysfs_lock */ |
51 | #define FLAG_SYSFS 4 /* exported via /sys/class/gpio/control */ | 53 | #define FLAG_SYSFS 4 /* exported via /sys/class/gpio/control */ |
54 | #define FLAG_TRIG_FALL 5 /* trigger on falling edge */ | ||
55 | #define FLAG_TRIG_RISE 6 /* trigger on rising edge */ | ||
56 | |||
57 | #define PDESC_ID_SHIFT 16 /* add new flags before this one */ | ||
58 | |||
59 | #define GPIO_FLAGS_MASK ((1 << PDESC_ID_SHIFT) - 1) | ||
60 | #define GPIO_TRIGGER_MASK (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE)) | ||
52 | 61 | ||
53 | #ifdef CONFIG_DEBUG_FS | 62 | #ifdef CONFIG_DEBUG_FS |
54 | const char *label; | 63 | const char *label; |
@@ -56,6 +65,15 @@ struct gpio_desc { | |||
56 | }; | 65 | }; |
57 | static struct gpio_desc gpio_desc[ARCH_NR_GPIOS]; | 66 | static struct gpio_desc gpio_desc[ARCH_NR_GPIOS]; |
58 | 67 | ||
68 | #ifdef CONFIG_GPIO_SYSFS | ||
69 | struct poll_desc { | ||
70 | struct work_struct work; | ||
71 | struct sysfs_dirent *value_sd; | ||
72 | }; | ||
73 | |||
74 | static struct idr pdesc_idr; | ||
75 | #endif | ||
76 | |||
59 | static inline void desc_set_label(struct gpio_desc *d, const char *label) | 77 | static inline void desc_set_label(struct gpio_desc *d, const char *label) |
60 | { | 78 | { |
61 | #ifdef CONFIG_DEBUG_FS | 79 | #ifdef CONFIG_DEBUG_FS |
@@ -188,10 +206,10 @@ static DEFINE_MUTEX(sysfs_lock); | |||
188 | * /value | 206 | * /value |
189 | * * always readable, subject to hardware behavior | 207 | * * always readable, subject to hardware behavior |
190 | * * may be writable, as zero/nonzero | 208 | * * may be writable, as zero/nonzero |
191 | * | 209 | * /edge |
192 | * REVISIT there will likely be an attribute for configuring async | 210 | * * configures behavior of poll(2) on /value |
193 | * notifications, e.g. to specify polling interval or IRQ trigger type | 211 | * * available only if pin can generate IRQs on input |
194 | * that would for example trigger a poll() on the "value". | 212 | * * is read/write as "none", "falling", "rising", or "both" |
195 | */ | 213 | */ |
196 | 214 | ||
197 | static ssize_t gpio_direction_show(struct device *dev, | 215 | static ssize_t gpio_direction_show(struct device *dev, |
@@ -288,6 +306,175 @@ static ssize_t gpio_value_store(struct device *dev, | |||
288 | static /*const*/ DEVICE_ATTR(value, 0644, | 306 | static /*const*/ DEVICE_ATTR(value, 0644, |
289 | gpio_value_show, gpio_value_store); | 307 | gpio_value_show, gpio_value_store); |
290 | 308 | ||
309 | static irqreturn_t gpio_sysfs_irq(int irq, void *priv) | ||
310 | { | ||
311 | struct work_struct *work = priv; | ||
312 | |||
313 | schedule_work(work); | ||
314 | return IRQ_HANDLED; | ||
315 | } | ||
316 | |||
317 | static void gpio_notify_sysfs(struct work_struct *work) | ||
318 | { | ||
319 | struct poll_desc *pdesc; | ||
320 | |||
321 | pdesc = container_of(work, struct poll_desc, work); | ||
322 | sysfs_notify_dirent(pdesc->value_sd); | ||
323 | } | ||
324 | |||
325 | static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev, | ||
326 | unsigned long gpio_flags) | ||
327 | { | ||
328 | struct poll_desc *pdesc; | ||
329 | unsigned long irq_flags; | ||
330 | int ret, irq, id; | ||
331 | |||
332 | if ((desc->flags & GPIO_TRIGGER_MASK) == gpio_flags) | ||
333 | return 0; | ||
334 | |||
335 | irq = gpio_to_irq(desc - gpio_desc); | ||
336 | if (irq < 0) | ||
337 | return -EIO; | ||
338 | |||
339 | id = desc->flags >> PDESC_ID_SHIFT; | ||
340 | pdesc = idr_find(&pdesc_idr, id); | ||
341 | if (pdesc) { | ||
342 | free_irq(irq, &pdesc->work); | ||
343 | cancel_work_sync(&pdesc->work); | ||
344 | } | ||
345 | |||
346 | desc->flags &= ~GPIO_TRIGGER_MASK; | ||
347 | |||
348 | if (!gpio_flags) { | ||
349 | ret = 0; | ||
350 | goto free_sd; | ||
351 | } | ||
352 | |||
353 | irq_flags = IRQF_SHARED; | ||
354 | if (test_bit(FLAG_TRIG_FALL, &gpio_flags)) | ||
355 | irq_flags |= IRQF_TRIGGER_FALLING; | ||
356 | if (test_bit(FLAG_TRIG_RISE, &gpio_flags)) | ||
357 | irq_flags |= IRQF_TRIGGER_RISING; | ||
358 | |||
359 | if (!pdesc) { | ||
360 | pdesc = kmalloc(sizeof(*pdesc), GFP_KERNEL); | ||
361 | if (!pdesc) { | ||
362 | ret = -ENOMEM; | ||
363 | goto err_out; | ||
364 | } | ||
365 | |||
366 | do { | ||
367 | ret = -ENOMEM; | ||
368 | if (idr_pre_get(&pdesc_idr, GFP_KERNEL)) | ||
369 | ret = idr_get_new_above(&pdesc_idr, | ||
370 | pdesc, 1, &id); | ||
371 | } while (ret == -EAGAIN); | ||
372 | |||
373 | if (ret) | ||
374 | goto free_mem; | ||
375 | |||
376 | desc->flags &= GPIO_FLAGS_MASK; | ||
377 | desc->flags |= (unsigned long)id << PDESC_ID_SHIFT; | ||
378 | |||
379 | if (desc->flags >> PDESC_ID_SHIFT != id) { | ||
380 | ret = -ERANGE; | ||
381 | goto free_id; | ||
382 | } | ||
383 | |||
384 | pdesc->value_sd = sysfs_get_dirent(dev->kobj.sd, "value"); | ||
385 | if (!pdesc->value_sd) { | ||
386 | ret = -ENODEV; | ||
387 | goto free_id; | ||
388 | } | ||
389 | INIT_WORK(&pdesc->work, gpio_notify_sysfs); | ||
390 | } | ||
391 | |||
392 | ret = request_irq(irq, gpio_sysfs_irq, irq_flags, | ||
393 | "gpiolib", &pdesc->work); | ||
394 | if (ret) | ||
395 | goto free_sd; | ||
396 | |||
397 | desc->flags |= gpio_flags; | ||
398 | return 0; | ||
399 | |||
400 | free_sd: | ||
401 | sysfs_put(pdesc->value_sd); | ||
402 | free_id: | ||
403 | idr_remove(&pdesc_idr, id); | ||
404 | desc->flags &= GPIO_FLAGS_MASK; | ||
405 | free_mem: | ||
406 | kfree(pdesc); | ||
407 | err_out: | ||
408 | return ret; | ||
409 | } | ||
410 | |||
411 | static const struct { | ||
412 | const char *name; | ||
413 | unsigned long flags; | ||
414 | } trigger_types[] = { | ||
415 | { "none", 0 }, | ||
416 | { "falling", BIT(FLAG_TRIG_FALL) }, | ||
417 | { "rising", BIT(FLAG_TRIG_RISE) }, | ||
418 | { "both", BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE) }, | ||
419 | }; | ||
420 | |||
421 | static ssize_t gpio_edge_show(struct device *dev, | ||
422 | struct device_attribute *attr, char *buf) | ||
423 | { | ||
424 | const struct gpio_desc *desc = dev_get_drvdata(dev); | ||
425 | ssize_t status; | ||
426 | |||
427 | mutex_lock(&sysfs_lock); | ||
428 | |||
429 | if (!test_bit(FLAG_EXPORT, &desc->flags)) | ||
430 | status = -EIO; | ||
431 | else { | ||
432 | int i; | ||
433 | |||
434 | status = 0; | ||
435 | for (i = 0; i < ARRAY_SIZE(trigger_types); i++) | ||
436 | if ((desc->flags & GPIO_TRIGGER_MASK) | ||
437 | == trigger_types[i].flags) { | ||
438 | status = sprintf(buf, "%s\n", | ||
439 | trigger_types[i].name); | ||
440 | break; | ||
441 | } | ||
442 | } | ||
443 | |||
444 | mutex_unlock(&sysfs_lock); | ||
445 | return status; | ||
446 | } | ||
447 | |||
448 | static ssize_t gpio_edge_store(struct device *dev, | ||
449 | struct device_attribute *attr, const char *buf, size_t size) | ||
450 | { | ||
451 | struct gpio_desc *desc = dev_get_drvdata(dev); | ||
452 | ssize_t status; | ||
453 | int i; | ||
454 | |||
455 | for (i = 0; i < ARRAY_SIZE(trigger_types); i++) | ||
456 | if (sysfs_streq(trigger_types[i].name, buf)) | ||
457 | goto found; | ||
458 | return -EINVAL; | ||
459 | |||
460 | found: | ||
461 | mutex_lock(&sysfs_lock); | ||
462 | |||
463 | if (!test_bit(FLAG_EXPORT, &desc->flags)) | ||
464 | status = -EIO; | ||
465 | else { | ||
466 | status = gpio_setup_irq(desc, dev, trigger_types[i].flags); | ||
467 | if (!status) | ||
468 | status = size; | ||
469 | } | ||
470 | |||
471 | mutex_unlock(&sysfs_lock); | ||
472 | |||
473 | return status; | ||
474 | } | ||
475 | |||
476 | static DEVICE_ATTR(edge, 0644, gpio_edge_show, gpio_edge_store); | ||
477 | |||
291 | static const struct attribute *gpio_attrs[] = { | 478 | static const struct attribute *gpio_attrs[] = { |
292 | &dev_attr_direction.attr, | 479 | &dev_attr_direction.attr, |
293 | &dev_attr_value.attr, | 480 | &dev_attr_value.attr, |
@@ -473,7 +660,7 @@ int gpio_export(unsigned gpio, bool direction_may_change) | |||
473 | struct device *dev; | 660 | struct device *dev; |
474 | 661 | ||
475 | dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0), | 662 | dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0), |
476 | desc, ioname ? ioname : "gpio%d", gpio); | 663 | desc, ioname ? ioname : "gpio%d", gpio); |
477 | if (dev) { | 664 | if (dev) { |
478 | if (direction_may_change) | 665 | if (direction_may_change) |
479 | status = sysfs_create_group(&dev->kobj, | 666 | status = sysfs_create_group(&dev->kobj, |
@@ -481,6 +668,14 @@ int gpio_export(unsigned gpio, bool direction_may_change) | |||
481 | else | 668 | else |
482 | status = device_create_file(dev, | 669 | status = device_create_file(dev, |
483 | &dev_attr_value); | 670 | &dev_attr_value); |
671 | |||
672 | if (!status && gpio_to_irq(gpio) >= 0 | ||
673 | && (direction_may_change | ||
674 | || !test_bit(FLAG_IS_OUT, | ||
675 | &desc->flags))) | ||
676 | status = device_create_file(dev, | ||
677 | &dev_attr_edge); | ||
678 | |||
484 | if (status != 0) | 679 | if (status != 0) |
485 | device_unregister(dev); | 680 | device_unregister(dev); |
486 | } else | 681 | } else |
@@ -572,6 +767,7 @@ void gpio_unexport(unsigned gpio) | |||
572 | 767 | ||
573 | dev = class_find_device(&gpio_class, NULL, desc, match_export); | 768 | dev = class_find_device(&gpio_class, NULL, desc, match_export); |
574 | if (dev) { | 769 | if (dev) { |
770 | gpio_setup_irq(desc, dev, 0); | ||
575 | clear_bit(FLAG_EXPORT, &desc->flags); | 771 | clear_bit(FLAG_EXPORT, &desc->flags); |
576 | put_device(dev); | 772 | put_device(dev); |
577 | device_unregister(dev); | 773 | device_unregister(dev); |
@@ -656,6 +852,8 @@ static int __init gpiolib_sysfs_init(void) | |||
656 | unsigned long flags; | 852 | unsigned long flags; |
657 | unsigned gpio; | 853 | unsigned gpio; |
658 | 854 | ||
855 | idr_init(&pdesc_idr); | ||
856 | |||
659 | status = class_register(&gpio_class); | 857 | status = class_register(&gpio_class); |
660 | if (status < 0) | 858 | if (status < 0) |
661 | return status; | 859 | return status; |