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