mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 01:24:47 +01:00
GPIO core already handles checking the offset against the number of
GPIOs as well as missing any of the GPIO chip callbacks. Remove the
unnecessary bits.
Also, the offset check was off-by-one as reported by Dan.
Fixes: 2b03d9a40c ("gpio: add gpio-line-mux driver")
Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
Closes: https://lore.kernel.org/all/aV4b6GAGz1zyf8Xy@stanley.mountain/
Tested-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Reviewed-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://lore.kernel.org/r/20260107085833.17338-1-bartosz.golaszewski@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
116 lines
2.9 KiB
C
116 lines
2.9 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* GPIO line mux which acts as virtual gpiochip and provides a 1-to-many
|
|
* mapping between virtual GPIOs and a real GPIO + multiplexer.
|
|
*
|
|
* Copyright (c) 2025 Jonas Jelonek <jelonek.jonas@gmail.com>
|
|
*/
|
|
|
|
#include <linux/gpio/consumer.h>
|
|
#include <linux/gpio/driver.h>
|
|
#include <linux/mod_devicetable.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/mux/consumer.h>
|
|
#include <linux/platform_device.h>
|
|
|
|
#define MUX_SELECT_DELAY_US 100
|
|
|
|
struct gpio_lmux {
|
|
struct gpio_chip gc;
|
|
struct mux_control *mux;
|
|
struct gpio_desc *muxed_gpio;
|
|
|
|
u32 num_gpio_mux_states;
|
|
unsigned int gpio_mux_states[] __counted_by(num_gpio_mux_states);
|
|
};
|
|
|
|
static int gpio_lmux_gpio_get(struct gpio_chip *gc, unsigned int offset)
|
|
{
|
|
struct gpio_lmux *glm = gpiochip_get_data(gc);
|
|
int ret;
|
|
|
|
ret = mux_control_select_delay(glm->mux, glm->gpio_mux_states[offset],
|
|
MUX_SELECT_DELAY_US);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = gpiod_get_raw_value_cansleep(glm->muxed_gpio);
|
|
mux_control_deselect(glm->mux);
|
|
return ret;
|
|
}
|
|
|
|
static int gpio_lmux_gpio_get_direction(struct gpio_chip *gc,
|
|
unsigned int offset)
|
|
{
|
|
return GPIO_LINE_DIRECTION_IN;
|
|
}
|
|
|
|
static int gpio_lmux_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct gpio_lmux *glm;
|
|
unsigned int ngpio;
|
|
size_t size;
|
|
int ret;
|
|
|
|
ngpio = device_property_count_u32(dev, "gpio-line-mux-states");
|
|
if (!ngpio)
|
|
return -EINVAL;
|
|
|
|
size = struct_size(glm, gpio_mux_states, ngpio);
|
|
glm = devm_kzalloc(dev, size, GFP_KERNEL);
|
|
if (!glm)
|
|
return -ENOMEM;
|
|
|
|
glm->gc.base = -1;
|
|
glm->gc.can_sleep = true;
|
|
glm->gc.fwnode = dev_fwnode(dev);
|
|
glm->gc.label = dev_name(dev);
|
|
glm->gc.ngpio = ngpio;
|
|
glm->gc.owner = THIS_MODULE;
|
|
glm->gc.parent = dev;
|
|
|
|
glm->gc.get = gpio_lmux_gpio_get;
|
|
glm->gc.get_direction = gpio_lmux_gpio_get_direction;
|
|
|
|
glm->mux = devm_mux_control_get(dev, NULL);
|
|
if (IS_ERR(glm->mux))
|
|
return dev_err_probe(dev, PTR_ERR(glm->mux),
|
|
"could not get mux controller\n");
|
|
|
|
glm->muxed_gpio = devm_gpiod_get(dev, "muxed", GPIOD_IN);
|
|
if (IS_ERR(glm->muxed_gpio))
|
|
return dev_err_probe(dev, PTR_ERR(glm->muxed_gpio),
|
|
"could not get muxed-gpio\n");
|
|
|
|
glm->num_gpio_mux_states = ngpio;
|
|
ret = device_property_read_u32_array(dev, "gpio-line-mux-states",
|
|
&glm->gpio_mux_states[0], ngpio);
|
|
if (ret)
|
|
return dev_err_probe(dev, ret, "could not get mux states\n");
|
|
|
|
ret = devm_gpiochip_add_data(dev, &glm->gc, glm);
|
|
if (ret)
|
|
return dev_err_probe(dev, ret, "failed to add gpiochip\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id gpio_lmux_of_match[] = {
|
|
{ .compatible = "gpio-line-mux" },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, gpio_lmux_of_match);
|
|
|
|
static struct platform_driver gpio_lmux_driver = {
|
|
.driver = {
|
|
.name = "gpio-line-mux",
|
|
.of_match_table = gpio_lmux_of_match,
|
|
},
|
|
.probe = gpio_lmux_probe,
|
|
};
|
|
module_platform_driver(gpio_lmux_driver);
|
|
|
|
MODULE_AUTHOR("Jonas Jelonek <jelonek.jonas@gmail.com>");
|
|
MODULE_DESCRIPTION("GPIO line mux driver");
|
|
MODULE_LICENSE("GPL");
|