diff options
Diffstat (limited to 'roms/u-boot/drivers/i2c/muxes')
-rw-r--r-- | roms/u-boot/drivers/i2c/muxes/Kconfig | 46 | ||||
-rw-r--r-- | roms/u-boot/drivers/i2c/muxes/Makefile | 7 | ||||
-rw-r--r-- | roms/u-boot/drivers/i2c/muxes/i2c-arb-gpio-challenge.c | 150 | ||||
-rw-r--r-- | roms/u-boot/drivers/i2c/muxes/i2c-mux-gpio.c | 141 | ||||
-rw-r--r-- | roms/u-boot/drivers/i2c/muxes/i2c-mux-uclass.c | 226 | ||||
-rw-r--r-- | roms/u-boot/drivers/i2c/muxes/pca954x.c | 177 |
6 files changed, 747 insertions, 0 deletions
diff --git a/roms/u-boot/drivers/i2c/muxes/Kconfig b/roms/u-boot/drivers/i2c/muxes/Kconfig new file mode 100644 index 000000000..39683fc43 --- /dev/null +++ b/roms/u-boot/drivers/i2c/muxes/Kconfig @@ -0,0 +1,46 @@ +config I2C_MUX + bool "Support I2C multiplexers" + depends on DM_I2C + help + This enables I2C buses to be multiplexed, so that you can select + one of several buses using some sort of control mechanism. The + bus select is handled automatically when that bus is accessed, + using a suitable I2C MUX driver. + +config SPL_I2C_MUX + bool "Support I2C multiplexers on SPL" + depends on I2C_MUX + help + This enables I2C buses to be multiplexed, so that you can select + one of several buses using some sort of control mechanism. The + bus select is handled automatically when that bus is accessed, + using a suitable I2C MUX driver. + +config I2C_ARB_GPIO_CHALLENGE + bool "GPIO-based I2C arbitration" + depends on I2C_MUX + help + If you say yes to this option, support will be included for an + I2C multimaster arbitration scheme using GPIOs and a challenge & + response mechanism where masters have to claim the bus by asserting + a GPIO. + +config I2C_MUX_PCA954x + tristate "TI PCA954x I2C Mux/switches" + depends on I2C_MUX + help + If you say yes here you get support for the TI PCA954x I2C mux/switch + devices. It is x width I2C multiplexer which enables to partitioning + I2C bus and connect multiple devices with the same address to the same + I2C controller where driver handles proper routing to target i2c + device. Supported chips are PCA9543, PCA9544, PCA9546, PCA9547, + PCA9548 and PCA9646. + +config I2C_MUX_GPIO + tristate "GPIO-based I2C multiplexer" + depends on I2C_MUX && DM_GPIO + help + If you say yes to this option, support will be included for + a GPIO based I2C multiplexer. This driver provides access to + I2C busses connected through a MUX, which is controlled + through GPIO pins. diff --git a/roms/u-boot/drivers/i2c/muxes/Makefile b/roms/u-boot/drivers/i2c/muxes/Makefile new file mode 100644 index 000000000..b69082119 --- /dev/null +++ b/roms/u-boot/drivers/i2c/muxes/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (c) 2015 Google, Inc +obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o +obj-$(CONFIG_I2C_MUX) += i2c-mux-uclass.o +obj-$(CONFIG_I2C_MUX_PCA954x) += pca954x.o +obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o diff --git a/roms/u-boot/drivers/i2c/muxes/i2c-arb-gpio-challenge.c b/roms/u-boot/drivers/i2c/muxes/i2c-arb-gpio-challenge.c new file mode 100644 index 000000000..ad730e0e7 --- /dev/null +++ b/roms/u-boot/drivers/i2c/muxes/i2c-arb-gpio-challenge.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <i2c.h> +#include <log.h> +#include <malloc.h> +#include <asm/global_data.h> +#include <asm/gpio.h> +#include <linux/delay.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct i2c_arbitrator_priv { + struct gpio_desc ap_claim; + struct gpio_desc ec_claim; + uint slew_delay_us; + uint wait_retry_ms; + uint wait_free_ms; +}; + +int i2c_arbitrator_deselect(struct udevice *mux, struct udevice *bus, + uint channel) +{ + struct i2c_arbitrator_priv *priv = dev_get_priv(mux); + int ret; + + debug("%s: %s\n", __func__, mux->name); + ret = dm_gpio_set_value(&priv->ap_claim, 0); + udelay(priv->slew_delay_us); + + return ret; +} + +int i2c_arbitrator_select(struct udevice *mux, struct udevice *bus, + uint channel) +{ + struct i2c_arbitrator_priv *priv = dev_get_priv(mux); + unsigned start; + int ret; + + debug("%s: %s\n", __func__, mux->name); + /* Start a round of trying to claim the bus */ + start = get_timer(0); + do { + unsigned start_retry; + int waiting = 0; + + /* Indicate that we want to claim the bus */ + ret = dm_gpio_set_value(&priv->ap_claim, 1); + if (ret) + goto err; + udelay(priv->slew_delay_us); + + /* Wait for the EC to release it */ + start_retry = get_timer(0); + while (get_timer(start_retry) < priv->wait_retry_ms) { + ret = dm_gpio_get_value(&priv->ec_claim); + if (ret < 0) { + goto err; + } else if (!ret) { + /* We got it, so return */ + return 0; + } + + if (!waiting) + waiting = 1; + } + + /* It didn't release, so give up, wait, and try again */ + ret = dm_gpio_set_value(&priv->ap_claim, 0); + if (ret) + goto err; + + mdelay(priv->wait_retry_ms); + } while (get_timer(start) < priv->wait_free_ms); + + /* Give up, release our claim */ + printf("I2C: Could not claim bus, timeout %lu\n", get_timer(start)); + ret = -ETIMEDOUT; + ret = 0; +err: + return ret; +} + +static int i2c_arbitrator_probe(struct udevice *dev) +{ + struct i2c_arbitrator_priv *priv = dev_get_priv(dev); + const void *blob = gd->fdt_blob; + int node = dev_of_offset(dev); + int ret; + + debug("%s: %s\n", __func__, dev->name); + priv->slew_delay_us = fdtdec_get_int(blob, node, "slew-delay-us", 0); + priv->wait_retry_ms = fdtdec_get_int(blob, node, "wait-retry-us", 0) / + 1000; + priv->wait_free_ms = fdtdec_get_int(blob, node, "wait-free-us", 0) / + 1000; + ret = gpio_request_by_name(dev, "our-claim-gpio", 0, &priv->ap_claim, + GPIOD_IS_OUT); + if (ret) + goto err; + ret = gpio_request_by_name(dev, "their-claim-gpios", 0, &priv->ec_claim, + GPIOD_IS_IN); + if (ret) + goto err_ec_gpio; + + return 0; + +err_ec_gpio: + dm_gpio_free(dev, &priv->ap_claim); +err: + debug("%s: ret=%d\n", __func__, ret); + return ret; +} + +static int i2c_arbitrator_remove(struct udevice *dev) +{ + struct i2c_arbitrator_priv *priv = dev_get_priv(dev); + + dm_gpio_free(dev, &priv->ap_claim); + dm_gpio_free(dev, &priv->ec_claim); + + return 0; +} + +static const struct i2c_mux_ops i2c_arbitrator_ops = { + .select = i2c_arbitrator_select, + .deselect = i2c_arbitrator_deselect, +}; + +static const struct udevice_id i2c_arbitrator_ids[] = { + { .compatible = "i2c-arb-gpio-challenge" }, + { } +}; + +U_BOOT_DRIVER(i2c_arbitrator) = { + .name = "i2c_arbitrator", + .id = UCLASS_I2C_MUX, + .of_match = i2c_arbitrator_ids, + .probe = i2c_arbitrator_probe, + .remove = i2c_arbitrator_remove, + .ops = &i2c_arbitrator_ops, + .priv_auto = sizeof(struct i2c_arbitrator_priv), +}; diff --git a/roms/u-boot/drivers/i2c/muxes/i2c-mux-gpio.c b/roms/u-boot/drivers/i2c/muxes/i2c-mux-gpio.c new file mode 100644 index 000000000..4ca206115 --- /dev/null +++ b/roms/u-boot/drivers/i2c/muxes/i2c-mux-gpio.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * I2C multiplexer using GPIO API + * + * Copyright 2017 NXP + * + * Peng Fan <peng.fan@nxp.com> + */ + +#include <asm/global_data.h> +#include <asm/io.h> +#include <asm-generic/gpio.h> +#include <common.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <dm/devres.h> +#include <dm/pinctrl.h> +#include <fdtdec.h> +#include <i2c.h> +#include <linux/errno.h> +#include <linux/libfdt.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * struct i2c_mux_gpio_priv - private data for i2c mux gpio + * + * @values: the reg value of each child node + * @n_values: num of regs + * @gpios: the mux-gpios array + * @n_gpios: num of gpios in mux-gpios + * @idle: the value of idle-state + */ +struct i2c_mux_gpio_priv { + u32 *values; + int n_values; + struct gpio_desc *gpios; + int n_gpios; + u32 idle; +}; + + +static int i2c_mux_gpio_select(struct udevice *dev, struct udevice *bus, + uint channel) +{ + struct i2c_mux_gpio_priv *priv = dev_get_priv(dev); + int i, ret; + + for (i = 0; i < priv->n_gpios; i++) { + ret = dm_gpio_set_value(&priv->gpios[i], (channel >> i) & 1); + if (ret) + return ret; + } + + return 0; +} + +static int i2c_mux_gpio_deselect(struct udevice *dev, struct udevice *bus, + uint channel) +{ + struct i2c_mux_gpio_priv *priv = dev_get_priv(dev); + int i, ret; + + for (i = 0; i < priv->n_gpios; i++) { + ret = dm_gpio_set_value(&priv->gpios[i], (priv->idle >> i) & 1); + if (ret) + return ret; + } + + return 0; +} + +static int i2c_mux_gpio_probe(struct udevice *dev) +{ + const void *fdt = gd->fdt_blob; + int node = dev_of_offset(dev); + struct i2c_mux_gpio_priv *mux = dev_get_priv(dev); + struct gpio_desc *gpios; + u32 *values; + int i = 0, subnode, ret; + + mux->n_values = fdtdec_get_child_count(fdt, node); + values = devm_kzalloc(dev, sizeof(*mux->values) * mux->n_values, + GFP_KERNEL); + if (!values) { + dev_err(dev, "Cannot alloc values array"); + return -ENOMEM; + } + + fdt_for_each_subnode(subnode, fdt, node) { + *(values + i) = fdtdec_get_uint(fdt, subnode, "reg", -1); + i++; + } + + mux->values = values; + + mux->idle = fdtdec_get_uint(fdt, node, "idle-state", -1); + + mux->n_gpios = gpio_get_list_count(dev, "mux-gpios"); + if (mux->n_gpios < 0) { + dev_err(dev, "Missing mux-gpios property\n"); + return -EINVAL; + } + + gpios = devm_kzalloc(dev, sizeof(struct gpio_desc) * mux->n_gpios, + GFP_KERNEL); + if (!gpios) { + dev_err(dev, "Cannot allocate gpios array\n"); + return -ENOMEM; + } + + ret = gpio_request_list_by_name(dev, "mux-gpios", gpios, mux->n_gpios, + GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); + if (ret <= 0) { + dev_err(dev, "Failed to request mux-gpios\n"); + return ret; + } + + mux->gpios = gpios; + + return 0; +} + +static const struct i2c_mux_ops i2c_mux_gpio_ops = { + .select = i2c_mux_gpio_select, + .deselect = i2c_mux_gpio_deselect, +}; + +static const struct udevice_id i2c_mux_gpio_ids[] = { + { .compatible = "i2c-mux-gpio", }, + {} +}; + +U_BOOT_DRIVER(i2c_mux_gpio) = { + .name = "i2c_mux_gpio", + .id = UCLASS_I2C_MUX, + .of_match = i2c_mux_gpio_ids, + .ops = &i2c_mux_gpio_ops, + .probe = i2c_mux_gpio_probe, + .priv_auto = sizeof(struct i2c_mux_gpio_priv), +}; diff --git a/roms/u-boot/drivers/i2c/muxes/i2c-mux-uclass.c b/roms/u-boot/drivers/i2c/muxes/i2c-mux-uclass.c new file mode 100644 index 000000000..dbca409ee --- /dev/null +++ b/roms/u-boot/drivers/i2c/muxes/i2c-mux-uclass.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <i2c.h> +#include <log.h> +#include <malloc.h> +#include <dm/lists.h> +#include <dm/root.h> + +/** + * struct i2c_mux: Information the uclass stores about an I2C mux + * + * @selected: Currently selected mux, or -1 for none + * @i2c_bus: I2C bus to use for communcation + */ +struct i2c_mux { + int selected; + struct udevice *i2c_bus; +}; + +/** + * struct i2c_mux_bus: Information about each bus the mux controls + * + * @channel: Channel number used to select this bus + */ +struct i2c_mux_bus { + uint channel; +}; + +/* Find out the mux channel number */ +static int i2c_mux_child_post_bind(struct udevice *dev) +{ + struct i2c_mux_bus *plat = dev_get_parent_plat(dev); + int channel; + + channel = dev_read_u32_default(dev, "reg", -1); + if (channel < 0) + return -EINVAL; + plat->channel = channel; + + return 0; +} + +/* Find the I2C buses selected by this mux */ +static int i2c_mux_post_bind(struct udevice *mux) +{ + ofnode node; + int ret; + + debug("%s: %s\n", __func__, mux->name); + /* + * There is no compatible string in the sub-nodes, so we must manually + * bind these + */ + dev_for_each_subnode(node, mux) { + struct udevice *dev; + const char *name; + const char *arrow = "->"; + char *full_name; + int parent_name_len, arrow_len, mux_name_len, name_len; + + name = ofnode_get_name(node); + + /* Calculate lenghts of strings */ + parent_name_len = strlen(mux->parent->name); + arrow_len = strlen(arrow); + mux_name_len = strlen(mux->name); + name_len = strlen(name); + + full_name = calloc(1, parent_name_len + arrow_len + + mux_name_len + arrow_len + name_len + 1); + if (!full_name) + return -ENOMEM; + + /* Compose bus name */ + strcat(full_name, mux->parent->name); + strcat(full_name, arrow); + strcat(full_name, mux->name); + strcat(full_name, arrow); + strcat(full_name, name); + + ret = device_bind_driver_to_node(mux, "i2c_mux_bus_drv", + full_name, node, &dev); + debug(" - bind ret=%d, %s, seq %d\n", ret, + dev ? dev->name : NULL, dev_seq(dev)); + if (ret) + return ret; + } + + return 0; +} + +/* Set up the mux ready for use */ +static int i2c_mux_post_probe(struct udevice *mux) +{ + struct i2c_mux *priv = dev_get_uclass_priv(mux); + int ret; + + debug("%s: %s\n", __func__, mux->name); + priv->selected = -1; + + /* if parent is of i2c uclass already, we'll take that, otherwise + * look if we find an i2c-parent phandle + */ + if (UCLASS_I2C == device_get_uclass_id(mux->parent)) { + priv->i2c_bus = dev_get_parent(mux); + debug("%s: bus=%p/%s\n", __func__, priv->i2c_bus, + priv->i2c_bus->name); + return 0; + } + + ret = uclass_get_device_by_phandle(UCLASS_I2C, mux, "i2c-parent", + &priv->i2c_bus); + if (ret) + return ret; + debug("%s: bus=%p/%s\n", __func__, priv->i2c_bus, priv->i2c_bus->name); + + return 0; +} + +int i2c_mux_select(struct udevice *dev) +{ + struct i2c_mux_bus *plat = dev_get_parent_plat(dev); + struct udevice *mux = dev->parent; + struct i2c_mux_ops *ops = i2c_mux_get_ops(mux); + + if (!ops->select) + return -ENOSYS; + + return ops->select(mux, dev, plat->channel); +} + +int i2c_mux_deselect(struct udevice *dev) +{ + struct i2c_mux_bus *plat = dev_get_parent_plat(dev); + struct udevice *mux = dev->parent; + struct i2c_mux_ops *ops = i2c_mux_get_ops(mux); + + if (!ops->deselect) + return -ENOSYS; + + return ops->deselect(mux, dev, plat->channel); +} + +static int i2c_mux_bus_set_bus_speed(struct udevice *dev, unsigned int speed) +{ + struct udevice *mux = dev->parent; + struct i2c_mux *priv = dev_get_uclass_priv(mux); + int ret, ret2; + + ret = i2c_mux_select(dev); + if (ret) + return ret; + ret = dm_i2c_set_bus_speed(priv->i2c_bus, speed); + ret2 = i2c_mux_deselect(dev); + + return ret ? ret : ret2; +} + +static int i2c_mux_bus_probe(struct udevice *dev, uint chip_addr, + uint chip_flags) +{ + struct udevice *mux = dev->parent; + struct i2c_mux *priv = dev_get_uclass_priv(mux); + struct dm_i2c_ops *ops = i2c_get_ops(priv->i2c_bus); + int ret, ret2; + + debug("%s: %s, bus %s\n", __func__, dev->name, priv->i2c_bus->name); + if (!ops->probe_chip) + return -ENOSYS; + ret = i2c_mux_select(dev); + if (ret) + return ret; + ret = ops->probe_chip(priv->i2c_bus, chip_addr, chip_flags); + ret2 = i2c_mux_deselect(dev); + + return ret ? ret : ret2; +} + +static int i2c_mux_bus_xfer(struct udevice *dev, struct i2c_msg *msg, + int nmsgs) +{ + struct udevice *mux = dev->parent; + struct i2c_mux *priv = dev_get_uclass_priv(mux); + struct dm_i2c_ops *ops = i2c_get_ops(priv->i2c_bus); + int ret, ret2; + + debug("%s: %s, bus %s\n", __func__, dev->name, priv->i2c_bus->name); + if (!ops->xfer) + return -ENOSYS; + ret = i2c_mux_select(dev); + if (ret) + return ret; + ret = ops->xfer(priv->i2c_bus, msg, nmsgs); + ret2 = i2c_mux_deselect(dev); + + return ret ? ret : ret2; +} + +static const struct dm_i2c_ops i2c_mux_bus_ops = { + .xfer = i2c_mux_bus_xfer, + .probe_chip = i2c_mux_bus_probe, + .set_bus_speed = i2c_mux_bus_set_bus_speed, +}; + +U_BOOT_DRIVER(i2c_mux_bus) = { + .name = "i2c_mux_bus_drv", + .id = UCLASS_I2C, + .ops = &i2c_mux_bus_ops, +}; + +UCLASS_DRIVER(i2c_mux) = { + .id = UCLASS_I2C_MUX, + .name = "i2c_mux", + .post_bind = i2c_mux_post_bind, + .post_probe = i2c_mux_post_probe, + .per_device_auto = sizeof(struct i2c_mux), + .per_child_plat_auto = sizeof(struct i2c_mux_bus), + .child_post_bind = i2c_mux_child_post_bind, +}; diff --git a/roms/u-boot/drivers/i2c/muxes/pca954x.c b/roms/u-boot/drivers/i2c/muxes/pca954x.c new file mode 100644 index 000000000..55858cf65 --- /dev/null +++ b/roms/u-boot/drivers/i2c/muxes/pca954x.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 - 2016 Xilinx, Inc. + * Copyright (C) 2017 National Instruments Corp + * Written by Michal Simek + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <i2c.h> +#include <log.h> +#include <malloc.h> +#include <asm/global_data.h> + +#include <asm-generic/gpio.h> + +DECLARE_GLOBAL_DATA_PTR; + +enum pca_type { + PCA9543, + PCA9544, + PCA9546, + PCA9547, + PCA9548, + PCA9646 +}; + +struct chip_desc { + u8 enable; /* Enable mask in ctl register (used for muxes only) */ + enum muxtype { + pca954x_ismux = 0, + pca954x_isswi, + } muxtype; + u32 width; +}; + +struct pca954x_priv { + u32 addr; /* I2C mux address */ + u32 width; /* I2C mux width - number of busses */ + struct gpio_desc gpio_mux_reset; +}; + +static const struct chip_desc chips[] = { + [PCA9543] = { + .muxtype = pca954x_isswi, + .width = 2, + }, + [PCA9544] = { + .enable = 0x4, + .muxtype = pca954x_ismux, + .width = 4, + }, + [PCA9546] = { + .muxtype = pca954x_isswi, + .width = 4, + }, + [PCA9547] = { + .enable = 0x8, + .muxtype = pca954x_ismux, + .width = 8, + }, + [PCA9548] = { + .muxtype = pca954x_isswi, + .width = 8, + }, + [PCA9646] = { + .muxtype = pca954x_isswi, + .width = 4, + }, +}; + +static int pca954x_deselect(struct udevice *mux, struct udevice *bus, + uint channel) +{ + struct pca954x_priv *priv = dev_get_priv(mux); + uchar byte = 0; + + return dm_i2c_write(mux, priv->addr, &byte, 1); +} + +static int pca954x_select(struct udevice *mux, struct udevice *bus, + uint channel) +{ + struct pca954x_priv *priv = dev_get_priv(mux); + const struct chip_desc *chip = &chips[dev_get_driver_data(mux)]; + uchar byte; + + if (chip->muxtype == pca954x_ismux) + byte = channel | chip->enable; + else + byte = 1 << channel; + + return dm_i2c_write(mux, priv->addr, &byte, 1); +} + +static const struct i2c_mux_ops pca954x_ops = { + .select = pca954x_select, + .deselect = pca954x_deselect, +}; + +static const struct udevice_id pca954x_ids[] = { + { .compatible = "nxp,pca9543", .data = PCA9543 }, + { .compatible = "nxp,pca9544", .data = PCA9544 }, + { .compatible = "nxp,pca9546", .data = PCA9546 }, + { .compatible = "nxp,pca9547", .data = PCA9547 }, + { .compatible = "nxp,pca9548", .data = PCA9548 }, + { .compatible = "nxp,pca9646", .data = PCA9646 }, + { } +}; + +static int pca954x_of_to_plat(struct udevice *dev) +{ + struct pca954x_priv *priv = dev_get_priv(dev); + const struct chip_desc *chip = &chips[dev_get_driver_data(dev)]; + + priv->addr = dev_read_u32_default(dev, "reg", 0); + if (!priv->addr) { + debug("MUX not found\n"); + return -ENODEV; + } + priv->width = chip->width; + + if (!priv->width) { + debug("No I2C MUX width specified\n"); + return -EINVAL; + } + + debug("Device %s at 0x%x with width %d\n", + dev->name, priv->addr, priv->width); + + return 0; +} + +static int pca954x_probe(struct udevice *dev) +{ + if (CONFIG_IS_ENABLED(DM_GPIO)) { + struct pca954x_priv *priv = dev_get_priv(dev); + int err; + + err = gpio_request_by_name(dev, "reset-gpios", 0, + &priv->gpio_mux_reset, GPIOD_IS_OUT); + + /* it's optional so only bail if we get a real error */ + if (err && (err != -ENOENT)) + return err; + + /* dm will take care of polarity */ + if (dm_gpio_is_valid(&priv->gpio_mux_reset)) + dm_gpio_set_value(&priv->gpio_mux_reset, 0); + } + + return 0; +} + +static int pca954x_remove(struct udevice *dev) +{ + if (CONFIG_IS_ENABLED(DM_GPIO)) { + struct pca954x_priv *priv = dev_get_priv(dev); + + if (dm_gpio_is_valid(&priv->gpio_mux_reset)) + dm_gpio_free(dev, &priv->gpio_mux_reset); + } + + return 0; +} + +U_BOOT_DRIVER(pca954x) = { + .name = "pca954x", + .id = UCLASS_I2C_MUX, + .of_match = pca954x_ids, + .probe = pca954x_probe, + .remove = pca954x_remove, + .ops = &pca954x_ops, + .of_to_plat = pca954x_of_to_plat, + .priv_auto = sizeof(struct pca954x_priv), +}; |