gpio-button-hotplug: convert to gpio descriptor (gpiod_) API

OpenWrt's special gpio-button-hotplug driver is still using
exclusively the legacy GPIO Subsystem gpio_ API.

While it still does work fine for most devices, upstream
linux is starting to convert platform support like that of
the APU2/3/4 to the new GPIOD LOOKUP tables that are not
supported by it.

Hence, this patch replaces the gpio_ calls present in
gpio-button-hotplug with gpiod_ equivalent wherever
it's possible. This allows the driver to use the
gpiod lookup tables and still have a fallback for
legacy platform data code that just sets button->gpio
set to the real button/switch GPIO.

As a bonus: the active_low logic is now being handled
by the linux's gpio subsystem too. Another issue that
was address is the of_handle leak in the dt parser
error path.

Tested with legacy platform data: x86_64: APU2, MX-100
Tested on OF: ATH79; MR18, APM821xx: Netgear WNDR4700,
	      RAMIPS: WL-330N3G
	      LANTIQ: AVM FritzBox 7360v1

Reported-by: Chris Blake <chrisrblake93@gmail.com>
Tested-by: Chris Blake <chrisrblake93@gmail.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
This commit is contained in:
Christian Lamparter 2021-08-05 15:29:25 +02:00
parent d98738b5c1
commit 2b0378cf9f

View File

