Some I2C devices are not or not correctly initialized by the firmware. Configuration would be possible via platform data, but that would require per-driver platform data and a lot of code, and changing it would not be possible without re-compiling the kernel. It is more elegant to do it generically via devicetree properties. Add a generic I2C devicetree property named "reg-init". This property provides a sequence of device initialization commands to be executed prior to calling the probe function for a given device. Signed-off-by: Guenter Roeck <linux@xxxxxxxxxxxx> --- Any comments / feedback ? .../devicetree/bindings/i2c/trivial-devices.txt | 24 +++++++ drivers/i2c/i2c-core.c | 68 ++++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt index 2f5322b..33b694e 100644 --- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt +++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt @@ -2,6 +2,30 @@ This is a list of trivial i2c devices that have simple device tree bindings, consisting only of a compatible field, an address and possibly an interrupt line. +Device initialization is supported with an optional reg-init property. +This property contains a sequence of commands to be written into the chip. +Each command consists of four values: Register, command type, mask, and data. + Register: + Register or command address + Command type: + 0: SMBus write byte + 1: SMBus write byte data + 2: SMBus write word data + Mask: + If set, the register is read and masked with this value. + Data: + Data to be written (or with original data and mask if set) + +Example: + max6696@1a { + compatible = "maxim,max6696"; + reg = <0x1a>; + reg-init = < + 0x09 1 0x00 0x24 + 0x21 1 0x00 0x05 + >; + }; + If a device needs more specific bindings, such as properties to describe some aspect of it, there needs to be a specific binding document for it just like any other devices. diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index a7edf98..49f8b74 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -104,6 +104,69 @@ static int i2c_device_uevent(struct device *dev, struct kobj_uevent_env *env) #define i2c_device_uevent NULL #endif /* CONFIG_HOTPLUG */ +static int i2c_dev_of_init(struct i2c_client *client, struct device *dev) +{ + const u32 *reg_init; + int rlen; + int val; + u32 reg, access, mask, data; + + reg_init = of_get_property(dev->of_node, "reg-init", &rlen); + if (!reg_init) + return 0; + + if (!rlen || rlen % 4) + return -EINVAL; + + while (rlen >= 4) { + reg = reg_init[0]; + access = reg_init[1]; + mask = reg_init[2]; + data = reg_init[3]; + + if (reg > 0xff) + return -EINVAL; + + switch (access) { + default: + return -EINVAL; + case 0: + val = 0; + break; + case 1: + val = mask ? i2c_smbus_read_byte_data(client, reg) : 0; + break; + case 2: + val = mask ? i2c_smbus_read_word_data(client, reg) : 0; + break; + } + if (val < 0) + return val; + + val &= mask; + val |= data; + + switch (access) { + default: + case 0: + val = i2c_smbus_write_byte(client, reg); + break; + case 1: + val = i2c_smbus_write_byte_data(client, reg, val); + break; + case 2: + val = i2c_smbus_write_word_data(client, reg, val); + break; + } + if (val < 0) + return val; + + reg_init += 4; + rlen -= 4 * sizeof(u32); + } + return 0; +} + static int i2c_device_probe(struct device *dev) { struct i2c_client *client = i2c_verify_client(dev); @@ -122,7 +185,12 @@ static int i2c_device_probe(struct device *dev) client->flags & I2C_CLIENT_WAKE); dev_dbg(dev, "probe\n"); + status = i2c_dev_of_init(client, dev); + if (status) + goto error; + status = driver->probe(client, i2c_match_id(driver->id_table, client)); +error: if (status) { client->driver = NULL; i2c_set_clientdata(client, NULL); -- 1.7.9.7 -- To unsubscribe from this list: send the line "unsubscribe linux-i2c" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html