iio: sensors: NVS 0.7 drivers
Erik Lilliebjerg [Mon, 4 Aug 2014 11:02:38 +0000 (04:02 -0700)]
- Fix register programming for gyro.
- Fix CM321x lux reporting.
- Fix interrupt disable deadlock using nosync.
- Add settled lux reporting.
- Add configurable timestamps (IIO vs ktime) for all NVS IIO drivers.

Bug 200024247
Bug 1537420
Bug 200023257
Bug 200025240
Bug 200022234
Bug 200017074

Change-Id: Id9f8aa4147afef5aa685a7c67d129b797ae729a4
Signed-off-by: Erik Lilliebjerg <elilliebjerg@nvidia.com>
Reviewed-on: http://git-master/r/450917
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Xiaohui Tao <xtao@nvidia.com>
Reviewed-by: Mitch Luban <mluban@nvidia.com>

drivers/iio/imu/nvi_mpu/invensense.c
drivers/iio/imu/nvi_mpu/nvi.c
drivers/iio/imu/nvi_mpu/nvi.h
drivers/iio/light/nvs_cm3217.c
drivers/iio/light/nvs_cm3218.c
drivers/iio/light/nvs_max4400x.c
drivers/iio/magnetometer/nvi_ak89xx.c
drivers/iio/pressure/nvi_bmpX80.c

index ffbfad3..2880095 100644 (file)
@@ -1385,7 +1385,7 @@ int inv_read_pedometer_counter(struct nvi_state *st)
        curr_counter = (u32)be32_to_cpup((__be32 *)(d));
        if (0 != last_step_counter)
                /* get_time_ns() - */
-               st->ped.last_step_time = iio_get_time_ns() -
+               st->ped.last_step_time = nvi_get_time_ns(st) -
                        ((u64)(curr_counter - last_step_counter)) *
                        DMP_INTERVAL_INIT;
 
index e9eca01..9388a4d 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/slab.h>
 #include <linux/err.h>
 #include <linux/delay.h>
+#include <linux/ktime.h>
 #include <linux/workqueue.h>
 #include <linux/interrupt.h>
 #include <linux/regulator/consumer.h>
@@ -27,7 +28,7 @@
 
 #include "nvi.h"
 
-#define NVI_DRIVER_VERSION             (101)
+#define NVI_DRIVER_VERSION             (102)
 #define NVI_NAME                       "mpu6xxx"
 #define NVI_NAME_MPU6050               "MPU6050"
 #define NVI_NAME_MPU6500               "MPU6500"
@@ -99,6 +100,9 @@ static unsigned short nvi_i2c_addrs[] = {
        0x69,
 };
 
+static struct iio_dev *indio_dev_local;
+
+
 static int nvi_nb_vreg(struct nvi_state *st,
                       unsigned long event, unsigned int i);
 
@@ -124,7 +128,16 @@ static int (* const nvi_nb_vreg_pf[])(struct notifier_block *nb,
        nvi_nb_vreg_vlogic,
 };
 
-static struct iio_dev *indio_dev_local;
+s64 nvi_get_time_ns(struct nvi_state *st)
+{
+       struct timespec ts;
+
+       if (st->iio_ts_en)
+               return iio_get_time_ns();
+
+       ktime_get_ts(&ts);
+       return timespec_to_ns(&ts);
+}
 
 static void nvi_err(struct nvi_state *st)
 {
@@ -386,7 +399,6 @@ static int nvi_wr_gyro_config1(struct nvi_state *st, u8 val)
                                         st->hal->reg->gyro_config1.reg,
                                         st->rc.gyro_config1, val);
                        st->rc.gyro_config1 = val;
-                       ret = 1; /* flag change made */
                }
        }
        return ret;
@@ -449,7 +461,6 @@ int nvi_wr_gyro_config(struct nvi_state *st, u8 test, u8 fsr, u8 lpf)
                                         st->hal->reg->gyro_config2.reg,
                                         st->rc.gyro_config2, val);
                        st->rc.gyro_config2 = val;
-                       ret = 1; /* flag change made */
                }
        }
        return ret;
@@ -478,7 +489,6 @@ int nvi_wr_accel_config2(struct nvi_state *st, u8 val)
                                         st->hal->reg->accel_config2.reg,
                                         st->rc.accel_config2, val);
                        st->rc.accel_config2 = val;
-                       ret = 1; /* flag change made */
                }
        }
        return ret;
@@ -543,7 +553,6 @@ int nvi_wr_accel_config(struct nvi_state *st, u8 test, u8 fsr, u8 lpf)
                                         st->hal->reg->accel_config.reg,
                                         st->rc.accel_config, val);
                        st->rc.accel_config = val;
-                       ret = 1; /* flag change made */
                }
        }
        return ret;
@@ -1255,12 +1264,12 @@ static int nvi_vreg_init(struct nvi_state *st)
                        ret = PTR_ERR(st->vreg[i].consumer);
                        dev_err(&st->i2c->dev, "%s ERR %d for %s\n",
                                __func__, ret, st->vreg[i].supply);
