diff --git a/drivers/Kconfig b/drivers/Kconfig index 34f40ea..6353238 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -78,6 +78,8 @@ source "drivers/mmc/Kconfig" source "drivers/leds/Kconfig" +source "drivers/buttons/Kconfig" + source "drivers/infiniband/Kconfig" source "drivers/edac/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index cfe38ff..1faca07 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -79,6 +79,7 @@ obj-$(CONFIG_CPU_FREQ) += cpufreq/ obj-$(CONFIG_CPU_IDLE) += cpuidle/ obj-$(CONFIG_MMC) += mmc/ obj-$(CONFIG_NEW_LEDS) += leds/ +obj-$(CONFIG_BUTTONS) += buttons/ obj-$(CONFIG_INFINIBAND) += infiniband/ obj-$(CONFIG_SGI_SN) += sn/ obj-y += firmware/ diff --git a/drivers/buttons/Kconfig b/drivers/buttons/Kconfig new file mode 100644 index 0000000..75764ea --- /dev/null +++ b/drivers/buttons/Kconfig @@ -0,0 +1,27 @@ +menuconfig BUTTONS + bool "Buttons Support" + depends on HAS_IOMEM + help + Say Y to enable Linux buttons support. This allows control of supported + LEDs from both userspace and optionally, by kernel events (triggers). + + This is not related to standard keyboard LEDs which are controlled + via the input system. + +if BUTTONS +config BUTTONS_CLASS + tristate "Buttons Class Support" + help + This option enables the led sysfs class in /sys/class/leds. You'll + need this to do anything useful with LEDs. If unsure, say N. + +comment "Buttons drivers" + +config BUTTONS_GPIO + tristate "Buttons Support for GPIO connected buttons" + depends on BUTTONS_CLASS && GENERIC_GPIO + help + This option enables support for the buttonss connected to GPIO + outputs. To be useful the particular board must have LEDs + and they must be connected to the GPIO lines. +endif # BUTTONS diff --git a/drivers/buttons/Makefile b/drivers/buttons/Makefile new file mode 100644 index 0000000..d606c9f --- /dev/null +++ b/drivers/buttons/Makefile @@ -0,0 +1,6 @@ +# Buttons Core +obj-$(CONFIG_BUTTONS) += btn-core.o +obj-$(CONFIG_BUTTONS_CLASS) += btn-class.o + +# Button Platform Drivers +obj-$(CONFIG_BUTTONS_GPIO) += btns-gpio.o diff --git a/drivers/buttons/btn-class.c b/drivers/buttons/btn-class.c new file mode 100644 index 0000000..3c94015 --- /dev/null +++ b/drivers/buttons/btn-class.c @@ -0,0 +1,134 @@ +/* + * Buttons Class Core + * + * Copyright (C) 2007 Florian Fainelli +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "buttons.h" + +static struct class *buttons_class; + +static ssize_t button_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct button_classdev *button_cdev = dev_get_drvdata(dev); + ssize_t ret = 0; + + /* no lock needed for this */ + sprintf(buf, "%u\n", button_cdev->state); + ret = strlen(buf) + 1; + + return ret; +} + +static DEVICE_ATTR(state, 0644, button_state_show, NULL); + +/** + * button_classdev_suspend - suspend an button_classdev. + * @button_cdev: the button_classdev to suspend. + */ +void button_classdev_suspend(struct button_classdev *button_cdev) +{ + button_cdev->flags |= BUTTON_SUSPENDED; +} +EXPORT_SYMBOL_GPL(button_classdev_suspend); + +/** + * button_classdev_resume - resume an button_classdev. + * @button_cdev: the button_classdev to resume. + */ +void button_classdev_resume(struct button_classdev *button_cdev) +{ + button_cdev->flags &= ~BUTTON_SUSPENDED; +} +EXPORT_SYMBOL_GPL(button_classdev_resume); + +/** + * button_classdev_register - register a new object of button_classdev class. + * @dev: The device to register. + * @button_cdev: the button_classdev structure for this device. + */ +int button_classdev_register(struct device *parent, struct button_classdev *button_cdev) +{ + int rc; + + button_cdev->dev = device_create(buttons_class, parent, 0, "%s", + button_cdev->name); + if (unlikely(IS_ERR(button_cdev->dev))) + return PTR_ERR(button_cdev->dev); + + dev_set_drvdata(button_cdev->dev, button_cdev); + + /* register the attributes */ + rc = device_create_file(button_cdev->dev, &dev_attr_state); + if (rc) + goto err_out; + + /* add to the list of buttons */ + write_lock(&buttons_list_lock); + list_add_tail(&button_cdev->node, &buttons_list); + write_unlock(&buttons_list_lock); + + printk(KERN_INFO "Registered button device: %s\n", + button_cdev->name); + + return 0; + +err_out: + device_unregister(button_cdev->dev); + return rc; +} +EXPORT_SYMBOL_GPL(button_classdev_register); + +/** + * button_classdev_unregister - unregisters a object of button_properties class. + * @button_cdev: the button device to unregister + * + * Unregisters a previously registered via button_classdev_register object. + */ +void button_classdev_unregister(struct button_classdev *button_cdev) +{ + device_remove_file(button_cdev->dev, &dev_attr_state); + + device_unregister(button_cdev->dev); + + write_lock(&buttons_list_lock); + list_del(&button_cdev->node); + write_unlock(&buttons_list_lock); +} +EXPORT_SYMBOL_GPL(button_classdev_unregister); + +static int __init buttons_init(void) +{ + buttons_class = class_create(THIS_MODULE, "buttons"); + if (IS_ERR(buttons_class)) + return PTR_ERR(buttons_class); + return 0; +} + +static void __exit buttons_exit(void) +{ + class_destroy(buttons_class); +} + +subsys_initcall(buttons_init); +module_exit(buttons_exit); + +MODULE_AUTHOR("Florian Fainelli "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Button Class Interface"); diff --git a/drivers/buttons/btn-core.c b/drivers/buttons/btn-core.c new file mode 100644 index 0000000..e3759ef --- /dev/null +++ b/drivers/buttons/btn-core.c @@ -0,0 +1,24 @@ +/* + * Buttons Class Core + * + * Copyright (C) 2007 Florian Fainelli + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include "buttons.h" + +DEFINE_RWLOCK(buttons_list_lock); +LIST_HEAD(buttons_list); + +EXPORT_SYMBOL_GPL(buttons_list); +EXPORT_SYMBOL_GPL(buttons_list_lock); diff --git a/drivers/buttons/btns-gpio.c b/drivers/buttons/btns-gpio.c new file mode 100644 index 0000000..3792146 --- /dev/null +++ b/drivers/buttons/btns-gpio.c @@ -0,0 +1,202 @@ +/* + * Buttons driver for GPIOs + * + * Copyright (C) 2007 Florian Fainelli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include +#include +#include +#include +#include + +#include + +enum button_polarity { + NORMAL = 0, + REVERSE = 1, +}; + +struct gpio_button_data { + struct button_classdev cdev; + unsigned gpio; + struct work_struct work; + u8 can_sleep; + enum button_polarity polarity; +}; + +static void gpio_button_work(struct work_struct *work) +{ + struct gpio_button_data *button_dat = + container_of(work, struct gpio_button_data, work); + + gpio_set_value_cansleep(button_dat->gpio, button_dat->new_level); +} + +static void gpio_button_get(struct button_classdev *button_cdev, + enum button_state value) +{ + struct gpio_button_data *button_dat = + container_of(button_cdev, struct gpio_button_data, cdev); + unsigned int level; + + if (value == BTN_RELEASED) + level = 0; + else + level = 1; + + if (button_dat->polarity == REVERSE) + level = !level; + + /* getting GPIOs with I2C/etc requires a preemptible task context */ + if (button_dat->can_sleep) { + if (preempt_count()) + schedule_work(&button_dat->work); + else + button_cdev->state = gpio_read_value_cansleep(button_dat->gpio); + } else + button_cdev->state = gpio_read_value(button_dat->gpio); +} + + +static int __init gpio_button_probe(struct platform_device *pdev) +{ + struct gpio_button_platform_data *pdata = pdev->dev.platform_data; + struct gpio_button *cur_button; + struct gpio_button_data *buttons_data, *button_dat; + int i, ret = 0; + + if (!pdata) + return -EBUSY; + + buttons_data = kzalloc(sizeof(struct gpio_button_data) * pdata->num_buttons, + GFP_KERNEL); + if (!buttons_data) + return -ENOMEM; + + for (i = 0; i < pdata->num_buttons; i++) { + cur_button = &pdata->buttons[i]; + button_dat = &buttons_data[i]; + + button_dat->cdev.name = cur_button->name; + button_dat->cdev.default_trigger = cur_button->default_trigger; + button_dat->gpio = cur_button->gpio; + button_dat->can_sleep = gpio_cansleep(cur_button->gpio); + button_dat->cdev.get_state = gpio_button_get; + button_dat->active_low = cur_button->active_low; + button_dat->cdev.state = cur_button->polarity ? BTN_PRESSED : BTN_RELEASED; + + ret = gpio_request(button_dat->gpio, button_dat->cdev.name); + if (ret < 0) + goto err; + + gpio_direction_output(button_dat->gpio, button_dat->active_low); + + ret = button_classdev_register(&pdev->dev, &button_dat->cdev); + if (ret < 0) { + gpio_free(button_dat->gpio); + goto err; + } + + INIT_WORK(&button_dat->work, gpio_button_work); + } + + platform_set_drvdata(pdev, buttons_data); + + return 0; + +err: + if (i > 0) { + for (i = i - 1; i >= 0; i--) { + button_classdev_unregister(&buttons_data[i].cdev); + gpio_free(buttons_data[i].gpio); + } + } + + flush_schedubutton_work(); + kfree(buttons_data); + + return ret; +} + +static int __exit gpio_button_remove(struct platform_device *pdev) +{ + int i; + struct gpio_button_platform_data *pdata = pdev->dev.platform_data; + struct gpio_button_data *buttons_data; + + buttons_data = platform_get_drvdata(pdev); + + for (i = 0; i < pdata->num_buttons; i++) { + button_classdev_unregister(&buttons_data[i].cdev); + gpio_free(buttons_data[i].gpio); + } + + kfree(buttons_data); + + return 0; +} + +#ifdef CONFIG_PM +static int gpio_button_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct gpio_button_platform_data *pdata = pdev->dev.platform_data; + struct gpio_button_data *buttons_data; + int i; + + buttons_data = platform_get_drvdata(pdev); + + for (i = 0; i < pdata->num_buttons; i++) + button_classdev_suspend(&buttons_data[i].cdev); + + return 0; +} + +static int gpio_button_resume(struct platform_device *pdev) +{ + struct gpio_button_platform_data *pdata = pdev->dev.platform_data; + struct gpio_button_data *buttons_data; + int i; + + buttons_data = platform_get_drvdata(pdev); + + for (i = 0; i < pdata->num_buttons; i++) + button_classdev_resume(&buttons_data[i].cdev); + + return 0; +} +#else +#define gpio_button_suspend NULL +#define gpio_button_resume NULL +#endif + +static struct platform_driver gpio_button_driver = { + .remove = __exit_p(gpio_button_remove), + .suspend = gpio_button_suspend, + .resume = gpio_button_resume, + .driver = { + .name = "buttons-gpio", + .owner = THIS_MODULE, + }, +}; + +static int __init gpio_button_init(void) +{ + return platform_driver_probe(&gpio_button_driver, gpio_button_probe); +} + +static void __exit gpio_button_exit(void) +{ + platform_driver_unregister(&gpio_button_driver); +} + +module_init(gpio_button_init); +module_exit(gpio_button_exit); + +MODULE_AUTHOR("Florian Fainelli "); +MODULE_DESCRIPTION("Button LED driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/buttons/buttons.h b/drivers/buttons/buttons.h new file mode 100644 index 0000000..181dd37 --- /dev/null +++ b/drivers/buttons/buttons.h @@ -0,0 +1,20 @@ +/* + * Buttons Core + * + * Copyright (C) 2007 Florian Fainelli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#ifndef __BUTTONS_H_INCLUDED +#define __BUTTONS_H_INCLUDED + +#include +#include + +extern rwlock_t buttons_list_lock; +extern struct list_head buttons_list; + +#endif /* __BUTTONS_H_INCLUDED */ diff --git a/include/linux/buttons.h b/include/linux/buttons.h new file mode 100644 index 0000000..d0af4d2 --- /dev/null +++ b/include/linux/buttons.h @@ -0,0 +1,56 @@ +/* + * Driver model for buttons + * + * Copyright (C) 2007 Florian Fainelli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#ifndef __LINUX_BUTTONS_H_INCLUDED +#define __LINUX_BUTTONS_H_INCLUDED + +#include +#include + +struct device; +/* + * Button Core + */ + +enum button_state { + BTN_RELEASED = 0, + BTN_PRESSED = 1, +}; + +struct button_classdev { + const char *name; + bool state; + int flags; +#define BUTTON_SUSPENDED (1 << 0) + void (*get_state)(struct button_classdev *button_cdev, + enum button_state value); + struct device *dev; + struct list_head node; /* Button Device list */ +}; + +extern int button_classdev_register(struct device *parent, + struct button_classdev *button_cdev); +extern void button_classdev_unregister(struct button_classdev *button_cdev); +extern void button_classdev_suspend(struct button_classdev *button_cdev); +extern void button_classdev_resume(struct button_classdev *button_cdev); + +/* For the buttons-gpio driver */ +struct gpio_button { + const char *name; + unsigned gpio; + u8 polarity; +}; + +struct gpio_button_platform_data { + int num_buttons; + struct gpio_button *buttons; +}; + +#endif /* __LINUX_BUTTONS_H_INCLUDED */