MLK-20886-3 i2c: Add virtual i2c and virtual i2c mux drivers

Add virtual i2c driver which replies on the VService to send SRTM i2c
messages with M4.
For each output on i2c mux, M4 side abstracts a i2c bus with special bus
id. The virtual i2c mux follows basic mux design, but uses dedicated flag
to pass the abstract bus id for the mux output to virtual i2c driver.

Virtual i2c and virtual i2c mux will bind nodes with compatible string
"fsl,imx-virt-i2c" and "fsl,imx-virt-i2c-mux".

To support binding local i2c driver or virtual i2c driver at runtime. We
provides a override function for the driver bind. ARCH level is responsible
to implement it.

Signed-off-by: Ye Li <ye.li@nxp.com>
(cherry picked from commit 25095e9f0d9816c22da97945b66439dfa277aa2b)
This commit is contained in:
Ye Li 2019-01-30 21:50:31 -08:00
parent 4db12a78ff
commit 09ffd8d155
6 changed files with 391 additions and 0 deletions

View File

@ -137,6 +137,13 @@ config SYS_I2C_IMX_LPI2C
help
Add support for the NXP i.MX LPI2C driver.
config SYS_I2C_IMX_VIRT_I2C
bool "NXP i.MX Virtual I2C driver"
select IMX_VSERVICE
help
Add support for the NXP i.MX Virtual I2C which needs AMP communtication
to work with remote core to access i2c bus.
config SYS_I2C_MESON
bool "Amlogic Meson I2C driver"
depends on DM_I2C && ARCH_MESON

View File

@ -23,6 +23,7 @@ obj-$(CONFIG_SYS_I2C_FSL) += fsl_i2c.o
obj-$(CONFIG_SYS_I2C_IHS) += ihs_i2c.o
obj-$(CONFIG_SYS_I2C_INTEL) += intel_i2c.o
obj-$(CONFIG_SYS_I2C_IMX_LPI2C) += imx_lpi2c.o
obj-$(CONFIG_SYS_I2C_IMX_VIRT_I2C) += imx_virt_i2c.o
obj-$(CONFIG_SYS_I2C_KONA) += kona_i2c.o
obj-$(CONFIG_SYS_I2C_LPC32XX) += lpc32xx_i2c.o
obj-$(CONFIG_SYS_I2C_MESON) += meson_i2c.o

302
drivers/i2c/imx_virt_i2c.c Normal file
View File

