311 lines
6.9 KiB
C
311 lines
6.9 KiB
C
/*
|
|
* 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,
|
|
};
|