u-boot/drivers/i2c/muxes/i2c-mux-gpio.c
Peng Fan a430556ecb i2c: muxes: add i2c gpio multiplexer driver
Add an i2c mux driver providing access to i2c bus segments using a
hardware MUX sitting on a master bus and controlled through gpio pins.

E.G. something like:

         ----------              ----------  Bus segment 1   - - - - -
        |          | SCL/SDA    |          |-------------- |           |
        |          |------------|          |
        |          |            |          | Bus segment 2 |           |
        |  Linux   | GPIO 1..N  |   MUX    |---------------   Devices
        |          |------------|          |               |           |
        |          |            |          | Bus segment M
        |          |            |          |---------------|           |
         ----------              ----------                  - - - - -

SCL/SDA of the master I2C bus is multiplexed to bus segment 1..M
according to the settings of the GPIO pins 1..N.

Note commit log from kernel
commit 92ed1a76("i2c: Add generic I2C multiplexer using GPIO API")

Signed-off-by: Peng Fan <peng.fan@nxp.com>
Tested-by: Peng Fan <peng.fan@nxp.com> (i.MX6QP-Sabreauto)
Cc: Heiko Schocher <hs@denx.de>
Cc: Stefano Babic <sbabic@denx.de>
Cc: Simon Glass <sjg@chromium.org>
2017-08-23 07:04:56 +02:00

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),
};