GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
@@ -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, 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.
|
||||
@@ -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_$(SPL_)I2C_MUX) += i2c-mux-uclass.o
|
||||
obj-$(CONFIG_I2C_MUX_PCA954x) += pca954x.o
|
||||
obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o
|
||||
@@ -0,0 +1,146 @@
|
||||
// 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 <asm/gpio.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_alloc_size = sizeof(struct i2c_arbitrator_priv),
|
||||
};
|
||||
@@ -0,0 +1,137 @@
|
||||
// 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 | 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),
|
||||
};
|
||||
@@ -0,0 +1,224 @@
|
||||
// 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 <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_platdata(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, req_seq %d\n", ret,
|
||||
dev ? dev->name : NULL, dev->req_seq);
|
||||
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_platdata(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_platdata(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_alloc_size = sizeof(struct i2c_mux),
|
||||
.per_child_platdata_auto_alloc_size = sizeof(struct i2c_mux_bus),
|
||||
.child_post_bind = i2c_mux_child_post_bind,
|
||||
};
|
||||
@@ -0,0 +1,168 @@
|
||||
// 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 <asm-generic/gpio.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
enum pca_type {
|
||||
PCA9543,
|
||||
PCA9544,
|
||||
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,
|
||||
},
|
||||
[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,pca9547", .data = PCA9547 },
|
||||
{ .compatible = "nxp,pca9548", .data = PCA9548 },
|
||||
{ .compatible = "nxp,pca9646", .data = PCA9646 },
|
||||
{ }
|
||||
};
|
||||
|
||||
static int pca954x_ofdata_to_platdata(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 (IS_ENABLED(CONFIG_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 (IS_ENABLED(CONFIG_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,
|
||||
.ofdata_to_platdata = pca954x_ofdata_to_platdata,
|
||||
.priv_auto_alloc_size = sizeof(struct pca954x_priv),
|
||||
};
|
||||
Reference in New Issue
Block a user