Re: [PATCH v2 2/4] iio: add LM3533 ambient light sensor driver

From: Jonathan Cameron
Date: Tue May 08 2012 - 09:47:33 EST


On 5/3/2012 5:36 PM, Johan Hovold wrote:
On Thu, May 03, 2012 at 12:40:10PM +0100, Jonathan Cameron wrote:
On 5/3/2012 11:26 AM, Johan Hovold wrote:
Add sub-driver for the ambient light sensor interface on National
Semiconductor / TI LM3533 lighting power chips.

The sensor interface can be used to control the LEDs and backlights of
the chip through defining five light zones and three sets of
corresponding brightness target levels.

The driver provides raw and mean adc readings along with the current
light zone through sysfs. A threshold event can be generated on zone
changes.
Code is fine. Pretty much all my comments are to do with the interface.
[...]

diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-lm3533-als b/drivers/staging/iio/Documentation/sysfs-bus-iio-light-lm3533-als
new file mode 100644
index 0000000..9849d14
--- /dev/null
+++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-light-lm3533-als
@@ -0,0 +1,62 @@
+What: /sys/bus/iio/devices/iio:deviceX/gain
+Date: April 2012
+KernelVersion: 3.5
+Contact: Johan Hovold<jhovold@xxxxxxxxx>
+Description:
+ Set the ALS gain-resistor setting (0..127) for analog input
+ mode, where
+
+ 0000000 - ALS input is high impedance
+ 0000001 - 200kOhm (10uA at 2V full-scale)
+ 0000010 - 100kOhm (20uA at 2V full-scale)
+ ...
+ 1111110 - 1.587kOhm (1.26mA at 2V full-scale)
+ 1111111 - 1.575kOhm (1.27mA at 2V full-scale)
+
+ R_als = 2V / (10uA * gain) (gain> 0)
Firstly, no magic numbers. These are definitely magic.
Not that magic as they're clearly documented (in code and public
datasheets), right? What would you prefer instead?
The numbers on the right of the - look good to me though then this isn't
a gain. (200kohm) and the infinite element is annoying. Why not
compute the actual gains?
Gain = (Rals*10e-6)/2 and use those values? Yes you will have to do
a bit of fixed point maths in the driver but the advantage is you'll
have real values that are standardizable across multiple devices
and hence allow your device to be operated by generic userspace
code. Welcome to standardising interfaces - my favourite occupation ;)

Secondly see
in_illuminance0_scale for a suitable existing attribute.
I didn't consider scale to be appropriate given the following
documentation (e.g, for in_voltageY_scale):
sorry I just did this to someone else in another review (so I'm consistently
wrong!)

in_voltageY_calibscale is what I should have said. That one applies a scaling
before the raw reading is generated (so in hardware).

"If known for a device, scale to be applied to<type>Y[_name]_raw post
addition of<type>[Y][_name]_offset in order to obtain the measured
value in<type> units as specified in<type>[Y][_name]_raw
documentation."

That is, the gain setting has nothing to do with scaling the raw adc
reading to SI-units or such, it's simply a setup dependent gain setting
(which affects the raw readings as well). [And as such, should probably
go into to the platform data eventually as well.]

+
+What: /sys/bus/iio/devices/iio:deviceX/illuminance_zone
+Date: April 2012
+KernelVersion: 3.5
+Contact: Johan Hovold<jhovold@xxxxxxxxx>
+Description:
+ Get the current light zone (0..4) as defined by the
+ in_illuminance_thresh[n]_{falling,rising} thresholds.
Hmm.. definitely have an in prefix, beyond that I'm not sure what the
cleanest
Thanks for catching this, it's a typo in the sysfs document -- the in_
prefix is in the code.

interface will be for this. Could extend the event codes to deal with the
zone index. Slightly tricky as the channel could already be modified so
chan2 isn't necesarily available.

+
+What: /sys/.../events/in_illuminance_thresh_either_en
+Date: April 2012
+KernelVersion: 3.5
+Contact: Johan Hovold<jhovold@xxxxxxxxx>
+Description:
+ Event generated when channel passes one of the four threshold
+ in either direction (rising|falling) and a zone change occurs.
+ The corresponding light zone can be read from
+ illuminance_zone.
+
+What: /sys/.../events/illuminance_thresh0_falling_value
hmm.. every time you think you are making progress a new and exciting
device comes
along requiring the abi to be extended.
Exciting isn't it. :)
humph.

in_illuminanceX_threshY_rising_value
in_illuminanceX_threshY_falling_value
should do with appropriate description.
Ok.

+What: /sys/.../events/illuminance_thresh0_raising_value
+What: /sys/.../events/illuminance_thresh1_falling_value
+What: /sys/.../events/illuminance_thresh1_raising_value
+What: /sys/.../events/illuminance_thresh2_falling_value
+What: /sys/.../events/illuminance_thresh2_raising_value
+What: /sys/.../events/illuminance_thresh3_falling_value
+What: /sys/.../events/illuminance_thresh3_raising_value
+Date: April 2012
+KernelVersion: 3.5
+Contact: Johan Hovold<jhovold@xxxxxxxxx>
+Description:
+ Specifies the value of threshold that the device is comparing
+ against for the events enabled by
+ in_illuminance_thresh_either_en, and defines the
+ the five light zones.
+
+ These thresholds correspond to the eight zone-boundary
+ registers (boundary[n]_{low,high}).
+
This interface is going to take some thought. We have
in_illuminance0_target at the
moment, so I guess we can add a zoning concept to that...
But target isn't really related, as far as I understand. That's another
calibration setting right? While zone is derived from the average adc
readings. (More below.)
True enough. I'd missunderstood this.

