Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756401AbbEVAc5 (ORCPT ); Thu, 21 May 2015 20:32:57 -0400 Received: from smtp107.biz.mail.bf1.yahoo.com ([98.139.244.55]:33566 "EHLO smtp107.biz.mail.bf1.yahoo.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755849AbbEVAcK (ORCPT ); Thu, 21 May 2015 20:32:10 -0400 X-Yahoo-Newman-Property: ymail-3 X-YMail-OSG: 1gydy7EVM1lUepLdy9ZvRxj9Q6ZUJRar0qbleay3mQ_6yZs 7_u6T.N4e.ZC_LXNhiWlw_ehjlFC_T3JzpGW_cIIYuNI24lZFqYgyMfHC9jF uCyhw.Nt8nKuefhEi54xEz4._FygZzpXneXj.JPTJ8JYCFqdV1fMafGWzcz_ lBTtTGhBw24jo.lisSqERbTsPuav2tBd8I3rPhhgX2g0NqocR2v5ddqohc1s .zUG9CBWPcAYfSAL.WTBoJd6yGVT6Mw68AcG1..1PPHByaMJ6Y6niicgdspl ibEUeR.P0X3LJ47j3SWmDFPqYCzAz2UNR8ahMhOkZ4Au9fplPqaGpdbttVgu O2zVrUB.VENeSvzhbI3VaNzb0OCsrICfCNylS.cNCzNbA8u6FTqsAaLlEf.h n7yxQ4rmM9FtEAit0jLC11foicmy6mFPAo_ajHZhI87Gj.HSU1Z7XhgzObSI 3eQ5zSNcMQDKC96V7sJYYG8uv1vESk7lRg8FtevjRjg_84sbfTR79o0044CY 1y1wcXf87xRyVkW42N5Aq X-Yahoo-SMTP: oqiDtsaswBDv2aVUlxVTYPpATqA- From: Kevin Tsai To: Kevin Tsai , Rob Herring , Pawel Moll , Mark Rutland , Ian Campbell , Kumar Gala , Jonathan Cameron , Hartmut Knaack , Lars-Peter Clausen , Peter Meerwald , Andrew Morton , Greg KH , "David S. Miller" , Mauro Carvalho Chehab , Arnd Bergmann , Joe Perches , Jingoo Han , Daniel Baluta , Roberta Dobrescu , Kuppuswamy Sathyanarayanan Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org Subject: [PATCH 6/6] iio: light: Updated Vishay Capella cm32181 ambient light sensor driver. - Added Interrupt support. Date: Thu, 21 May 2015 17:30:45 -0700 Message-Id: <1432254645-5349-6-git-send-email-ktsai@capellamicro.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1432254645-5349-1-git-send-email-ktsai@capellamicro.com> References: <1432254645-5349-1-git-send-email-ktsai@capellamicro.com> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 7259 Lines: 260 Signed-off-by: Kevin Tsai --- 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 linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/