mirror of
https://github.com/torvalds/linux.git
synced 2024-12-28 05:41:55 +00:00
1ec5c1867a
Core changes: - Augment fwnode_get_named_gpiod() to configure the GPIO pin immediately after requesting it like all other APIs do. This is a treewide change also updating all users. - Pass a GPIO label down to gpiod_request() from fwnode_get_named_gpiod(). This makes debugfs and the userspace ABI correctly reflect the current in-kernel consumer of a pin taken using this abstraction. This is a treewide change also updating all users. - Rename devm_get_gpiod_from_child() to devm_fwnode_get_gpiod_from_child() to reflect the fact that this function is operating on a fwnode object. This is a treewide change also updating all users. - Make it possible to take multiple GPIOs in a single hog of device tree hogs. - The refactorings switching GPIO chips to use the .set_config() callback using standard pin control properties and providing a backend into the pin control subsystem that were also merged into the pin control tree naturally appear here too. Testing instrumentation: - A whole slew of cleanups and improvements to the mockup GPIO driver. We now have an extended userspace test exercising the subsystem, and we can inject interrupts etc from userspace to fully test the core GPIO functionality. New drivers: - New driver for the Cortina Systems Gemini GPIO controller. - New driver for the Exar XR17V352/354/358 chips. - New driver for the ACCES PCI-IDIO-16 PCI GPIO card. Driver changes: - RCAR: set the irqchip parent device, add fine-grained runtime PM support. - pca953x: support optional RESET control line on the chip. - DaVinci: cleanups and simplifications. Add support for multiple instances. - .set_multiple() and naming of lines on more or less all of the ISA/PCI GPIO controllers. - mcp23s08: refactored to use regmap as a first step to further rewrites and modernizations. -----BEGIN PGP SIGNATURE----- iQIcBAABAgAGBQJYrqvqAAoJEEEQszewGV1zoHsP/i1iZBEywR9+yIx/p2/F2mJu nriuYFlp0V3FjHQAQ//YCA9+Catri+ZqT5l+BmG/EYdqqikHbziTyS0YArlfrMHv OOBfDmfftexvRI/jQAl+X/nIW531ZjYo6ZApFy/2TirTwfkI7DIMi6ujm09fcG5D BgCT1KuszbVtyrmhrQvbeEdVKw0qLAgwnn5eOOCQE4KuDB3s7eyal0rJaDEXhpMF kH/y6eySs4FChEhAEmCkM6205F5T4c2YFjL1bo5Fkh/WPrVPaKI0Ny16qbaDWU9K W9RaJUzf92KIW0MgcRl+r8Lxn+GekN6/jvrxddQ/Ajs/Dkh5r2JCrm7RIC9tBPcJ VbLfjL+cMehlSEu9eyxRQcAIeuUYCqkN8ghuVoj9xt/tDtNYsQIcJZtfW1yjmONq mFsd5KhfBFgspQkwF4IX3hthaqj8MH4zefQdWzAGPZMGEA1rrx2kVSEdZD3EV4VN 84qt5Cx9hLllafthJOGjEIZFCjPIpbMRwTQ+fmc+1IB1DgN8Kc5E1FMssKbUEoOK 2eLquLvd7iNDMidTjoi87YAisW9qnrPeRDywsqeXdQf7fzpB97gX4MQfJ5fJWEYr 3uHCfu2u4J4cff9ygg8c4ut7ePEjz+ld/sBh9EHicbbryR4I5ZG7Ne1aQhsmb2M5 dHZSRfQYEQ4Nl7cMJQuh =O81I -----END PGP SIGNATURE----- Merge tag 'gpio-v4.11-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio Pull GPIO updates from Linus Walleij: "This is the bulk of GPIO changes for the v4.11 cycle Core changes: - Augment fwnode_get_named_gpiod() to configure the GPIO pin immediately after requesting it like all other APIs do. This is a treewide change also updating all users. - Pass a GPIO label down to gpiod_request() from fwnode_get_named_gpiod(). This makes debugfs and the userspace ABI correctly reflect the current in-kernel consumer of a pin taken using this abstraction. This is a treewide change also updating all users. - Rename devm_get_gpiod_from_child() to devm_fwnode_get_gpiod_from_child() to reflect the fact that this function is operating on a fwnode object. This is a treewide change also updating all users. - Make it possible to take multiple GPIOs in a single hog of device tree hogs. - The refactorings switching GPIO chips to use the .set_config() callback using standard pin control properties and providing a backend into the pin control subsystem that were also merged into the pin control tree naturally appear here too. Testing instrumentation: - A whole slew of cleanups and improvements to the mockup GPIO driver. We now have an extended userspace test exercising the subsystem, and we can inject interrupts etc from userspace to fully test the core GPIO functionality. New drivers: - New driver for the Cortina Systems Gemini GPIO controller. - New driver for the Exar XR17V352/354/358 chips. - New driver for the ACCES PCI-IDIO-16 PCI GPIO card. Driver changes: - RCAR: set the irqchip parent device, add fine-grained runtime PM support. - pca953x: support optional RESET control line on the chip. - DaVinci: cleanups and simplifications. Add support for multiple instances. - .set_multiple() and naming of lines on more or less all of the ISA/PCI GPIO controllers. - mcp23s08: refactored to use regmap as a first step to further rewrites and modernizations" * tag 'gpio-v4.11-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio: (61 commits) gpio: reintroduce devm_get_gpiod_from_child() gpio: pci-idio-16: Fix PCI BAR index gpio: pci-idio-16: Fix PCI device ID code gpio: mockup: implement event injecting over debugfs gpio: mockup: add a dummy irqchip gpio: mockup: implement naming the lines gpio: mockup: code shrink gpio: mockup: readability tweaks gpio: Add GPIO support for the ACCES PCI-IDIO-16 gpio: Add the devm_fwnode_get_index_gpiod_from_child() helper gpio: Rename devm_get_gpiod_from_child() gpio: mcp23s08: Select REGMAP/REGMAP_I2C to fix build error gpio: ws16c48: Add support for GPIO names gpio: gpio-mm: Add support for GPIO names gpio: 104-idio-16: Add support for GPIO names gpio: 104-idi-48: Add support for GPIO names gpio: 104-dio-48e: Add support for GPIO names gpio: ws16c48: Remove unnecessary driver_data set gpio: gpio-mm: Remove unnecessary driver_data set gpio: 104-idio-16: Remove unnecessary driver_data set ...
393 lines
9.9 KiB
C
393 lines
9.9 KiB
C
/*
|
|
* Driver for buttons on GPIO lines not capable of generating interrupts
|
|
*
|
|
* Copyright (C) 2007-2010 Gabor Juhos <juhosg@openwrt.org>
|
|
* Copyright (C) 2010 Nuno Goncalves <nunojpg@gmail.com>
|
|
*
|
|
* This file was based on: /drivers/input/misc/cobalt_btns.c
|
|
* Copyright (C) 2007 Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>
|
|
*
|
|
* also was based on: /drivers/input/keyboard/gpio_keys.c
|
|
* Copyright 2005 Phil Blundell
|
|
*
|
|
* 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 <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/input.h>
|
|
#include <linux/input-polldev.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/gpio/consumer.h>
|
|
#include <linux/gpio_keys.h>
|
|
#include <linux/property.h>
|
|
|
|
#define DRV_NAME "gpio-keys-polled"
|
|
|
|
struct gpio_keys_button_data {
|
|
struct gpio_desc *gpiod;
|
|
int last_state;
|
|
int count;
|
|
int threshold;
|
|
};
|
|
|
|
struct gpio_keys_polled_dev {
|
|
struct input_polled_dev *poll_dev;
|
|
struct device *dev;
|
|
const struct gpio_keys_platform_data *pdata;
|
|
unsigned long rel_axis_seen[BITS_TO_LONGS(REL_CNT)];
|
|
unsigned long abs_axis_seen[BITS_TO_LONGS(ABS_CNT)];
|
|
struct gpio_keys_button_data data[0];
|
|
};
|
|
|
|
static void gpio_keys_button_event(struct input_polled_dev *dev,
|
|
const struct gpio_keys_button *button,
|
|
int state)
|
|
{
|
|
struct gpio_keys_polled_dev *bdev = dev->private;
|
|
struct input_dev *input = dev->input;
|
|
unsigned int type = button->type ?: EV_KEY;
|
|
|
|
if (type == EV_REL) {
|
|
if (state) {
|
|
input_event(input, type, button->code, button->value);
|
|
__set_bit(button->code, bdev->rel_axis_seen);
|
|
}
|
|
} else if (type == EV_ABS) {
|
|
if (state) {
|
|
input_event(input, type, button->code, button->value);
|
|
__set_bit(button->code, bdev->abs_axis_seen);
|
|
}
|
|
} else {
|
|
input_event(input, type, button->code, state);
|
|
input_sync(input);
|
|
}
|
|
}
|
|
|
|
static void gpio_keys_polled_check_state(struct input_polled_dev *dev,
|
|
const struct gpio_keys_button *button,
|
|
struct gpio_keys_button_data *bdata)
|
|
{
|
|
int state;
|
|
|
|
state = gpiod_get_value_cansleep(bdata->gpiod);
|
|
if (state < 0) {
|
|
dev_err(dev->input->dev.parent,
|
|
"failed to get gpio state: %d\n", state);
|
|
} else {
|
|
gpio_keys_button_event(dev, button, state);
|
|
|
|
if (state != bdata->last_state) {
|
|
bdata->count = 0;
|
|
bdata->last_state = state;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void gpio_keys_polled_poll(struct input_polled_dev *dev)
|
|
{
|
|
struct gpio_keys_polled_dev *bdev = dev->private;
|
|
const struct gpio_keys_platform_data *pdata = bdev->pdata;
|
|
struct input_dev *input = dev->input;
|
|
int i;
|
|
|
|
memset(bdev->rel_axis_seen, 0, sizeof(bdev->rel_axis_seen));
|
|
memset(bdev->abs_axis_seen, 0, sizeof(bdev->abs_axis_seen));
|
|
|
|
for (i = 0; i < pdata->nbuttons; i++) {
|
|
struct gpio_keys_button_data *bdata = &bdev->data[i];
|
|
|
|
if (bdata->count < bdata->threshold) {
|
|
bdata->count++;
|
|
gpio_keys_button_event(dev, &pdata->buttons[i],
|
|
bdata->last_state);
|
|
} else {
|
|
gpio_keys_polled_check_state(dev, &pdata->buttons[i],
|
|
bdata);
|
|
}
|
|
}
|
|
|
|
for_each_set_bit(i, input->relbit, REL_CNT) {
|
|
if (!test_bit(i, bdev->rel_axis_seen))
|
|
input_event(input, EV_REL, i, 0);
|
|
}
|
|
|
|
for_each_set_bit(i, input->absbit, ABS_CNT) {
|
|
if (!test_bit(i, bdev->abs_axis_seen))
|
|
input_event(input, EV_ABS, i, 0);
|
|
}
|
|
|
|
input_sync(input);
|
|
}
|
|
|
|
static void gpio_keys_polled_open(struct input_polled_dev *dev)
|
|
{
|
|
struct gpio_keys_polled_dev *bdev = dev->private;
|
|
const struct gpio_keys_platform_data *pdata = bdev->pdata;
|
|
|
|
if (pdata->enable)
|
|
pdata->enable(bdev->dev);
|
|
}
|
|
|
|
static void gpio_keys_polled_close(struct input_polled_dev *dev)
|
|
{
|
|
struct gpio_keys_polled_dev *bdev = dev->private;
|
|
const struct gpio_keys_platform_data *pdata = bdev->pdata;
|
|
|
|
if (pdata->disable)
|
|
pdata->disable(bdev->dev);
|
|
}
|
|
|
|
static struct gpio_keys_platform_data *
|
|
gpio_keys_polled_get_devtree_pdata(struct device *dev)
|
|
{
|
|
struct gpio_keys_platform_data *pdata;
|
|
struct gpio_keys_button *button;
|
|
struct fwnode_handle *child;
|
|
int nbuttons;
|
|
|
|
nbuttons = device_get_child_node_count(dev);
|
|
if (nbuttons == 0)
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
pdata = devm_kzalloc(dev, sizeof(*pdata) + nbuttons * sizeof(*button),
|
|
GFP_KERNEL);
|
|
if (!pdata)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
button = (struct gpio_keys_button *)(pdata + 1);
|
|
|
|
pdata->buttons = button;
|
|
pdata->nbuttons = nbuttons;
|
|
|
|
pdata->rep = device_property_present(dev, "autorepeat");
|
|
device_property_read_u32(dev, "poll-interval", &pdata->poll_interval);
|
|
|
|
device_for_each_child_node(dev, child) {
|
|
if (fwnode_property_read_u32(child, "linux,code",
|
|
&button->code)) {
|
|
dev_err(dev, "button without keycode\n");
|
|
fwnode_handle_put(child);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
fwnode_property_read_string(child, "label", &button->desc);
|
|
|
|
if (fwnode_property_read_u32(child, "linux,input-type",
|
|
&button->type))
|
|
button->type = EV_KEY;
|
|
|
|
if (fwnode_property_read_u32(child, "linux,input-value",
|
|
(u32 *)&button->value))
|
|
button->value = 1;
|
|
|
|
button->wakeup =
|
|
fwnode_property_read_bool(child, "wakeup-source") ||
|
|
/* legacy name */
|
|
fwnode_property_read_bool(child, "gpio-key,wakeup");
|
|
|
|
if (fwnode_property_read_u32(child, "debounce-interval",
|
|
&button->debounce_interval))
|
|
button->debounce_interval = 5;
|
|
|
|
button++;
|
|
}
|
|
|
|
return pdata;
|
|
}
|
|
|
|
static void gpio_keys_polled_set_abs_params(struct input_dev *input,
|
|
const struct gpio_keys_platform_data *pdata, unsigned int code)
|
|
{
|
|
int i, min = 0, max = 0;
|
|
|
|
for (i = 0; i < pdata->nbuttons; i++) {
|
|
const struct gpio_keys_button *button = &pdata->buttons[i];
|
|
|
|
if (button->type != EV_ABS || button->code != code)
|
|
continue;
|
|
|
|
if (button->value < min)
|
|
min = button->value;
|
|
if (button->value > max)
|
|
max = button->value;
|
|
}
|
|
|
|
input_set_abs_params(input, code, min, max, 0, 0);
|
|
}
|
|
|
|
static const struct of_device_id gpio_keys_polled_of_match[] = {
|
|
{ .compatible = "gpio-keys-polled", },
|
|
{ },
|
|
};
|
|
MODULE_DEVICE_TABLE(of, gpio_keys_polled_of_match);
|
|
|
|
static int gpio_keys_polled_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct fwnode_handle *child = NULL;
|
|
const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev);
|
|
struct gpio_keys_polled_dev *bdev;
|
|
struct input_polled_dev *poll_dev;
|
|
struct input_dev *input;
|
|
size_t size;
|
|
int error;
|
|
int i;
|
|
|
|
if (!pdata) {
|
|
pdata = gpio_keys_polled_get_devtree_pdata(dev);
|
|
if (IS_ERR(pdata))
|
|
return PTR_ERR(pdata);
|
|
}
|
|
|
|
if (!pdata->poll_interval) {
|
|
dev_err(dev, "missing poll_interval value\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
size = sizeof(struct gpio_keys_polled_dev) +
|
|
pdata->nbuttons * sizeof(struct gpio_keys_button_data);
|
|
bdev = devm_kzalloc(dev, size, GFP_KERNEL);
|
|
if (!bdev) {
|
|
dev_err(dev, "no memory for private data\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
poll_dev = devm_input_allocate_polled_device(dev);
|
|
if (!poll_dev) {
|
|
dev_err(dev, "no memory for polled device\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
poll_dev->private = bdev;
|
|
poll_dev->poll = gpio_keys_polled_poll;
|
|
poll_dev->poll_interval = pdata->poll_interval;
|
|
poll_dev->open = gpio_keys_polled_open;
|
|
poll_dev->close = gpio_keys_polled_close;
|
|
|
|
input = poll_dev->input;
|
|
|
|
input->name = pdev->name;
|
|
input->phys = DRV_NAME"/input0";
|
|
|
|
input->id.bustype = BUS_HOST;
|
|
input->id.vendor = 0x0001;
|
|
input->id.product = 0x0001;
|
|
input->id.version = 0x0100;
|
|
|
|
__set_bit(EV_KEY, input->evbit);
|
|
if (pdata->rep)
|
|
__set_bit(EV_REP, input->evbit);
|
|
|
|
for (i = 0; i < pdata->nbuttons; i++) {
|
|
const struct gpio_keys_button *button = &pdata->buttons[i];
|
|
struct gpio_keys_button_data *bdata = &bdev->data[i];
|
|
unsigned int type = button->type ?: EV_KEY;
|
|
|
|
if (button->wakeup) {
|
|
dev_err(dev, DRV_NAME " does not support wakeup\n");
|
|
fwnode_handle_put(child);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!dev_get_platdata(dev)) {
|
|
/* No legacy static platform data */
|
|
child = device_get_next_child_node(dev, child);
|
|
if (!child) {
|
|
dev_err(dev, "missing child device node\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
bdata->gpiod = devm_fwnode_get_gpiod_from_child(dev,
|
|
NULL, child,
|
|
GPIOD_IN,
|
|
button->desc);
|
|
if (IS_ERR(bdata->gpiod)) {
|
|
error = PTR_ERR(bdata->gpiod);
|
|
if (error != -EPROBE_DEFER)
|
|
dev_err(dev,
|
|
"failed to get gpio: %d\n",
|
|
error);
|
|
fwnode_handle_put(child);
|
|
return error;
|
|
}
|
|
} else if (gpio_is_valid(button->gpio)) {
|
|
/*
|
|
* Legacy GPIO number so request the GPIO here and
|
|
* convert it to descriptor.
|
|
*/
|
|
unsigned flags = GPIOF_IN;
|
|
|
|
if (button->active_low)
|
|
flags |= GPIOF_ACTIVE_LOW;
|
|
|
|
error = devm_gpio_request_one(dev, button->gpio,
|
|
flags, button->desc ? : DRV_NAME);
|
|
if (error) {
|
|
dev_err(dev,
|
|
"unable to claim gpio %u, err=%d\n",
|
|
button->gpio, error);
|
|
return error;
|
|
}
|
|
|
|
bdata->gpiod = gpio_to_desc(button->gpio);
|
|
if (!bdata->gpiod) {
|
|
dev_err(dev,
|
|
"unable to convert gpio %u to descriptor\n",
|
|
button->gpio);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
bdata->last_state = -1;
|
|
bdata->threshold = DIV_ROUND_UP(button->debounce_interval,
|
|
pdata->poll_interval);
|
|
|
|
input_set_capability(input, type, button->code);
|
|
if (type == EV_ABS)
|
|
gpio_keys_polled_set_abs_params(input, pdata,
|
|
button->code);
|
|
}
|
|
|
|
fwnode_handle_put(child);
|
|
|
|
bdev->poll_dev = poll_dev;
|
|
bdev->dev = dev;
|
|
bdev->pdata = pdata;
|
|
|
|
error = input_register_polled_device(poll_dev);
|
|
if (error) {
|
|
dev_err(dev, "unable to register polled device, err=%d\n",
|
|
error);
|
|
return error;
|
|
}
|
|
|
|
/* report initial state of the buttons */
|
|
for (i = 0; i < pdata->nbuttons; i++)
|
|
gpio_keys_polled_check_state(poll_dev, &pdata->buttons[i],
|
|
&bdev->data[i]);
|
|
|
|
input_sync(input);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver gpio_keys_polled_driver = {
|
|
.probe = gpio_keys_polled_probe,
|
|
.driver = {
|
|
.name = DRV_NAME,
|
|
.of_match_table = gpio_keys_polled_of_match,
|
|
},
|
|
};
|
|
module_platform_driver(gpio_keys_polled_driver);
|
|
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
|
|
MODULE_DESCRIPTION("Polled GPIO Buttons driver");
|
|
MODULE_ALIAS("platform:" DRV_NAME);
|