/* * Copyright 2019 NXP * * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License * Version 2 or later at the following locations: * * http://www.opensource.org/licenses/gpl-license.html * http://www.gnu.org/copyleft/gpl.html */ /* The i2c-rpmsg transfer protocol: * * +---------------+-------------------------------+ * | Byte Offset | Content | * +---------------+---+---+---+---+---+---+---+---+ * | 0 | Category | * +---------------+---+---+---+---+---+---+---+---+ * | 1 ~ 2 | Version | * +---------------+---+---+---+---+---+---+---+---+ * | 3 | Type | * +---------------+---+---+---+---+---+---+---+---+ * | 4 | Command | * +---------------+---+---+---+---+---+---+---+---+ * | 5 | Priority | * +---------------+---+---+---+---+---+---+---+---+ * | 6 | Reserved1 | * +---------------+---+---+---+---+---+---+---+---+ * | 7 | Reserved2 | * +---------------+---+---+---+---+---+---+---+---+ * | 8 | Reserved3 | * +---------------+---+---+---+---+---+---+---+---+ * | 9 | Reserved4 | * +---------------+---+---+---+---+---+---+---+---+ * | 10 | BUS ID | * +---------------+---+---+---+---+---+---+---+---+ * | 11 | Return Value | * +---------------+---+---+---+---+---+---+---+---+ * | 12 ~ 13 | BUS ID | * +---------------+---+---+---+---+---+---+---+---+ * | 14 ~ 15 | Address | * +---------------+---+---+---+---+---+---+---+---+ * | 16 ~ 17 | Data Len | * +---------------+---+---+---+---+---+---+---+---+ * | 18 ~ 33 | 16 Bytes Data | * +---------------+---+---+---+---+---+---+---+---+ * * The definition of Return Value: * 0x00 = Success * 0x01 = Failed * 0x02 = Invalid parameter * 0x03 = Invalid message * 0x04 = Operate in invalid state * 0x05 = Memory allocation failed * 0x06 = Timeout when waiting for an event * 0x07 = Cannot add to list as node already in another list * 0x08 = Cannot remove from list as node not in list * 0x09 = Transfer timeout * 0x0A = Transfer failed due to peer core not ready * 0x0B = Transfer failed due to communication failure * 0x0C = Cannot find service for a request/notification * 0x0D = Service version cannot support the request/notification * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define I2C_RPMSG_MAX_BUF_SIZE 16 #define I2C_RPMSG_TIMEOUT 500 /* unit: ms */ #define I2C_RPMSG_CATEGORY 0x09 #define I2C_RPMSG_VERSION 0x0001 #define I2C_RPMSG_TYPE_REQUEST 0x00 #define I2C_RPMSG_TYPE_RESPONSE 0x01 #define I2C_RPMSG_COMMAND_READ 0x00 #define I2C_RPMSG_COMMAND_WRITE 0x01 #define I2C_RPMSG_PRIORITY 0x01 #define I2C_RPMSG_M_STOP 0x0200 struct i2c_rpmsg_msg { struct imx_rpmsg_head header; /* Payload Start*/ u8 bus_id; u8 ret_val; u16 addr; u16 flags; u16 len; u8 buf[I2C_RPMSG_MAX_BUF_SIZE]; } __packed __aligned(1); struct i2c_rpmsg_info { struct rpmsg_device *rpdev; struct device *dev; struct i2c_rpmsg_msg *msg; struct completion cmd_complete; struct mutex lock; u8 bus_id; u16 addr; }; static struct i2c_rpmsg_info i2c_rpmsg; struct imx_rpmsg_i2c_data { struct i2c_adapter adapter; }; static int i2c_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len, void *priv, u32 src) { struct i2c_rpmsg_msg *msg = (struct i2c_rpmsg_msg *)data; if (msg->header.type != I2C_RPMSG_TYPE_RESPONSE) return -EINVAL; if (msg->bus_id != i2c_rpmsg.bus_id || msg->addr != i2c_rpmsg.addr) { dev_err(&rpdev->dev, "expected bus_id:%d, addr:%2x, received bus_id:%d, addr:%2x\n", i2c_rpmsg.bus_id, i2c_rpmsg.addr, msg->bus_id, msg->addr); return -EINVAL; } if (msg->len > I2C_RPMSG_MAX_BUF_SIZE) { dev_err(&rpdev->dev, "%s failed: data length greater than %d, len=%d\n", __func__, I2C_RPMSG_MAX_BUF_SIZE, msg->len); return -EINVAL; } /* Receive Success */ i2c_rpmsg.msg = msg; complete(&i2c_rpmsg.cmd_complete); return 0; } static int rpmsg_xfer(struct i2c_rpmsg_msg *rmsg, struct i2c_rpmsg_info *info) { int ret = 0; ret = rpmsg_send(info->rpdev->ept, (void *)rmsg, sizeof(struct i2c_rpmsg_msg)); if (ret < 0) { dev_err(&info->rpdev->dev, "rpmsg_send failed: %d\n", ret); return ret; } ret = wait_for_completion_timeout(&info->cmd_complete, msecs_to_jiffies(I2C_RPMSG_TIMEOUT)); if (!ret) { dev_err(&info->rpdev->dev, "%s failed: timeout\n", __func__); return -ETIMEDOUT; } if (info->msg->ret_val) { dev_dbg(&info->rpdev->dev, "%s failed: %d\n", __func__, info->msg->ret_val); return -(info->msg->ret_val); } return 0; } static int i2c_rpmsg_read(struct i2c_msg *msg, struct i2c_rpmsg_info *info, int bus_id, bool is_last) { int ret; struct i2c_rpmsg_msg rmsg; if (!info->rpdev) return -EINVAL; if (msg->len > I2C_RPMSG_MAX_BUF_SIZE) { dev_err(&info->rpdev->dev, "%s failed: data length greater than %d, len=%d\n", __func__, I2C_RPMSG_MAX_BUF_SIZE, msg->len); return -EINVAL; } memset(&rmsg, 0, sizeof(struct i2c_rpmsg_msg)); rmsg.header.cate = I2C_RPMSG_CATEGORY; rmsg.header.major = I2C_RPMSG_VERSION; rmsg.header.minor = I2C_RPMSG_VERSION >> 8; rmsg.header.type = I2C_RPMSG_TYPE_REQUEST; rmsg.header.cmd = I2C_RPMSG_COMMAND_READ; rmsg.header.reserved[0] = I2C_RPMSG_PRIORITY; rmsg.bus_id = bus_id; rmsg.ret_val = 0; rmsg.addr = msg->addr; if (is_last) rmsg.flags = msg->flags | I2C_RPMSG_M_STOP; else rmsg.flags = msg->flags; rmsg.len = (msg->len); reinit_completion(&info->cmd_complete); ret = rpmsg_xfer(&rmsg, info); if (ret) return ret; if (!info->msg || (info->msg->len != msg->len)) { dev_err(&info->rpdev->dev, "%s failed: %d\n", __func__, -EPROTO); return -EPROTO; } memcpy(msg->buf, info->msg->buf, info->msg->len); return msg->len; } int i2c_rpmsg_write(struct i2c_msg *msg, struct i2c_rpmsg_info *info, int bus_id, bool is_last) { int i, ret; struct i2c_rpmsg_msg rmsg; if (!info || !info->rpdev) return -EINVAL; if (msg->len > I2C_RPMSG_MAX_BUF_SIZE) { dev_err(&info->rpdev->dev, "%s failed: data length greater than %d, len=%d\n", __func__, I2C_RPMSG_MAX_BUF_SIZE, msg->len); return -EINVAL; } memset(&rmsg, 0, sizeof(struct i2c_rpmsg_msg)); rmsg.header.cate = I2C_RPMSG_CATEGORY; rmsg.header.major = I2C_RPMSG_VERSION; rmsg.header.minor = I2C_RPMSG_VERSION >> 8; rmsg.header.type = I2C_RPMSG_TYPE_REQUEST; rmsg.header.cmd = I2C_RPMSG_COMMAND_WRITE; rmsg.header.reserved[0] = I2C_RPMSG_PRIORITY; rmsg.bus_id = bus_id; rmsg.ret_val = 0; rmsg.addr = msg->addr; if (is_last) rmsg.flags = msg->flags | I2C_RPMSG_M_STOP; else rmsg.flags = msg->flags; rmsg.len = msg->len; for (i = 0; i < rmsg.len; i++) rmsg.buf[i] = msg->buf[i]; reinit_completion(&info->cmd_complete); ret = rpmsg_xfer(&rmsg, info); if (ret) return ret; return ret; } static int i2c_rpmsg_probe(struct rpmsg_device *rpdev) { int ret = 0; if (!rpdev) { dev_info(&rpdev->dev, "%s failed, rpdev=NULL\n", __func__); return -EINVAL; } i2c_rpmsg.rpdev = rpdev; mutex_init(&i2c_rpmsg.lock); init_completion(&i2c_rpmsg.cmd_complete); dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n", rpdev->src, rpdev->dst); return ret; } static void i2c_rpmsg_remove(struct rpmsg_device *rpdev) { i2c_rpmsg.rpdev = NULL; dev_info(&rpdev->dev, "i2c rpmsg driver is removed\n"); } static struct rpmsg_device_id i2c_rpmsg_id_table[] = { { .name = "rpmsg-i2c-channel" }, { }, }; static struct rpmsg_driver i2c_rpmsg_driver = { .drv.name = "i2c-rpmsg", .drv.owner = THIS_MODULE, .id_table = i2c_rpmsg_id_table, .probe = i2c_rpmsg_probe, .remove = i2c_rpmsg_remove, .callback = i2c_rpmsg_cb, }; static int i2c_rpbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) { struct imx_rpmsg_i2c_data *rdata = container_of(adapter, struct imx_rpmsg_i2c_data, adapter); struct i2c_msg *pmsg; int i, ret; bool is_last = false; mutex_lock(&i2c_rpmsg.lock); for (i = 0; i < num; i++) { if (i == num - 1) is_last = true; pmsg = &msgs[i]; i2c_rpmsg.bus_id = rdata->adapter.nr; i2c_rpmsg.addr = pmsg->addr; if (pmsg->flags & I2C_M_RD) { ret = i2c_rpmsg_read(pmsg, &i2c_rpmsg, rdata->adapter.nr, is_last); if (ret < 0) { mutex_unlock(&i2c_rpmsg.lock); return ret; } pmsg->len = ret; } else { ret = i2c_rpmsg_write(pmsg, &i2c_rpmsg, rdata->adapter.nr, is_last); if (ret < 0) { mutex_unlock(&i2c_rpmsg.lock); return ret; } } } mutex_unlock(&i2c_rpmsg.lock); return num; } static u32 i2c_rpbus_func(struct i2c_adapter *a) { return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_READ_BLOCK_DATA; } static const struct i2c_algorithm i2c_rpbus_algorithm = { .master_xfer = i2c_rpbus_xfer, .functionality = i2c_rpbus_func, }; static const struct i2c_adapter_quirks i2c_rpbus_quirks = { .max_write_len = I2C_RPMSG_MAX_BUF_SIZE, .max_read_len = I2C_RPMSG_MAX_BUF_SIZE, }; static int i2c_rpbus_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct imx_rpmsg_i2c_data *rdata; struct i2c_adapter *adapter; int ret; rdata = devm_kzalloc(&pdev->dev, sizeof(*rdata), GFP_KERNEL); if (!rdata) return -ENOMEM; adapter = &rdata->adapter; /* setup i2c adapter description */ adapter->owner = THIS_MODULE; adapter->class = I2C_CLASS_HWMON; adapter->algo = &i2c_rpbus_algorithm; adapter->dev.parent = dev; adapter->dev.of_node = np; adapter->nr = of_alias_get_id(np, "i2c"); /* * The driver will send the adapter->nr as BUS ID to the other * side, and the other side will check the BUS ID to see whether * the BUS has been registered. If there is alias id for this * virtual adapter, linux kernel will automatically allocate one * id which might be not the same number used in the other side, * cause i2c slave probe failure under this virtual I2C bus. * So let's add a BUG_ON to catch this issue earlier. */ BUG_ON(adapter->nr < 0); adapter->quirks = &i2c_rpbus_quirks; snprintf(rdata->adapter.name, sizeof(rdata->adapter.name), "%s", "i2c-rpmsg-adapter"); platform_set_drvdata(pdev, rdata); ret = i2c_add_adapter(&rdata->adapter); if (ret < 0) { dev_err(dev, "failed to add I2C adapter: %d\n", ret); return ret; } dev_info(dev, "add I2C adapter %s successfully\n", rdata->adapter.name); return 0; } static int i2c_rpbus_remove(struct platform_device *pdev) { struct imx_rpmsg_i2c_data *rdata = platform_get_drvdata(pdev); i2c_del_adapter(&rdata->adapter); return 0; } static const struct of_device_id imx_rpmsg_i2c_dt_ids[] = { { .compatible = "fsl,i2c-rpbus", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, imx_rpmsg_i2c_dt_ids); static struct platform_driver imx_rpmsg_i2c_driver = { .driver = { .name = "imx_rpmsg_i2c", .of_match_table = imx_rpmsg_i2c_dt_ids, }, .probe = i2c_rpbus_probe, .remove = i2c_rpbus_remove }; static int __init imx_rpmsg_i2c_driver_init(void) { int ret = 0; ret = register_rpmsg_driver(&i2c_rpmsg_driver); if (ret < 0) return ret; return platform_driver_register(&(imx_rpmsg_i2c_driver)); } subsys_initcall(imx_rpmsg_i2c_driver_init); MODULE_AUTHOR("Clark Wang"); MODULE_DESCRIPTION("Driver for i2c over rpmsg"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:i2c-rpbus");