// SPDX-License-Identifier: GPL-2.0+ /* * I2C multiplexer using GPIO API * * Copyright 2017 NXP * * Peng Fan <peng.fan@nxp.com> */ #include <asm/io.h> #include <asm-generic/gpio.h> #include <common.h> #include <dm.h> #include <dm/pinctrl.h> #include <fdtdec.h> #include <i2c.h> #include <linux/errno.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_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_alloc_size = sizeof(struct i2c_mux_gpio_priv), };