Signed-off-by: Kevin Tsai <ktsai@xxxxxxxxxxxxxxxx> --- Added Interrupt support. drivers/iio/light/cm32181.c | 156 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 153 insertions(+), 3 deletions(-) diff --git a/drivers/iio/light/cm32181.c b/drivers/iio/light/cm32181.c index b7abd46..1ae32a0 100644 --- a/drivers/iio/light/cm32181.c +++ b/drivers/iio/light/cm32181.c @@ -47,6 +47,10 @@ /* ALS_WL register */ #define CM32181_ALS_WL_DEFAULT 0x0000 +/* STATUS register */ +#define CM32181_STATUS_ALS_IF_L BIT(15) +#define CM32181_STATUS_ALS_IF_H BIT(14) + /* Software parameters */ #define CM32181_HW_ID 0x81 #define CM32181_MLUX_PER_BIT 5 /* ALS_SM=01 IT=800ms */ @@ -122,7 +126,8 @@ void cm32181_parse_dt(struct cm32181_chip *chip) if (!of_property_read_u32(dn, "capella,mlux_per_bit", &temp_val)) als_info->mlux_per_bit = (int)temp_val; if (!of_property_read_u32(dn, "capella,thd_percent", &temp_val)) - als_info->thd_percent = (int)temp_val; + if (((int)temp_val >= 0) && ((int)temp_val < 100)) + als_info->thd_percent = (int)temp_val; } #endif @@ -173,6 +178,63 @@ static int cm32181_reg_init(struct cm32181_chip *chip) } /** + * cm32181_interrupt() - enable/disable interrupt + * @chip: pointer of struct cm32181_chip + * @ int_en: truen for enable; false for disable + * + * Enable/disable interrupt mode + * + * Return: 0 for success; otherwise for error code. + */ +static int cm32181_interrupt(struct cm32181_chip *cm32181, bool int_en) +{ + int ret; + + mutex_lock(&cm32181->lock); + if (int_en) + cm32181->conf_regs[CM32181_REG_ADDR_CMD] |= + CM32181_CMD_ALS_INT_EN; + else + cm32181->conf_regs[CM32181_REG_ADDR_CMD] &= + ~CM32181_CMD_ALS_INT_EN; + + ret = i2c_smbus_write_word_data(cm32181->client, + CM32181_REG_ADDR_CMD, + cm32181->conf_regs[CM32181_REG_ADDR_CMD]); + mutex_unlock(&cm32181->lock); + return ret; +} + +static irqreturn_t cm32181_irq_handler(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct cm32181_chip *cm32181 = iio_priv(indio_dev); + struct i2c_client *client = cm32181->client; + int ret; + u64 ev_code; + + ret = i2c_smbus_read_word_data(cm32181->client, + CM32181_REG_ADDR_STATUS); + if (ret < 0) { + dev_err(&client->dev, + "%s: Data read failed: %d\n", __func__, ret); + return IRQ_NONE; + } + + if (!(ret & (CM32181_STATUS_ALS_IF_H | CM32181_STATUS_ALS_IF_L))) + return IRQ_NONE; + + ev_code = IIO_UNMOD_EVENT_CODE(IIO_LIGHT, + 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER); + + iio_push_event(indio_dev, ev_code, iio_get_time_ns()); + + return IRQ_HANDLED; +} + +/** * cm32181_read_als_it() - Get sensor integration time * @chip: pointer of struct cm32181_chip * @val: pointer of int to load the integration (sec) @@ -242,6 +304,51 @@ static int cm32181_write_als_it(struct cm32181_chip *chip, int val, int val2) } /** + * cm32181_ials_read() - read ALS raw data + * @chip: pointer of struct cm32181_chip + * + * Read the ALS raw data and update the interrupt threshold windows. + * + * Return: Positive value is ALS raw data, otherwise is error code. + */ +static int cm32181_als_read(struct cm32181_chip *chip) +{ + struct i2c_client *client = chip->client; + struct cm32181_als_info *als_info = chip->als_info; + u16 als, wh, wl, delta; + int ret; + + ret = i2c_smbus_read_word_data(client, CM32181_REG_ADDR_ALS); + if (ret < 0) + return ret; + + als = (u16)ret; + + if (als_info->thd_percent) { + delta = als * als_info->thd_percent / 100; + if (delta < 3) + delta = 3; + wh = (als + delta > 0xFFFF) ? 0xFFFF : (als + delta); + wl = (als < delta) ? 0 : (als - delta); + + ret = i2c_smbus_write_word_data(client, + CM32181_REG_ADDR_ALS_WH, wh); + if (ret < 0) + return ret; + chip->conf_regs[CM32181_REG_ADDR_ALS_WH] = wh; + + ret = i2c_smbus_write_word_data(client, + CM32181_REG_ADDR_ALS_WL, wl); + if (ret < 0) + return ret; + chip->conf_regs[CM32181_REG_ADDR_ALS_WL] = wl; + ret = als; + } + + return ret; +} + +/** * cm32181_get_lux() - report current lux value * @chip: pointer of struct cm32181_chip * @@ -252,7 +359,6 @@ static int cm32181_write_als_it(struct cm32181_chip *chip, int val, int val2) */ static int cm32181_get_lux(struct cm32181_chip *chip) { - struct i2c_client *client = chip->client; struct cm32181_als_info *als_info = chip->als_info; int ret; int val, val2; @@ -268,7 +374,7 @@ static int cm32181_get_lux(struct cm32181_chip *chip) lux *= als_info->mlux_per_bit_base_it; lux = div_u64(lux, als_it); - ret = i2c_smbus_read_word_data(client, CM32181_REG_ADDR_ALS); + ret = cm32181_als_read(chip); if (ret < 0) return ret; @@ -350,6 +456,15 @@ static ssize_t cm32181_get_it_available(struct device *dev, return len + scnprintf(buf + len, PAGE_SIZE - len, "\n"); } +static const struct iio_event_spec cm32181_event_spec[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + } +}; + static const struct iio_chan_spec cm32181_channels[] = { { .type = IIO_LIGHT, @@ -357,6 +472,8 @@ static const struct iio_chan_spec cm32181_channels[] = { BIT(IIO_CHAN_INFO_PROCESSED) | BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_INT_TIME), + .event_spec = cm32181_event_spec, + .num_event_specs = ARRAY_SIZE(cm32181_event_spec), } }; @@ -383,6 +500,7 @@ static int cm32181_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct cm32181_chip *chip; + struct cm32181_als_info *als_info; struct iio_dev *indio_dev; int ret; @@ -412,11 +530,35 @@ static int cm32181_probe(struct i2c_client *client, return ret; } + als_info = chip->als_info; + if (als_info->thd_percent && client->irq) { + ret = request_threaded_irq(client->irq, NULL, + cm32181_irq_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + indio_dev->name, indio_dev); + if (ret < 0) { + dev_err(&client->dev, "%s: request irq failed\n", + __func__); + return ret; + } + + ret = cm32181_interrupt(chip, true); + if (ret) { + als_info->thd_percent = 0; + dev_err(&client->dev, + "%s: failed to enable interrupt\n", __func__); + } + } else { + als_info->thd_percent = 0; /* Polling Mode */ + } + ret = devm_iio_device_register(&client->dev, indio_dev); if (ret) { dev_err(&client->dev, "%s: regist device failed\n", __func__); + if (als_info->thd_percent) + free_irq(client->irq, indio_dev); return ret; } @@ -425,9 +567,17 @@ static int cm32181_probe(struct i2c_client *client, static int cm32181_remove(struct i2c_client *client) { + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct cm32181_chip *chip = iio_priv(indio_dev); + struct cm32181_als_info *als_info = chip->als_info; + + cm32181_interrupt(chip, false); i2c_smbus_write_word_data(client, CM32181_REG_ADDR_CMD, CM32181_CMD_ALS_DISABLE); + if (als_info->thd_percent) + free_irq(client->irq, indio_dev); + return 0; } -- 1.8.3.1 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html