MLK-20940-1 i2c: busses: Add virtual i2c driver based on rpmsg
Add virtual i2c driver to send SRTM i2c messages to M4. Each virtual I2C bus has a specal bus id, which is abstracted by M4. Each SRTM message include a bus id for the bus which the device is on. Virtual i2c rpmsg bus will bind rpbus nodes with compatible string "fsl,i2c-rpbus". And "rpmsg-i2c-channel" will probe only one rpmsg channel for all rpbuses. This virtual i2c driver depends on CONFIG_I2C and CONFIG_RPMSG. Signed-off-by: Clark Wang <xiaoning.wang@nxp.com> (cherry picked from commit 9feeac93a7d91ce67537a8a6c67e624eb7986a01) (cherry picked from commit 379ab8392ef404dc7eea9a7492231a9d9d250ed5)5.4-rM2-2.2.x-imx-squashed
parent
9955e666a9
commit
b927082852
|
@ -0,0 +1,29 @@
|
||||||
|
* Freescale Virtual I2C RPMSG bus driver for i.MX
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible :
|
||||||
|
- "fsl,i2c-rpbus" for I2C bus over RPMSG compatible on i.MX8QXP/QM soc
|
||||||
|
The i2c-rpbus node should define its bus id (which is the node communicating
|
||||||
|
with M4) in alias.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
aliases {
|
||||||
|
...
|
||||||
|
i2c1 = &i2c_rpbus_1;
|
||||||
|
...
|
||||||
|
};
|
||||||
|
|
||||||
|
&i2c_rpbus_1 {
|
||||||
|
compatible = "fsl,i2c-rpbus";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
status = "okay";
|
||||||
|
|
||||||
|
devs_in_this_i2c_bus__for_example: pca6416@20 {
|
||||||
|
compatible = "ti,tca6416";
|
||||||
|
reg = <0x20>;
|
||||||
|
gpio-controller;
|
||||||
|
#gpio-cells = <2>;
|
||||||
|
};
|
||||||
|
};
|
|
@ -922,6 +922,12 @@ config I2C_RK3X
|
||||||
This driver can also be built as a module. If so, the module will
|
This driver can also be built as a module. If so, the module will
|
||||||
be called i2c-rk3x.
|
be called i2c-rk3x.
|
||||||
|
|
||||||
|
config I2C_RPBUS
|
||||||
|
tristate "I2C proxy bus over RPMSG"
|
||||||
|
depends on I2C && RPMSG
|
||||||
|
help
|
||||||
|
This driver can support virtual i2c-rpmsg function.
|
||||||
|
|
||||||
config HAVE_S3C2410_I2C
|
config HAVE_S3C2410_I2C
|
||||||
bool
|
bool
|
||||||
help
|
help
|
||||||
|
|
|
@ -130,6 +130,7 @@ obj-$(CONFIG_I2C_DLN2) += i2c-dln2.o
|
||||||
obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o
|
obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o
|
||||||
obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o
|
obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o
|
||||||
obj-$(CONFIG_I2C_ROBOTFUZZ_OSIF) += i2c-robotfuzz-osif.o
|
obj-$(CONFIG_I2C_ROBOTFUZZ_OSIF) += i2c-robotfuzz-osif.o
|
||||||
|
obj-$(CONFIG_I2C_RPBUS) += i2c-rpmsg-imx.o
|
||||||
obj-$(CONFIG_I2C_TAOS_EVM) += i2c-taos-evm.o
|
obj-$(CONFIG_I2C_TAOS_EVM) += i2c-taos-evm.o
|
||||||
obj-$(CONFIG_I2C_TINY_USB) += i2c-tiny-usb.o
|
obj-$(CONFIG_I2C_TINY_USB) += i2c-tiny-usb.o
|
||||||
obj-$(CONFIG_I2C_VIPERBOARD) += i2c-viperboard.o
|
obj-$(CONFIG_I2C_VIPERBOARD) += i2c-viperboard.o
|
||||||
|
|
|
@ -0,0 +1,427 @@
|
||||||
|
/*
|
||||||
|
* 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 <linux/init.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/imx_rpmsg.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/time.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/rpmsg.h>
|
||||||
|
|
||||||
|
#define I2C_RPMSG_MAX_BUF_SIZE 16
|
||||||
|
#define I2C_RPMSG_TIMEOUT 100 /* 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
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->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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
for (i = 0; i < num; i++) {
|
||||||
|
if (i == num - 1)
|
||||||
|
is_last = true;
|
||||||
|
|
||||||
|
pmsg = &msgs[i];
|
||||||
|
|
||||||
|
if (pmsg->flags & I2C_M_RD) {
|
||||||
|
ret = i2c_rpmsg_read(pmsg, &i2c_rpmsg,
|
||||||
|
rdata->adapter.nr, is_last);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
pmsg->len = ret;
|
||||||
|
} else {
|
||||||
|
ret = i2c_rpmsg_write(pmsg, &i2c_rpmsg,
|
||||||
|
rdata->adapter.nr, is_last);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
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<xiaoning.wang@nxp.com>");
|
||||||
|
MODULE_DESCRIPTION("Driver for i2c over rpmsg");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
|
MODULE_ALIAS("platform:i2c-rpbus");
|
Loading…
Reference in New Issue