-                       st->vreg_en_ts[i] = iio_get_time_ns();
+                       st->vreg_en_ts[i] = nvi_get_time_ns(st);
                        st->vreg[i].consumer = NULL;
                } else {
                        ret = regulator_is_enabled(st->vreg[i].consumer);
                        if (ret > 0)
-                               st->vreg_en_ts[i] = iio_get_time_ns();
+                               st->vreg_en_ts[i] = nvi_get_time_ns(st);
                        else
                                st->vreg_en_ts[i] = 0;
                        st->nb_vreg[i].notifier_call = nvi_nb_vreg_pf[i];
@@ -1278,7 +1287,7 @@ static int nvi_nb_vreg(struct nvi_state *st,
                       unsigned long event, unsigned int i)
 {
        if (event & REGULATOR_EVENT_POST_ENABLE)
-               st->vreg_en_ts[i] = iio_get_time_ns();
+               st->vreg_en_ts[i] = nvi_get_time_ns(st);
        else if (event & (REGULATOR_EVENT_DISABLE |
                          REGULATOR_EVENT_FORCE_DISABLE))
                st->vreg_en_ts[i] = 0;
@@ -1304,7 +1313,7 @@ int nvi_pm_wr(struct nvi_state *st, u8 pwr_mgmt_1, u8 pwr_mgmt_2, u8 lp)
                st->rc_dis = true;
                delay_ms = 0;
                for (i = 0; i < ARRAY_SIZE(nvi_vregs); i++) {
-                       por_ns = iio_get_time_ns() - st->vreg_en_ts[i];
+                       por_ns = nvi_get_time_ns(st) - st->vreg_en_ts[i];
                        if ((por_ns < 0) || (!st->vreg_en_ts[i])) {
                                delay_ms = (POR_MS * 1000000);
                                break;
@@ -1528,7 +1537,7 @@ int nvi_pm(struct nvi_state *st, int pm_req)
                if (pm > NVI_PM_ON_CYCLE)
                        nvi_user_ctrl_en(st, true, true);
                if ((pm == NVI_PM_ON_FULL) && (!st->push_ts))
-                       st->push_ts = iio_get_time_ns() +
+                       st->push_ts = nvi_get_time_ns(st) +
                                           st->chip_config.gyro_start_delay_ns;
        } else {
                /* interrupts are disabled until NVI_PM_AUTO */
@@ -1655,6 +1664,7 @@ static int nvi_dev_delay(struct nvi_state *st, unsigned int dev)
                                break;
                }
        }
+
        if (st->dbg)
                dev_info(&st->i2c->dev, "%s dev=%u delay=%u\n",
                         __func__, dev, st->smplrt_delay_us[dev]);
@@ -2049,7 +2059,7 @@ static int nvi_reset(struct nvi_state *st,
                spin_lock_irqsave(&st->time_stamp_lock, flags);
                kfifo_reset(&st->timestamps);
                spin_unlock_irqrestore(&st->time_stamp_lock, flags);
-               st->fifo_ts = iio_get_time_ns();
+               st->fifo_ts = nvi_get_time_ns(st);
        }
        if (irq)
                ret |= nvi_int_able(st, true);
@@ -2128,13 +2138,13 @@ static int nvi_aux_bypass_request(struct nvi_state *st, bool enable)
        int ret = 0;
 
        if ((bool)(st->rc.int_pin_cfg & BIT_BYPASS_EN) == enable) {
-               st->aux.bypass_timeout_ns = iio_get_time_ns();
+               st->aux.bypass_timeout_ns = nvi_get_time_ns(st);
                st->aux.bypass_lock++;
                if (!st->aux.bypass_lock)
                        dev_err(&st->i2c->dev, "%s rollover ERR\n", __func__);
        } else {
                if (st->aux.bypass_lock) {
-                       ns = iio_get_time_ns() - st->aux.bypass_timeout_ns;
+                       ns = nvi_get_time_ns(st) - st->aux.bypass_timeout_ns;
                        to = st->chip_config.bypass_timeout_ms * 1000000;
                        if (ns > to)
                                st->aux.bypass_lock = 0;
@@ -2855,7 +2865,7 @@ static irqreturn_t nvi_irq_thread(int irq, void *dev_id)
 
        mutex_lock(&st->srlock);
        mutex_lock(&indio_dev->mlock);
-       ts = iio_get_time_ns();
+       ts = nvi_get_time_ns(st);
        /* if only accelermeter data */
        if (st->rc.pwr_mgmt_1 & BIT_CYCLE) {
                ret = nvi_accel_read(st, ts);
@@ -3070,7 +3080,7 @@ static irqreturn_t nvi_irq_handler(int irq, void *dev_id)
        s64 ts = 0;
 
        if (!(st->master_enable & (1 << DEV_DMP))) {
-               ts = iio_get_time_ns();
+               ts = nvi_get_time_ns(st);
                kfifo_in_spinlocked(&st->timestamps, &ts, 1,
                                    &st->time_stamp_lock);
                if (kfifo_is_full(&st->timestamps)) {
@@ -4316,8 +4326,8 @@ static struct nvi_rr nvi_rr_accel[] = {
        /* all accelerometer values are in g's */
        {
                .max_range              = {
-                       .ival           = 2,
-                       .micro          = 0,
+                       .ival           = 19,
+                       .micro          = 613300,
                },
                .resolution             = {
                        .ival           = 0,
@@ -4326,8 +4336,8 @@ static struct nvi_rr nvi_rr_accel[] = {
        },
        {
                .max_range              = {
-                       .ival           = 4,
-                       .micro          = 0,
+                       .ival           = 39,
+                       .micro          = 226600,
                },
                .resolution             = {
                        .ival           = 0,
@@ -4336,8 +4346,8 @@ static struct nvi_rr nvi_rr_accel[] = {
        },
        {
                .max_range              = {
-                       .ival           = 8,
-                       .micro          = 0,
+                       .ival           = 78,
+                       .micro          = 453200,
                },
                .resolution             = {
                        .ival           = 0,
@@ -4346,8 +4356,8 @@ static struct nvi_rr nvi_rr_accel[] = {
        },
        {
                .max_range              = {
-                       .ival           = 16,
-                       .micro          = 0,
+                       .ival           = 156,
+                       .micro          = 906400,
                },
                .resolution             = {
                        .ival           = 0,
@@ -4357,44 +4367,45 @@ static struct nvi_rr nvi_rr_accel[] = {
 };
 
 static struct nvi_rr nvi_rr_anglvel[] = {
+       /* rad / sec */
        {
                .max_range              = {
-                       .ival           = 250, /* degrees / sec */
-                       .micro          = 0,
+                       .ival           = 4,
+                       .micro          = 363323,
                },
                .resolution             = {
                        .ival           = 0,
-                       .micro          = 133, /* rad / sec */
+                       .micro          = 133,
                },
        },
        {
                .max_range              = {
-                       .ival           = 500, /* degrees / sec */
-                       .micro          = 0,
+                       .ival           = 8,
+                       .micro          = 726646,
                },
                .resolution             = {
                        .ival           = 0,
-                       .micro          = 266, /* rad / sec */
+                       .micro          = 266,
                },
        },
        {
                .max_range              = {
-                       .ival           = 1000, /* degrees / sec */
-                       .micro          = 0,
+                       .ival           = 17,
+                       .micro          = 453292,
                },
                .resolution             = {
                        .ival           = 0,
-                       .micro          = 533, /* rad / sec */
+                       .micro          = 533,
                },
        },
        {
                .max_range              = {
-                       .ival           = 2000, /* degrees / sec */
-                       .micro          = 0,
+                       .ival           = 34,
+                       .micro          = 906585,
                },
                .resolution             = {
                        .ival           = 0,
-                       .micro          = 1065, /* rad / sec */
+                       .micro          = 1065,
                },
        },
 };
@@ -4413,7 +4424,7 @@ static struct nvi_rr nvi_rr_temp[] = {
 };
 
 static const struct nvi_hal_dev nvi_hal_6050_accel = {
-       .version                        = 2,
+       .version                        = 3,
        .selftest_scale                 = 8,
        .rr                             = nvi_rr_accel,
        .scale                          = {
@@ -4428,7 +4439,7 @@ static const struct nvi_hal_dev nvi_hal_6050_accel = {
 };
 
 static const struct nvi_hal_dev nvi_hal_6050_anglvel = {
-       .version                        = 2,
+       .version                        = 3,
        .selftest_scale                 = 250,
        .rr                             = nvi_rr_anglvel,
        .scale                          = {
@@ -4643,7 +4654,7 @@ static const unsigned long nvi_lpa_delay_us_tbl_6500[] = {
 };
 
 static const struct nvi_hal_dev nvi_hal_6500_accel = {
-       .version                        = 2,
+       .version                        = 3,
        .selftest_scale                 = 2,
        .rr                             = nvi_rr_accel,
        .scale                          = {
@@ -5345,7 +5356,7 @@ static void nvi_shutdown(struct i2c_client *client)
        mutex_lock(&indio_dev->mlock);
        st->shutdown = true;
        if (st->i2c->irq)
-               disable_irq(st->i2c->irq);
+               disable_irq_nosync(st->i2c->irq);
        if (st->hal)
                nvi_pm(st, NVI_PM_OFF);
        mutex_unlock(&indio_dev->mlock);
@@ -5388,6 +5399,9 @@ static int nvi_of_dt(struct i2c_client *client, struct nvi_state *st)
        u32 tmp;
        int len;
 
+       /* common NVS programmable parameters */
+       st->iio_ts_en = of_property_read_bool(dn, "invensense,iio_timestamps");
+       /* device specific parameters */
        pchar = of_get_property(dn, "invensense,matrix", &len);
        if (pchar && len == sizeof(st->pdata.orientation)) {
                memcpy(&st->pdata.orientation, pchar, len);
index 044fc23..d983ae3 100644 (file)
@@ -538,6 +538,7 @@ struct nvi_state {
        unsigned int batch_period_us[DEV_N]; /* batch period us */
        unsigned int batch_timeout_ms[DEV_N]; /* batch timeout ms */
        unsigned short i2c_addr;        /* I2C address */
+       bool iio_ts_en;                 /* use IIO timestamps */
        bool shutdown;
        bool suspend;
        bool flush;
@@ -578,6 +579,7 @@ struct nvi_state {
 #endif /* NVI_I2C_DEBUG_INTERFACE */
 };
 
+s64 nvi_get_time_ns(struct nvi_state *st);
 int nvi_i2c_read(struct nvi_state *st, u16 addr, u8 reg, u16 len, u8 *buf);
 int nvi_i2c_rd(struct nvi_state *st, u8 bank, u8 reg, u16 len, u8 *buf);
 int nvi_i2c_write(struct nvi_state *st, u16 addr, u16 len, u8 *buf);
index 9199b59..abfc1a6 100644 (file)
 #include <linux/slab.h>
 #include <linux/err.h>
 #include <linux/delay.h>
+#include <linux/ktime.h>
 #include <linux/regulator/consumer.h>
 #include <linux/workqueue.h>
 #include <linux/of.h>
 #include <linux/iio/kfifo_buf.h>
 #include <linux/iio/trigger.h>
 
-#define CM_VERSION_DRIVER              (101)
+#define CM_VERSION_DRIVER              (102)
 #define CM_VENDOR                      "Capella Microsystems, Inc."
 #define CM_NAME                                "cm3217"
+/* setting _REPORT_N to 2 causes an extra reading after crossing the threshold
+ * allowing a more accurate settled reported value.
+ */
+#define CM_REPORT_N                    (2)
 #define CM_LIGHT_VERSION               (1)
 #define CM_LIGHT_MAX_RANGE_IVAL                (119154)
 #define CM_LIGHT_MAX_RANGE_MICRO       (420000)
@@ -224,6 +229,7 @@ struct cm_state {
        unsigned int it_i_lo;           /* integration time index low limit */
        unsigned int it_i_hi;           /* integration time index high limit */
        unsigned long mult;             /* used to calc lux from HW values */
+       bool iio_ts_en;                 /* use IIO timestamps */
        bool shutdown;                  /* shutdown active flag */
        bool suspend;                   /* suspend active flag */
        bool hw_change;                 /* HW changed so drop first sample */
@@ -235,6 +241,17 @@ struct cm_state {
 };
 
 
+static s64 cm_get_time_ns(struct cm_state *st)
+{
+       struct timespec ts;
+
+       if (st->iio_ts_en)
+               return iio_get_time_ns();
+
+       ktime_get_ts(&ts);
+       return timespec_to_ns(&ts);
+}
+
 static void cm_err(struct cm_state *st)
 {
        st->errs++;
@@ -476,22 +493,23 @@ static void cm_buf_push(struct iio_dev *indio_dev, struct cm_state *st)
        unsigned int i;
        unsigned int bytes = 0;
 
+       if (!iio_buffer_enabled(indio_dev))
+               return;
+
        if (iio_scan_mask_query(indio_dev, indio_dev->buffer, CM_SCAN_LIGHT)) {
                n = sizeof(st->light);
                i = cm_buf_index(n, &bytes);
                memcpy(&buf[i], &st->light, n);
+               if (st->dbg & CM_DBG_SPEW_LIGHT)
+                       dev_info(&st->i2c->dev, "light %u %lld\n",
+                                st->light, st->ts);
        }
        if (indio_dev->buffer->scan_timestamp) {
                n = sizeof(st->ts);
                i = cm_buf_index(n, &bytes);
                memcpy(&buf[i], &st->ts, n);
        }
-       if (iio_buffer_enabled(indio_dev)) {
-               iio_push_to_buffers(indio_dev, buf);
-               if (st->dbg & CM_DBG_SPEW_LIGHT)
-                       dev_info(&st->i2c->dev, "light %u %lld\n",
-                                st->light, st->ts);
-       }
+       iio_push_to_buffers(indio_dev, buf);
 }
 
 static int cm_cmd_wr(struct cm_state *st, u8 it_t, u8 fd_it)
@@ -575,10 +593,8 @@ static int cm_interpolate(int x1, int x2, int x3, int y1, int *y2, int y3)
 
 static void cm_report_init(struct cm_state *st)
 {
-       if (st->report_n)
-               st->report = st->report_n;
-       else
-               st->report = 1;
+       st->ts = 0;
+       st->report = st->report_n;
 }
 
 static int cm_rd(struct iio_dev *indio_dev)
@@ -588,17 +604,16 @@ static int cm_rd(struct iio_dev *indio_dev)
        u32 lux;
        s64 ts;
        s64 ts_elapsed;
-       bool report = false;
        bool t_min = false;
        unsigned long calc;
-       unsigned int ms;
        int ret;
 
-       if (st->hw_change && !st->report)
+       if (st->hw_change && !st->report) {
                /* drop first sample after HW change */
+               st->hw_change = false;
                return 0;
+       }
 
-       st->hw_change = false;
        ret = cm_i2c_rd(st, &step);
        if (ret)
                return ret;
@@ -625,34 +640,26 @@ static int cm_rd(struct iio_dev *indio_dev)
                st->hw_sync = false;
                cm_delay(st, false);
        }
-       if (st->lux_thr_lo || st->lux_thr_hi) {
-               if (lux > (st->light + st->lux_thr_hi)) {
-                       report = true;
-               } else if (st->light > st->lux_thr_lo) {
-                       if (lux < (st->light - st->lux_thr_lo))
-                               report = true;
-               }
-       } else {
-               report = true;
+       if (lux > (st->light + st->lux_thr_hi)) {
+               st->report = st->report_n;
+       } else if (st->light > st->lux_thr_lo) {
+               if (lux < (st->light - st->lux_thr_lo))
+                       st->report = st->report_n;
        }
-       ts = iio_get_time_ns();
+       ts = cm_get_time_ns(st);
        ts_elapsed = ts - st->ts;
        if (ts_elapsed >= st->poll_delay_ms * 1000000)
                t_min = true;
-       ms = ts_elapsed;
-       ms /= 1000000;
        if (st->dbg & CM_DBG_SPEW_LIGHT_POLL)
-               dev_info(&st->i2c->dev, "poll light %d %lld  diff: %d %ums\n",
-                        lux, ts, lux - st->light, ms);
-       /* if IIO scale == 1 then in calibrate/test mode */
-       if ((st->scale_val == 1) && !st->scale_val2)
-               cm_report_init(st);
-       /* report if:
-        * - st->report (usually used to report the first sample regardless)
-        * - time since last report >= polling delay && data outside thresholds
-        * - report regardless of change (for test and calibration)
-        */
-       if (st->report || (report && t_min)) {
+               dev_info(&st->i2c->dev,
+                        "poll light %d %lld  diff: %d %lldns\n",
+                        lux, ts, lux - st->light, ts_elapsed);
+       if ((st->report && t_min) || ((st->scale_val == 1) &&
+                                     !st->scale_val2)) {
+               /* report if:
+                * - st->report && time since last report >= polling delay
+                * - in calibration mode (scale == 1)
+                */
                if (st->report)
                        st->report--;
                if (!(st->dbg & CM_DBG_VAL_LIGHT))
@@ -670,7 +677,7 @@ static void cm_read(struct iio_dev *indio_dev)
 
        mutex_lock(&indio_dev->mlock);
        if (st->enable) {
-               ret = cm_rd(indio_dev);
+               cm_rd(indio_dev);
                schedule_delayed_work(&st->dw,
                                      msecs_to_jiffies(st->queue_delay_ms));
        }
@@ -709,13 +716,12 @@ static int cm_enable(struct iio_dev *indio_dev)
        if (iio_scan_mask_query(indio_dev, indio_dev->buffer, CM_SCAN_LIGHT)) {
                ret = cm_pm(st, true);
                if (!ret) {
-                       cm_report_init(st);
                        ret = cm_it_wr(st, 0);
-                       cm_delay(st, true);
                        if (ret) {
                                cm_disable(indio_dev);
                        } else {
-                               st->ts = iio_get_time_ns();
+                               cm_delay(st, true);
+                               cm_report_init(st);
                                st->enable = 1;
                                schedule_delayed_work(&st->dw,
                                             msecs_to_jiffies(CM_HW_DELAY_MS));
@@ -899,7 +905,7 @@ static ssize_t cm_data_show(struct device *dev,
 
        case CM_INFO_DBG:
                return sprintf(buf, "debug spew=%x\n",
-                              st->dbg & CM_DBG_SPEW_MSG);
+                              !!(st->dbg & CM_DBG_SPEW_MSG));
 
        case CM_INFO_LIGHT_SPEW:
                return sprintf(buf, "lux_ts spew=%x\n",
@@ -1042,7 +1048,7 @@ static int cm_write_raw(struct iio_dev *indio_dev,
                msg = "IIO_CHAN_INFO_RAW";
                old = st->light;
                st->light = val;
-               st->ts = iio_get_time_ns();
+               st->ts = cm_get_time_ns(st);
                st->dbg |= CM_DBG_VAL_LIGHT;
                cm_buf_push(indio_dev, st);
                break;
@@ -1215,7 +1221,7 @@ static int cm_remove(struct i2c_client *client)
 
 static int cm_of_dt(struct i2c_client *client, struct cm_state *st)
 {
-       struct device_node *np = client->dev.of_node;
+       struct device_node *dn = client->dev.of_node;
        unsigned int val;
        unsigned int i;
 
@@ -1230,30 +1236,32 @@ static int cm_of_dt(struct i2c_client *client, struct cm_state *st)
        st->it_i_hi = ARRAY_SIZE(cm_it_tbl);
        /* device tree parameters */
        if (client->dev.of_node) {
+               /* common NVS programmable parameters */
+               st->iio_ts_en = of_property_read_bool(dn, "iio_timestamps");
+               of_property_read_u32(dn, "report_count", &st->report_n);
                /* common NVS ALS programmable parameters */
-               of_property_read_u32(np, "report_count", &st->report_n);
-               of_property_read_s32(np, "light_uncalibrated_lo",
+               of_property_read_s32(dn, "light_uncalibrated_lo",
                                     &st->lux_uc_lo);
-               of_property_read_s32(np, "light_uncalibrated_hi",
+               of_property_read_s32(dn, "light_uncalibrated_hi",
                                     &st->lux_uc_hi);
-               of_property_read_s32(np, "light_calibrated_lo",
+               of_property_read_s32(dn, "light_calibrated_lo",
                                     &st->lux_c_lo);
-               of_property_read_s32(np, "light_calibrated_hi",
+               of_property_read_s32(dn, "light_calibrated_hi",
                                     &st->lux_c_hi);
-               of_property_read_s32(np, "light_scale_val",
+               of_property_read_s32(dn, "light_scale_val",
                                     &st->scale_val);
-               of_property_read_s32(np, "light_scale_val2",
+               of_property_read_s32(dn, "light_scale_val2",
                                     &st->scale_val2);
-               of_property_read_s32(np, "light_offset_val",
+               of_property_read_s32(dn, "light_offset_val",
                                     &st->offset_val);
-               of_property_read_s32(np, "light_offset_val2",
+               of_property_read_s32(dn, "light_offset_val2",
                                     &st->offset_val2);
-               if (of_property_read_u32(np, "light_threshold_lo", &val))
+               if (of_property_read_u32(dn, "light_threshold_lo", &val))
                        st->lux_thr_lo = val;
-               if (of_property_read_u32(np, "light_threshold_hi", &val))
+               if (of_property_read_u32(dn, "light_threshold_hi", &val))
                        st->lux_thr_hi = val;
                /* this device supports these programmable parameters */
-               if (of_property_read_u32(np, "light_integration_time_ms_lo",
+               if (of_property_read_u32(dn, "light_integration_time_ms_lo",
                                         &val)) {
                        for (i = ARRAY_SIZE(cm_it_tbl); i > 1; i--) {
                                if (val <= cm_it_tbl[i - 1].ms)
@@ -1261,7 +1269,7 @@ static int cm_of_dt(struct i2c_client *client, struct cm_state *st)
                        }
                        st->it_i_hi = i;
                }
-               if (of_property_read_u32(np, "light_integration_time_ms_hi",
+               if (of_property_read_u32(dn, "light_integration_time_ms_hi",
                                         &val)) {
                        for (i = 0; i < ARRAY_SIZE(cm_it_tbl) - 1; i++) {
                                if (val >= cm_it_tbl[i].ms)
@@ -1277,6 +1285,8 @@ static int cm_of_dt(struct i2c_client *client, struct cm_state *st)
                        st->it_i_hi = ARRAY_SIZE(cm_it_tbl);
                }
        }
+       if (!st->report_n)
+               st->report_n = CM_REPORT_N;
        return 0;
 }
 
index 97dd407..a830edf 100644 (file)
 #include <linux/slab.h>
 #include <linux/err.h>
 #include <linux/delay.h>
+#include <linux/ktime.h>
 #include <linux/regulator/consumer.h>
 #include <linux/workqueue.h>
 #include <linux/interrupt.h>
 #include <linux/iio/kfifo_buf.h>
 #include <linux/iio/trigger.h>
 
-#define CM_VERSION_DRIVER              (101)
+#define CM_VERSION_DRIVER              (102)
 #define CM_VENDOR                      "Capella Microsystems, Inc."
 #define CM_NAME                                "cm3218x"
 #define CM_NAME_CM3218                 "cm3218"
 #define CM_NAME_CM32181                        "cm32181"
+/* setting _REPORT_N to 2 causes an extra reading after crossing the threshold
+ * allowing a more accurate settled reported value.
+ */
+#define CM_REPORT_N                    (2)
 #define CM_LIGHT_VERSION               (1)
 #define CM_LIGHT_MAX_RANGE_IVAL                (119156)
 #define CM_LIGHT_MAX_RANGE_MICRO       (0)
 #define CM_DEVID_CM32181               (0x02)
 #define CM_HW_DELAY_MS                 (10)
 #define CM_POLL_DELAY_MS_DFLT          (2000)
-#define CM_THRESHOLD_LUX_DFLT          (10)
+#define CM_THRESHOLD_LUX_DFLT          (50)
 #define CM_ALS_SM_DFLT                 (0x01)
 #define CM_ALS_PERS_DFLT               (0x00)
 #define CM_ALS_PSM_DFLT                        (0x07)
@@ -195,17 +200,17 @@ static unsigned short cm_i2c_addrs[] = {
 
 struct cm_it {                         /* integration time */
        unsigned int ms;                /* time ms */
-       unsigned int resolution;        /* lux/bit * 1000 */
+       unsigned int resolution;        /* lux/bit to be scaled by val2 */
        u8 als_it;                      /* FD_IT HW */
 };
 
 static struct cm_it cm_it_tbl[] = {
-       { 800, 5, 0x03 },
-       { 400, 10, 0x02 },
-       { 200, 21, 0x01 },
-       { 100, 42, 0x00 },
-       { 50, 84, 0x08 },
-       { 25, 168, 0x0C }
+       { 800, 5000, 0x03 },            /* 0.005 lux / LSb */
+       { 400, 10000, 0x02 },           /* 0.010 lux / LSb */
+       { 200, 21000, 0x01 },           /* 0.021 lux / LSb */
+       { 100, 42000, 0x00 },           /* 0.042 lux / LSb */
+       { 50, 84000, 0x08 },            /* 0.084 lux / LSb */
+       { 25, 167000, 0x0C }            /* 0.168 lux / LSb */
 };
 
 static unsigned int cm_psm_ms_tbl[] = {
@@ -242,6 +247,7 @@ struct cm_state {
        unsigned int it_i_lo;           /* integration time index low limit */
        unsigned int it_i_hi;           /* integration time index high limit */
        unsigned int psm_ms;            /* additional IT for PSM */
+       bool iio_ts_en;                 /* use IIO timestamps */
        bool shutdown;                  /* shutdown active flag */
        bool suspend;                   /* suspend active flag */
        bool hw_change;                 /* HW changed so drop first sample */
@@ -256,6 +262,17 @@ struct cm_state {
 };
 
 
+static s64 cm_get_time_ns(struct cm_state *st)
+{
+       struct timespec ts;
+
+       if (st->iio_ts_en)
+               return iio_get_time_ns();
+
+       ktime_get_ts(&ts);
+       return timespec_to_ns(&ts);
+}
+
 static void cm_err(struct cm_state *st)
 {
        st->errs++;
@@ -289,17 +306,19 @@ static int cm_i2c_wr(struct cm_state *st, u8 reg, u16 val)
        struct i2c_msg msg;
        u8 buf[3];
 
-       buf[0] = reg;
-       val = cpu_to_le16(val);
-       buf[1] = val & 0xFF;
-       buf[2] = val >> 8;
-       msg.addr = st->i2c_addr;
-       msg.flags = 0;
-       msg.len = 3;
-       msg.buf = buf;
-       if (i2c_transfer(st->i2c->adapter, &msg, 1) != 1) {
-               cm_err(st);
-               return -EIO;
+       if (st->i2c_addr) {
+               buf[0] = reg;
+               val = cpu_to_le16(val);
+               buf[1] = val & 0xFF;
+               buf[2] = val >> 8;
+               msg.addr = st->i2c_addr;
+               msg.flags = 0;
+               msg.len = 3;
+               msg.buf = buf;
+               if (i2c_transfer(st->i2c->adapter, &msg, 1) != 1) {
+                       cm_err(st);
+                       return -EIO;
+               }
        }
 
        return 0;
@@ -493,22 +512,23 @@ static void cm_buf_push(struct iio_dev *indio_dev, struct cm_state *st)
        unsigned int i;
        unsigned int bytes = 0;
 
+       if (!iio_buffer_enabled(indio_dev))
+               return;
+
        if (iio_scan_mask_query(indio_dev, indio_dev->buffer, CM_SCAN_LIGHT)) {
                n = sizeof(st->light);
                i = cm_buf_index(n, &bytes);
                memcpy(&buf[i], &st->light, n);
+               if (st->dbg & CM_DBG_SPEW_LIGHT)
+                       dev_info(&st->i2c->dev, "light %u %lld\n",
+                                st->light, st->ts);
        }
        if (indio_dev->buffer->scan_timestamp) {
                n = sizeof(st->ts);
                i = cm_buf_index(n, &bytes);
                memcpy(&buf[i], &st->ts, n);
        }
-       if (iio_buffer_enabled(indio_dev)) {
-               iio_push_to_buffers(indio_dev, buf);
-               if (st->dbg & CM_DBG_SPEW_LIGHT)
-                       dev_info(&st->i2c->dev, "light %u %lld\n",
-                                st->light, st->ts);
-       }
+       iio_push_to_buffers(indio_dev, buf);
 }
 
 static int cm_cmd_wr(struct cm_state *st, int als_it, bool irq_en)
@@ -518,7 +538,7 @@ static int cm_cmd_wr(struct cm_state *st, int als_it, bool irq_en)
 
        als_cfg = st->als_cfg;
        als_cfg |= als_it << CM_REG_CFG_ALS_IT;
-       if (irq_en)
+       if (irq_en && st->i2c->irq)
                als_cfg |= (1 << CM_REG_CFG_ALS_INT_EN);
        ret = cm_i2c_wr(st, CM_REG_CFG, als_cfg);
        if (st->dbg & CM_DBG_SPEW_MSG)
@@ -527,6 +547,29 @@ static int cm_cmd_wr(struct cm_state *st, int als_it, bool irq_en)
        return ret;
 }
 
+static int cm_it_wr(struct cm_state *st, unsigned int ms)
+{
+       unsigned int i;
+       int ret;
+
+       /* get the HW settings for integration time (IT) ms */
+       for (i = st->it_i_lo; i < st->it_i_hi; i++) {
+               if (ms >= cm_it_tbl[i].ms + st->psm_ms)
+                       break;
+       }
+       if (i >= st->it_i_hi)
+               i = (st->it_i_hi - 1);
+       ret = cm_cmd_wr(st, cm_it_tbl[i].als_it, false);
+       if (!ret) {
+               st->hw_change = true;
+               st->scale_i = i;
+       }
+       if (st->dbg & CM_DBG_SPEW_MSG)
+               dev_info(&st->i2c->dev, "%s IT=%ums + %u psm_ms  err=%d\n",
+                        __func__, cm_it_tbl[i].ms, st->psm_ms, ret);
+       return ret;
+}
+
 static void cm_delay(struct cm_state *st, bool hw_sync)
 {
        unsigned int ms;
@@ -549,50 +592,73 @@ static void cm_delay(struct cm_state *st, bool hw_sync)
                         __func__, st->queue_delay_ms);
 }
 
-static int cm_it_wr(struct cm_state *st, unsigned int ms)
+static int cm_interpolate(int x1, int x2, int x3, int y1, int *y2, int y3)
 {
-       unsigned int i;
-       int ret;
+       int dividend;
+       int divisor;
 
-       /* get the HW settings for integration time (IT) ms */
-       for (i = st->it_i_lo; i < st->it_i_hi; i++) {
-               if (ms >= cm_it_tbl[i].ms + st->psm_ms)
-                       break;
-       }
-       if (i >= st->it_i_hi)
-               i = (st->it_i_hi - 1);
-       ret = cm_cmd_wr(st, cm_it_tbl[i].als_it, false);
-       if (!ret) {
-               st->hw_change = true;
-               st->scale_i = i;
-       }
-       if (st->dbg & CM_DBG_SPEW_MSG)
-               dev_info(&st->i2c->dev, "%s IT=%ums + %u psm_ms  err=%d\n",
-                        __func__, cm_it_tbl[i].ms, st->psm_ms, ret);
-       return ret;
+       /* y2 = ((x2 - x1)(y3 - y1)/(x3 - x1)) + y1 */
+       divisor = (x3 - x1);
+       if (!divisor)
+               return -EINVAL;
+
+       dividend = (x2 - x1) * (y3 - y1);
+       *y2 = (dividend / divisor) + y1;
+       return 0;
+}
+
+static void cm_report_init(struct cm_state *st)
+{
+       st->ts = 0;
+       st->report = st->report_n;
 }
 
 static int cm_step(struct cm_state *st, u16 step, u32 lux, bool irq_en)
 {
-       u16 step_lo;
-       u16 step_hi;
+       u64 calc;
+       int thr_lo;
+       int thr_hi;
        int ret = 0;
 
-       /* lux back to HW values */
-       if (lux > st->lux_thr_lo)
-               step_lo = (lux - st->lux_thr_lo) /
-                          cm_it_tbl[st->scale_i].resolution;
-       else
-               step_lo = 0;
-       step_hi = (lux + st->lux_thr_hi) / cm_it_tbl[st->scale_i].resolution;
+       /* lux lo threshold to HW value */
+       thr_lo = lux - st->lux_thr_lo;
+       /* get the uncalibrated value */
+       cm_interpolate(st->lux_c_lo, thr_lo, st->lux_c_hi,
+                      st->lux_uc_lo, &thr_lo, st->lux_uc_hi);
+       if (thr_lo < 0)
+               thr_lo = 0;
+       /* convert to HW value */
+       calc = thr_lo;
+       if (st->scale_val2)
+               calc *= st->scale_val2;
+       if (cm_it_tbl[st->scale_i].resolution)
+               do_div(calc, cm_it_tbl[st->scale_i].resolution);
+       thr_lo = calc;
+       /* lux hi threshold to HW value */
+       thr_hi = lux + st->lux_thr_hi;
+       /* get the uncalibrated value */
+       cm_interpolate(st->lux_c_lo, thr_hi, st->lux_c_hi,
+                      st->lux_uc_lo, &thr_hi, st->lux_uc_hi);
+       /* convert to HW value */
+       calc = thr_hi;
+       if (st->scale_val2)
+               calc *= st->scale_val2;
+       if (cm_it_tbl[st->scale_i].resolution)
+               do_div(calc, cm_it_tbl[st->scale_i].resolution);
+       thr_hi = calc;
+       if (thr_hi > 0xFFFF)
+               thr_hi = 0xFFFF;
+       if (st->dbg & CM_DBG_SPEW_MSG)
+               dev_info(&st->i2c->dev, "%s lo=%d step=%hu hi=%d\n",
+                        __func__, thr_lo, step, thr_hi);
        /* adjust IT if need be to make room for HW thresholds */
-       if ((step > (0xFFFF - step_hi)) && (st->scale_i < (st->it_i_hi - 1))) {
+       if ((step > (0xFFFF - thr_hi)) && (st->scale_i < (st->it_i_hi - 1))) {
                /* too many photons - need to decrease integration time */
                ret = cm_it_wr(st, cm_it_tbl[st->scale_i + 1].ms);
                if (!ret)
                        cm_delay(st, true);
                return ret;
-       } else if ((step < step_lo) && (st->scale_i > (st->it_i_lo + 1))) {
+       } else if ((step < thr_lo) && (st->scale_i > (st->it_i_lo + 1))) {
                /* not enough photons - need to increase integration time */
                ret = cm_it_wr(st, cm_it_tbl[st->scale_i - 1].ms);
                if (!ret)
@@ -604,13 +670,9 @@ static int cm_step(struct cm_state *st, u16 step, u32 lux, bool irq_en)
                cm_delay(st, false);
        }
        if (irq_en && st->i2c->irq && (st->lux_thr_lo || st->lux_thr_hi)) {
-               ret = cm_i2c_wr(st, CM_REG_WL, step_lo);
-               ret |= cm_i2c_wr(st, CM_REG_WH, step_hi);
+               ret = cm_i2c_wr(st, CM_REG_WL, thr_lo);
+               ret |= cm_i2c_wr(st, CM_REG_WH, thr_hi);
                if (!ret) {
-                       if (st->dbg & CM_DBG_SPEW_MSG)
-                               dev_info(&st->i2c->dev,
-                                        "%s lo=%hu step=%hu hi=%hu\n",
-                                        __func__, step_lo, step, step_hi);
                        ret = cm_cmd_wr(st,
                                        cm_it_tbl[st->scale_i].als_it, true);
                        if (!ret)
@@ -620,40 +682,16 @@ static int cm_step(struct cm_state *st, u16 step, u32 lux, bool irq_en)
        return ret;
 }
 
-static int cm_interpolate(int x1, int x2, int x3, int y1, int *y2, int y3)
-{
-       int dividend;
-       int divisor;
-
-       /* y2 = ((x2 - x1)(y3 - y1)/(x3 - x1)) + y1 */
-       divisor = (x3 - x1);
-       if (!divisor)
-               return -EINVAL;
-
-       dividend = (x2 - x1) * (y3 - y1);
-       *y2 = (dividend / divisor) + y1;
-       return 0;
-}
-
-static void cm_report_init(struct cm_state *st)
-{
-       if (st->report_n)
-               st->report = st->report_n;
-       else
-               st->report = 1;
-}
-
 static int cm_rd(struct iio_dev *indio_dev)
 {
        struct cm_state *st = iio_priv(indio_dev);
        u16 sts;
        u16 step;
        u32 lux;
+       u64 calc;
        s64 ts;
        s64 ts_elapsed;
-       bool report = false;
        bool t_min = false;
-       unsigned int ms;
        int ret;
 
        /* spec is vague so one of these should clear the IRQ */
@@ -662,54 +700,55 @@ static int cm_rd(struct iio_dev *indio_dev)
        if (ret)
                return ret;
 
-       if (st->hw_change && !st->report)
+       if (st->hw_change && !st->report) {
                /* drop first sample after HW change */
+               st->hw_change = false;
                return 0;
+       }
 
-       st->hw_change = false;
-       lux = step * cm_it_tbl[st->scale_i].resolution;
+       calc = cm_it_tbl[st->scale_i].resolution;
+       calc *= step;
+       if (st->scale_val2)
+               do_div(calc, st->scale_val2);
+       lux = calc;
+       /* get calibrated value */
        cm_interpolate(st->lux_uc_lo, lux, st->lux_uc_hi,
                       st->lux_c_lo, &lux, st->lux_c_hi);
-       if (sts & (CM_REG_ALS_IF_L | CM_REG_ALS_IF_H)) {
-               report = true;
+       if (sts & ((1 << CM_REG_ALS_IF_L) | (1 << CM_REG_ALS_IF_H))) {
+               st->report = st->report_n;
        } else {
-               if (st->lux_thr_lo || st->lux_thr_hi) {
-                       if (lux > (st->light + st->lux_thr_hi)) {
-                               report = true;
-                       } else if (st->light > st->lux_thr_lo) {
-                               if (lux < (st->light - st->lux_thr_lo))
-                                       report = true;
-                       }
-               } else {
-                       report = true;
+               if (lux > (st->light + st->lux_thr_hi)) {
+                       st->report = st->report_n;
+               } else if (st->light > st->lux_thr_lo) {
+                       if (lux < (st->light - st->lux_thr_lo))
+                               st->report = st->report_n;
                }
        }
        /* calculate elapsed time for allowed report rate */
-       ts = iio_get_time_ns();
+       ts = cm_get_time_ns(st);
        ts_elapsed = ts - st->ts;
        if (ts_elapsed >= st->poll_delay_ms * 1000000)
                t_min = true;
-       ms = ts_elapsed;
-       ms /= 1000000;
        if (st->dbg & CM_DBG_SPEW_LIGHT_POLL)
-               dev_info(&st->i2c->dev, "poll light %d %lld  diff: %d %ums\n",
-                        lux, ts, lux - st->light, ms);
-       /* if IIO scale == 1 then in calibrate/test mode */
-       if ((st->scale_val == 1) && (st->scale_val2 == 0))
-               cm_report_init(st);
-       /* report if:
-        * - st->report (usually used to report the first sample regardless)
-        * - time since last report >= polling delay && data outside thresholds
-        * - report regardless of change (for test and calibration)
-        */
-       if (st->report || (report && t_min)) {
+               dev_info(&st->i2c->dev,
+                        "poll light %d %lld  diff: %d %lldns\n",
+                        lux, ts, lux - st->light, ts_elapsed);
+       if ((st->report && t_min) || ((st->scale_val == 1) &&
+                                     !st->scale_val2)) {
+               /* report if:
+                * - st->report && time since last report >= polling delay
+                * - in calibration mode (scale == 1)
+                */
                if (st->report)
                        st->report--;
                if (!(st->dbg & CM_DBG_VAL_LIGHT))
                        st->light = lux;
                st->ts = ts;
                cm_buf_push(indio_dev, st);
-               ret = cm_step(st, step, lux, true);
+               if (st->report)
+                       ret = cm_step(st, step, lux, false);
+               else
+                       ret = cm_step(st, step, lux, true);
        } else if (t_min && !st->report) {
                ret = cm_step(st, step, lux, true);
        } else {
@@ -779,13 +818,12 @@ static int cm_enable(struct iio_dev *indio_dev)
        if (iio_scan_mask_query(indio_dev, indio_dev->buffer, CM_SCAN_LIGHT)) {
                ret = cm_pm(st, true);
                if (!ret) {
-                       cm_report_init(st);
                        ret = cm_it_wr(st, 0);
-                       cm_delay(st, true);
                        if (ret) {
                                cm_disable(indio_dev);
                        } else {
-                               st->ts = iio_get_time_ns();
+                               cm_delay(st, true);
+                               cm_report_init(st);
                                st->enable = 1;
                                schedule_delayed_work(&st->dw,
                                             msecs_to_jiffies(CM_HW_DELAY_MS));
@@ -1127,7 +1165,7 @@ static int cm_write_raw(struct iio_dev *indio_dev,
                msg = "IIO_CHAN_INFO_RAW";
                old = st->light;
                st->light = val;
-               st->ts = iio_get_time_ns();
+               st->ts = cm_get_time_ns(st);
                st->dbg |= CM_DBG_VAL_LIGHT;
                cm_buf_push(indio_dev, st);
                break;
@@ -1321,6 +1359,8 @@ static int cm_id_i2c(struct iio_dev *indio_dev, const char *name)
                                break;
                }
        }
+       if (ret)
+               st->i2c_addr = 0;
        return ret;
 }
 
@@ -1368,7 +1408,7 @@ static int cm_remove(struct i2c_client *client)
 
 static int cm_of_dt(struct i2c_client *client, struct cm_state *st)
 {
-       struct device_node *np = client->dev.of_node;
+       struct device_node *dn = client->dev.of_node;
        u16 als_sm;
        u16 als_pers;
        unsigned int val;
@@ -1390,29 +1430,30 @@ static int cm_of_dt(struct i2c_client *client, struct cm_state *st)
        /* device tree parameters */
        if (client->dev.of_node) {
                /* device specific parameters */
-               of_property_read_u16(np, "als_sm", &als_sm);
-               of_property_read_u16(np, "als_pers", &als_pers);
-               of_property_read_u16(np, "als_psm", &st->als_psm);
+               of_property_read_u16(dn, "als_sm", &als_sm);
+               of_property_read_u16(dn, "als_pers", &als_pers);
+               of_property_read_u16(dn, "als_psm", &st->als_psm);
                /* common NVS programmable parameters */
-               of_property_read_u32(np, "report_count", &st->report_n);
+               st->iio_ts_en = of_property_read_bool(dn, "iio_timestamps");
+               of_property_read_u32(dn, "report_count", &st->report_n);
                /* common NVS ALS programmable parameters */
-               of_property_read_s32(np, "light_uncalibrated_lo",
+               of_property_read_s32(dn, "light_uncalibrated_lo",
                                     &st->lux_uc_lo);
-               of_property_read_s32(np, "light_uncalibrated_hi",
+               of_property_read_s32(dn, "light_uncalibrated_hi",
                                     &st->lux_uc_hi);
-               of_property_read_s32(np, "light_calibrated_lo", &st->lux_c_lo);
-               of_property_read_s32(np, "light_calibrated_hi", &st->lux_c_hi);
-               of_property_read_s32(np, "light_scale_val", &st->scale_val);
-               of_property_read_s32(np, "light_scale_val2", &st->scale_val2);
-               of_property_read_s32(np, "light_offset_val", &st->offset_val);
-               of_property_read_s32(np, "light_offset_val2",
+               of_property_read_s32(dn, "light_calibrated_lo", &st->lux_c_lo);
+               of_property_read_s32(dn, "light_calibrated_hi", &st->lux_c_hi);
+               of_property_read_s32(dn, "light_scale_val", &st->scale_val);
+               of_property_read_s32(dn, "light_scale_val2", &st->scale_val2);
+               of_property_read_s32(dn, "light_offset_val", &st->offset_val);
+               of_property_read_s32(dn, "light_offset_val2",
                                     &st->offset_val2);
-               if (of_property_read_u32(np, "light_threshold_lo", &val))
-                       st->lux_thr_lo = val;
-               if (of_property_read_u32(np, "light_threshold_hi", &val))
-                       st->lux_thr_hi = val;
+               of_property_read_u32(dn, "light_threshold_lo",
+                                    &st->lux_thr_lo);
+               of_property_read_u32(dn, "light_threshold_hi",
+                                    &st->lux_thr_hi);
                /* this device supports these programmable parameters */
-               if (of_property_read_u32(np, "light_integration_time_ms_lo",
+               if (of_property_read_u32(dn, "light_integration_time_ms_lo",
                                         &val)) {
                        for (i = ARRAY_SIZE(cm_it_tbl); i > 1; i--) {
                                if (val <= cm_it_tbl[i - 1].ms)
@@ -1420,7 +1461,7 @@ static int cm_of_dt(struct i2c_client *client, struct cm_state *st)
                        }
                        st->it_i_hi = i;
                }
-               if (of_property_read_u32(np, "light_integration_time_ms_hi",
+               if (of_property_read_u32(dn, "light_integration_time_ms_hi",
                                         &val)) {
                        for (i = 0; i < ARRAY_SIZE(cm_it_tbl) - 1; i++) {
                                if (val >= cm_it_tbl[i].ms)
@@ -1436,6 +1477,8 @@ static int cm_of_dt(struct i2c_client *client, struct cm_state *st)
                        st->it_i_hi = ARRAY_SIZE(cm_it_tbl);
                }
        }
+       if (!st->report_n)
+               st->report_n = CM_REPORT_N;
        st->als_psm &= CM_REG_PSM_MASK;
        if (st->als_psm & (1 << CM_REG_PSM_EN))
                st->psm_ms = cm_psm_ms_tbl[st->als_psm >> 1];
index 4025252..138aa40 100644 (file)
 #include <linux/slab.h>
 #include <linux/err.h>
 #include <linux/delay.h>
+#include <linux/ktime.h>
 #include <linux/regulator/consumer.h>
 #include <linux/workqueue.h>
 #include <linux/interrupt.h>
 #include <linux/iio/trigger.h>
 
 
-#define MX_VERSION_DRIVER              (101)
+#define MX_VERSION_DRIVER              (102)
 #define MX_VENDOR                      "Maxim"
 #define MX_NAME                                "max4400x"
 #define MX_NAME_MAX44005               "max44005"
 #define MX_HW_DELAY_MS                 (1)
 #define MX_POLL_DLY_MS_DFLT            (2000)
 #define MX_POLL_DLY_MS_MIN             (100)
-#define MX_THRESHOLD_LUX_DFLT          (50)
-#define MX_THRESHOLD_PROX_DFLT         (10)
-#define MX_THRESHOLD_PROX_BINARY_DFLT  (1)
+/* setting _REPORT_N to 2 causes an extra reading after crossing the threshold
+ * allowing a more accurate settled reported value.
+ */
+#define MX_REPORT_N                    (2)
 #define MX_AMB_CFG_DFLT                        (0x43)
 #define MX_PRX_CFG_DFLT                        (0x12)
 /* light defines */
+#define MX_LIGHT_THRESHOLD_DFLT                (50)
 #define MX_LIGHT_VERSION               (1)
 #define MX_LIGHT_MAX_RANGE_IVAL                (14323)
 #define MX_LIGHT_MAX_RANGE_MICRO       (0)
 #define MX_LIGHT_OFFSET_MICRO          (0)
 #define MX_LIGHT_MILLIAMP              "0.0135"
 /* temp defines */
+#define MX_TEMP_THRESHOLD_DFLT         (2)
 #define MX_TEMP_VERSION                        (1)
 #define MX_TEMP_MAX_RANGE_IVAL         (125)
 #define MX_TEMP_MAX_RANGE_MICRO                (0)
 #define MX_TEMP_OFFSET_MICRO           (0)
 #define MX_TEMP_MILLIAMP               "0.0135"
 /* proximity defines */
+#define MX_PROX_THRESHOLD_DFLT         (10)
+#define MX_PROX_THRESHOLD_BINARY_DFLT  (1) /* > 0 = enable */
 #define MX_PROX_VERSION                        (1)
 #define MX_PROX_MAX_RANGE_IVAL         (1)
 #define MX_PROX_MAX_RANGE_MICRO                (0)
 #define MX_PROX_MILLIAMP               "10.0195"
 /* HW registers */
 #define MX_REG_STS                     (0x00)
+#define MX_REG_STS_POR                 (0x04)
 #define MX_REG_STS_RESET               (4)
 #define MX_REG_STS_SHDN                        (3)
 #define MX_REG_STS_PWRON               (2)
 #define MX_REG_CFG_MAIN_AMBSEL         (2)
 #define MX_REG_CFG_MAIN_PRXINTE                (1)
 #define MX_REG_CFG_MAIN_AMBINTE                (0)
+#define MX_REG_CFG_MAIN_INTE_MASK      (0x03)
 #define MX_REG_CFG_AMB                 (0x02)
 #define MX_REG_CFG_AMB_POR             (0x20)
 #define MX_REG_CFG_AMB_TRIM            (7)
@@ -289,6 +297,8 @@ enum MX_ATTR {
        MX_ATTR_TEMP_PART,
        MX_ATTR_TEMP_VERSION,
        MX_ATTR_TEMP_MILLIAMP,
+       MX_ATTR_TEMP_THRESH_LO,
+       MX_ATTR_TEMP_THRESH_HI,
        MX_ATTR_PROX_PART,
        MX_ATTR_PROX_VERSION,
        MX_ATTR_PROX_MILLIAMP,
@@ -334,10 +344,10 @@ static u8 mx_mode_tbl[] = {               /* device enable */
        0x00,                           /* light */
        0x00,                           /* temp */
        0x00,                           /* temp + light */
-       0x05,                           /* proximity */
-       0x03,                           /* proximity + light */
-       0x03,                           /* proximity + temp */
-       0x03                            /* proximity + temp + light */
+       0x50,                           /* proximity */
+       0x30,                           /* proximity + light */
+       0x30,                           /* proximity + temp */
+       0x30                            /* proximity + temp + light */
 };
 
 /* 1 nW/cm^2 = 0.00683 lux */
@@ -392,13 +402,12 @@ struct mx_state {
        int i_uc_hi[MX_DEV_N];          /* interpolation x3 uncalibrated hi */
        int i_c_lo[MX_DEV_N];           /* interpolation y1 calibrated lo */
        int i_c_hi[MX_DEV_N];           /* interpolation y3 calibrated hi */
-       unsigned int report;            /* used to report first valid sample */
-       unsigned int report_n;          /* this many on-change data reports */
-       unsigned int lux_thr_lo;        /* report when lux below this */
-       unsigned int lux_thr_hi;        /* report when lux above this */
-       unsigned int prx_thr_lo;        /* report when proximity below this */
-       unsigned int prx_thr_hi;        /* report when proximity above this */
+       int thr_lo[MX_DEV_N];           /* report when reported value < this */
+       int thr_hi[MX_DEV_N];           /* report when reported value > this */
        unsigned int prx_thr_bin;       /* proximity binary mode threshold */
+       unsigned int report;            /* report count */
+       unsigned int report_n;          /* this many on-change data reports */
+       bool iio_ts_en;                 /* use IIO timestamps */
        bool shutdown;                  /* shutdown active flag */
        bool suspend;                   /* suspend active flag */
        const char *part;               /* part name */
@@ -410,13 +419,24 @@ struct mx_state {
        u8 rc_main_cfg;                 /* cache of main configuration */
        u8 rc_amb_cfg;                  /* cache of ambient configuration */
        u32 light;                      /* sample light data */
-       s16 temp;                       /* sample temp data */
+       s32 temp;                       /* sample temp data */
        u32 proximity;                  /* reported proximity data */
        u32 prox;                       /* actual proximity data */
        s64 ts;                         /* sample data timestamp */
 };
 
 
+static s64 mx_get_time_ns(struct mx_state *st)
+{
+       struct timespec ts;
+
+       if (st->iio_ts_en)
+               return iio_get_time_ns();
+
+       ktime_get_ts(&ts);
+       return timespec_to_ns(&ts);
+}
+
 static void mx_err(struct mx_state *st)
 {
        st->errs++;
@@ -453,13 +473,15 @@ static int mx_i2c_write(struct mx_state *st, u16 len, u8 *buf)
 {
        struct i2c_msg msg;
 
-       msg.addr = st->i2c_addr;
-       msg.flags = 0;
-       msg.len = len;
-       msg.buf = buf;
-       if (i2c_transfer(st->i2c->adapter, &msg, 1) != 1) {
-               mx_err(st);
-               return -EIO;
+       if (st->i2c_addr) {
+               msg.addr = st->i2c_addr;
+               msg.flags = 0;
+               msg.len = len;
+               msg.buf = buf;
+               if (i2c_transfer(st->i2c->adapter, &msg, 1) != 1) {
+                       mx_err(st);
+                       return -EIO;
+               }
        }
 
        return 0;
@@ -595,13 +617,12 @@ static int mx_pm(struct mx_state *st, bool enable)
 
        if (enable) {
                ret = mx_vreg_en_all(st);
-               if (ret) {
-                       mdelay(MX_HW_DELAY_MS);
-                       mx_i2c_wr(st, MX_REG_STS, (1 << MX_REG_STS_RESET));
+               if (ret)
                        mdelay(MX_HW_DELAY_MS);
-                       st->rc_main_cfg = MX_REG_CFG_MAIN_POR;
-                       st->rc_amb_cfg = MX_REG_CFG_AMB_POR;
-               }
+               mx_i2c_wr(st, MX_REG_STS, (1 << MX_REG_STS_RESET));
+               mdelay(MX_HW_DELAY_MS);
+               st->rc_main_cfg = MX_REG_CFG_MAIN_POR;
+               st->rc_amb_cfg = MX_REG_CFG_AMB_POR;
                mx_i2c_wr(st, MX_REG_STS, 0);
        } else {
                ret = mx_vreg_sts(st);
@@ -664,38 +685,39 @@ static void mx_buf_push(struct iio_dev *indio_dev)
        unsigned int i;
        unsigned int bytes = 0;
 
+       if (!iio_buffer_enabled(indio_dev))
+               return;
+
        if (iio_scan_mask_query(indio_dev, indio_dev->buffer, MX_SCAN_LIGHT)) {
                n = sizeof(st->light);
                i = mx_buf_index(n, &bytes);
                memcpy(&buf[i], &st->light, n);
+               if (st->dbg & MX_DBG_SPEW_LIGHT)
+                       dev_info(&st->i2c->dev, "light: %u %lld\n",
+                                st->light, st->ts);
        }
        if (iio_scan_mask_query(indio_dev, indio_dev->buffer, MX_SCAN_TEMP)) {
                n = sizeof(st->temp);
                i = mx_buf_index(n, &bytes);
                memcpy(&buf[i], &st->temp, n);
+               if (st->dbg & MX_DBG_SPEW_TEMP)
+                       dev_info(&st->i2c->dev, "temp: %d %lld\n",
+                                st->temp, st->ts);
        }
        if (iio_scan_mask_query(indio_dev, indio_dev->buffer, MX_SCAN_PROX)) {
                n = sizeof(st->proximity);
                i = mx_buf_index(n, &bytes);
                memcpy(&buf[i], &st->proximity, n);
+               if (st->dbg & MX_DBG_SPEW_PROX)
+                       dev_info(&st->i2c->dev, "proximity: %u %lld\n",
+                                st->proximity, st->ts);
        }
        if (indio_dev->buffer->scan_timestamp) {
                n = sizeof(st->ts);
                i = mx_buf_index(n, &bytes);
                memcpy(&buf[i], &st->ts, n);
        }
-       if (iio_buffer_enabled(indio_dev)) {
-               iio_push_to_buffers(indio_dev, buf);
-               if (st->dbg & MX_DBG_SPEW_LIGHT)
-                       dev_info(&st->i2c->dev, "light: %u %lld\n",
-                                st->light, st->ts);
-               if (st->dbg & MX_DBG_SPEW_TEMP)
-                       dev_info(&st->i2c->dev, "temp: %hd %lld\n",
-                                st->temp, st->ts);
-               if (st->dbg & MX_DBG_SPEW_PROX)
-                       dev_info(&st->i2c->dev, "proximity: %u %lld\n",
-                                st->proximity, st->ts);
-       }
+       iio_push_to_buffers(indio_dev, buf);
 }
 
 static int mx_cmd_wr(struct mx_state *st, unsigned int enable, bool irq_en)
@@ -717,8 +739,8 @@ static int mx_cmd_wr(struct mx_state *st, unsigned int enable, bool irq_en)
                        dev_info(&st->i2c->dev, "%s amb_cfg=%hhx err=%d\n",
                                 __func__, amb_cfg, ret);
        }
-       main_cfg = mx_mode_tbl[enable] << MX_REG_CFG_MAIN_MODE;
-       if (irq_en) {
+       main_cfg = mx_mode_tbl[enable];
+       if (irq_en && st->i2c->irq) {
                if (enable & (1 << MX_DEV_LIGHT))
                        main_cfg |= (1 << MX_REG_CFG_MAIN_AMBINTE);
                if (enable & (1 << MX_DEV_PROX))
@@ -734,7 +756,7 @@ static int mx_cmd_wr(struct mx_state *st, unsigned int enable, bool irq_en)
                        dev_info(&st->i2c->dev, "%s main_cfg=%hhx err=%d\n",
                                 __func__, main_cfg, ret);
        }
-       if (irq_en && !ret_t)
+       if (st->rc_main_cfg & MX_REG_CFG_MAIN_INTE_MASK)
                ret_t = 1; /* flag IRQ enabled */
        return ret_t;
 }
@@ -774,11 +796,8 @@ static int mx_interpolate(int x1, int x2, int x3, int y1, int *y2, int y3)
 
 static void mx_report_init(struct mx_state *st)
 {
-       st->ts = iio_get_time_ns();
-       if (st->report_n)
-               st->report = st->report_n;
-       else
-               st->report = 1;
+       st->ts = 0;
+       st->report = st->report_n;
 }
 
 static int mx_thr_wr(struct mx_state *st, u8 reg, u16 thr_lo, u16 thr_hi)
@@ -804,21 +823,39 @@ static int mx_thr_wr(struct mx_state *st, u8 reg, u16 thr_lo, u16 thr_hi)
        return ret;
 }
 
-static int mx_thr(struct mx_state *st, unsigned int dev, u8 reg, unsigned lsb,
-                 int val_new, int val_old, int thr_lo, int thr_hi, u16 hw_max)
+static int mx_thr(struct mx_state *st, unsigned int dev, u8 reg,
+                 unsigned int lsb, int val_new, int *val_old, u16 hw_max)
 {
        u64 calc;
-       int ret = 0;
+       int thr_lo;
+       int thr_hi;
+       int ret = 1;
 
-       if (thr_lo || thr_hi) {
-               if (val_new > (val_old + thr_hi)) {
-                       ret = 1;
-               } else if (val_old > thr_lo) {
-                       if (val_new < (val_old - thr_lo))
-                               ret = 1;
-               }
-               if (ret) {
-                       thr_lo = val_new - thr_lo;
+       /* get calibrated value */
+       mx_interpolate(st->i_uc_lo[dev], val_new,
+                      st->i_uc_hi[dev],
+                      st->i_c_lo[dev], &val_new,
+                      st->i_c_hi[dev]);
+       if ((st->scale_val[dev] == 1) && (st->scale_val2[dev] == 0)) {
+               /* calibration mode */
+               st->report = 2; /* 2 causes polling after reporting */
+               *val_old = val_new;
+               return 0;
+       }
+
+       if (val_new > (*val_old + st->thr_hi[dev])) {
+               ret = 0;
+       } else if (*val_old > st->thr_lo[dev]) {
+               if (val_new < (*val_old - st->thr_lo[dev]))
+                       ret = 0;
+       }
+       /* if already reporting IIO buffer, might as well set new thresholds */
+       if (st->report || !ret) {
+               if (!st->report)
+                       st->report = st->report_n;
+               *val_old = val_new;
+               if (st->i2c->irq && reg) {
+                       thr_lo = val_new - st->thr_lo[dev];
                        /* get the uncalibrated value */
                        mx_interpolate(st->i_c_lo[dev], thr_lo,
                                       st->i_c_hi[dev],
@@ -833,7 +870,7 @@ static int mx_thr(struct mx_state *st, unsigned int dev, u8 reg, unsigned lsb,
                        if (lsb)
                                do_div(calc, lsb);
                        thr_lo = calc;
-                       thr_hi = val_new + thr_hi;
+                       thr_hi = val_new + st->thr_hi[dev];
                        /* get the uncalibrated value */
                        mx_interpolate(st->i_c_lo[dev], thr_hi,
                                       st->i_c_hi[dev],
@@ -849,161 +886,140 @@ static int mx_thr(struct mx_state *st, unsigned int dev, u8 reg, unsigned lsb,
                        if (thr_hi > hw_max)
                                thr_hi = hw_max;
                        ret = mx_thr_wr(st, reg, thr_lo, thr_hi);
-                       if (ret)
-                               ret = 2; /* poll mode */
-                       else
-                               ret = 1; /* flag report */
                }
-       } else if (val_new != val_old) {
-               ret = 2; /* poll mode */
        }
-       return ret;
+       return ret; /* ret == 0: report, ret < 0: report && poll (error) */
 }
 
 static int mx_rd_light(struct mx_state *st, s64 ts)
 {
-       u16 lux;
-       u16 lux_max;
        u64 calc;
        u32 light;
+       u16 hw;
        unsigned int lsb;
        int i;
        int ret;
 
-       ret = mx_i2c_read(st, MX_REG_DATA_AMB_CLEAR, 2, (u8 *)&lux);
+       ret = mx_i2c_read(st, MX_REG_DATA_AMB_CLEAR, 2, (u8 *)&hw);
        if (ret)
                return ret;
 
-       lux = be16_to_cpu(lux);
+       hw = be16_to_cpu(hw);
        i = st->rc_amb_cfg & MX_REG_CFG_AMB_AMBPGA_MASK;
        if (st->dev_id == MX_DEVID_MAX44005)
                lsb = mx_ambpga_44005[i];
        else
                lsb = mx_ambpga_44006[i];
        calc = lsb;
-       calc *= lux;
+       calc *= hw;
        if (st->scale_val2[MX_DEV_LIGHT])
                do_div(calc, st->scale_val2[MX_DEV_LIGHT]);
        light = calc;
-       /* get calibrated value */
-       mx_interpolate(st->i_uc_lo[MX_DEV_LIGHT], light,
-                      st->i_uc_hi[MX_DEV_LIGHT],
-                      st->i_c_lo[MX_DEV_LIGHT], &light,
-                      st->i_c_hi[MX_DEV_LIGHT]);
+       if (st->dbg & MX_DBG_SPEW_LIGHT_POLL)
+               dev_info(&st->i2c->dev,
+                        "poll light %u %lld  diff=%d %lldns  hw=%hu\n",
+                        light, ts, light - st->light, ts - st->ts, hw);
        i = st->rc_amb_cfg & MX_REG_CFG_AMB_AMBTIM_MASK;
        i >>= MX_REG_CFG_AMB_AMBTIM;
-       lux_max = mx_ambtim_mask[i];
        ret = mx_thr(st, MX_DEV_LIGHT, MX_REG_AMB_UPTHR_H, lsb, light,
-                    st->light, st->lux_thr_lo, st->lux_thr_hi, lux_max);
-       if ((ret > 0) && !(st->dbg & MX_DBG_VAL_LIGHT))
-               st->light = light;
-       if (st->dbg & MX_DBG_SPEW_LIGHT_POLL) {
-               dev_info(&st->i2c->dev,
-                        "poll light %u %lld  diff=%d %lldns  hw=%hu\n",
-                        light, ts, light - st->light, ts - st->ts, lux);
-               if (ret > 0)
-                       ret = 2; /* poll mode */
-               else
-                       ret = -1; /* poll without report */
-       }
+                    &st->light, mx_ambtim_mask[i]);
        return ret;
 }
 
 static int mx_rd_temp(struct mx_state *st, s64 ts)
 {
-       u16 temp;
+       s32 temp;
+       s16 hw;
        int ret;
 
-       ret = mx_i2c_read(st, MX_REG_DATA_TEMP_H, 2, (u8 *)&temp);
+       ret = mx_i2c_read(st, MX_REG_DATA_TEMP_H, 2, (u8 *)&hw);
        if (ret)
                return ret;
 
-       temp = be16_to_cpu(temp);
-       if (!(st->dbg & MX_DBG_VAL_TEMP))
-               st->temp = temp;
-       if (st->dbg & MX_DBG_SPEW_TEMP_POLL) {
+       hw = be16_to_cpu(hw);
+       temp = hw;
+       temp *= (MX_TEMP_RESOLUTION_MICRO / 10000);
+       if (st->dbg & MX_DBG_SPEW_TEMP_POLL)
                dev_info(&st->i2c->dev,
                         "poll temp %d %lld  diff=%d %lldns  hw=%hd\n",
-                        temp, ts, temp - st->temp, ts - st->ts, temp);
-               if (ret > 0)
-                       ret = 2; /* poll mode */
-               else
-                       ret = -1; /* poll without report */
-       }
-       return 1;
+                        temp, ts, temp - st->temp, ts - st->ts, hw);
+       ret = mx_thr(st, MX_DEV_TEMP, 0, 0, temp, &st->temp, 0);
+       return ret;
 }
 
 static int mx_rd_prox(struct mx_state *st, s64 ts)
 {
-       u16 prox;
-       u16 prox_lo;
-       u16 prox_hi;
+       u16 hw;
+       u16 hw_lo;
+       u16 hw_hi;
        unsigned int proximity;
        int ret = 0;
 
-       ret = mx_i2c_read(st, MX_REG_DATA_PROX_H, 2, (u8 *)&prox);
+       ret = mx_i2c_read(st, MX_REG_DATA_PROX_H, 2, (u8 *)&hw);
        if (ret)
                return ret;
 
-       st->prox = be16_to_cpu(prox);
+       hw = be16_to_cpu(hw);
        if (st->prx_cfg & (1 << MX_REG_CFG_PRX_PRXTIM))
-               prox_hi = 0x00FF;
+               hw_hi = 0x00FF;
        else
-               prox_hi = 0x03FF;
+               hw_hi = 0x03FF;
        if (st->prx_thr_bin) {
                /* proximity has binary threshold */
+               if (st->dbg & MX_DBG_SPEW_PROX_POLL)
+                       dev_info(&st->i2c->dev,
+                                "poll proximity %hu %lld  diff=%d %lldns\n",
+                                hw, ts, hw - st->prox, ts - st->ts);
                if ((st->scale_val[MX_DEV_PROX] == 1) &&
                                          (st->scale_val2[MX_DEV_PROX] == 0)) {
-                       /* binary proximity in test mode - display raw data */
-                       proximity = st->prox;
-                       ret = 2; /* flag report and poll */
+                       st->report = 2; /* 2 causes polling after reporting */
+                       st->proximity = hw; /* display raw data */
                } else {
-                       if (st->prox > st->prx_thr_bin) {
+                       /* the proximity threshold lo/hi are reversed */
+                       if (hw > st->prx_thr_bin) {
                                proximity = 0;
-                               if (st->prx_thr_bin > st->prx_thr_lo)
-                                       prox_lo = st->prx_thr_bin -
-                                                               st->prx_thr_lo;
+                               if (st->prx_thr_bin > st->thr_hi[MX_DEV_PROX])
+                                       hw_lo = st->prx_thr_bin -
+                                                      st->thr_hi[MX_DEV_PROX];
                                else
-                                       prox_lo = 1;
+                                       hw_lo = 1;
                                /* prox_hi already disabled */
                        } else {
                                proximity = 1;
-                               prox_lo = 0; /* disable */
-                               prox = st->prx_thr_bin + st->prx_thr_hi;
-                               if (prox < prox_hi)
-                                       prox_hi = prox;
+                               hw_lo = 0; /* disable */
+                               hw = st->prx_thr_bin + st->thr_lo[MX_DEV_PROX];
+                               if (hw < hw_hi)
+                                       hw_hi = hw;
                                else
-                                       prox_hi--;
+                                       hw_hi--;
                        }
                        if (proximity != st->proximity) {
+                               if (!st->report)
+                                       st->report = st->report_n;
+                               st->proximity = proximity;
+                               st->prox = hw;
                                ret = mx_thr_wr(st, MX_REG_PRX_UPTHR_H,
-                                               prox_lo, prox_hi);
-                               if (ret)
-                                       ret = 2; /* flag report */
-                               else
-                                       ret = 1; /* flag report */
+                                               hw_lo, hw_hi);
                        }
                }
        } else {
-               /* FIXME */
-               /* reverse the lo/hi thresholds */
-               ret = mx_thr(st, MX_DEV_PROX, MX_REG_PRX_UPTHR_H, 0,
-                            st->prox, st->proximity,
-                            st->prx_thr_hi, st->prx_thr_lo, prox_hi);
                /* reverse the high/low order */
-               proximity = prox_hi - st->prox;
-       }
-       if ((ret > 0) && !(st->dbg & MX_DBG_VAL_PROX))
-               st->proximity = proximity;
-       if (st->dbg & MX_DBG_SPEW_PROX_POLL) {
-               dev_info(&st->i2c->dev,
-                        "poll proximity %u %lld  diff=%d %lldns  hw=%hu\n",
-                        proximity, ts,
-                        proximity - st->proximity, ts - st->ts, st->prox);
-               if (ret > 0)
-                       ret = 2; /* poll mode */
-               else
-                       ret = -1; /* poll without report */
+               proximity = hw_hi - hw;
+               /* get calibrated value with reversed lo/hi */
+               mx_interpolate(st->i_uc_lo[MX_DEV_PROX], proximity,
+                              st->i_uc_hi[MX_DEV_PROX],
+                              st->i_c_lo[MX_DEV_PROX], &proximity,
+                              st->i_c_hi[MX_DEV_PROX]);
+               if (st->dbg & MX_DBG_SPEW_PROX_POLL)
+                       dev_info(&st->i2c->dev,
+                                "poll prox %u %lld  diff=%d %lldns  hw=%hu\n",
+                                proximity, ts,
+                                proximity - st->proximity, ts - st->ts, hw);
+               /* the proximity threshold lo/hi are reversed */
+               ret = mx_thr(st, MX_DEV_PROX, MX_REG_PRX_UPTHR_H, 0,
+                            hw, &st->prox, hw_hi);
+               if (ret <= 0)
+                       st->proximity = proximity;
        }
        return ret;
 }
@@ -1034,10 +1050,7 @@ static int mx_rd(struct iio_dev *indio_dev)
        struct mx_state *st = iio_priv(indio_dev);
        u8 sts;
        s64 ts;
-       bool report = false;
-       unsigned int i;
        int ret;
-       int ret_t = 0;
 
        /* clear possible IRQ */
        ret = mx_i2c_rd(st, MX_REG_STS, &sts);
@@ -1050,60 +1063,33 @@ static int mx_rd(struct iio_dev *indio_dev)
                return -1;
        }
 
-       ts = iio_get_time_ns();
-       if ((st->report < st->report_n) || !st->report) {
+       ts = mx_get_time_ns(st);
+       if (st->report < st->report_n) {
                /* calculate elapsed time for allowed report rate */
                if ((ts - st->ts) < st->poll_delay_ms * 1000000)
                        /* data changes are happening faster than allowed to
                         * report so we poll for the next data at an allowed
                         * rate with interrupts disabled.
                         */
-                       ret = mx_cmd_wr(st, st->enable, false);
-       }
-       /* proximity device */
-       if (st->enable & (1 << MX_DEV_PROX)) {
-               ret = mx_rd_prox(st, ts);
-               if (ret > 0)
-                       report = true;
-               if ((ret < 0) || (ret > 1))
-                       ret_t |= ret;
-       }
-       /* temp device */
-       if (st->enable & (1 << MX_DEV_TEMP)) {
-               ret = mx_rd_temp(st, ts);
-               if (ret > 0)
-                       report = true;
-               if ((ret < 0) || (ret > 1))
-                       ret_t |= ret;
+                       return mx_cmd_wr(st, st->enable, false);
        }
-       /* light device */
-       if (st->enable & (1 << MX_DEV_LIGHT)) {
-               ret = mx_rd_light(st, ts);
-               if (ret > 0)
-                       report = true;
-               if ((ret < 0) || (ret > 1))
-                       ret_t |= ret;
-       }
-       /* if IIO scale == 1 then in calibrate/test mode */
-       for (i = 0; i < MX_DEV_N; i++) {
-               if ((st->scale_val[i] == 1) && (st->scale_val2[i] == 0)) {
-                       report = true;
-                       break;
-               }
-       }
-       /* report if:
-        * - st->report (usually used to report the first sample regardless)
-        * - time since last report >= polling delay && data outside thresholds
-        * - report regardless of change (for test and calibration)
-        */
-       if (st->report || report) {
+       if ((st->enable & (1 << MX_DEV_PROX)) &&
+                                               (!(st->dbg & MX_DBG_VAL_PROX)))
+               ret |= mx_rd_prox(st, ts);
+       if ((st->enable & (1 << MX_DEV_TEMP)) &&
+                                               (!(st->dbg & MX_DBG_VAL_TEMP)))
+               ret |= mx_rd_temp(st, ts);
+       if ((st->enable & (1 << MX_DEV_LIGHT)) &&
+                                              (!(st->dbg & MX_DBG_VAL_LIGHT)))
+               ret |= mx_rd_light(st, ts);
+       if (st->report) {
                if (st->report)
                        st->report--;
                st->ts = ts;
                mx_buf_push(indio_dev);
        }
-       if (ret_t || st->report)
-               /* poll if error or forcing reports */
+       if (st->report || ret < 0)
+               /* poll if error or more reporting */
                ret = mx_cmd_wr(st, st->enable, false);
        else
                ret = mx_cmd_wr(st, st->enable, true);
@@ -1232,26 +1218,40 @@ static ssize_t mx_attr_store(struct device *dev,
 
        case MX_ATTR_LIGHT_THRESH_LO:
                msg = "ATTR_THRESH_LO";
-               old = st->lux_thr_lo;
-               st->lux_thr_lo = new;
+               old = st->thr_lo[MX_DEV_LIGHT];
+               st->thr_lo[MX_DEV_LIGHT] = new;
                break;
 
        case MX_ATTR_LIGHT_THRESH_HI:
                msg = "ATTR_LIGHT_THRESH_HI";
-               old = st->lux_thr_hi;
-               st->lux_thr_hi = new;
+               old = st->thr_hi[MX_DEV_LIGHT];
+               st->thr_hi[MX_DEV_LIGHT] = new;
+               break;
+
+       case MX_ATTR_TEMP_THRESH_LO:
+               msg = "ATTR_TEMP_THRESH_LO";
+               old = st->thr_lo[MX_DEV_TEMP];
+               st->thr_lo[MX_DEV_TEMP] = new;
+               break;
+
+       case MX_ATTR_TEMP_THRESH_HI:
+               msg = "ATTR_TEMP_THRESH_HI";
+               old = st->thr_hi[MX_DEV_TEMP];
+               st->thr_hi[MX_DEV_TEMP] = new;
                break;
 
        case MX_ATTR_PROX_THRESH_LO:
                msg = "ATTR_PROX_THRESH_LO";
-               old = st->prx_thr_lo;
-               st->prx_thr_lo = new;
+               /* proximity threshold lo/hi are reversed */
+               old = st->thr_hi[MX_DEV_PROX];
+               st->thr_hi[MX_DEV_PROX] = new;
                break;
 
        case MX_ATTR_PROX_THRESH_HI:
                msg = "ATTR_PROX_THRESH_HI";
-               old = st->prx_thr_hi;
-               st->prx_thr_hi = new;
+               /* proximity threshold lo/hi are reversed */
+               old = st->thr_lo[MX_DEV_PROX];
+               st->thr_lo[MX_DEV_PROX] = new;
                break;
 
        default:
@@ -1300,10 +1300,10 @@ static ssize_t mx_attr_show(struct device *dev, struct device_attribute *attr,
                return sprintf(buf, "%s\n", MX_LIGHT_MILLIAMP);
 
        case MX_ATTR_LIGHT_THRESH_LO:
-               return sprintf(buf, "%u\n", st->lux_thr_lo);
+               return sprintf(buf, "%u\n", st->thr_lo[MX_DEV_LIGHT]);
 
        case MX_ATTR_LIGHT_THRESH_HI:
-               return sprintf(buf, "%u\n", st->lux_thr_hi);
+               return sprintf(buf, "%u\n", st->thr_hi[MX_DEV_LIGHT]);
 
        case MX_ATTR_TEMP_PART:
                return sprintf(buf, "%s temperature\n", st->part);
@@ -1314,6 +1314,12 @@ static ssize_t mx_attr_show(struct device *dev, struct device_attribute *attr,
        case MX_ATTR_TEMP_MILLIAMP:
                return sprintf(buf, "%s\n", MX_TEMP_MILLIAMP);
 
+       case MX_ATTR_TEMP_THRESH_LO:
+               return sprintf(buf, "%u\n", st->thr_lo[MX_DEV_TEMP]);
+
+       case MX_ATTR_TEMP_THRESH_HI:
+               return sprintf(buf, "%u\n", st->thr_hi[MX_DEV_TEMP]);
+
        case MX_ATTR_PROX_PART:
                return sprintf(buf, "%s proximity\n", st->part);
 
@@ -1324,10 +1330,12 @@ static ssize_t mx_attr_show(struct device *dev, struct device_attribute *attr,
                return sprintf(buf, "%s\n", MX_PROX_MILLIAMP);
 
        case MX_ATTR_PROX_THRESH_LO:
-               return sprintf(buf, "%u\n", st->prx_thr_lo);
+               /* proximity threshold lo/hi are reversed */
+               return sprintf(buf, "%u\n", st->thr_hi[MX_DEV_PROX]);
 
        case MX_ATTR_PROX_THRESH_HI:
-               return sprintf(buf, "%u\n", st->prx_thr_hi);
+               /* proximity threshold lo/hi are reversed */
+               return sprintf(buf, "%u\n", st->thr_lo[MX_DEV_PROX]);
 
        default:
                return -EINVAL;
@@ -1521,6 +1529,12 @@ static IIO_DEVICE_ATTR(temp_version, S_IRUGO,
                       mx_attr_show, NULL, MX_ATTR_TEMP_VERSION);
 static IIO_DEVICE_ATTR(temp_milliamp, S_IRUGO,
                       mx_attr_show, NULL, MX_ATTR_TEMP_MILLIAMP);
+static IIO_DEVICE_ATTR(temp_thresh_rising_value,
+                      S_IRUGO | S_IWUSR | S_IWGRP,
+                      mx_attr_show, mx_attr_store, MX_ATTR_TEMP_THRESH_HI);
+static IIO_DEVICE_ATTR(temp_thresh_falling_value,
+                      S_IRUGO | S_IWUSR | S_IWGRP,
+                      mx_attr_show, mx_attr_store, MX_ATTR_TEMP_THRESH_LO);
 static IIO_DEVICE_ATTR(proximity_part, S_IRUGO,
                       mx_attr_show, NULL, MX_ATTR_PROX_PART);
 static IIO_DEVICE_ATTR(proximity_vendor, S_IRUGO,
@@ -1551,6 +1565,8 @@ static struct attribute *mx_attrs[] = {
        &iio_dev_attr_temp_vendor.dev_attr.attr,
        &iio_dev_attr_temp_version.dev_attr.attr,
        &iio_dev_attr_temp_milliamp.dev_attr.attr,
+       &iio_dev_attr_temp_thresh_rising_value.dev_attr.attr,
+       &iio_dev_attr_temp_thresh_falling_value.dev_attr.attr,
        /* proximity_part must be the first proximity attribute */
        &iio_dev_attr_proximity_part.dev_attr.attr,
        &iio_dev_attr_proximity_vendor.dev_attr.attr,
@@ -1691,18 +1707,24 @@ static int mx_write_raw(struct iio_dev *indio_dev,
                        old = st->light;
                        st->light = val;
                        st->dbg |= MX_DBG_VAL_LIGHT;
+                       if ((st->enable & (1 << MX_DEV_LIGHT)) && !st->report)
+                               st->report = 1;
                        break;
 
                case IIO_TEMP:
                        old = st->temp;
                        st->temp = val;
                        st->dbg |= MX_DBG_VAL_TEMP;
+                       if ((st->enable & (1 << MX_DEV_TEMP)) && !st->report)
+                               st->report = 1;
                        break;
 
                case IIO_PROXIMITY:
                        old = st->proximity;
                        st->proximity = val;
                        st->dbg |= MX_DBG_VAL_PROX;
+                       if ((st->enable & (1 << MX_DEV_PROX)) && !st->report)
+                               st->report = 1;
                        break;
 
                default:
@@ -1760,7 +1782,7 @@ static struct iio_chan_spec mx_channels[] = {
        {
                .type                   = IIO_TEMP,
                .scan_index             = MX_SCAN_TEMP,
-               .scan_type              = IIO_ST('s', 14, 16, 0),
+               .scan_type              = IIO_ST('s', 32, 32, 0),
                .info_mask              = BIT(IIO_CHAN_INFO_RAW) |
                                          BIT(IIO_CHAN_INFO_SAMP_FREQ) |
                                          BIT(IIO_CHAN_INFO_PEAK) |
@@ -1876,40 +1898,36 @@ static SIMPLE_DEV_PM_OPS(mx_pm_ops, mx_suspend, mx_resume);
 static int mx_id_dev(struct iio_dev *indio_dev, const char *name)
 {
        struct mx_state *st = iio_priv(indio_dev);
-       u8 val = 0;
+       u8 val;
        int ret = 0;
 
-       if (!strcmp(name, MX_NAME_MAX44005)) {
-               st->dev_id = MX_DEVID_MAX44005;
-               st->part = MX_NAME_MAX44005;
+       if (!strcmp(name, MX_NAME_MAX44008)) {
+               st->dev_id = MX_DEVID_MAX44008;
+               st->part = MX_NAME_MAX44008;
        } else if (!strcmp(name, MX_NAME_MAX44006)) {
                st->dev_id = MX_DEVID_MAX44006;
                st->part = MX_NAME_MAX44006;
-       } else if (!strcmp(name, MX_NAME_MAX44008)) {
-               st->dev_id = MX_DEVID_MAX44008;
-               st->part = MX_NAME_MAX44008;
-       }
-       if (!st->dev_id) {
+       } else if (!strcmp(name, MX_NAME_MAX44005)) {
+               st->dev_id = MX_DEVID_MAX44005;
+               st->part = MX_NAME_MAX44005;
+       } else if (!strcmp(name, MX_NAME)) {
+               /* There is no way to auto-detect the device since the
+                * MX44006/8 has actual proximity HW that works but just
+                * doesn't have the undetectable LED driver HW.  And of
+                * course there isn't an ID register either.  So we'll
+                * just confirm that our device exists and default to the
+                * MX44005 with proximity support if using max4400x.
+                */
+               st->dev_id = MX_DEVID_MAX44005;
+               st->part = MX_NAME_MAX44005;
                ret = mx_i2c_rd(st, MX_REG_STS, &val);
                if (ret)
                        return -ENODEV;
 
-               if (val != (1 << MX_REG_STS_PWRON))
+               if (val != MX_REG_STS_POR)
                        return -ENODEV;
-
-               ret = mx_i2c_rd(st, MX_REG_CFG_PRX, &val);
-               if (!ret) {
-                       if (val == (1 << MX_REG_CFG_PRX_PRXTIM)) {
-                               st->dev_id = MX_DEVID_MAX44005;
-                               st->part = MX_NAME_MAX44005;
-                       } else {
-                               st->dev_id = MX_DEVID_MAX44006;
-                               st->part = MX_NAME_MAX44006;
-                       }
-                       dev_info(&st->i2c->dev, "%s found %s\n",
-                               __func__, st->part);
-               }
        }
+
        return ret;
 }
 
@@ -1928,7 +1946,6 @@ static int mx_id_i2c(struct iio_dev *indio_dev, const char *name)
                st->i2c_addr = st->i2c->addr;
                ret = mx_id_dev(indio_dev, name);
        } else {
-               name = MX_NAME;
                for (i = 0; i < ARRAY_SIZE(mx_i2c_addrs); i++) {
                        st->i2c_addr = mx_i2c_addrs[i];
                        ret = mx_id_dev(indio_dev, name);
@@ -1936,6 +1953,8 @@ static int mx_id_i2c(struct iio_dev *indio_dev, const char *name)
                                break;
                }
        }
+       if (ret)
+               st->i2c_addr = 0;
        return ret;
 }
 
@@ -2041,9 +2060,16 @@ static const struct mx_iio_float mx_peak_scale_dflt[] = {
        },
 };
 
+static const unsigned int mx_threshold_dflt[] = {
+       MX_LIGHT_THRESHOLD_DFLT,
+       MX_TEMP_THRESHOLD_DFLT,
+       MX_PROX_THRESHOLD_DFLT,
+};
+
 static int mx_parse_dt(struct i2c_client *client, struct mx_state *st)
 {
-       struct device_node *np = client->dev.of_node;
+       struct device_node *dn = client->dev.of_node;
+       unsigned int thresh;
        unsigned int i;
 
        /* default device specific parameters */
@@ -2059,102 +2085,106 @@ static int mx_parse_dt(struct i2c_client *client, struct mx_state *st)
                st->peak_val2[i] = mx_peak_dflt[i].micro;
                st->peak_scale_val[i] = mx_peak_scale_dflt[i].ival;
                st->peak_scale_val2[i] = mx_peak_scale_dflt[i].micro;
+               if (st->scale_val2[i]) {
+                       thresh = 1000000 / st->scale_val2[i];
+                       thresh *= mx_threshold_dflt[i];
+               } else {
+                       thresh = mx_threshold_dflt[i];
+               }
+               st->thr_lo[i] = thresh;
+               st->thr_hi[i] = thresh;
        }
-
-       if (st->scale_val2[MX_DEV_LIGHT]) {
-               st->lux_thr_lo = 1000000 / st->scale_val2[MX_DEV_LIGHT];
-               st->lux_thr_hi = 1000000 / st->scale_val2[MX_DEV_LIGHT];
-       }
-       st->lux_thr_lo *= MX_THRESHOLD_LUX_DFLT;
-       st->lux_thr_hi *= MX_THRESHOLD_LUX_DFLT;
-       /* proximity has binary threshold */
-       st->prx_thr_bin = 1;
-       st->prx_thr_lo = MX_THRESHOLD_PROX_DFLT;
-       st->prx_thr_hi = MX_THRESHOLD_PROX_DFLT;
+       st->prx_thr_bin = MX_PROX_THRESHOLD_BINARY_DFLT;
        /* device tree parameters */
        if (client->dev.of_node) {
                /* device specific parameters */
-               of_property_read_u8(np, "ambient_cfg_reg", &st->amb_cfg);
-               of_property_read_u8(np, "proximity_cfg_reg", &st->prx_cfg);
-               of_property_read_u8(np, "threshold_persist_reg", &st->thr_cfg);
+               of_property_read_u8(dn, "ambient_cfg_reg", &st->amb_cfg);
+               of_property_read_u8(dn, "proximity_cfg_reg", &st->prx_cfg);
+               of_property_read_u8(dn, "threshold_persist_reg", &st->thr_cfg);
                /* common NVS programmable parameters */
-               of_property_read_u32(np, "report_count", &st->report_n);
+               st->iio_ts_en = of_property_read_bool(dn, "iio_timestamps");
+               of_property_read_u32(dn, "report_count", &st->report_n);
                /* common NVS ALS programmable parameters */
-               of_property_read_s32(np, "light_uncalibrated_lo",
+               of_property_read_s32(dn, "light_uncalibrated_lo",
                                     &st->i_uc_lo[MX_DEV_LIGHT]);
-               of_property_read_s32(np, "light_uncalibrated_hi",
+               of_property_read_s32(dn, "light_uncalibrated_hi",
                                     &st->i_uc_hi[MX_DEV_LIGHT]);
-               of_property_read_s32(np, "light_calibrated_lo",
+               of_property_read_s32(dn, "light_calibrated_lo",
                                     &st->i_c_lo[MX_DEV_LIGHT]);
-               of_property_read_s32(np, "light_calibrated_hi",
+               of_property_read_s32(dn, "light_calibrated_hi",
                                     &st->i_c_hi[MX_DEV_LIGHT]);
-               of_property_read_s32(np, "light_max_range_val",
+               of_property_read_s32(dn, "light_max_range_val",
                                     &st->peak_val[MX_DEV_LIGHT]);
-               of_property_read_s32(np, "light_max_range_val2",
+               of_property_read_s32(dn, "light_max_range_val2",
                                     &st->peak_val2[MX_DEV_LIGHT]);
-               of_property_read_s32(np, "light_resolution_val",
+               of_property_read_s32(dn, "light_resolution_val",
                                     &st->peak_scale_val[MX_DEV_LIGHT]);
-               of_property_read_s32(np, "light_resolution_val2",
+               of_property_read_s32(dn, "light_resolution_val2",
                                     &st->peak_scale_val2[MX_DEV_LIGHT]);
-               of_property_read_s32(np, "light_scale_val",
+               of_property_read_s32(dn, "light_scale_val",
                                     &st->scale_val[MX_DEV_LIGHT]);
-               of_property_read_s32(np, "light_scale_val2",
+               of_property_read_s32(dn, "light_scale_val2",
                                     &st->scale_val2[MX_DEV_LIGHT]);
-               of_property_read_s32(np, "light_offset_val",
+               of_property_read_s32(dn, "light_offset_val",
                                     &st->offset_val[MX_DEV_LIGHT]);
-               of_property_read_s32(np, "light_offset_val2",
+               of_property_read_s32(dn, "light_offset_val2",
                                     &st->offset_val2[MX_DEV_LIGHT]);
-               of_property_read_u32(np, "light_threshold_lo",
-                                    &st->lux_thr_lo);
-               of_property_read_u32(np, "light_threshold_hi",
-                                    &st->lux_thr_hi);
+               of_property_read_u32(dn, "light_threshold_lo",
+                                    &st->thr_lo[MX_DEV_LIGHT]);
+               of_property_read_u32(dn, "light_threshold_hi",
+                                    &st->thr_hi[MX_DEV_LIGHT]);
                /* common NVS temp programmable parameters */
-               of_property_read_s32(np, "temp_max_range_val",
+               of_property_read_s32(dn, "temp_max_range_val",
                                     &st->peak_val[MX_DEV_TEMP]);
-               of_property_read_s32(np, "temp_max_range_val2",
+               of_property_read_s32(dn, "temp_max_range_val2",
                                     &st->peak_val2[MX_DEV_TEMP]);
-               of_property_read_s32(np, "temp_resolution_val",
+               of_property_read_s32(dn, "temp_resolution_val",
                                     &st->peak_scale_val[MX_DEV_TEMP]);
-               of_property_read_s32(np, "temp_resolution_val2",
+               of_property_read_s32(dn, "temp_resolution_val2",
                                     &st->peak_scale_val2[MX_DEV_TEMP]);
-               of_property_read_s32(np, "temp_scale_val",
+               of_property_read_s32(dn, "temp_scale_val",
                                     &st->scale_val[MX_DEV_TEMP]);
-               of_property_read_s32(np, "temp_scale_val2",
+               of_property_read_s32(dn, "temp_scale_val2",
                                     &st->scale_val2[MX_DEV_TEMP]);
-               of_property_read_s32(np, "temp_offset_val",
+               of_property_read_s32(dn, "temp_offset_val",
                                     &st->offset_val[MX_DEV_TEMP]);
-               of_property_read_s32(np, "temp_offset_val2",
+               of_property_read_s32(dn, "temp_offset_val2",
                                     &st->offset_val2[MX_DEV_TEMP]);
+               of_property_read_u32(dn, "temp_threshold_lo",
+                                    &st->thr_lo[MX_DEV_TEMP]);
+               of_property_read_u32(dn, "temp_threshold_hi",
+                                    &st->thr_hi[MX_DEV_TEMP]);
                /* common NVS proximity programmable parameters */
-               of_property_read_s32(np, "proximity_uncalibrated_lo",
+               of_property_read_s32(dn, "proximity_uncalibrated_lo",
                                     &st->i_uc_lo[MX_DEV_PROX]);
-               of_property_read_s32(np, "proximity_uncalibrated_hi",
+               of_property_read_s32(dn, "proximity_uncalibrated_hi",
                                     &st->i_uc_hi[MX_DEV_PROX]);
-               of_property_read_s32(np, "proximity_calibrated_lo",
+               of_property_read_s32(dn, "proximity_calibrated_lo",
                                     &st->i_c_lo[MX_DEV_PROX]);
-               of_property_read_s32(np, "proximity_calibrated_hi",
+               of_property_read_s32(dn, "proximity_calibrated_hi",
                                     &st->i_c_hi[MX_DEV_PROX]);
-               of_property_read_s32(np, "proximity_max_range_val",
+               of_property_read_s32(dn, "proximity_max_range_val",
                                     &st->peak_val[MX_DEV_PROX]);
-               of_property_read_s32(np, "proximity_max_range_val2",
+               of_property_read_s32(dn, "proximity_max_range_val2",
                                     &st->peak_val2[MX_DEV_PROX]);
-               of_property_read_s32(np, "proximity_resolution_val",
+               of_property_read_s32(dn, "proximity_resolution_val",
                                     &st->peak_scale_val[MX_DEV_PROX]);
-               of_property_read_s32(np, "proximity_resolution_val2",
+               of_property_read_s32(dn, "proximity_resolution_val2",
                                     &st->peak_scale_val2[MX_DEV_PROX]);
-               of_property_read_s32(np, "proximity_scale_val",
+               of_property_read_s32(dn, "proximity_scale_val",
                                     &st->scale_val[MX_DEV_PROX]);
-               of_property_read_s32(np, "proximity_scale_val2",
+               of_property_read_s32(dn, "proximity_scale_val2",
                                     &st->scale_val2[MX_DEV_PROX]);
-               of_property_read_s32(np, "proximity_offset_val",
+               of_property_read_s32(dn, "proximity_offset_val",
                                     &st->offset_val[MX_DEV_PROX]);
-               of_property_read_s32(np, "proximity_offset_val2",
+               of_property_read_s32(dn, "proximity_offset_val2",
                                     &st->offset_val2[MX_DEV_PROX]);
-               of_property_read_u32(np, "proximity_threshold_lo",
-                                    &st->prx_thr_lo);
-               of_property_read_u32(np, "proximity_threshold_hi",
-                                    &st->prx_thr_hi);
-               of_property_read_u32(np, "proximity_binary_threshold",
+               /* proximity threshold lo/hi are reversed */
+               of_property_read_u32(dn, "proximity_threshold_lo",
+                                    &st->thr_hi[MX_DEV_PROX]);
+               of_property_read_u32(dn, "proximity_threshold_hi",
+                                    &st->thr_lo[MX_DEV_PROX]);
+               of_property_read_u32(dn, "proximity_binary_threshold",
                                     &st->prx_thr_bin);
                if (st->prx_thr_bin) {
                        /* proximity has binary threshold */
@@ -2168,6 +2198,8 @@ static int mx_parse_dt(struct i2c_client *client, struct mx_state *st)
                        st->offset_val2[MX_DEV_PROX] = 0;
                }
        }
+       if (!st->report_n)
+               st->report_n = MX_REPORT_N;
        return 0;
 }
 
@@ -2175,6 +2207,7 @@ static int mx_probe(struct i2c_client *client, const struct i2c_device_id *id)
 {
        struct iio_dev *indio_dev;
        struct mx_state *st;
+       unsigned long irqflags;
        unsigned int i;
        int ret;
 
@@ -2241,7 +2274,7 @@ static int mx_probe(struct i2c_client *client, const struct i2c_device_id *id)
 
        INIT_DELAYED_WORK(&st->dw, mx_work);
        if (client->irq) {
-               unsigned long irqflags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
+               irqflags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
                if (st->dev_id == MX_DEVID_MAX44005)
                        irqflags |= IRQF_NO_SUSPEND; /* for proximity */
                ret = request_threaded_irq(client->irq, NULL, mx_irq_thread,
index 401e946..0d7ee78 100644 (file)
 #include <linux/slab.h>
 #include <linux/err.h>
 #include <linux/delay.h>
+#include <linux/ktime.h>
 #include <linux/regulator/consumer.h>
 #include <linux/workqueue.h>
 #include <linux/interrupt.h>
 #include <linux/mpu_iio.h>
 #endif /* AKM_NVI_MPU_SUPPORT */
 
-#define AKM_VERSION_DRIVER             (101)
+#define AKM_VERSION_DRIVER             (102)
 #define AKM_VENDOR                     "AsahiKASEI"
 #define AKM_NAME                       "ak89xx"
 #define AKM_NAME_AK8963                        "ak8963"
@@ -264,8 +265,10 @@ struct akm_state {
        unsigned int fifo_max;          /* fifoMaxEventCount */
        u16 i2c_addr;                   /* I2C address */
        u8 dev_id;                      /* device ID */
+       bool iio_ts_en;                 /* use IIO timestamps */
        bool shutdown;                  /* shutdown active flag */
        bool suspend;                   /* suspend active flag */
+       bool irq_dis;                   /* interrupt host disable flag */
        bool initd;                     /* set if initialized */
        bool mpu_en;                    /* if device behind MPU */
        bool fifo_en;                   /* MPU FIFO enable */
@@ -303,6 +306,17 @@ struct akm_hal {
 };
 
 
+static s64 akm_get_time_ns(struct akm_state *st)
+{
+       struct timespec ts;
+
+       if (st->iio_ts_en)
+               return iio_get_time_ns();
+
+       ktime_get_ts(&ts);
+       return timespec_to_ns(&ts);
+}
+
 static void akm_err(struct akm_state *st)
 {
        st->errs++;
@@ -335,15 +349,17 @@ static int akm_i2c_wr(struct akm_state *st, u8 reg, u8 val)
        struct i2c_msg msg;
        u8 buf[2];
 
-       buf[0] = reg;
-       buf[1] = val;
-       msg.addr = st->i2c_addr;
-       msg.flags = 0;
-       msg.len = 2;
-       msg.buf = buf;
-       if (i2c_transfer(st->i2c->adapter, &msg, 1) != 1) {
-               akm_err(st);
-               return -EIO;
+       if (st->i2c_addr) {
+               buf[0] = reg;
+               buf[1] = val;
+               msg.addr = st->i2c_addr;
+               msg.flags = 0;
+               msg.len = 2;
+               msg.buf = buf;
+               if (i2c_transfer(st->i2c->adapter, &msg, 1) != 1) {
+                       akm_err(st);
+                       return -EIO;
+               }
        }
 
        return 0;
@@ -732,6 +748,9 @@ static void akm_buf_push(struct iio_dev *indio_dev, s64 ts)
        unsigned int bytes = 0;
        unsigned int axis;
 
+       if (!iio_buffer_enabled(indio_dev))
+               return;
+
        for (axis = 0; axis < AXIS_N; axis++) {
                if (iio_scan_mask_query(indio_dev, indio_dev->buffer, axis)) {
                        n = sizeof(st->magn[axis]);
@@ -744,8 +763,7 @@ static void akm_buf_push(struct iio_dev *indio_dev, s64 ts)
                i = akm_buf_index(n, &bytes);
                memcpy(&buf[i], &ts, n);
        }
-       if (iio_buffer_enabled(indio_dev))
-               iio_push_to_buffers(indio_dev, buf);
+       iio_push_to_buffers(indio_dev, buf);
        if (st->dbg & AKM_DBG_SPEW_MAGNETIC_FIELD_UC)
                dev_info(&st->i2c->dev, "uc %hd %hd %hd %lld\n",
                         st->magn_uc[AXIS_X], st->magn_uc[AXIS_Y],
@@ -819,7 +837,7 @@ static int akm_read(struct iio_dev *indio_dev)
        if (ret)
                return ret;
 
-       ts = iio_get_time_ns();
+       ts = akm_get_time_ns(st);
        ret = akm_read_sts(st, data);
        if (ret > 0) {
                akm_calc(st, data, ts);
@@ -987,6 +1005,22 @@ static int akm_init_hw(struct akm_state *st)
        return ret;
 }
 
+static void nvi_disable_irq(struct akm_state *st)
+{
+       if (!st->irq_dis) {
+               disable_irq_nosync(st->i2c->irq);
+               st->irq_dis = true;
+       }
+}
+
+static void nvi_enable_irq(struct akm_state *st)
+{
+       if (st->irq_dis) {
+               enable_irq(st->i2c->irq);
+               st->irq_dis = false;
+       }
+}
+
 static int akm_dis(struct akm_state *st)
 {
        int ret = 0;
@@ -995,7 +1029,7 @@ static int akm_dis(struct akm_state *st)
                ret = akm_ports_enable(st, false);
        } else {
                if (st->i2c->irq)
-                       disable_irq(st->i2c->irq);
+                       nvi_disable_irq(st);
                else
                        cancel_delayed_work_sync(&st->dw);
        }
@@ -1053,7 +1087,7 @@ static int akm_enable(struct iio_dev *indio_dev)
                                st->enable = 1;
                                if (!st->mpu_en) {
                                        if (st->i2c->irq)
-                                               enable_irq(st->i2c->irq);
+                                               nvi_enable_irq(st);
                                        else
                                                schedule_delayed_work(&st->dw,
                                                              usecs_to_jiffies(
@@ -2144,7 +2178,7 @@ static int akm_id_dev(struct iio_dev *indio_dev, const char *name)
        }
        if (st->i2c->irq && !ret) {
                if ((st->hal->cmode_tbl == NULL) || !st->hal->irq) {
-                       disable_irq(st->i2c->irq);
+                       nvi_disable_irq(st);
                        st->i2c->irq = 0;
                }
        }
@@ -2174,6 +2208,8 @@ static int akm_id_i2c(struct iio_dev *indio_dev,
                                break;
                }
        }
+       if (ret)
+               st->i2c_addr = 0;
        return ret;
 }
 
@@ -2226,6 +2262,9 @@ static int akm_of_dt(struct i2c_client *client, struct akm_state *st)
        int len;
        u8 cfg;
 
+       /* common NVS programmable parameters */
+       st->iio_ts_en = of_property_read_bool(dn, "iio_timestamps");
+       /* magn programmable parameters */
        pchar = of_get_property(dn, "matrix", &len);
        if (pchar && len == sizeof(st->matrix)) {
                memcpy(&st->matrix, pchar, len);
@@ -2234,6 +2273,7 @@ static int akm_of_dt(struct i2c_client *client, struct akm_state *st)
                if (pchar && len == sizeof(st->matrix))
                        memcpy(&st->matrix, pchar, len);
        }
+       /* this device supports these programmable parameters */
        if (!(of_property_read_string(dn, "nvi_config", &pchar))) {
                for (cfg = 0; cfg < ARRAY_SIZE(akm_configs); cfg++) {
                        if (!strcasecmp(pchar, akm_configs[cfg])) {
index ef436bd..af808af 100644 (file)
 #include <linux/slab.h>
 #include <linux/err.h>
 #include <linux/delay.h>
+#include <linux/ktime.h>
 #include <linux/regulator/consumer.h>
 #include <linux/workqueue.h>
 #include <linux/of.h>
 #include <linux/mpu_iio.h>
 #endif /* BMP_NVI_MPU_SUPPORT */
 
-#define BMP_VERSION_DRIVER             (101)
+#define BMP_VERSION_DRIVER             (102)
 #define BMP_VENDOR                     "Bosch"
 #define BMP_NAME                       "bmpX80"
 #define BMP180_NAME                    "bmp180"
@@ -352,6 +353,7 @@ struct bmp_state {
        unsigned int fifo_max;          /* fifoMaxEventCount */
        u16 i2c_addr;                   /* I2C address */
        u8 dev_id;                      /* device ID */
+       bool iio_ts_en;                 /* use IIO timestamps */
        bool shutdown;                  /* shutdown active flag */
        bool suspend;                   /* suspend active flag */
        bool initd;                     /* set if initialized */
@@ -383,6 +385,17 @@ struct bmp_hal {
 };
 
 
+static s64 bmp_get_time_ns(struct bmp_state *st)
+{
+       struct timespec ts;
+
+       if (st->iio_ts_en)
+               return iio_get_time_ns();
+
+       ktime_get_ts(&ts);
+       return timespec_to_ns(&ts);
+}
+
 static void bmp_err(struct bmp_state *st)
 {
        st->errs++;
@@ -415,15 +428,17 @@ static int bmp_i2c_wr(struct bmp_state *st, u8 reg, u8 val)
        struct i2c_msg msg;
        u8 buf[2];
 
-       buf[0] = reg;
-       buf[1] = val;
-       msg.addr = st->i2c_addr;
-       msg.flags = 0;
-       msg.len = 2;
-       msg.buf = buf;
-       if (i2c_transfer(st->i2c->adapter, &msg, 1) != 1) {
-               bmp_err(st);
-               return -EIO;
+       if (st->i2c_addr) {
+               buf[0] = reg;
+               buf[1] = val;
+               msg.addr = st->i2c_addr;
+               msg.flags = 0;
+               msg.len = 2;
+               msg.buf = buf;
+               if (i2c_transfer(st->i2c->adapter, &msg, 1) != 1) {
+                       bmp_err(st);
+                       return -EIO;
+               }
        }
 
        return 0;
@@ -807,31 +822,34 @@ static void bmp_buf_push(struct iio_dev *indio_dev, s64 ts)
        unsigned int i;
        unsigned int bytes = 0;
 
+       if (!iio_buffer_enabled(indio_dev))
+               return;
+
        if (iio_scan_mask_query(indio_dev, indio_dev->buffer, BMP_SCAN_PRES)) {
                n = sizeof(st->pressure);
                i = bmp_buf_index(n, &bytes);
                memcpy(&buf[i], &st->pressure, n);
+               if (st->dbg & BMP_DBG_SPEW_PRESSURE_RAW)
+                       dev_info(&st->i2c->dev, "pr %d %lld\n", st->up, ts);
+               if (st->dbg & BMP_DBG_SPEW_PRESSURE)
+                       dev_info(&st->i2c->dev, "p %d %lld\n",
+                                st->pressure, ts);
        }
        if (iio_scan_mask_query(indio_dev, indio_dev->buffer, BMP_SCAN_TEMP)) {
                n = sizeof(st->temp);
                i = bmp_buf_index(n, &bytes);
                memcpy(&buf[i], &st->temp, n);
+               if (st->dbg & BMP_DBG_SPEW_TEMPERATURE_UC)
+                       dev_info(&st->i2c->dev, "tr %d %lld\n", st->ut, ts);
+               if (st->dbg & BMP_DBG_SPEW_TEMPERATURE)
+                       dev_info(&st->i2c->dev, "t %d %lld\n", st->temp, ts);
        }
        if (indio_dev->buffer->scan_timestamp) {
                n = sizeof(ts);
                i = bmp_buf_index(n, &bytes);
                memcpy(&buf[i], &ts, n);
        }
-       if (iio_buffer_enabled(indio_dev))
-               iio_push_to_buffers(indio_dev, buf);
-       if (st->dbg & BMP_DBG_SPEW_PRESSURE_RAW)
-               dev_info(&st->i2c->dev, "pr %d %lld\n", st->up, ts);
-       if (st->dbg & BMP_DBG_SPEW_PRESSURE)
-               dev_info(&st->i2c->dev, "p %d %lld\n", st->pressure, ts);
-       if (st->dbg & BMP_DBG_SPEW_TEMPERATURE_UC)
-               dev_info(&st->i2c->dev, "tr %d %lld\n", st->ut, ts);
-       if (st->dbg & BMP_DBG_SPEW_TEMPERATURE)
-               dev_info(&st->i2c->dev, "t %d %lld\n", st->temp, ts);
+       iio_push_to_buffers(indio_dev, buf);
 }
 
 static void bmp_calc_180(struct bmp_state *st)
@@ -903,7 +921,7 @@ static int bmp_read_180(struct iio_dev *indio_dev)
        if (ret)
                return ret;
 
-       ts = iio_get_time_ns();
+       ts = bmp_get_time_ns(st);
        ret = bmp_read_sts_180(st, data, ts);
        if (ret > 0) {
                bmp_buf_push(indio_dev, ts);
@@ -1027,7 +1045,7 @@ static int bmp_read_280(struct iio_dev *indio_dev)
        if (ret)
                return ret;
 
-       ts = iio_get_time_ns();
+       ts = bmp_get_time_ns(st);
        ret = bmp_read_sts_280(st, data, ts);
        if (ret > 0) {
                bmp_buf_push(indio_dev, ts);
@@ -2501,6 +2519,8 @@ static int bmp_id_i2c(struct iio_dev *indio_dev,
                                break;
                }
        }
+       if (ret)
+               st->i2c_addr = 0;
        return ret;
 }
 
@@ -2550,6 +2570,9 @@ static int bmp_of_dt(struct i2c_client *client, struct bmp_state *st)
        char const *pchar;
        u8 cfg;
 
+       /* common NVS programmable parameters */
+       st->iio_ts_en = of_property_read_bool(dn, "iio_timestamps");
+       /* this device supports these programmable parameters */
        if (!(of_property_read_string(dn, "nvi_config", &pchar))) {
                for (cfg = 0; cfg < ARRAY_SIZE(bmp_configs); cfg++) {
                        if (!strcasecmp(pchar, bmp_configs[cfg])) {