iio: light: Add WAKE_ON capability
Erik Lilliebjerg [Fri, 23 Oct 2015 08:44:23 +0000 (01:44 -0700)]
- Add ability to wake AP from proximity (WAKE_ON) sensors.
- Add proximity debug dump.
- Add NVS light and proximity module versioning.
- Fix light enable initial poll delay.
- Add CM32180 ARA WAR device tree configuration option.  If enabled, the WAR
  does an ARA before every I2C transaction regardless of the interrupt GPIO
  status.  The DT option: ARA_WAR = <1>;
  This is an extension to the current WAR that tests the interrupt GPIO to
  determine if an ARA is needed.

Bug 200122255
Bug 200121777

Change-Id: Id334d165cd7f6962c47592392089c55e70b50d22
Signed-off-by: Erik Lilliebjerg <elilliebjerg@nvidia.com>
Reviewed-on: http://git-master/r/821953
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Robert Collins <rcollins@nvidia.com>
Tested-by: Robert Collins <rcollins@nvidia.com>

drivers/iio/common/nvs/nvs_light.c
drivers/iio/common/nvs/nvs_proximity.c
drivers/iio/light/nvs_cm3218.c
drivers/iio/light/nvs_isl2902x.c
drivers/iio/light/nvs_ltr659.c
drivers/iio/light/nvs_max4400x.c
include/linux/nvs_proximity.h

index 0acb706..af35730 100644 (file)
 #include <linux/of.h>
 #include <linux/nvs_light.h>
 
