c - How to write multiple slave i2c client device driver? -
i trying develop driver embedded board. driver supposed open interface v4l2 , communicate 2 devices using i2c. driver act master.
i can't seem understand how i2c_device_id
array , i2c_add_driver
functions work. read documentation in kernel source won't me on multiple slave clients.
- do have have 2 seperate probe functions?
- do have call
i2c_add_driver
2 times? - if not how going able save 2 different clients able send different bytes different addresses.
i pasting code here. tried instantiate 2 i2c_drivers
, called i2c_driver_add
2 times , implemented i2c probe seperately. code doesn't work telling me foo1
registered when calls i2c_add_driver
second time.
i defined 2 blocks under i2c1
in 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 vgen3, on rev b board 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 vgen3, on rev b board 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 same except names.
in driver file 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 init
, 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 probe
function. have 2 copies 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 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 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 set here */ foo_reset(); ret = foo_get_id(); if(ret < 0 /* || ret != foo_id */) { clk_disable_unprepare(foo_data.sensor_clk); pr_warning("foo 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 found\n"); i2c0initialized = 1; return ret; }
this answer late 5 months else had same issue (as did) , not find suitable answer.
in short solution use minor number represent each slave. driver minor number in stored list of clients obtain proper i2c_client.
long version i2c driver character device driver such unique device. otherwise framework (such hwmon) may implemented , handling multiple slaves done framework need not worry it. see http://lxr.free-electrons.com/source/drivers/hwmon/ examples.
now assuming character device driver, in driver __init need allocate many minor numbers slave devices:
alloc_chrdev_region(&dev, *minor_start*, *num_devices*, name) /* each minor or slave device, cdev_init , cdev_add */
now onto module_device_table
. enter entries each slave remembering string must match device tree compatible entry. 2nd field number, use both unique identifier , minor number (this trick):
struct i2c_device_id foo_idtable[] = { { "foo_1", 0 }, { "foo_2", 1 }, { }, }; module_device_table(i2c, foo_idtable);
ok in place, .probe
function called each matching device-tree entry. when .probe
function called, linux passes in i2c_client pointer has instantiated you. here's other part of trick, have global table stores these individual i2c_client pointers. index of global table minor number. minor number id->driver_data
passed in, number assigned in foo_idtable
before.
static int foo_probe(struct i2c_client *client, const struct i2c_device_id *id) { /* store i2c_client pointer in global table using minor number index * make sure allocate global table dynamically */ that_table[id->driver_data] = client; /* id->driver_data minor number! */ }
hopefully catching on now. when insmod .ko, want make multiple nodes, 1 each minor number:
insmod foo.ko make_dir /dev/foo find_major foo make_node /dev/foo/foo_0 c <major> 0 make_node /dev/foo/foo_1 c <major> 1
now when userspace code tries use driver, open file corresponds correct minor number (slave device). in userspace like:
/* accessing slave #0 */ int fd = open("/dev/foo/foo_0", o_rdwr); /* accessing slave #1 */ int fd_1 = open("/dev/foo/foo_1", o_rdwr);
your .open implementation called.
static int foo_driver_open(struct inode *inode, struct file *filep) { int minor_num = minor(inode->i_rdev); /* remember global table had before indexed using minor number? * let's i2c_client stored there , filep->private_data * point i2c_client. subsequent read/write/ioctl can * obtain i2c_client filep->private_data */ filep->private_data = that_table[minor_num]; }
then example userspace code makes call driver's ioctl:
ioctl(fd, foo_ioctl_do_read, &msg); ioctl(fd_1, foo_ioctl_do_write, &msg);
in driver's ioctl implementation:
long foo_driver_ioctl(struct file *filep, unsinged int cmd, unsigned long arg) { /* filep->private_data has i2c_client pointer! yay! */ struct i2c_client *client = filep->private_data; /* can talk slave device i2c_client knowing * correct i2c_client */ }
that's :). hope makes sense. it's long explanation hope thorough not confusing. biggest problem have global table stores i2c_cient pointers, can't think of way not have since .probe
, .open
have no way pass parameters between each other. if has better solution let me know.
Comments
Post a Comment