Software reset feature is not implemented as it causes I2C bus error,
the I2C bus driver throws an ugly error message.
That's unfortunate and perhaps something we should consider fixing
at the i2c layer. Could you point to where it happens?
We have a lot of drivers where reset causes an error (Ack missing normally
due to simple state machines in the devices).
This device is similar to LTRF216A.
Could not locate the Lux calculations from datasheet, only exporting
raw values.
Ah. That's annoying as userspace is generally not able to do much with
the raw values. Any other known code supporting this device that you
can raid for info?
If not, then this ist he best we can do.
Answers are in below comment.
Reading of the Status register clears the Data Ready and the Interrupt
Status flags. It makes it tricky to read oneshot values together with
interrupts enabled as the IRQ handler clears the status on receipt
of an interrupt signal.
Not checking the status in IRQ handler will make the interrupt line
unsharable and it does not reset the interrupt line if the Interrupt
status flag is not cleared.
Definitely need to check it but I'm not sure I follow why you can't
use it for both purposes with a slightly complex interrupt handler design.
Maybe the code makes it clear what the issue is here.
If I have a flag in the state structure for this, then a timestamp would+
+static int apds9306_read_data(struct apds9306_data *data, int *val, int reg)
+{
+ struct device *dev = &data->client->dev;
+ int ret, delay, status, int_en;
+ int retries = 4;
+ u8 buff[3];
+
+ ret = apds9306_runtime_power(data, 1);
+ if (ret)
+ return ret;
+
+ /*
+ * Whichever is greater - integration time period or
+ * sampling period.
+ */
+ delay = max(apds9306_intg_time[data->intg_time_idx][1],
+ apds9306_repeat_rate_period[data->repeat_rate_idx]);
+
+ /*
+ * If interrupts are enabled then Status resistor cannot be
+ * relied upon as all the status bits are cleared by the
+ * interrupt handler in case of an event.
Ah. I was assuming sane hardware (always an error :) that would issue
an interrupt on the data being ready. I think we can make this work
but it is ugly. Add some flags to the state structure. Then whenever
you read this register, set whether the two status flags are set of not.
Thus in the interrupt handler you can tell if this got there first and
here you can tell if the interrupt handler got their first.
One messy corner. A status read resets the interrupt line, potentially before
we saw the interrupt. Oh goody - normally this silliness only happens as
a result of complex interrupt migration or errata. However it is understood
what to do about it.
If you see the interrupt status flag here, you have no way of knowing
if the interrupt line was high for long enough that the interrupt controller
saw it. As such your only option is to assume it didn't and inject an extra
one. Given a passing of the threshold could in theory have been noisy enough
to trigger two actual interrupts very close together userspace should be fine
with the extra event - we probably just wasted some cycles doing something twice.
The annoying bit will be testing as these races will be somewhat rare.
+ */
+ ret = regmap_field_read(data->regfield_int_en, &int_en);
+ if (ret) {
+ dev_err(dev, "read interrupt status failed: %d\n", ret);
+ return ret;
+ }
+
+ if (!int_en) {
+ while (retries--) {
+ ret = regmap_read(data->regmap, APDS9306_MAIN_STATUS,
+ &status);
+ if (ret) {
+ dev_err(dev, "read status failed: %d\n", ret);
+ return ret;
+ }
+ if (status & APDS9306_ALS_DATA_STAT_MASK)
+ break;
+ /*
+ * In case of continuous one-shot read from userspace,
+ * new data is available after sampling period.
+ * Delays are in the range of 25ms to 2secs.
+ */
+ fsleep(delay);
+ }
+ } else
+ fsleep(delay);
+
+ if (!retries)
+ return -EBUSY;
+
+ ret = regmap_bulk_read(data->regmap, reg, buff, sizeof(buff));
+ if (ret) {
+ dev_err(&data->client->dev, "read data failed\n");
+ return ret;
+ }
+
+ *val = get_unaligned_le24(&buff[0]);
+
+ ret = apds9306_runtime_power(data, 0);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+