diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 9308e49430..b34927b07a 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -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 diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 169a2f1d7a..4557364b62 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -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 diff --git a/drivers/i2c/imx_virt_i2c.c b/drivers/i2c/imx_virt_i2c.c new file mode 100644 index 0000000000..68ad9f491e --- /dev/null +++ b/drivers/i2c/imx_virt_i2c.c @@ -0,0 +1,302 @@ +/* + * Copyright 2019 NXP + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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, +}; diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig index 156380c1cc..afa3623338 100644 --- a/drivers/i2c/muxes/Kconfig +++ b/drivers/i2c/muxes/Kconfig @@ -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 diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile index 3831f4e4fb..87540680d2 100644 --- a/drivers/i2c/muxes/Makefile +++ b/drivers/i2c/muxes/Makefile @@ -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 diff --git a/drivers/i2c/muxes/imx_virt_i2c_mux.c b/drivers/i2c/muxes/imx_virt_i2c_mux.c new file mode 100644 index 0000000000..cdf26e3987 --- /dev/null +++ b/drivers/i2c/muxes/imx_virt_i2c_mux.c @@ -0,0 +1,74 @@ +/* + * Copyright 2019 NXP + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include +#include + +#include + +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), +};