@ -0,0 +1,302 @@
/*
* Copyright 2019 NXP
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <errno.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/sys_proto.h>
#include <dm.h>
#include <fdtdec.h>
#include <i2c.h>
#include <asm/mach-imx/imx_vservice.h>
DECLARE_GLOBAL_DATA_PTR;
#define MAX_SRTM_I2C_BUF_SIZE 16
#define SRTM_I2C_CATEGORY 0x09
#define SRTM_VERSION 0x0001
#define SRTM_TYPE_REQ 0x0
#define SRTM_TYPE_RESP 0x1
#define SRTM_CMD_READ 0x0
#define SRTM_CMD_WRITE 0x1
struct imx_virt_i2c_bus {
int index;
ulong base;
struct imx_vservice_channel *vservice;
};
struct imx_srtm_i2c_msg {
u8 categary;
u8 version[2];
u8 type;
u8 command;
u8 priority;
u8 reserved[4];
u8 i2c_bus;
u8 return_val;
u16 slave_addr;
u16 flag;
u16 data_length;
u8 data_buf[MAX_SRTM_I2C_BUF_SIZE];
};
static void imx_virt_i2c_msg_dump(struct imx_srtm_i2c_msg *msg)
{
u32 i = 0;
u32 size = sizeof(struct imx_srtm_i2c_msg);
u8 *buf = (u8 *)msg;
for (; i < size; i++) {
debug("%02x ", buf[i]);
if (i % 16 == 15)
debug("\n");
}
}
static int imx_virt_i2c_read(struct udevice *bus, u32 chip, u8 *buf, int len)
{
struct imx_srtm_i2c_msg *msg;
u32 size;
int ret = 0;
struct imx_virt_i2c_bus *i2c_bus = dev_get_priv(bus);
debug("imx_virt_i2c_read, bus %d\n", i2c_bus->index);
if (len > MAX_SRTM_I2C_BUF_SIZE) {
printf("virt_i2c_read exceed the buf length, len=%d\n", len);
return -EINVAL;
}
size = sizeof(struct imx_srtm_i2c_msg);
msg = imx_vservice_get_buffer(i2c_bus->vservice, size);
if (msg == NULL)
return -ENOMEM;
/* Fill buf with SRTM i2c format */
msg->categary = SRTM_I2C_CATEGORY;
msg->version[0] = SRTM_VERSION & 0xff;
msg->version[1] = (SRTM_VERSION >> 8) & 0xff;
msg->type = SRTM_TYPE_REQ;
msg->command = SRTM_CMD_READ;
msg->priority = 1;
msg->i2c_bus = i2c_bus->index;
msg->return_val = 0;
msg->slave_addr = (u16)chip;
msg->flag = I2C_M_RD;
msg->data_length = len;
imx_virt_i2c_msg_dump(msg);
/* Send request and get return data */
ret = imx_vservice_blocking_request(i2c_bus->vservice, (u8 *)msg, &size);
if (ret) {
printf("Vservice request is failed, ret %d\n", ret);
return ret;
}
if (msg->type != SRTM_TYPE_RESP || msg->categary != SRTM_I2C_CATEGORY
|| msg->command !=SRTM_CMD_READ) {
printf("Error read response message\n");
return -EIO;
}
if (msg->return_val != 0)
return msg->return_val;
if (len != 0)
memcpy(buf, msg->data_buf, msg->data_length);
return ret;
}
static int imx_virt_i2c_write(struct udevice *bus, u32 chip, u8 *buf, int len)
{
struct imx_srtm_i2c_msg *msg;
u32 size;
int ret = 0;
struct imx_virt_i2c_bus *i2c_bus = dev_get_priv(bus);
debug("imx_virt_i2c_write, bus %d\n", i2c_bus->index);
if (len > MAX_SRTM_I2C_BUF_SIZE) {
printf("virt_i2c_read exceed the buf length, len=%d\n", len);
return -EINVAL;
}
size = sizeof(struct imx_srtm_i2c_msg);
msg = imx_vservice_get_buffer(i2c_bus->vservice, size);
if (msg == NULL)
return -ENOMEM;
/* Fill buf with SRTM i2c format */
msg->categary = SRTM_I2C_CATEGORY;
msg->version[0] = SRTM_VERSION & 0xff;
msg->version[1] = (SRTM_VERSION >> 8) & 0xff;
msg->type = SRTM_TYPE_REQ;
msg->command = SRTM_CMD_WRITE;
msg->priority = 1;
msg->i2c_bus = i2c_bus->index;
msg->return_val = 0;
msg->slave_addr = (u16)chip;
msg->flag = 0;
msg->data_length = len;
imx_virt_i2c_msg_dump(msg);
if (buf) /* probe chip does not have data buffer */
memcpy(msg->data_buf, buf, msg->data_length);
/* Send request and get return data */
ret = imx_vservice_blocking_request(i2c_bus->vservice, (u8 *)msg, &size);
if (ret) {
printf("Vservice request is failed, ret %d\n", ret);
return ret;
}
if (msg->type != SRTM_TYPE_RESP || msg->categary != SRTM_I2C_CATEGORY
|| msg->command !=SRTM_CMD_WRITE) {
printf("Error write response message\n");
return -EIO;
}
if (msg->return_val != 0) {
debug("Peer process message, ret %d\n", msg->return_val);
return -EACCES;
}
debug("imx_vservice_blocking_request get size = %d\n", size);
return ret;
}
static int imx_virt_i2c_probe_chip(struct udevice *bus, u32 chip,
u32 chip_flags)
{
debug("imx_virt_i2c_probe_chip\n");
return imx_virt_i2c_write(bus, chip, NULL, 0);
}
static int imx_virt_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs)
{
int ret = 0;
for (; nmsgs > 0; nmsgs--, msg++) {
debug("virt_i2c_xfer: chip=0x%x, len=0x%x, buf=0x%08x\n", msg->addr, msg->len, *msg->buf);
if (msg->flags & I2C_M_RD)
ret = imx_virt_i2c_read(bus, msg->addr, msg->buf, msg->len);
else {
ret = imx_virt_i2c_write(bus, msg->addr, msg->buf,
msg->len);
if (ret)
break;
}
}
if (ret)
printf("i2c_xfer: error %d\n", ret);
return ret;
}
static int imx_virt_i2c_probe(struct udevice *bus)
{
struct imx_virt_i2c_bus *i2c_bus = dev_get_priv(bus);
fdt_addr_t addr;
addr = devfdt_get_addr(bus);
if (addr == FDT_ADDR_T_NONE)
return -EINVAL;
i2c_bus->base = addr;
i2c_bus->index = bus->seq;
debug("virt_i2c : controller bus %d at 0x%lx, bus udev 0x%lx\n",
bus->seq, i2c_bus->base, (ulong)bus);
i2c_bus->vservice = imx_vservice_setup(bus);
if (i2c_bus->vservice == NULL) {
printf("virt_i2c: Faild to setup vservice\n");
return -ENODEV;
}
return 0;
}
#define I2C_M_SELECT_MUX_BUS 0x010000
static int imx_virt_i2c_set_flags(struct udevice *child_dev, uint flags)
{
#ifdef CONFIG_I2C_MUX_IMX_VIRT
if (child_dev->uclass->uc_drv->id == UCLASS_I2C_MUX) {
struct udevice *bus = child_dev->parent;
struct imx_virt_i2c_bus *i2c_bus = dev_get_priv(bus);
if (flags == 0) {
i2c_bus->index = bus->seq;
} else if (flags & I2C_M_SELECT_MUX_BUS) {
i2c_bus->index = (flags >> 24) & 0xff;
}
debug("virt_i2c_set_flags bus %d\n", i2c_bus->index);
}
#endif
return 0;
}
int __weak board_imx_virt_i2c_bind(struct udevice *dev)
{
return 0;
}
static int imx_virt_i2c_bind(struct udevice *dev)
{
debug("imx_virt_i2c_bind, %s, seq %d\n", dev->name, dev->req_seq);
return board_imx_virt_i2c_bind(dev);
}
static int imx_virt_i2c_child_post_bind(struct udevice *child_dev)
{
#ifdef CONFIG_I2C_MUX_IMX_VIRT
if (child_dev->uclass->uc_drv->id == UCLASS_I2C_MUX) {
if (!strcmp(child_dev->driver->name, "imx_virt_i2c_mux"))
return 0;
else
return -ENODEV;
}
#endif
return 0;
}
static const struct dm_i2c_ops imx_virt_i2c_ops = {
.xfer = imx_virt_i2c_xfer,
.probe_chip = imx_virt_i2c_probe_chip,
.set_flags = imx_virt_i2c_set_flags,
};
static const struct udevice_id imx_virt_i2c_ids[] = {
{ .compatible = "fsl,imx-virt-i2c", },
{}
};
U_BOOT_DRIVER(imx_virt_i2c) = {
.name = "imx_virt_i2c",
.id = UCLASS_I2C,
.of_match = imx_virt_i2c_ids,
.bind = imx_virt_i2c_bind,
.probe = imx_virt_i2c_probe,
.child_post_bind = imx_virt_i2c_child_post_bind,
.priv_auto_alloc_size = sizeof(struct imx_virt_i2c_bus),
.ops = &imx_virt_i2c_ops,
};

