blob: 678e22fb91516f8b48909a7f1ac122d8ad486fe1 [file] [log] [blame]
/*
* 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
#define I2C_M_SELECT_MUX_BUS 0x010000
#define I2C_M_SRTM_STOP 0x0200
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, uint flag)
{
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 = (u16)flag;
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, uint flag)
{
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 = (u16)flag;
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, I2C_M_SRTM_STOP);
}
static int imx_virt_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs)
{
int ret = 0;
uint flag = 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);
flag = msg->flags;
if (nmsgs == 1)
flag |= I2C_M_SRTM_STOP;
if (flag & I2C_M_RD)
ret = imx_virt_i2c_read(bus, msg->addr, msg->buf, msg->len, flag);
else {
ret = imx_virt_i2c_write(bus, msg->addr, msg->buf,
msg->len, flag);
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;
}
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,
.flags = DM_FLAG_IGNORE_POWER_ON | DM_FLAG_IGNORE_DEFAULT_CLKS,
};