+What: /sys/bus/iio/devices/iio:deviceX/target[m]_[n]
+Date: April 2012
+KernelVersion: 3.5
+Contact: Johan Hovold<jhovold@xxxxxxxxx>
+Description:
+ Set the target brightness for ALS-mapper m in light zone n
+ (0..255), where m in 1..3 and n in 0..4.
Don't suppose you could do a quick summary of what these zones are and
why there
are 3 ALS-mappers? I'm not getting terribly far on a quick look at the
datasheet!
Of course. The average adc readings are mapped to five light zones using
eight zone boundary registers (4 boundaries with hysteresis) and a set
of rules.
This is going to be fun. We'll need the boundaries and attached hysteresis attributes
to fully specify these (nothing would indicate that hysterisis is involved otherwise).

To simplify somewhat (by ignoring some of the rules): If the average
adc input drops below boundary0_low, the zone register reads 0; if it
drops below boundary1_low, it reads 1, and so on. If the input it
increases over boundary3_high, the zone register return 4; if it
increases passed boundary2_high, it returns zone 3, etc.

That is, roughly something like (we get 8-bits of input from the ADC):

zone 0

boundary0_low 51
boundary0_high 53

zone 1

boundary1_low 102
boundary1_high 106

zone 2

boundary2_low 153
boundary2_high 161

zone 3

boundary3_low 204
boundary3_high 220

zone 4

[ Figure 6 on page 20 in the datasheets should make it clear. ]

The ALS interface and it's zone concept can then be used to control the
LEDs and backlights of the chip, by determining the target brightness for
each zone, e.g., set brightness to 52 when in zone 0.

To complicate things further (and it is complicated), there are three
such sets of target brightness values: ALSM1, ALSM2, ALSM3.

So for each LED or backlight you can set ALS-input control mode, by
saying that the device should get it's brightness levels from target set
1, 2, or 3.

[ And it gets even more complicated, as ALSM1 can only control
backlight0, where as ALSM2 and ALSM3 can control any of the remaining
devices, but that's irrelevant here. ]

Initially, I thought this interface to be too esoteric to be worth
generalising, but it sort of fits with event thresholds so I gave it a
try.
Glad you did and it pretty much fits, be it with a few extensions being necessary.
The biggest conceptual problem, I think, is that the zone
boundaries can be used to control the other devices, even when the event
is not enabled (or even an irq line not configured). That is, I find it
a bit awkward that the event thresholds also defines the zones (a sort of
discrete scaling factor).
That is indeed awkward. I'm not sure how we handle this either. If we need to control
these from the other devices (e.g. the back light driver) then we'll have to get them
into chan_spec and use the inkernel interfaces to do it. Not infeasible but
I was hoping to avoid that until we have had a few months to see what similar
devices show up (on basis nothing in this world is a one off for long ;)


Perhaps simply keeping the attributes outside of events (e.g. named
boundary[n]_{low,high}) and having a custom event enabled (e.g.
in_illuminance_zone_change_en) is the best solution?
Maybe, but it's ugly and as you have said, they do correspond pretty well to
thresholds so I'd rather you went with that.
The core stuff for registering events clearly needs a rethink.... For now doing
it as you describe above (with the addition fo hysteresis attributes) should
be fine. Just document the 'quirks'.

[...]

diff --git a/drivers/staging/iio/light/lm3533-als.c b/drivers/staging/iio/light/lm3533-als.c
new file mode 100644
index 0000000..e2c9be6
--- /dev/null
+++ b/drivers/staging/iio/light/lm3533-als.c
@@ -0,0 +1,617 @@
+/*
+ * lm3533-als.c -- LM3533 Ambient Light Sensor driver
+ *
+ * Copyright (C) 2011-2012 Texas Instruments
+ *
+ * Author: Johan Hovold<jhovold@xxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include<linux/atomic.h>
+#include<linux/fs.h>
+#include<linux/interrupt.h>
+#include<linux/io.h>
+#include<linux/module.h>
+#include<linux/mfd/core.h>
+#include<linux/platform_device.h>
+#include<linux/slab.h>
+#include<linux/uaccess.h>
+
+#include<linux/mfd/lm3533.h>
+
+#include "../events.h"
+#include "../iio.h"
This will need to go through the staging-next tree. In there these
headers have moved.
I'm aware of the move. Should the different sub-drivers go in through
each tree respectively? Right now the four patches are all against rc5.
They will probably have to. Typically mfd bit goes in first, then the rest get added
in a random order after that.

[...]

+static const struct iio_chan_spec lm3533_als_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .info_mask = IIO_CHAN_INFO_AVERAGE_RAW_SEPARATE_BIT,
+ .channel = 0,
channel doesn't get used unless you also set indexed = 1.
So, you mean I could drop channel as well? Or should I add indexed, as I
use channel 0 when reporting the event?
Either option is valid. I personally tend to set indexed = 1 but we decided that
it didn't matter either way. Userspace code that uses the abi right should allow
for either.

[...]

+static int lm3533_als_set_int_mode(struct iio_dev *indio_dev, int enable)
+{
+ struct lm3533_als *als = iio_priv(indio_dev);
+ u8 mask = LM3533_ALS_INT_ENABLE_MASK;
+ u8 val;
+ int ret;
+
+ if (enable)
+ val = mask;
+ else
+ val = 0;
+
+ ret = lm3533_update(als->lm3533, LM3533_REG_ALS_ZONE_INFO, val, mask);
+ if (ret) {
+ dev_err(&indio_dev->dev, "failed to set int mode %d\n",
+ enable);
extra brackets.
I prefer the brackets for multi-line (single) statements even though
they are not required. (Especially if the single statement spans
several lines -- but I try to be consistent.) If you have a strong
opinion about this, I'll drop them.
I don't care either way.

+ }
+
+ return ret;
+}
+
Thanks,
Johan

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/