// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2017 Zodiac Inflight Innovation * Author: Andrey Smirnov * * Based on analogous code from Linux kernel * * Copyright (C) 2011, 2012 Cavium, Inc. */ #include #include #include #include #include #include struct mdio_mux_gpio_state { struct gpio *gpios; int gpios_num; }; static void mdio_mux_gpio_set_active(struct mdio_mux_gpio_state *s, unsigned int mask, int value) { while (mask) { const int n = __ffs(mask); mask >>= n + 1; if (WARN_ON(n >= s->gpios_num)) break; gpio_set_active(s->gpios[n].gpio, value); } } static int mdio_mux_gpio_switch_fn(int current_child, int desired_child, void *data) { if (current_child < 0) current_child = 0; if (current_child != desired_child) { struct mdio_mux_gpio_state *s = data; /* * GPIOs that are set in both current and desired * states can be left untouched. So we calculate them * and clear corresponding bits in both states. */ const int unchanged = current_child & desired_child; current_child &= ~unchanged; desired_child &= ~unchanged; /* * First step: disable all of the currently selected * bits that are no loger needed */ mdio_mux_gpio_set_active(s, current_child, 0); /* * Second step: enable all of the GPIOs that are * desired to be selected */ mdio_mux_gpio_set_active(s, desired_child, 1); } return 0; } static int mdio_mux_gpio_probe(struct device_d *dev) { struct mdio_mux_gpio_state *s; int i, r; s = xzalloc(sizeof(*s)); s->gpios_num = of_gpio_count(dev->device_node); if (s->gpios_num <= 0) { dev_err(dev, "No GPIOs specified\n"); r = -EINVAL; goto free_mem; } s->gpios = xzalloc(s->gpios_num * sizeof(s->gpios[0])); for (i = 0; i < s->gpios_num; i++) { enum of_gpio_flags flags; r = of_get_gpio_flags(dev->device_node, i, &flags); if (!gpio_is_valid(r)) { r = (r < 0) ? r : -EINVAL; goto free_mem; } s->gpios[i].gpio = r; s->gpios[i].label = dev_name(dev); s->gpios[i].flags = GPIOF_OUT_INIT_INACTIVE; if (flags & OF_GPIO_ACTIVE_LOW) s->gpios[i].flags |= GPIOF_ACTIVE_LOW; } r = gpio_request_array(s->gpios, s->gpios_num); if (r < 0) goto free_gpios; r = mdio_mux_init(dev, dev->device_node, mdio_mux_gpio_switch_fn, s, NULL); if (r < 0) goto free_gpios; return 0; free_gpios: gpio_free_array(s->gpios, s->gpios_num); free_mem: free(s->gpios); free(s); return r; } static const struct of_device_id mdio_mux_gpio_match[] = { { .compatible = "mdio-mux-gpio", }, {}, }; static struct driver_d mdio_mux_gpio_driver = { .name = "mdio-mux-gpio", .probe = mdio_mux_gpio_probe, .of_compatible = mdio_mux_gpio_match, }; device_platform_driver(mdio_mux_gpio_driver);