+#define NVS_LIGHT_VERSION              (100)
+
 
 ssize_t nvs_light_dbg(struct nvs_light *nl, char *buf)
 {
        ssize_t t;
        unsigned int i;
 
-       t = sprintf(buf, "%s:\n", __func__);
-       t += sprintf(buf + t, "nvs_light.timestamp=%lld\n", nl->timestamp);
-       t += sprintf(buf + t, "nvs_light.timestamp_report=%lld\n",
-                    nl->timestamp_report);
-       t += sprintf(buf + t, "nvs_light.lux=%u\n", nl->lux);
-       t += sprintf(buf + t, "nvs_light.hw=%u\n", nl->hw);
-       t += sprintf(buf + t, "nvs_light.hw_mask=%x\n", nl->hw_mask);
-       t += sprintf(buf + t, "nvs_light.hw_thresh_lo=%u\n", nl->hw_thresh_lo);
-       t += sprintf(buf + t, "nvs_light.hw_thresh_hi=%u\n", nl->hw_thresh_hi);
-       t += sprintf(buf + t, "nvs_light.hw_limit_lo=%x\n", nl->hw_limit_lo);
-       t += sprintf(buf + t, "nvs_light.hw_limit_hi=%x\n", nl->hw_limit_hi);
-       t += sprintf(buf + t, "nvs_light.thresh_valid_lo=%x\n",
-                    nl->thresh_valid_lo);
-       t += sprintf(buf + t, "nvs_light.thresh_valid_hi=%x\n",
-                    nl->thresh_valid_hi);
-       t += sprintf(buf + t, "nvs_light.thresholds_valid=%x\n",
-                    nl->thresholds_valid);
-       t += sprintf(buf + t, "nvs_light.nld_i_change=%x\n", nl->nld_i_change);
-       t += sprintf(buf + t, "nvs_light.calibration_en=%x\n",
-                    nl->calibration_en);
-       t += sprintf(buf + t, "nvs_light.poll_delay_ms=%u\n",
-                    nl->poll_delay_ms);
-       t += sprintf(buf + t, "nvs_light.delay_us=%u\n", nl->delay_us);
-       t += sprintf(buf + t, "nvs_light.report=%u\n", nl->report);
-       t += sprintf(buf + t, "nvs_light.nld_i=%u\n", nl->nld_i);
-       t += sprintf(buf + t, "nvs_light.nld_i_lo=%u\n", nl->nld_i_lo);
-       t += sprintf(buf + t, "nvs_light.nld_i_hi=%u\n", nl->nld_i_hi);
+       t = sprintf(buf, "%s v.%u:\n", __func__, NVS_LIGHT_VERSION);
+       t += sprintf(buf + t, "timestamp=%lld\n", nl->timestamp);
+       t += sprintf(buf + t, "timestamp_report=%lld\n", nl->timestamp_report);
+       t += sprintf(buf + t, "lux=%u\n", nl->lux);
+       t += sprintf(buf + t, "hw=%u\n", nl->hw);
+       t += sprintf(buf + t, "hw_mask=%x\n", nl->hw_mask);
+       t += sprintf(buf + t, "hw_thresh_lo=%u\n", nl->hw_thresh_lo);
+       t += sprintf(buf + t, "hw_thresh_hi=%u\n", nl->hw_thresh_hi);
+       t += sprintf(buf + t, "hw_limit_lo=%x\n", nl->hw_limit_lo);
+       t += sprintf(buf + t, "hw_limit_hi=%x\n", nl->hw_limit_hi);
+       t += sprintf(buf + t, "thresh_valid_lo=%x\n", nl->thresh_valid_lo);
+       t += sprintf(buf + t, "thresh_valid_hi=%x\n", nl->thresh_valid_hi);
+       t += sprintf(buf + t, "thresholds_valid=%x\n", nl->thresholds_valid);
+       t += sprintf(buf + t, "nld_i_change=%x\n", nl->nld_i_change);
+       t += sprintf(buf + t, "calibration_en=%x\n", nl->calibration_en);
+       t += sprintf(buf + t, "poll_delay_ms=%u\n", nl->poll_delay_ms);
+       t += sprintf(buf + t, "delay_us=%u\n", nl->delay_us);
+       t += sprintf(buf + t, "report=%u\n", nl->report);
+       t += sprintf(buf + t, "nld_i=%u\n", nl->nld_i);
+       t += sprintf(buf + t, "nld_i_lo=%u\n", nl->nld_i_lo);
+       t += sprintf(buf + t, "nld_i_hi=%u\n", nl->nld_i_hi);
        if (nl->nld_tbl) {
                for (i = nl->nld_i_lo; i <= nl->nld_i_hi; i++) {
                        if (nl->cfg->float_significance) {
@@ -459,8 +455,7 @@ int nvs_light_enable(struct nvs_light *nl)
        nl->hw_thresh_lo = -1;
        if (nl->nld_tbl)
                nvs_light_nld(nl, nl->nld_i_hi);
-       else
-               nl->poll_delay_ms = nl->cfg->delay_us_min / 1000;
+       nl->poll_delay_ms = nl->cfg->delay_us_min / 1000;
        if (nl->cfg->scale.ival == 1 && !nl->cfg->scale.fval)
                nl->calibration_en = true;
        else
index cb1bc41..93d636c 100644 (file)
 #include <linux/of.h>
 #include <linux/nvs_proximity.h>
 
-
-/* to allow 1980's code style rules: */
+#define NVS_PROXIMITY_VERSION          (100)
 #define NVS_FS_NANO                    NVS_FLOAT_SIGNIFICANCE_NANO
 #define NVS_FS_MICRO                   NVS_FLOAT_SIGNIFICANCE_MICRO
 
 
+ssize_t nvs_proximity_dbg(struct nvs_proximity *np, char *buf)
+{
+       ssize_t t;
+
+       t = sprintf(buf, "%s v.%u:\n", __func__, NVS_PROXIMITY_VERSION);
+       t += sprintf(buf + t, "timestamp=%lld\n", np->timestamp);
+       t += sprintf(buf + t, "timestamp_report=%lld\n", np->timestamp_report);
+       t += sprintf(buf + t, "proximity=%u\n", np->proximity);
+       t += sprintf(buf + t, "hw=%u\n", np->hw);
+       t += sprintf(buf + t, "hw_mask=%x\n", np->hw_mask);
+       t += sprintf(buf + t, "hw_thresh_lo=%u\n", np->hw_thresh_lo);
+       t += sprintf(buf + t, "hw_thresh_hi=%u\n", np->hw_thresh_hi);
+       t += sprintf(buf + t, "hw_limit_lo=%x\n", np->hw_limit_lo);
+       t += sprintf(buf + t, "hw_limit_hi=%x\n", np->hw_limit_hi);
+       t += sprintf(buf + t, "thresh_valid_lo=%x\n", np->thresh_valid_lo);
+       t += sprintf(buf + t, "thresh_valid_hi=%x\n", np->thresh_valid_hi);
+       t += sprintf(buf + t, "thresholds_valid=%x\n", np->thresholds_valid);
+       t += sprintf(buf + t, "calibration_en=%x\n", np->calibration_en);
+       t += sprintf(buf + t, "dynamic_resolution_dis=%x\n",
+                    np->dynamic_resolution_dis);
+       t += sprintf(buf + t, "proximity_reverse_range_dis=%x\n",
+                    np->proximity_reverse_range_dis);
+       t += sprintf(buf + t, "proximity_binary_en=%x\n",
+                    np->proximity_binary_en);
+       t += sprintf(buf + t, "proximity_binary_hw=%x\n",
+                    np->proximity_binary_hw);
+       t += sprintf(buf + t, "poll_delay_ms=%u\n", np->poll_delay_ms);
+       t += sprintf(buf + t, "delay_us=%u\n", np->delay_us);
+       t += sprintf(buf + t, "report=%u\n", np->report);
+       return t;
+}
+
 static void nvs_proximity_interpolate(int x1, s64 x2, int x3,
                                      int y1, u32 *y2, int y3)
 {
index 359d3d9..23f4cc9 100644 (file)
@@ -29,7 +29,7 @@
 #include <linux/nvs.h>
 #include <linux/nvs_light.h>
 
-#define CM_DRIVER_VERSION              (300)
+#define CM_DRIVER_VERSION              (301)
 #define CM_VENDOR                      "Capella Microsystems, Inc."
 #define CM_NAME                                "cm3218x"
 #define CM_NAME_CM3218                 "cm3218"
@@ -142,6 +142,7 @@ struct cm_state {
        unsigned int errs;              /* error count */
        unsigned int enabled;           /* enable status */
        int gpio_irq;                   /* interrupt GPIO */
+       bool ara_war;                   /* force ARA on every I2C */
        u16 i2c_addr;                   /* I2C address */
        u8 dev_id;                      /* device ID */
        u16 als_cfg_mask;               /* ALS register 0 user mask */
@@ -191,7 +192,7 @@ static int cm_i2c_rd(struct cm_state *st, u8 reg, u16 *val)
        struct i2c_msg msg[2];
        int ret;
 
-       cm_irq_ack(st, false);
+       cm_irq_ack(st, st->ara_war);
        msg[0].addr = st->i2c_addr;
        msg[0].flags = 0;
        msg[0].len = 1;
@@ -573,16 +574,17 @@ static int cm_nvs_read(void *client, int snsr_id, char *buf)
        ssize_t t;
        int ret;
 
-       t = sprintf(buf, "driver v. %u\n", CM_DRIVER_VERSION);
+       t = sprintf(buf, "driver v.%u\n", CM_DRIVER_VERSION);
        if (st->ara && st->gpio_irq >= 0) {
                ret = gpio_get_value(st->gpio_irq);
                t += sprintf(buf + t, "gpio_irq %d=%d\n", st->gpio_irq, ret);
+               t += sprintf(buf + t, "ARA_WAR=%x\n", st->ara_war);
        }
        t += sprintf(buf + t, "irq=%d\n", st->i2c->irq);
        t += sprintf(buf + t, "als_cfg=%hx\n", st->als_cfg);
        if (st->dev_id == CM_DEVID_CM32181)
                t += sprintf(buf + t, "als_psm=%hx\n", st->als_psm);
-       return t;
+       return nvs_light_dbg(&st->light, buf + t);
 }
 
 static struct nvs_fn_dev cm_fn_dev = {
@@ -823,6 +825,10 @@ static int cm_of_dt(struct cm_state *st, struct device_node *dn)
                of_property_read_u16(dn, "als_psm", &st->als_psm);
                st->als_psm &= CM_REG_PSM_MASK;
                of_property_read_u32(dn, "Rset", &st->r_set);
+               ret = 0;
+               of_property_read_s32(dn, "ARA_WAR", &ret);
+               if (ret)
+                       st->ara_war = true;
                st->gpio_irq = of_get_named_gpio(dn, "gpio_irq", 0);
        }
 
index 2a07dd5..4dd1512 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/nvs_proximity.h>
 
 
+#define ISL_DRIVER_VERSION             (2)
 #define ISL_VENDOR                     "InterSil"
 #define ISL_NAME                       "isl2902x"
 #define ISL_NAME_ISL29028              "isl29028"
@@ -126,6 +127,7 @@ struct isl_state {
        unsigned int errs;              /* error count */
        unsigned int enabled;           /* enable status */
        bool irq_dis;                   /* interrupt host disable flag */
+       bool irq_set_irq_wake;          /* IRQ suspend active */
        u16 i2c_addr;                   /* I2C address */
        u8 dev_id;                      /* device ID */
        u8 reg_cfg;                     /* configuration register default */
@@ -320,7 +322,7 @@ static int isl_cmd_wr(struct isl_state *st, unsigned int enable, bool irq_en)
        int ret;
        int ret_t = 0;
 
-       if (st->i2c->irq && !irq_en) {
+       if ((st->i2c->irq > 0) && !irq_en) {
                isl_disable_irq(st);
                /* clear possible IRQ */
                ret_t = isl_i2c_wr(st, ISL_REG_INT, st->reg_int);
@@ -339,7 +341,7 @@ static int isl_cmd_wr(struct isl_state *st, unsigned int enable, bool irq_en)
                        dev_info(&st->i2c->dev, "%s reg_cfg=%hhx err=%d\n",
                                 __func__, reg_cfg, ret);
        }
-       if (st->i2c->irq && irq_en) {
+       if (irq_en && (st->i2c->irq > 0)) {
                /* clear possible IRQ */
                ret_t |= isl_i2c_wr(st, ISL_REG_INT, st->reg_int);
                if (!ret_t)
@@ -356,7 +358,7 @@ static int isl_thr_wr(struct isl_state *st, bool als, u16 thr_lo, u16 thr_hi)
        u16 thr_le;
        int ret = 0;
 
-       if (st->i2c->irq) {
+       if (st->i2c->irq > 0) {
                if (als) {
                        buf[0] = ISL_REG_ALSIR_TH1;
                        thr_le = cpu_to_le16(thr_lo);
@@ -517,7 +519,7 @@ static int isl_disable(struct isl_state *st, int snsr_id)
                }
        }
        if (disable) {
-               if (st->i2c->irq)
+               if (st->i2c->irq > 0)
                        isl_disable_irq(st);
                cancel_delayed_work(&st->dw);
                ret |= isl_pm(st, false);
@@ -611,12 +613,30 @@ static int isl_regs(void *client, int snsr_id, char *buf)
        return t;
 }
 
+static int isl_nvs_read(void *client, int snsr_id, char *buf)
+{
+       struct isl_state *st = (struct isl_state *)client;
+       ssize_t t;
+
+       t = sprintf(buf, "driver v.%u\n", ISL_DRIVER_VERSION);
+       t += sprintf(buf + t, "irq=%d\n", st->i2c->irq);
+       t += sprintf(buf + t, "irq_set_irq_wake=%x\n", st->irq_set_irq_wake);
+       t += sprintf(buf + t, "reg_configure=%x\n", st->reg_cfg);
+       t += sprintf(buf + t, "reg_interrupt=%x\n", st->reg_int);
+       if (snsr_id == ISL_DEV_LIGHT)
+               t += nvs_light_dbg(&st->light, buf + t);
+       else if (snsr_id == ISL_DEV_PROX)
+               t += nvs_proximity_dbg(&st->prox, buf + t);
+       return t;
+}
+
 static struct nvs_fn_dev isl_fn_dev = {
        .enable                         = isl_enable,
        .batch                          = isl_batch,
        .thresh_lo                      = isl_thresh_lo,
        .thresh_hi                      = isl_thresh_hi,
        .regs                           = isl_regs,
+       .nvs_read                       = isl_nvs_read,
 };
 
 static int isl_suspend(struct device *dev)
@@ -633,8 +653,20 @@ static int isl_suspend(struct device *dev)
                                ret |= st->nvs->suspend(st->nvs_st[i]);
                }
        }
+
+       /* determine if we'll be operational during suspend */
+       for (i = 0; i < ISL_DEV_N; i++) {
+               if ((st->enabled & (1 << i)) && (st->cfg[i].flags &
+                                                SENSOR_FLAG_WAKE_UP))
+                       break;
+       }
+       if (i < ISL_DEV_N) {
+               irq_set_irq_wake(st->i2c->irq, 1);
+               st->irq_set_irq_wake = true;
+       }
        if (st->sts & NVS_STS_SPEW_MSG)
-               dev_info(&client->dev, "%s\n", __func__);
+               dev_info(&client->dev, "%s WAKE_ON=%x\n",
+                        __func__, st->irq_set_irq_wake);
        return ret;
 }
 
@@ -645,6 +677,10 @@ static int isl_resume(struct device *dev)
        unsigned int i;
        int ret = 0;
 
+       if (st->irq_set_irq_wake) {
+               irq_set_irq_wake(st->i2c->irq, 0);
+               st->irq_set_irq_wake = false;
+       }
        if (st->nvs) {
                for (i = 0; i < ISL_DEV_N; i++) {
                        if (st->nvs_st[i])
@@ -856,8 +892,8 @@ static int isl_of_dt(struct isl_state *st, struct device_node *dn)
                }
 
                /* device specific parameters */
-               of_property_read_u8(dn, "configure_reg", &st->reg_cfg);
-               of_property_read_u8(dn, "interrupt_reg", &st->reg_int);
+               of_property_read_u8(dn, "reg_configure", &st->reg_cfg);
+               of_property_read_u8(dn, "reg_interrupt", &st->reg_int);
        }
        /* this device supports these programmable parameters */
        if (nvs_light_of_dt(&st->light, dn, NULL)) {
@@ -924,6 +960,12 @@ static int isl_probe(struct i2c_client *client, const struct i2c_device_id *id)
 
        st->light.handler = st->nvs->handler;
        st->prox.handler = st->nvs->handler;
+       if (client->irq < 1) {
+               /* disable WAKE_ON ability when no interrupt */
+               for (i = 0; i < ISL_DEV_N; i++)
+                       st->cfg[i].flags &= ~SENSOR_FLAG_WAKE_UP;
+       }
+
        n = 0;
        for (i = 0; i < ISL_DEV_N; i++) {
                ret = st->nvs->probe(&st->nvs_st[i], st, &client->dev,
index 566eda2..dfc54eb 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/nvs_proximity.h>
 
 
+#define LTR_DRIVER_VERSION             (2)
 #define LTR_VENDOR                     "Lite-On Technology Corp."
 #define LTR_NAME                       "ltrX5X"
 #define LTR_NAME_LTR558ALS             "ltr558als"
 /* regulator names in order of powering on */
 static char *ltr_vregs[] = {
        "vdd",
+       "vled"
 };
+#define LTR_PM_ON                      (1)
+#define LTR_PM_LED                     (ARRAY_SIZE(ltr_vregs))
 
 static u8 ltr_ids[] = {
        LTR_DEVID_558ALS,
@@ -159,6 +163,7 @@ struct ltr_state {
        unsigned int sts;               /* status flags */
        unsigned int errs;              /* error count */
        unsigned int enabled;           /* enable status */
+       bool irq_set_irq_wake;          /* IRQ suspend active */
        u16 i2c_addr;                   /* I2C address */
        u8 dev_id;                      /* device ID */
        u8 ps_contr;                    /* PS_CONTR register default */
@@ -279,28 +284,38 @@ static int ltr_reset_sw(struct ltr_state *st)
        buf[3] = st->ps_meas_rate;
        buf[4] = st->als_meas_rate;
        ret |= ltr_i2c_write(st, sizeof(buf), buf);
-       ret |= ltr_i2c_wr(st, LTR_REG_INTERRUPT_PERSIST, st->interrupt_persist);
+       ret |= ltr_i2c_wr(st, LTR_REG_INTERRUPT_PERSIST,
+                         st->interrupt_persist);
        return ret;
 }
 
-static int ltr_pm(struct ltr_state *st, bool enable)
+static int ltr_pm(struct ltr_state *st, unsigned int en_msk)
 {
+       unsigned int vreg_n;
+       unsigned int vreg_n_dis;
        int ret = 0;
 
-       if (enable) {
-               ret = nvs_vregs_enable(&st->i2c->dev, st->vreg,
-                                ARRAY_SIZE(ltr_vregs));
+       if (en_msk) {
+               if (en_msk & (1 < LTR_DEV_PROX))
+                       vreg_n = LTR_PM_LED;
+               else
+                       vreg_n = LTR_PM_ON;
+               ret = nvs_vregs_enable(&st->i2c->dev, st->vreg, vreg_n);
                if (ret > 0)
                        mdelay(LTR_HW_DELAY_MS);
                ret = ltr_reset_sw(st);
+               vreg_n_dis = ARRAY_SIZE(ltr_vregs) - vreg_n;
+               if (vreg_n_dis)
+                       ret |= nvs_vregs_disable(&st->i2c->dev,
+                                                &st->vreg[vreg_n],
+                                                vreg_n_dis);
        } else {
-               ret = nvs_vregs_sts(st->vreg, ARRAY_SIZE(ltr_vregs));
-               if ((ret < 0) || (ret == ARRAY_SIZE(ltr_vregs))) {
+               ret = nvs_vregs_sts(st->vreg, LTR_PM_ON);
+               if ((ret < 0) || (ret == LTR_PM_ON)) {
                        ret = ltr_i2c_wr(st, LTR_REG_ALS_CONTR, 0);
                        ret |= ltr_i2c_wr(st, LTR_REG_PS_CONTR, 0);
                } else if (ret > 0) {
-                       nvs_vregs_enable(&st->i2c->dev, st->vreg,
-                                        ARRAY_SIZE(ltr_vregs));
+                       nvs_vregs_enable(&st->i2c->dev, st->vreg, LTR_PM_ON);
                        mdelay(LTR_HW_DELAY_MS);
                        ret = ltr_i2c_wr(st, LTR_REG_ALS_CONTR, 0);
                        ret |= ltr_i2c_wr(st, LTR_REG_PS_CONTR, 0);
@@ -311,19 +326,19 @@ static int ltr_pm(struct ltr_state *st, bool enable)
        if (ret > 0)
                ret = 0;
        if (ret) {
-               dev_err(&st->i2c->dev, "%s pwr=%x ERR=%d\n",
-                       __func__, enable, ret);
+               dev_err(&st->i2c->dev, "%s en_msk=%x ERR=%d\n",
+                       __func__, en_msk, ret);
        } else {
                if (st->sts & NVS_STS_SPEW_MSG)
-                       dev_info(&st->i2c->dev, "%s pwr=%x\n",
-                                __func__, enable);
+                       dev_info(&st->i2c->dev, "%s en_msk=%x\n",
+                                __func__, en_msk);
        }
        return ret;
 }
 
 static void ltr_pm_exit(struct ltr_state *st)
 {
-       ltr_pm(st, false);
+       ltr_pm(st, 0);
        nvs_vregs_exit(&st->i2c->dev, st->vreg, ARRAY_SIZE(ltr_vregs));
 }
 
@@ -334,7 +349,7 @@ static int ltr_pm_init(struct ltr_state *st)
        st->enabled = 0;
        nvs_vregs_init(&st->i2c->dev,
                       st->vreg, ARRAY_SIZE(ltr_vregs), ltr_vregs);
-       ret = ltr_pm(st, true);
+       ret = ltr_pm(st, (1 << LTR_DEV_N));
        return ret;
 }
 
@@ -384,7 +399,7 @@ static int ltr_cmd_wr(struct ltr_state *st, unsigned int enable, bool irq_en)
                else
                        st->rc_ps_contr = ps_contr;
        }
-       if (st->i2c->irq) {
+       if (st->i2c->irq > 0) {
                interrupt = st->interrupt;
                if (irq_en) {
                        if (enable & (1 << LTR_DEV_LIGHT))
@@ -408,7 +423,7 @@ static int ltr_thr_wr(struct ltr_state *st, u8 reg, u16 thr_lo, u16 thr_hi)
        int ret;
 
        ret = ltr_interrupt_wr(st, st->interrupt); /* irq disable */
-       if (st->i2c->irq) {
+       if (st->i2c->irq > 0) {
                buf[0] = reg;
                buf[1] = thr_hi & 0xFF;
                buf[2] = thr_hi >> 8;
@@ -612,11 +627,12 @@ static int ltr_disable(struct ltr_state *st, int snsr_id)
                                ret = ltr_i2c_wr(st, LTR_REG_ALS_CONTR, 0);
                        else if (snsr_id == LTR_DEV_PROX)
                                ret = ltr_i2c_wr(st, LTR_REG_PS_CONTR, 0);
+                       ret |= ltr_pm(st, st->enabled);
                }
        }
        if (disable) {
                cancel_delayed_work(&st->dw);
-               ret |= ltr_pm(st, false);
+               ret |= ltr_pm(st, 0);
                if (!ret)
                        st->enabled = 0;
        }
@@ -633,7 +649,7 @@ static int ltr_enable(void *client, int snsr_id, int enable)
 
        if (enable) {
                enable = st->enabled | (1 << snsr_id);
-               ret = ltr_pm(st, true);
+               ret = ltr_pm(st, enable);
                if (!ret) {
                        ret = ltr_en(st, enable);
                        if (ret < 0) {
@@ -641,7 +657,7 @@ static int ltr_enable(void *client, int snsr_id, int enable)
                        } else {
                                st->enabled = enable;
                                schedule_delayed_work(&st->dw,
-                                        msecs_to_jiffies(LTR_POLL_DLY_MS_MIN));
+                                       msecs_to_jiffies(LTR_POLL_DLY_MS_MIN));
                        }
                }
        } else {
@@ -779,12 +795,36 @@ static int ltr_regs(void *client, int snsr_id, char *buf)
        return t;
 }
 
+static int ltr_nvs_read(void *client, int snsr_id, char *buf)
+{
+       struct ltr_state *st = (struct ltr_state *)client;
+       ssize_t t;
+
+       t = sprintf(buf, "driver v.%u\n", LTR_DRIVER_VERSION);
+       t += sprintf(buf + t, "irq=%d\n", st->i2c->irq);
+       t += sprintf(buf + t, "irq_set_irq_wake=%x\n", st->irq_set_irq_wake);
+       t += sprintf(buf + t, "reg_ps_contr=%x\n", st->ps_contr);
+       t += sprintf(buf + t, "reg_ps_led=%x\n", st->ps_led);
+       t += sprintf(buf + t, "reg_ps_n_pulses=%x\n", st->ps_n_pulses);
+       t += sprintf(buf + t, "reg_ps_meas_rate=%x\n", st->ps_meas_rate);
+       t += sprintf(buf + t, "reg_als_meas_rate=%x\n", st->als_meas_rate);
+       t += sprintf(buf + t, "reg_interrupt=%x\n", st->interrupt);
+       t += sprintf(buf + t, "reg_interrupt_persist=%x\n",
+                    st->interrupt_persist);
+       if (snsr_id == LTR_DEV_LIGHT)
+               t += nvs_light_dbg(&st->light, buf + t);
+       else if (snsr_id == LTR_DEV_PROX)
+               t += nvs_proximity_dbg(&st->prox, buf + t);
+       return t;
+}
+
 static struct nvs_fn_dev ltr_fn_dev = {
        .enable                         = ltr_enable,
        .batch                          = ltr_batch,
        .thresh_lo                      = ltr_thresh_lo,
        .thresh_hi                      = ltr_thresh_hi,
        .regs                           = ltr_regs,
+       .nvs_read                       = ltr_nvs_read,
 };
 
 static int ltr_suspend(struct device *dev)
@@ -801,8 +841,20 @@ static int ltr_suspend(struct device *dev)
                                ret |= st->nvs->suspend(st->nvs_st[i]);
                }
        }
+
+       /* determine if we'll be operational during suspend */
+       for (i = 0; i < LTR_DEV_N; i++) {
+               if ((st->enabled & (1 << i)) && (st->cfg[i].flags &
+                                                SENSOR_FLAG_WAKE_UP))
+                       break;
+       }
+       if (i < LTR_DEV_N) {
+               irq_set_irq_wake(st->i2c->irq, 1);
+               st->irq_set_irq_wake = true;
+       }
        if (st->sts & NVS_STS_SPEW_MSG)
-               dev_info(&client->dev, "%s\n", __func__);
+               dev_info(&client->dev, "%s WAKE_ON=%x\n",
+                        __func__, st->irq_set_irq_wake);
        return ret;
 }
 
@@ -813,6 +865,10 @@ static int ltr_resume(struct device *dev)
        unsigned int i;
        int ret = 0;
 
+       if (st->irq_set_irq_wake) {
+               irq_set_irq_wake(st->i2c->irq, 0);
+               st->irq_set_irq_wake = false;
+       }
        if (st->nvs) {
                for (i = 0; i < LTR_DEV_N; i++) {
                        if (st->nvs_st[i])
@@ -1050,19 +1106,19 @@ static int ltr_of_dt(struct ltr_state *st, struct device_node *dn)
                }
 
                /* device specific parameters */
-               of_property_read_u8(dn, "register_ps_contr", &st->ps_contr);
+               of_property_read_u8(dn, "reg_ps_contr", &st->ps_contr);
                st->ps_contr &= LTR_REG_PS_CONTR_POR_MASK;
-               of_property_read_u8(dn, "register_ps_led", &st->ps_led);
+               of_property_read_u8(dn, "reg_ps_led", &st->ps_led);
                of_property_read_u8(dn, "register_ps_n_pulses",
                                    &st->ps_n_pulses);
-               of_property_read_u8(dn, "register_ps_meas_rate",
+               of_property_read_u8(dn, "reg_ps_meas_rate",
                                    &st->ps_meas_rate);
-               of_property_read_u8(dn, "register_als_meas_rate",
+               of_property_read_u8(dn, "reg_als_meas_rate",
                                    &st->als_meas_rate);
-               of_property_read_u8(dn, "register_interrupt", &st->interrupt);
+               of_property_read_u8(dn, "reg_interrupt", &st->interrupt);
                /* just interrupt polarity */
                st->interrupt &= LTR_REG_INTERRUPT_POLARITY;
-               of_property_read_u8(dn, "register_interrupt_persist",
+               of_property_read_u8(dn, "reg_interrupt_persist",
                                    &st->interrupt_persist);
        }
        /* this device supports these programmable parameters */
@@ -1109,7 +1165,7 @@ static int ltr_probe(struct i2c_client *client, const struct i2c_device_id *id)
                goto ltr_probe_exit;
        }
 
-       ltr_pm(st, false);
+       ltr_pm(st, 0);
        ltr_fn_dev.sts = &st->sts;
        ltr_fn_dev.errs = &st->errs;
        st->nvs = nvs_iio();
@@ -1120,6 +1176,12 @@ static int ltr_probe(struct i2c_client *client, const struct i2c_device_id *id)
 
        st->light.handler = st->nvs->handler;
        st->prox.handler = st->nvs->handler;
+       if (client->irq < 1) {
+               /* disable WAKE_ON ability when no interrupt */
+               for (i = 0; i < LTR_DEV_N; i++)
+                       st->cfg[i].flags &= ~SENSOR_FLAG_WAKE_UP;
+       }
+
        n = 0;
        for (i = 0; i < LTR_DEV_N; i++) {
                ret = st->nvs->probe(&st->nvs_st[i], st, &client->dev,
index a4baf61..14d3f42 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/nvs_proximity.h>
 
 
+#define MX_DRIVER_VERSION              (2)
 #define MX_VENDOR                      "Maxim"
 #define MX_NAME                                "max4400x"
 #define MX_NAME_MAX44005               "max44005"
 /* regulator names in order of powering on */
 static char *mx_vregs[] = {
        "vdd",
+       "vled"
 };
+#define MX_PM_ON                       (1)
+#define MX_PM_LED                      (ARRAY_SIZE(mx_vregs))
 
 static unsigned short mx_i2c_addrs[] = {
        0x40,
@@ -197,6 +201,7 @@ struct mx_state {
        unsigned int sts;               /* status flags */
        unsigned int errs;              /* error count */
        unsigned int enabled;           /* enable status */
+       bool irq_set_irq_wake;          /* IRQ suspend active */
        u16 i2c_addr;                   /* I2C address */
        u8 dev_id;                      /* device ID */
        u8 amb_cfg;                     /* ambient configuration register */
@@ -320,24 +325,33 @@ static int mx_reset_sw(struct mx_state *st)
        return ret;
 }
 
-static int mx_pm(struct mx_state *st, bool enable)
+static int mx_pm(struct mx_state *st, unsigned int en_msk)
 {
+       unsigned int vreg_n;
+       unsigned int vreg_n_dis;
        int ret = 0;
 
-       if (enable) {
-               nvs_vregs_enable(&st->i2c->dev, st->vreg,
-                                ARRAY_SIZE(mx_vregs));
+       if (en_msk) {
+               if (en_msk & (1 < MX_DEV_PROX))
+                       vreg_n = MX_PM_LED;
+               else
+                       vreg_n = MX_PM_ON;
+               nvs_vregs_enable(&st->i2c->dev, st->vreg, vreg_n);
                if (ret)
                        mdelay(MX_HW_DELAY_MS);
                ret = mx_reset_sw(st);
+               vreg_n_dis = ARRAY_SIZE(mx_vregs) - vreg_n;
+               if (vreg_n_dis)
+                       ret |= nvs_vregs_disable(&st->i2c->dev,
+                                                &st->vreg[vreg_n],
+                                                vreg_n_dis);
        } else {
-               ret = nvs_vregs_sts(st->vreg, ARRAY_SIZE(mx_vregs));
-               if ((ret < 0) || (ret == ARRAY_SIZE(mx_vregs))) {
+               ret = nvs_vregs_sts(st->vreg, MX_PM_ON);
+               if ((ret < 0) || (ret == MX_PM_ON)) {
                        ret = mx_i2c_wr(st, MX_REG_CFG_PRX, 0);
                        ret |= mx_i2c_wr(st, MX_REG_STS, 1 << MX_REG_STS_SHDN);
                } else if (ret > 0) {
-                       nvs_vregs_enable(&st->i2c->dev, st->vreg,
-                                        ARRAY_SIZE(mx_vregs));
+                       nvs_vregs_enable(&st->i2c->dev, st->vreg, MX_PM_ON);
                        mdelay(MX_HW_DELAY_MS);
                        ret = mx_i2c_wr(st, MX_REG_CFG_PRX, 0);
                        ret |= mx_i2c_wr(st, MX_REG_STS, 1 << MX_REG_STS_SHDN);
@@ -348,19 +362,19 @@ static int mx_pm(struct mx_state *st, bool enable)
        if (ret > 0)
                ret = 0;
        if (ret) {
-               dev_err(&st->i2c->dev, "%s pwr=%x ERR=%d\n",
-                       __func__, enable, ret);
+               dev_err(&st->i2c->dev, "%s en_msk=%x ERR=%d\n",
+                       __func__, en_msk, ret);
        } else {
                if (st->sts & NVS_STS_SPEW_MSG)
-                       dev_info(&st->i2c->dev, "%s pwr=%x\n",
-                                __func__, enable);
+                       dev_info(&st->i2c->dev, "%s en_msk=%x\n",
+                                __func__, en_msk);
        }
        return ret;
 }
 
 static void mx_pm_exit(struct mx_state *st)
 {
-       mx_pm(st, false);
+       mx_pm(st, 0);
        nvs_vregs_exit(&st->i2c->dev, st->vreg, ARRAY_SIZE(mx_vregs));
 }
 
@@ -371,7 +385,7 @@ static int mx_pm_init(struct mx_state *st)
        st->enabled = 0;
        nvs_vregs_init(&st->i2c->dev,
                       st->vreg, ARRAY_SIZE(mx_vregs), mx_vregs);
-       ret = mx_pm(st, true);
+       ret = mx_pm(st, (1 << MX_DEV_N));
        return ret;
 }
 
@@ -396,7 +410,7 @@ static int mx_cmd_wr(struct mx_state *st, unsigned int enable, bool irq_en)
                                 __func__, amb_cfg, ret);
        }
        main_cfg = mx_mode_tbl[enable];
-       if (irq_en && st->i2c->irq) {
+       if (irq_en && (st->i2c->irq > 0)) {
                if (enable & (1 << MX_DEV_LIGHT))
                        main_cfg |= (1 << MX_REG_CFG_MAIN_AMBINTE);
                if (enable & (1 << MX_DEV_PROX))
@@ -423,7 +437,7 @@ static int mx_thr_wr(struct mx_state *st, u8 reg, u16 thr_lo, u16 thr_hi)
        u16 thr_be;
        int ret = RET_POLL_NEXT;
 
-       if (st->i2c->irq) {
+       if (st->i2c->irq > 0) {
                buf[0] = reg;
                thr_be = cpu_to_be16(thr_hi);
                buf[1] = thr_be & 0xFF;
@@ -607,11 +621,12 @@ static int mx_disable(struct mx_state *st, int snsr_id)
                        disable = false;
                        if (snsr_id == MX_DEV_PROX)
                                ret = mx_i2c_wr(st, MX_REG_CFG_PRX, 0);
+                       ret |= mx_pm(st, st->enabled);
                }
        }
        if (disable) {
                cancel_delayed_work(&st->dw);
-               ret |= mx_pm(st, false);
+               ret = mx_pm(st, 0);
                if (!ret)
                        st->enabled = 0;
        }
@@ -628,7 +643,7 @@ static int mx_enable(void *client, int snsr_id, int enable)
 
        if (enable) {
                enable = st->enabled | (1 << snsr_id);
-               ret = mx_pm(st, true);
+               ret = mx_pm(st, enable);
                if (!ret) {
                        ret = mx_en(st, enable);
                        if (ret < 0) {
@@ -724,12 +739,31 @@ static int mx_regs(void *client, int snsr_id, char *buf)
        return t;
 }
 
+static int mx_nvs_read(void *client, int snsr_id, char *buf)
+{
+       struct mx_state *st = (struct mx_state *)client;
+       ssize_t t;
+
+       t = sprintf(buf, "driver v.%u\n", MX_DRIVER_VERSION);
+       t += sprintf(buf + t, "irq=%d\n", st->i2c->irq);
+       t += sprintf(buf + t, "irq_set_irq_wake=%x\n", st->irq_set_irq_wake);
+       t += sprintf(buf + t, "reg_ambient_cfg=%x\n", st->amb_cfg);
+       t += sprintf(buf + t, "reg_proximity_cfg=%x\n", st->prx_cfg);
+       t += sprintf(buf + t, "reg_threshold_persist=%x\n", st->thr_cfg);
+       if (snsr_id == MX_DEV_LIGHT)
+               t += nvs_light_dbg(&st->light, buf + t);
+       else if (snsr_id == MX_DEV_PROX)
+               t += nvs_proximity_dbg(&st->prox, buf + t);
+       return t;
+}
+
 static struct nvs_fn_dev mx_fn_dev = {
        .enable                         = mx_enable,
        .batch                          = mx_batch,
        .thresh_lo                      = mx_thresh_lo,
        .thresh_hi                      = mx_thresh_hi,
        .regs                           = mx_regs,
+       .nvs_read                       = mx_nvs_read,
 };
 
 static int mx_suspend(struct device *dev)
@@ -746,8 +780,20 @@ static int mx_suspend(struct device *dev)
                                ret |= st->nvs->suspend(st->nvs_st[i]);
                }
        }
+
+       /* determine if we'll be operational during suspend */
+       for (i = 0; i < MX_DEV_N; i++) {
+               if ((st->enabled & (1 << i)) && (st->cfg[i].flags &
+                                                SENSOR_FLAG_WAKE_UP))
+                       break;
+       }
+       if (i < MX_DEV_N) {
+               irq_set_irq_wake(st->i2c->irq, 1);
+               st->irq_set_irq_wake = true;
+       }
        if (st->sts & NVS_STS_SPEW_MSG)
-               dev_info(&client->dev, "%s\n", __func__);
+               dev_info(&client->dev, "%s WAKE_ON=%x\n",
+                        __func__, st->irq_set_irq_wake);
        return ret;
 }
 
@@ -758,6 +804,10 @@ static int mx_resume(struct device *dev)
        unsigned int i;
        int ret = 0;
 
+       if (st->irq_set_irq_wake) {
+               irq_set_irq_wake(st->i2c->irq, 0);
+               st->irq_set_irq_wake = false;
+       }
        if (st->nvs) {
                for (i = 0; i < MX_DEV_N; i++) {
                        if (st->nvs_st[i])
@@ -980,9 +1030,9 @@ static int mx_of_dt(struct mx_state *st, struct device_node *dn)
                }
 
                /* device specific parameters */
-               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);
+               of_property_read_u8(dn, "reg_ambient_cfg", &st->amb_cfg);
+               of_property_read_u8(dn, "reg_proximity_cfg", &st->prx_cfg);
+               of_property_read_u8(dn, "reg_threshold_persist", &st->thr_cfg);
        }
        /* this device supports these programmable parameters */
        if (nvs_light_of_dt(&st->light, dn, NULL)) {
@@ -1028,7 +1078,7 @@ static int mx_probe(struct i2c_client *client, const struct i2c_device_id *id)
                goto mx_probe_exit;
        }
 
-       mx_pm(st, false);
+       mx_pm(st, 0);
        mx_fn_dev.sts = &st->sts;
        mx_fn_dev.errs = &st->errs;
        st->nvs = nvs_iio();
@@ -1039,6 +1089,12 @@ static int mx_probe(struct i2c_client *client, const struct i2c_device_id *id)
 
        st->light.handler = st->nvs->handler;
        st->prox.handler = st->nvs->handler;
+       if (client->irq < 1) {
+               /* disable WAKE_ON ability when no interrupt */
+               for (i = 0; i < MX_DEV_N; i++)
+                       st->cfg[i].flags &= ~SENSOR_FLAG_WAKE_UP;
+       }
+
        n = 0;
        for (i = 0; i < MX_DEV_N; i++) {
                if (st->dev_id != MX_DEVID_MAX44005) {
index 59ca327..516ec1c 100644 (file)
@@ -106,5 +106,6 @@ int nvs_proximity_of_dt(struct nvs_proximity *np, const struct device_node *dn,
                        const char *dev_name);
 void nvs_proximity_threshold_calibrate_lo(struct nvs_proximity *np, int lo);
 void nvs_proximity_threshold_calibrate_hi(struct nvs_proximity *np, int hi);
+ssize_t nvs_proximity_dbg(struct nvs_proximity *np, char *buf);
 
 #endif /* _NVS_PROXIMITY_H_ */