View File

@ -35,6 +35,12 @@ config I2C_MUX_PCA954x
to the same I2C controller where driver handles proper routing to
target i2c device. PCA9544 and PCA9548 are supported.
config I2C_MUX_IMX_VIRT
bool "i.MX Virtual I2C Mux/switches"
depends on I2C_MUX && SYS_I2C_IMX_VIRT_I2C
help
If you say yes here you get support for the i.MX Virtual I2C mux
config I2C_MUX_GPIO
tristate "GPIO-based I2C multiplexer"
depends on I2C_MUX && DM_GPIO

View File

@ -7,3 +7,4 @@ 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
obj-$(CONFIG_I2C_MUX_IMX_VIRT) += imx_virt_i2c_mux.o

View File

@ -0,0 +1,74 @@
/*
* Copyright 2019 NXP
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <i2c.h>
#include <asm-generic/gpio.h>
DECLARE_GLOBAL_DATA_PTR;
#define I2C_M_SELECT_MUX_BUS 0x010000
struct imx_virt_i2c_mux_priv {
u32 addr; /* I2C mux address */
u32 i2c_bus_alias_off;
};
static int imx_virt_i2c_mux_deselect(struct udevice *mux, struct udevice *bus,
uint channel)
{
return i2c_set_chip_flags(mux, 0);
}
static int imx_virt_i2c_mux_select(struct udevice *mux, struct udevice *bus,
uint channel)
{
struct imx_virt_i2c_mux_priv *priv = dev_get_priv(mux);
uint flags = I2C_M_SELECT_MUX_BUS;
flags |= ((priv->i2c_bus_alias_off + channel) << 24);
return i2c_set_chip_flags(mux, flags);
}
static const struct i2c_mux_ops imx_virt_i2c_mux_ops = {
.select = imx_virt_i2c_mux_select,
.deselect = imx_virt_i2c_mux_deselect,
};
static const struct udevice_id imx_virt_i2c_mux_ids[] = {
{ .compatible = "fsl,imx-virt-i2c-mux", },
{ }
};
static int imx_virt_i2c_mux_probe(struct udevice *dev)
{
struct imx_virt_i2c_mux_priv *priv = dev_get_priv(dev);
priv->addr = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "reg", 0);
if (!priv->addr) {
debug("MUX not found\n");
return -ENODEV;
}
priv->i2c_bus_alias_off = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "virtual-bus-seq", 0);
debug("Device %s at 0x%x with i2c_bus_alias_off %d\n",
dev->name, priv->addr, priv->i2c_bus_alias_off);
return 0;
}
U_BOOT_DRIVER(imx_virt_i2c_mux) = {
.name = "imx_virt_i2c_mux",
.id = UCLASS_I2C_MUX,
.of_match = imx_virt_i2c_mux_ids,
.probe = imx_virt_i2c_mux_probe,
.ops = &imx_virt_i2c_mux_ops,
.priv_auto_alloc_size = sizeof(struct imx_virt_i2c_mux_priv),
};