mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-30 06:53:09 +00:00
41575d8e4c
This construct is quite long-winded. In earlier days it made some sense since auto-allocation was a strange concept. But with driver model now used pretty universally, we can shorten this to 'auto'. This reduces verbosity and makes it easier to read. Coincidentally it also ensures that every declaration is on one line, thus making dtoc's job easier. Signed-off-by: Simon Glass <sjg@chromium.org>
140 lines
3 KiB
C
140 lines
3 KiB
C
// 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/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),
|
|
};
|