mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-04 20:19:47 +08:00
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);
|