Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * GPIO line mux which acts as virtual gpiochip and provides a 1-to-many
4 * mapping between virtual GPIOs and a real GPIO + multiplexer.
5 *
6 * Copyright (c) 2025 Jonas Jelonek <jelonek.jonas@gmail.com>
7 */
8
9#include <linux/gpio/consumer.h>
10#include <linux/gpio/driver.h>
11#include <linux/mod_devicetable.h>
12#include <linux/mutex.h>
13#include <linux/mux/consumer.h>
14#include <linux/platform_device.h>
15
16#define MUX_SELECT_DELAY_US 100
17
18struct gpio_lmux {
19 struct gpio_chip gc;
20 struct mux_control *mux;
21 struct gpio_desc *muxed_gpio;
22
23 u32 num_gpio_mux_states;
24 unsigned int gpio_mux_states[] __counted_by(num_gpio_mux_states);
25};
26
27static int gpio_lmux_gpio_get(struct gpio_chip *gc, unsigned int offset)
28{
29 struct gpio_lmux *glm = gpiochip_get_data(gc);
30 int ret;
31
32 ret = mux_control_select_delay(glm->mux, glm->gpio_mux_states[offset],
33 MUX_SELECT_DELAY_US);
34 if (ret < 0)
35 return ret;
36
37 ret = gpiod_get_raw_value_cansleep(glm->muxed_gpio);
38 mux_control_deselect(glm->mux);
39 return ret;
40}
41
42static int gpio_lmux_gpio_get_direction(struct gpio_chip *gc,
43 unsigned int offset)
44{
45 return GPIO_LINE_DIRECTION_IN;
46}
47
48static int gpio_lmux_probe(struct platform_device *pdev)
49{
50 struct device *dev = &pdev->dev;
51 struct gpio_lmux *glm;
52 unsigned int ngpio;
53 size_t size;
54 int ret;
55
56 ngpio = device_property_count_u32(dev, "gpio-line-mux-states");
57 if (!ngpio)
58 return -EINVAL;
59
60 size = struct_size(glm, gpio_mux_states, ngpio);
61 glm = devm_kzalloc(dev, size, GFP_KERNEL);
62 if (!glm)
63 return -ENOMEM;
64
65 glm->gc.base = -1;
66 glm->gc.can_sleep = true;
67 glm->gc.fwnode = dev_fwnode(dev);
68 glm->gc.label = dev_name(dev);
69 glm->gc.ngpio = ngpio;
70 glm->gc.owner = THIS_MODULE;
71 glm->gc.parent = dev;
72
73 glm->gc.get = gpio_lmux_gpio_get;
74 glm->gc.get_direction = gpio_lmux_gpio_get_direction;
75
76 glm->mux = devm_mux_control_get(dev, NULL);
77 if (IS_ERR(glm->mux))
78 return dev_err_probe(dev, PTR_ERR(glm->mux),
79 "could not get mux controller\n");
80
81 glm->muxed_gpio = devm_gpiod_get(dev, "muxed", GPIOD_IN);
82 if (IS_ERR(glm->muxed_gpio))
83 return dev_err_probe(dev, PTR_ERR(glm->muxed_gpio),
84 "could not get muxed-gpio\n");
85
86 glm->num_gpio_mux_states = ngpio;
87 ret = device_property_read_u32_array(dev, "gpio-line-mux-states",
88 &glm->gpio_mux_states[0], ngpio);
89 if (ret)
90 return dev_err_probe(dev, ret, "could not get mux states\n");
91
92 ret = devm_gpiochip_add_data(dev, &glm->gc, glm);
93 if (ret)
94 return dev_err_probe(dev, ret, "failed to add gpiochip\n");
95
96 return 0;
97}
98
99static const struct of_device_id gpio_lmux_of_match[] = {
100 { .compatible = "gpio-line-mux" },
101 { }
102};
103MODULE_DEVICE_TABLE(of, gpio_lmux_of_match);
104
105static struct platform_driver gpio_lmux_driver = {
106 .driver = {
107 .name = "gpio-line-mux",
108 .of_match_table = gpio_lmux_of_match,
109 },
110 .probe = gpio_lmux_probe,
111};
112module_platform_driver(gpio_lmux_driver);
113
114MODULE_AUTHOR("Jonas Jelonek <jelonek.jonas@gmail.com>");
115MODULE_DESCRIPTION("GPIO line mux driver");
116MODULE_LICENSE("GPL");