139 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			139 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			C
		
	
	
	
| /*
 | |
|  * I2C multiplexer using GPIO API
 | |
|  *
 | |
|  * Copyright 2017 NXP
 | |
|  *
 | |
|  * Peng Fan <peng.fan@nxp.com>
 | |
|  *
 | |
|  * SPDX-License-Identifier:	GPL-2.0+
 | |
|  */
 | |
| 
 | |
| #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),
 | |
| };
 |