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;
}