I am trying to develop a driver for an embedded board. The driver is supposed to open up an interface for v4l2 and communicate with 2 devices using i2c. the driver will act as a master.
I can't seem to understand how i2c_device_id
array and i2c_add_driver
functions work. I read documentation in kernel source but it won't help me on multiple slave clients.
- Do I have to have two seperate probe functions?
- Do i have to call
i2c_add_driver
two times? - If not how am I going to be able to save two different clients to be able to send different bytes to different addresses.
I am pasting my code here. I tried to instantiate two i2c_drivers
, called i2c_driver_add
two times and implemented i2c probe seperately. The code doesn't work telling me that foo1
is already registered when it calls i2c_add_driver
for the second time.
I defined two blocks under i2c1
in my dts file like:
&i2c1 {
...
foo0: foo0@00 {
compatible = "bar,foo0";
reg = <0x00>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ipu1_2>;
clocks = <&clks IMX6QDL_CLK_CKO>;
clock-names = "csi_mclk";
DOVDD-supply = <&vgen4_reg>; /* 1.8v */
AVDD-supply = <&vgen3_reg>; /* 2.8v, on rev C board is VGEN3,
on rev B board is VGEN5 */
DVDD-supply = <&vgen2_reg>; /* 1.5v*/
pwn-gpios = <&gpio1 16 1>; /* active low: SD1_DAT0 */
rst-gpios = <&gpio1 17 0>; /* active high: SD1_DAT1 */
csi_id = <0>;
mclk = <24000000>;
mclk_source = <0>;
};
foo1: foo1@02 {
compatible = "bar, foo1";
reg = <0x02>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ipu1_2>;
clocks = <&clks IMX6QDL_CLK_CKO>;
clock-names = "csi_mclk";
DOVDD-supply = <&vgen4_reg>; /* 1.8v */
AVDD-supply = <&vgen3_reg>; /* 2.8v, on rev C board is VGEN3,
on rev B board is VGEN5 */
DVDD-supply = <&vgen2_reg>; /* 1.5v*/
pwn-gpios = <&gpio1 16 1>; /* active low: SD1_DAT0 */
rst-gpios = <&gpio1 17 0>; /* active high: SD1_DAT1 */
csi_id = <0>;
mclk = <24000000>;
mclk_source = <0>;
};
...
Two blocks are exactly the same except their names.
In the driver file I instantiated following structs:
static const struct i2c_device_id foo_id[] = {
{"foo0", 0},
{"foo1", 1},
{},
};
static struct i2c_driver foo0_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "foo0",
},
.probe = foo0_probe,
.remove = foo0_remove,
.id_table = foo_id,
};
static struct i2c_driver foo1_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "foo1",
},
.probe = foo1_probe,
.remove = foo1_remove,
.id_table = foo_id,
};
Below are my init
and exit
functions:
MODULE_DEVICE_TABLE(i2c, foo_id);
static __init int foo_init(void)
{
u8 err;
err = i2c_add_driver(&foo0_i2c_driver);
if (err != 0)
pr_err("%s:driver registration failed i2c-slave0, error=%d\n",
__func__, err);
err = i2c_add_driver(&foo1_i2c_driver);
if (err != 0)
pr_err("%s:driver registration failed i2c-slave1, error=%d\n",
__func__, err);
return err;
}
static void __exit foo_clean(void)
{
if((&foo0_i2c_driver) != NULL && i2c0initialized)
{
i2c_del_driver(&foo0_i2c_driver);
i2c0initialized = 0;
}
if((&foo1_i2c_driver) != NULL && i2c1initialized)
{
i2c_del_driver(&foo1_i2c_driver);
i2c1initialized = 0;
}
}
module_init(foo_init);
module_exit(foo_clean);
Below is my probe
function. I have two copies for it for both slaves.
static int foo_probe(struct i2c_client *client,
const struct i2c_device_id *device_id)
{
struct pinctrl *pinctrls;
struct device *dev = &client->dev;
int ret = 0;
pinctrls = devm_pinctrl_get_select_default(dev);
if(IS_ERR(pinctrls))
{
dev_err(dev, "pinctrl setup failed\n");
return PTR_ERR(pinctrls);
}
memset(&foo_data, 0, sizeof(foo_data));
foo_data.sensor_clk = devm_clk_get(dev, "csi_mclk");
if(IS_ERR(foo_data.sensor_clk))
{
dev_err(dev, "get mclk failed\n");
return PTR_ERR(foo_data.sensor_clk);
}
ret = of_property_read_u32(dev->of_node, "mclk", &(foo_data.mclk));
if(ret < 0)
{
dev_err(dev, "mclk frequency is invalid\n");
return ret;
}
ret = of_property_read_u32(dev->of_node, "mclk_source",
(u32 *)&(foo_data.mclk_source));
if(ret < 0)
{
dev_err(dev, "mclk source is invalid\n");
return ret;
}
ret = of_property_read_u32(dev->of_node, "csi_id", &(foo_data.csi));
if(ret < 0)
{
dev_err(dev, "csi_id invalid\n");
return ret;
}
clk_prepare_enable(foo_data.sensor_clk);
i2c_client0 = client;
/* custom data structures are set here */
foo_reset();
ret = foo_get_id();
if(ret < 0 /* || ret != foo_ID */)
{
clk_disable_unprepare(foo_data.sensor_clk);
pr_warning("foo is not found\n");
return -ENODEV;
}
clk_disable_unprepare(foo_data.sensor_clk);
foo_int_device.priv = &foo_data;
ret = v4l2_int_device_register(&foo_int_device);
pr_info("foo is found\n");
i2c0initialized = 1;
return ret;
}
This answer is late by 5 months but hopefully it will help someone else who had the same issue (as I did) and could not find a suitable answer.
In short the solution is to use a minor number to represent each slave. Your driver will look up that minor number in your stored list of clients to obtain the proper i2c_client.
Long version Your I2C driver will eventually probably be a character device driver for such an unique device. Otherwise a framework (such as hwmon) may already be implemented and handling multiple slaves is already done by the framework so you need not worry about it. See http://lxr.free-electrons.com/source/drivers/hwmon/ for examples.
Now assuming it is a character device driver, in your driver __init you need to allocate as many minor numbers as slave devices:
Now onto your
MODULE_DEVICE_TABLE
. Enter the entries for each slave remembering that the string must match the device tree compatible entry. The 2nd field is a number, we will use it as both an unique identifier and a minor number (this is the trick):Ok with that in place, your
.probe
function will be called for each matching device-tree entry. When the.probe
function is called, Linux passes in the i2c_client pointer it has instantiated for you. Here's the other part of the trick, have a global table that stores these individual i2c_client pointers. The index of the global table is the minor number. The minor number is theid->driver_data
that is also passed in, which was the number you assigned in thefoo_idtable
before.Hopefully you are catching on now. When you insmod your .ko, you want to make multiple nodes, one for each minor number:
Now when your userspace code tries to use your driver, it will open the file that corresponds to the correct minor number (slave device). In userspace you would do something like:
Your .open implementation will get called.
Then for example your userspace code makes a call to the driver's ioctl:
In your driver's ioctl implementation:
That's it :). I hope that makes sense. It's a long explanation but I hope I was thorough but not too confusing. The biggest problem is that we have a global table that stores the i2c_cient pointers, but I can't think of a way not to have it since
.probe
and.open
have no way to pass parameters between each other. If anyone has a better solution let me know.This is a workaround I am using right now, I will not accept this one as answer as this method doesn't feel right.