[PATCH RESEND] i2c: Add support for device-tree based chip initialization
Guenter Roeck
linux at roeck-us.net
Tue Nov 27 07:19:54 EST 2012
On Mon, Nov 26, 2012 at 10:43:38AM -0600, Rob Herring wrote:
> On 11/25/2012 10:53 PM, Guenter Roeck wrote:
> > 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 at roeck-us.net>
> > ---
> > Any comments / feedback ?
>
> This has been discussed before in terms of memory mapped registers. I
> think this is of questionable use of DT and could easily be abused. Not
Isn't that true for pretty much everything ?
Really, I don't think that "it can be abused" should be considered a valid
argument. It is almost as good (or bad) as "we have always done it that way"
or "this doesn't scale".
> all systems use DT, so this needs to be solved without DT anyway.
>
> Do you have examples of drivers that would use this?
>
I would need it for MAX6697 (for which I submitted a driver a week or so ago),
and possibly for others. I didn't check further because I don't want to go along
on this road too far if the idea is rejected.
I took the idea from Broadcom and Marvell PHY chip initialization, which uses
a similar approach, including the reg-init keyword. As far as I can see
both don't support platform data based initialization, so one question
to ask would be when it is mandatory to support platforma data based
initialization and when it isn't.
> Can this be handled in userspace using the i2c device interface before
> loading the device's module?
>
Not in my use case.
Thanks,
Guenter
> Rob
>
> >
> > .../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 at 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);
> >
>
>
More information about the devicetree-discuss
mailing list