/* * 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 #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, };