@ -242,11 +242,11 @@ static int gpio_button_get_value(struct gpio_keys_button_data *bdata)
int val; int val;
if (bdata->can_sleep) if (bdata->can_sleep)
val = !!gpio_get_value_cansleep(bdata->b->gpio); val = !!gpiod_get_value_cansleep(bdata->gpiod);
else else
val = !!gpio_get_value(bdata->b->gpio); val = !!gpiod_get_value(bdata->gpiod);
return val ^ bdata->b->active_low; return val;
} }
static void gpio_keys_handle_button(struct gpio_keys_button_data *bdata) static void gpio_keys_handle_button(struct gpio_keys_button_data *bdata)
@ -365,7 +365,6 @@ gpio_keys_get_devtree_pdata(struct device *dev)
struct device_node *node, *pp; struct device_node *node, *pp;
struct gpio_keys_platform_data *pdata; struct gpio_keys_platform_data *pdata;
struct gpio_keys_button *button; struct gpio_keys_button *button;
int error;
int nbuttons; int nbuttons;
int i = 0; int i = 0;
@ -375,14 +374,12 @@ gpio_keys_get_devtree_pdata(struct device *dev)
nbuttons = of_get_child_count(node); nbuttons = of_get_child_count(node);
if (nbuttons == 0) if (nbuttons == 0)
return NULL; return ERR_PTR(-EINVAL);
pdata = devm_kzalloc(dev, sizeof(*pdata) + nbuttons * (sizeof *button), pdata = devm_kzalloc(dev, sizeof(*pdata) + nbuttons * (sizeof *button),
GFP_KERNEL); GFP_KERNEL);
if (!pdata) { if (!pdata)
error = -ENOMEM; return ERR_PTR(-ENOMEM);
goto err_out;
}
pdata->buttons = (struct gpio_keys_button *)(pdata + 1); pdata->buttons = (struct gpio_keys_button *)(pdata + 1);
pdata->nbuttons = nbuttons; pdata->nbuttons = nbuttons;
@ -391,37 +388,13 @@ gpio_keys_get_devtree_pdata(struct device *dev)
of_property_read_u32(node, "poll-interval", &pdata->poll_interval); of_property_read_u32(node, "poll-interval", &pdata->poll_interval);
for_each_child_of_node(node, pp) { for_each_child_of_node(node, pp) {
enum of_gpio_flags flags;
if (!of_find_property(pp, "gpios", NULL)) {
pdata->nbuttons--;
dev_warn(dev, "Found button without gpios\n");
continue;
}
button = (struct gpio_keys_button *)(&pdata->buttons[i++]); button = (struct gpio_keys_button *)(&pdata->buttons[i++]);
button->irq = irq_of_parse_and_map(pp, 0);
button->gpio = of_get_gpio_flags(pp, 0, &flags);
if (button->gpio < 0) {
error = button->gpio;
if (error != -ENOENT) {
if (error != -EPROBE_DEFER)
dev_err(dev,
"Failed to get gpio flags, error: %d\n",
error);
return ERR_PTR(error);
}
} else {
button->active_low = !!(flags & OF_GPIO_ACTIVE_LOW);
}
if (of_property_read_u32(pp, "linux,code", &button->code)) { if (of_property_read_u32(pp, "linux,code", &button->code)) {
dev_err(dev, "Button without keycode: 0x%x\n", dev_err(dev, "Button node '%s' without keycode\n",
button->gpio); pp->full_name);
error = -EINVAL; of_node_put(pp);
goto err_out; return ERR_PTR(-EINVAL);
} }
button->desc = of_get_property(pp, "label", NULL); button->desc = of_get_property(pp, "label", NULL);
@ -434,17 +407,12 @@ gpio_keys_get_devtree_pdata(struct device *dev)
if (of_property_read_u32(pp, "debounce-interval", if (of_property_read_u32(pp, "debounce-interval",
&button->debounce_interval)) &button->debounce_interval))
button->debounce_interval = 5; button->debounce_interval = 5;
}
if (pdata->nbuttons == 0) { button->irq = irq_of_parse_and_map(pp, 0);
error = -EINVAL; button->gpio = -ENOENT; /* mark this as device-tree */
goto err_out;
} }
return pdata; return pdata;
err_out:
return ERR_PTR(error);
} }
static struct of_device_id gpio_keys_of_match[] = { static struct of_device_id gpio_keys_of_match[] = {
@ -471,11 +439,12 @@ gpio_keys_get_devtree_pdata(struct device *dev)
static int gpio_keys_button_probe(struct platform_device *pdev, static int gpio_keys_button_probe(struct platform_device *pdev,
struct gpio_keys_button_dev **_bdev, int polled) struct gpio_keys_button_dev **_bdev, int polled)
{ {
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct gpio_keys_platform_data *pdata = dev_get_platdata(dev);
struct gpio_keys_button_dev *bdev; struct gpio_keys_button_dev *bdev;
struct gpio_keys_button *buttons; struct gpio_keys_button *buttons;
int error; struct device_node *prev = NULL;
int error = 0;
int i; int i;
if (!pdata) { if (!pdata) {
@ -514,46 +483,67 @@ static int gpio_keys_button_probe(struct platform_device *pdev,
for (i = 0; i < pdata->nbuttons; i++) { for (i = 0; i < pdata->nbuttons; i++) {
struct gpio_keys_button *button = &buttons[i]; struct gpio_keys_button *button = &buttons[i];
struct gpio_keys_button_data *bdata = &bdev->data[i]; struct gpio_keys_button_data *bdata = &bdev->data[i];
unsigned int gpio = button->gpio; const char *desc = button->desc ? button->desc : DRV_NAME;
if (button->wakeup) { if (button->wakeup) {
dev_err(dev, "does not support wakeup\n"); dev_err(dev, "does not support wakeup\n");
return -EINVAL; error = -EINVAL;
goto out;
} }
bdata->map_entry = button_get_index(button->code); bdata->map_entry = button_get_index(button->code);
if (bdata->map_entry < 0) { if (bdata->map_entry < 0) {
dev_warn(dev, "does not support key code:%u\n", dev_err(dev, "does not support key code:%u\n",
button->code); button->code);
continue; error = -EINVAL;
goto out;
} }
if (!(button->type == 0 || button->type == EV_KEY || if (!(button->type == 0 || button->type == EV_KEY ||
button->type == EV_SW)) { button->type == EV_SW)) {
dev_warn(dev, "only supports buttons or switches\n"); dev_err(dev, "only supports buttons or switches\n");
continue; error = -EINVAL;
goto out;
} }
error = devm_gpio_request(dev, gpio, if (gpio_is_valid(button->gpio)) {
button->desc ? button->desc : DRV_NAME); /* legacy platform data... but is it the lookup table? */
bdata->gpiod = devm_gpiod_get_index(dev, desc, i,
GPIOD_IN);
if (IS_ERR(bdata->gpiod)) {
/* or the legacy (button->gpio is good) way? */
error = devm_gpio_request_one(dev,
button->gpio, GPIOF_IN | (
button->active_low ? GPIOF_ACTIVE_LOW :
0), desc);
if (error) { if (error) {
dev_err(dev, "unable to claim gpio %u, err=%d\n", if (error != -EPROBE_DEFER) {
gpio, error); dev_err(dev, "unable to claim gpio %d, err=%d\n",
return error; button->gpio, error);
} }
bdata->gpiod = gpio_to_desc(gpio); goto out;
if (!bdata->gpiod)
return -EINVAL;
error = gpio_direction_input(gpio);
if (error) {
dev_err(dev,
"unable to set direction on gpio %u, err=%d\n",
gpio, error);
return error;
} }
bdata->can_sleep = gpio_cansleep(gpio); bdata->gpiod = gpio_to_desc(button->gpio);
}
} else {
/* Device-tree */
struct device_node *child =
of_get_next_child(dev->of_node, prev);
bdata->gpiod = devm_gpiod_get_from_of_node(dev,
child, "gpios", 0, GPIOD_IN, desc);
prev = child;
}
if (IS_ERR_OR_NULL(bdata->gpiod)) {
error = IS_ERR(bdata->gpiod) ? PTR_ERR(bdata->gpiod) :
-EINVAL;
goto out;
}
bdata->can_sleep = gpiod_cansleep(bdata->gpiod);
bdata->last_state = -1; /* Unknown state on boot */ bdata->last_state = -1; /* Unknown state on boot */
if (bdev->polled) { if (bdev->polled) {
@ -584,8 +574,11 @@ static int gpio_keys_button_probe(struct platform_device *pdev,
platform_set_drvdata(pdev, bdev); platform_set_drvdata(pdev, bdev);
*_bdev = bdev; *_bdev = bdev;
error = 0;
return 0; out:
of_node_put(prev);
return error;
} }
static int gpio_keys_probe(struct platform_device *pdev) static int gpio_keys_probe(struct platform_device *pdev)
@ -594,9 +587,7 @@ static int gpio_keys_probe(struct platform_device *pdev)
struct gpio_keys_button_dev *bdev; struct gpio_keys_button_dev *bdev;
int ret, i; int ret, i;
ret = gpio_keys_button_probe(pdev, &bdev, 0); ret = gpio_keys_button_probe(pdev, &bdev, 0);
if (ret) if (ret)
return ret; return ret;
@ -608,12 +599,8 @@ static int gpio_keys_probe(struct platform_device *pdev)
INIT_DELAYED_WORK(&bdata->work, gpio_keys_irq_work_func); INIT_DELAYED_WORK(&bdata->work, gpio_keys_irq_work_func);
if (!bdata->gpiod)
continue;
if (!button->irq) { if (!button->irq) {
bdata->irq = gpio_to_irq(button->gpio); bdata->irq = gpiod_to_irq(bdata->gpiod);
if (bdata->irq < 0) { if (bdata->irq < 0) {
dev_err(&pdev->dev, "failed to get irq for gpio:%d\n", dev_err(&pdev->dev, "failed to get irq for gpio:%d\n",
button->gpio); button->gpio);
@ -631,7 +618,6 @@ static int gpio_keys_probe(struct platform_device *pdev)
ret = devm_request_threaded_irq(&pdev->dev, ret = devm_request_threaded_irq(&pdev->dev,
bdata->irq, NULL, button_handle_irq, bdata->irq, NULL, button_handle_irq,
irqflags, dev_name(&pdev->dev), bdata); irqflags, dev_name(&pdev->dev), bdata);
if (ret < 0) { if (ret < 0) {
bdata->irq = 0; bdata->irq = 0;
dev_err(&pdev->dev, "failed to request irq:%d for gpio:%d\n", dev_err(&pdev->dev, "failed to request irq:%d for gpio:%d\n",
@ -653,14 +639,12 @@ static int gpio_keys_polled_probe(struct platform_device *pdev)
int ret; int ret;
ret = gpio_keys_button_probe(pdev, &bdev, 1); ret = gpio_keys_button_probe(pdev, &bdev, 1);
if (ret) if (ret)
return ret; return ret;
INIT_DELAYED_WORK(&bdev->work, gpio_keys_polled_poll); INIT_DELAYED_WORK(&bdev->work, gpio_keys_polled_poll);
pdata = bdev->pdata; pdata = bdev->pdata;
if (pdata->enable) if (pdata->enable)
pdata->enable(bdev->dev); pdata->enable(bdev->dev);