// 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 */ #include #include #include #include #include #include #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 "); MODULE_DESCRIPTION("GPIO line mux driver"); MODULE_LICENSE("GPL");