#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) {
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
#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)
{
#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"
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 */
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;
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 = {
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);
}
#include <linux/nvs_proximity.h>
+#define ISL_DRIVER_VERSION (2)
#define ISL_VENDOR "InterSil"
#define ISL_NAME "isl2902x"
#define ISL_NAME_ISL29028 "isl29028"
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 */
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);
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)
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);
}
}
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);
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)
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;
}
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])
}
/* 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)) {
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,
#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,
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 */
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);
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));
}
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;
}
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))
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;
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;
}
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) {
} 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 {
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)
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;
}
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])
}
/* 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 */
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();
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,
#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,
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 */
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);
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));
}
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;
}
__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))
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;
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;
}
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) {
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)
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;
}
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])
}
/* 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)) {
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();
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) {
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_ */