linux-iio.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Felipe Balbi <balbi@ti.com>
To: Jonathan Cameron <jic23@kernel.org>
Cc: Felipe Balbi <balbi@ti.com>, <linux-iio@vger.kernel.org>
Subject: Re: [RFC/PATCH] iio: light: add support for TI's opt3001 ligth sensor
Date: Thu, 7 Aug 2014 09:28:30 -0500	[thread overview]
Message-ID: <20140807142829.GA24581@saruman.home> (raw)
In-Reply-To: <53E34E60.80507@kernel.org>

[-- Attachment #1: Type: text/plain, Size: 13300 bytes --]

Hi,

On Thu, Aug 07, 2014 at 11:01:04AM +0100, Jonathan Cameron wrote:
> On 06/08/14 17:10, Felipe Balbi wrote:
> > TI's opt3001 light sensor is a simple and yet powerful
> > little device. The device provides 99% IR rejection,
> > Automatic full-scale, very low power consumption and
> > measurements from 0.01 to 83k lux.
> >
> > This patch adds support for that device using the IIO
> > framework.
> >
> > Signed-off-by: Felipe Balbi <balbi@ti.com>
> I don't suppose there is a datasheet available?

There is a summary datasheet available. The device isn't released yet:

http://www.ti.com/product/OPT3001?keyMatch=opt3001&tisearch=Search-EN

> When in M_CONTINUOUS is it just providing event interrupts
> (threshold etc)?

It depends on the mode. If Latch bit is set, then it'll report event
interrupts. Datasheet calls this a "window-style comparison operation).

If Latch bit is cleared, then it'll continuously report the comparison
result. Datasheet calls this a "hysteresis-style comoparison operation).

> > Jonathan, I have a few doubts on how can I test buffered
> > mode properly. I can see my IRQs triggering now problem,
> > but where is the data pushed ? Can it be read from sysfs
> > or /dev somehwere ?
> As  I guess has become clear from your thread with Peter,
> what you have here in IIO terms are called events (separate
> form the buffer which is for the main data flow).
> 
> Anyhow, see drivers/staging/iio/Documentation/iio_event_monitor.c.
> Some trickery with an anonymous file descriptor is used to avoid
> using lots of chardevs for the same device.

ok, will look at it.

> > Also, there are a couple details about this device which
> > I need to know if it looks for you:
> >
> > This device has a single enable bit which will enable both
> > rising and falling edge triggers, but the limits are separate.
> >
> > The same limits are also used for hysteresis-style capture and
> > that's controlled by a single bit flip.
> With this on, it generates an event whenever the value changes by
> more than a certain amount?  If so this is better handled by
> providing a trigger and a buffer.  Note that the events path
> carries no data other than an event code.

It's still a bit weird to me. So, without Latch bit, then yeah, it'll be
more of a trigger e.g:

I can set low limit to 200 lux and high limit to 50 lux, then I'll get a
rising edge IRQ when I have more than 50 lux and a falling edge when I
get less than 200 lux.

If Latch bit is set, however, then it's almost like it ignores low/high
limit altogether and I get an IRQ ever $int_time ms or so.

> > How do you want this to look on sysfs ? Currently, enable/disabling
> > any of rising/falling edges, will disable both.
> That's pretty common.  The ABI basically allows for any setting to
> change any other (as there are much weirder connections than this
> in some devices).

ok, so it's alright to have two "enable" files and have they refer to
the same bit ?

> > +static int opt3001_read(const struct opt3001 *opt, u8 reg)
> > +{
> > +	return i2c_smbus_read_word_swapped(opt->client, reg);
> > +}
> I'm very much against wrapper functions unless they add something. These
> really do not.

as I mentioned before, it helps a lot when using function tracer. I can
just "echo opt3001* > set_trace_function". But if you guys are so much
against it, I'll remove it.

> > +static const struct iio_chan_spec opt3001_channels[] = {
> > +	{
> > +		.type = IIO_LIGHT,
> > +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> > +				BIT(IIO_CHAN_INFO_SCALE) |
> > +				BIT(IIO_CHAN_INFO_INT_TIME),
> > +		.channel = 0,
> 
> No buffering at the moment so can drop scan_type and scan_index.

will do

> > +static int opt3001_read_event_value(struct iio_dev *iio,
> > +		const struct iio_chan_spec *chan, enum iio_event_type type,
> > +		enum iio_event_direction dir, enum iio_event_info info,
> > +		int *val, int *val2)
> > +{
> > +	struct opt3001 *opt = iio_priv(iio);
> > +	int ret = IIO_VAL_INT_PLUS_MICRO;
> > +
> > +	mutex_lock(&opt->lock);
> > +
> So these will return the same value whether the value attribute is read
> or the hysteresis one?

yeah, the comparison done is the same.

> > +static int opt3001_write_event_value(struct iio_dev *iio,
> > +		const struct iio_chan_spec *chan, enum iio_event_type type,
> > +		enum iio_event_direction dir, enum iio_event_info info,
> > +		int val, int val2)
> > +{
> > +	struct opt3001 *opt = iio_priv(iio);
> > +	int ret = 0;
> > +
> > +	u16 *thresh_mantissa;
> > +	u16 mantissa;
> > +	u16 value;
> > +	u16 reg;
> > +
> > +	u8 *thresh_exp;
> > +
> > +	u8 exponent;
> > +
> > +	mutex_lock(&opt->lock);
> > +
> > +	ret = opt3001_read(opt, OPT3001_CONFIGURATION);
> > +	if (ret < 0) {
> > +		dev_err(opt->dev, "failed to read register %02x\n",
> > +				OPT3001_CONFIGURATION);
> > +		goto err;
> > +	}
> > +
> > +	reg = ret;
> > +	if (info == IIO_EV_INFO_HYSTERESIS)
> Without a datasheet I'm not entirely sure how the hysteresis is
> applied here and what it's connection to the thresholds is...

yeah, that'll still take a while to be fully released.

> 
> > +		opt->hysteresis = true;
> > +	else
> > +		opt->hysteresis = false;
> > +
> > +	switch (dir) {
> > +	case IIO_EV_DIR_RISING:
> > +		reg = OPT3001_HIGH_LIMIT;
> Why bother with the indirection via a pointer. Just have a second
> switch statement at the end of the function that sets the right ones.
> Easier to follow than this.

sure

> > +		thresh_mantissa = &opt->high_thresh_mantissa;
> > +		thresh_exp = &opt->high_thresh_exp;
> > +		break;
> > +	case IIO_EV_DIR_FALLING:
> > +		reg = OPT3001_LOW_LIMIT;
> > +		thresh_mantissa = &opt->low_thresh_mantissa;
> > +		thresh_exp = &opt->low_thresh_exp;
> > +		break;
> > +	default:
> > +		ret = -EINVAL;
> > +		goto err;
> > +	}
> > +
> > +	ret = opt3001_find_scale(opt, val, val2, &exponent);
> > +	if (ret < 0) {
> > +		dev_err(opt->dev, "can't find scale for %d.%d\n", val, val2);
> > +		goto err;
> > +	}
> > +
> > +	mantissa = (((val * 1000) + (val2 / 1000)) / 10) >> exponent;
> > +	value = exponent << 12 | mantissa;
> > +	ret = opt3001_write(opt, reg, value);
> > +	if (ret < 0) {
> > +		dev_err(opt->dev, "failed to read register %02x\n", reg);
> > +		goto err;
> > +	}
> > +
> As stated above an additional switch statement here with direct assignment
> of the relevant threshold values would be clearer.
> > +	*thresh_mantissa = mantissa;
> > +	*thresh_exp = exponent;
> > +
> > +err:
> > +	mutex_unlock(&opt->lock);
> > +
> > +	return ret;
> > +}
> > +
> > +static int opt3001_read_event_config(struct iio_dev *iio,
> > +		const struct iio_chan_spec *chan, enum iio_event_type type,
> > +		enum iio_event_direction dir)
> > +{
> > +	struct opt3001 *opt = iio_priv(iio);
> > +
> > +	return opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS;
> > +}
> > +
> > +static int opt3001_write_event_config(struct iio_dev *iio,
> > +		const struct iio_chan_spec *chan, enum iio_event_type type,
> > +		enum iio_event_direction dir, int state)
> > +{
> > +	struct opt3001 *opt = iio_priv(iio);
> > +	int ret;
> > +	u16 mode;
> > +	u16 reg;
> > +
> > +	if (state && opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS)
> > +		return 0;
> > +
> > +	if (!state && opt->mode == OPT3001_CONFIGURATION_M_SHUTDOWN)
> > +		return 0;
> > +
> > +	mode = state ? OPT3001_CONFIGURATION_M_CONTINUOUS
> > +		: OPT3001_CONFIGURATION_M_SHUTDOWN;
> > +
> > +	ret = opt3001_read(opt, OPT3001_CONFIGURATION);
> > +	if (ret < 0) {
> > +		dev_err(opt->dev, "failed to read register %02x\n",
> > +				OPT3001_CONFIGURATION);
> > +		return ret;
> > +	}
> > +
> > +	reg = ret;
> > +	opt3001_set_mode(opt, &reg, mode);
> > +
> > +	if (opt->hysteresis)
> > +		reg |= OPT3001_CONFIGURATION_L;
> > +	else
> > +		reg &= ~OPT3001_CONFIGURATION_L;
> > +
> > +	ret = opt3001_write(opt, OPT3001_CONFIGURATION, reg);
> > +	if (ret < 0) {
> > +		dev_err(opt->dev, "failed to read register %02x\n",
> > +				OPT3001_CONFIGURATION);
> > +		return ret;
> > +	}
> > +
> > +	/* wait for mode change to go through */
> > +	usleep_range(opt->int_time + 5000, opt->int_time + 10000);
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct iio_info opt3001_info = {
> > +	.driver_module = THIS_MODULE,
> > +	.attrs = &opt3001_attribute_group,
> > +	.read_raw = opt3001_read_raw,
> > +	.write_raw = opt3001_write_raw,
> > +	.read_event_value = opt3001_read_event_value,
> > +	.write_event_value = opt3001_write_event_value,
> > +	.read_event_config = opt3001_read_event_config,
> > +	.write_event_config = opt3001_write_event_config,
> > +};
> > +
> > +static int opt3001_read_id(struct opt3001 *opt)
> > +{
> > +	char manufacturer[2];
> > +	u16 device_id;
> > +	int ret;
> > +
> > +	ret = opt3001_read(opt, OPT3001_MANUFACTURER_ID);
> > +	if (ret < 0) {
> > +		dev_err(opt->dev, "failed to read register %02x\n",
> > +				OPT3001_MANUFACTURER_ID);
> > +		return ret;
> > +	}
> > +
> > +	manufacturer[0] = ret >> 8;
> > +	manufacturer[1] = ret & 0xff;
> > +
> > +	ret = opt3001_read(opt, OPT3001_DEVICE_ID);
> > +	if (ret < 0) {
> > +		dev_err(opt->dev, "failed to read register %02x\n",
> > +				OPT3001_DEVICE_ID);
> > +		return ret;
> > +	}
> > +
> > +	device_id = ret;
> > +
> > +	dev_info(opt->dev, "Found %c%c OPT%04x\n", manufacturer[0],
> > +			manufacturer[1], device_id);
> > +
> > +	return 0;
> > +}
> > +
> > +static int opt3001_configure(struct opt3001 *opt)
> > +{
> > +	int ret;
> > +	u16 reg;
> > +
> > +	ret = opt3001_read(opt, OPT3001_CONFIGURATION);
> > +	if (ret < 0) {
> > +		dev_err(opt->dev, "failed to read register %02x\n",
> > +				OPT3001_CONFIGURATION);
> > +		return ret;
> > +	}
> > +
> > +	reg = ret;
> > +
> > +	if (reg & OPT3001_CONFIGURATION_CT)
> > +		opt->int_time = 800000;
> > +	else
> > +		opt->int_time = 100000;
> > +
> > +	reg &= ~OPT3001_CONFIGURATION_L;
> > +	reg &= ~OPT3001_CONFIGURATION_RN_MASK;
> > +	reg |= OPT3001_CONFIGURATION_RN_AUTO;
> > +
> > +	ret = opt3001_write(opt, OPT3001_CONFIGURATION, reg);
> > +	if (ret < 0) {
> > +		dev_err(opt->dev, "failed to write register %02x\n",
> > +				OPT3001_CONFIGURATION);
> > +		return ret;
> > +	}
> > +
> > +	ret = opt3001_read(opt, OPT3001_LOW_LIMIT);
> > +	if (ret < 0) {
> > +		dev_err(opt->dev, "failed to read register %02x\n",
> > +				OPT3001_LOW_LIMIT);
> > +		return ret;
> > +	}
> > +
> > +	opt->low_thresh_mantissa = OPT3001_REG_MANTISSA(ret);
> > +	opt->low_thresh_exp = OPT3001_REG_EXPONENT(ret);
> > +
> > +	ret = opt3001_read(opt, OPT3001_HIGH_LIMIT);
> > +	if (ret < 0) {
> > +		dev_err(opt->dev, "failed to read register %02x\n",
> > +				OPT3001_HIGH_LIMIT);
> > +		return ret;
> > +	}
> > +
> > +	opt->high_thresh_mantissa = OPT3001_REG_MANTISSA(ret);
> > +	opt->high_thresh_exp = OPT3001_REG_EXPONENT(ret);
> > +
> > +	return 0;
> > +}
> > +
> > +static irqreturn_t opt3001_irq(int irq, void *_opt)
> > +{
> 
> Pass the iio_dev in to the irq setup instead of opt and
> you will then not need the opt reference to the struct iio_dev.

What's the difference ? When used this way we have:

	struct opt3001 *opt = _opt;
	struct iio_dev *iio = opt->dev;

Done your way we will have:

	struct iio_dev *iio = _iio;
	struct opt3001 *opt = iio_priv(iio);

I don't see the benefit of changing this as I'll access to both opt and
iio.

> > +static int opt3001_probe(struct i2c_client *client,
> > +		const struct i2c_device_id *id)
> > +{
> > +	struct device *dev = &client->dev;
> > +
> > +	struct iio_dev *iio;
> > +	struct opt3001 *opt;
> > +	int irq = client->irq;
> > +	int ret;
> > +
> > +	iio = devm_iio_device_alloc(dev, sizeof(*opt));
> > +	if (!iio)
> > +		return -ENOMEM;
> > +
> > +	opt = iio_priv(iio);
> > +	opt->client = client;
> > +	opt->dev = dev;
> 
> This is very circular and it rarely makes sense to have
> the device private structure have a reference to the
> iio_dev.  Here it is only used in the interrupt handler.
> Avoid that by making the private data passed to the interrupt
> handler the struct iio_dev instead of the struct opt3001.
> 
> > +	opt->iio = iio;
> > +
> > +	mutex_init(&opt->lock);
> > +	i2c_set_clientdata(client, opt);
> > +
> > +	ret = opt3001_read_id(opt);
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret = opt3001_configure(opt);
> > +	if (ret)
> > +		return ret;
> > +
> > +	iio->name = client->name;
> > +	iio->channels = opt3001_channels;
> > +	iio->num_channels = ARRAY_SIZE(opt3001_channels);
> > +	iio->dev.parent = dev;
> > +	iio->modes = INDIO_DIRECT_MODE;
> > +	iio->info = &opt3001_info;
> > +
> > +	ret = devm_iio_device_register(dev, iio);
> > +	if (ret) {
> > +		dev_err(dev, "failed to register IIO device\n");
> > +		return ret;
> > +	}
> > +
> Normally we'd expect the devm_iio_device_register (that provides the
> userspace interfaces) to be after the irq request.

that's wrong. By the time the IRQ is requested, an IRQ can actually fire
and you better have a valid e.g. iio_dev by then.

-- 
balbi

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

  reply	other threads:[~2014-08-07 14:28 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-08-06 16:10 [RFC/PATCH] iio: light: add support for TI's opt3001 ligth sensor Felipe Balbi
2014-08-06 21:11 ` Peter Meerwald
2014-08-06 21:42   ` Felipe Balbi
2014-08-06 22:09     ` Peter Meerwald
2014-08-06 22:18       ` Felipe Balbi
2014-08-06 22:25         ` Peter Meerwald
2014-08-06 22:30           ` Felipe Balbi
2014-08-06 22:35             ` Peter Meerwald
2014-08-06 22:38               ` Felipe Balbi
2014-08-07 10:13                 ` Jonathan Cameron
2014-08-07 10:08     ` Jonathan Cameron
2014-08-07 14:39       ` Felipe Balbi
2014-08-07 16:27         ` Jonathan Cameron
2014-08-07 16:36           ` Felipe Balbi
2014-08-07 17:00             ` Jonathan Cameron
2014-08-07 10:01 ` Jonathan Cameron
2014-08-07 14:28   ` Felipe Balbi [this message]
2014-08-07 16:26     ` Jonathan Cameron
2014-08-07 16:30       ` Jonathan Cameron
2014-08-07 16:35       ` Felipe Balbi
2014-08-07 16:58         ` Jonathan Cameron
2014-08-07 17:54           ` Felipe Balbi
2014-08-11 14:34 ` [RFC/PATCH v2] " Felipe Balbi
2014-08-11 14:46   ` Peter Meerwald
2014-08-11 14:58     ` Felipe Balbi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20140807142829.GA24581@saruman.home \
    --to=balbi@ti.com \
    --cc=jic23@kernel.org \
    --cc=linux-iio@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).