Received: by 2002:ac0:a5a7:0:0:0:0:0 with SMTP id m36-v6csp3612308imm; Mon, 20 Aug 2018 01:29:38 -0700 (PDT) X-Google-Smtp-Source: AA+uWPxWQ3oTM2NFNWUsHpYT9ChEjBkV5C8xIQNy+Q0fswXtTeMowhW4cIvt7xbnI07mDhy45L5X X-Received: by 2002:a62:b917:: with SMTP id z23-v6mr47311394pfe.131.1534753778097; Mon, 20 Aug 2018 01:29:38 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1534753778; cv=none; d=google.com; s=arc-20160816; b=YQmOM75F/Q5v0A0b5t0WoChRU9dTbt2/iPkJWbOs10i+UsrjtylJZktpctYCqjfaep O6hPGJ5FzbywBnFgdOXLiaSwyOmKUDy0nqKYirZi3Y1SPYrENOfXZy7VCDRBgtD1Whmj maUEZPFDa4/msKH17IRw+2TbeJ6xAf6dxt1bNhUnMjRJhBVUngWLlbq3KTFPUPdgUKKb aQrlIAa6KgtSzbNZ1xtB9cTV+yezEYBzBGtOakObVU9DyLi441wU/7lftbUwNmEopW2p rd+bVtlt/4yzwFzCQJ+Wc0gtA/vfPMJXrqg/8uNHbZlj8NevomBa4gIPsFlFcA/UpgbK nerA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding :content-language:in-reply-to:mime-version:user-agent:date :message-id:from:references:to:subject:cc:arc-authentication-results; bh=2aOIE2axujIp9Rwe4/US8pWJbeqLhiNVsci3ZvZx7lY=; b=kI0cD7VyQ7pueIeMmAFi2k8iFRMG2u16eREnO1RzVmjL3k/6xbC4vYOuGy2hh9yaNZ uKh+VIOG0pV7gnRFkuDw8h9iepsp96SoOi/Sa2e7i8LwrAv8QY/yeCHjGLwG75OjK1q8 N7NheF03kMI4Cllr9aqp2yV0NN4B3pEwuPJSKFh3AEb7KLdivaM+gHcXyISIRM0KxdRa QO1SV5yUaGeBgj/YlSzF8jpQvaQsyILn1FguGYupS+lDT/PWtAaUX+foS8q+HY9Uu9BG gZclSndkVuNP28szOWPztwdJLcgyc2BEkWJuWlfmdkCKG3QezWfH46tOk35ggjRar4Uy 4C+w== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id q66-v6si9794955pfd.153.2018.08.20.01.29.22; Mon, 20 Aug 2018 01:29:38 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726634AbeHTLmv (ORCPT + 99 others); Mon, 20 Aug 2018 07:42:51 -0400 Received: from mail-out.m-online.net ([212.18.0.10]:34646 "EHLO mail-out.m-online.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726032AbeHTLmu (ORCPT ); Mon, 20 Aug 2018 07:42:50 -0400 Received: from frontend01.mail.m-online.net (unknown [192.168.8.182]) by mail-out.m-online.net (Postfix) with ESMTP id 41v6Pt1Ygxz1qxDj; Mon, 20 Aug 2018 10:28:02 +0200 (CEST) Received: from localhost (dynscan1.mnet-online.de [192.168.6.70]) by mail.m-online.net (Postfix) with ESMTP id 41v6Ps6Qw6z1qrW5; Mon, 20 Aug 2018 10:28:01 +0200 (CEST) X-Virus-Scanned: amavisd-new at mnet-online.de Received: from mail.mnet-online.de ([192.168.8.182]) by localhost (dynscan1.mail.m-online.net [192.168.6.70]) (amavisd-new, port 10024) with ESMTP id oXJZwyE8qS-3; Mon, 20 Aug 2018 10:27:57 +0200 (CEST) X-Auth-Info: f9fuvWMTHlJVQOC5oZoR0y/mWZiO/7N7Iwxxu94GbVI= Received: from antares.denx.de (unknown [62.91.23.180]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.mnet-online.de (Postfix) with ESMTPSA; Mon, 20 Aug 2018 10:27:57 +0200 (CEST) Cc: pn@denx.de, jic23@kernel.org, knaack.h@gmx.de, lars@metafoo.de, pmeerw@pmeerw.net, robh+dt@kernel.org, andy.shevchenko@gmail.com, linux-iio@vger.kernel.org, linux-kernel@vger.kernel.org, mark.rutland@arm.com, devicetree@vger.kernel.org, matthias.bgg@gmail.com, wd@denx.de, sbabic@denx.de, hs@denx.de Subject: Re: [PATCH v6 1/2] iio: light: Add support for vishay vcnl4035 To: Marcus Folkesson References: <20180807102704.1652656-1-pn@denx.de> <20180819215033.GD5217@gmail.com> From: Parthiban Nallathambi Message-ID: Date: Mon, 20 Aug 2018 10:27:57 +0200 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.9.1 MIME-Version: 1.0 In-Reply-To: <20180819215033.GD5217@gmail.com> Content-Type: text/plain; charset=windows-1252; format=flowed Content-Language: en-US Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Hi Marcus, On 08/19/2018 11:50 PM, Marcus Folkesson wrote: > Hi, > > On Tue, Aug 07, 2018 at 12:27:03PM +0200, Parthiban Nallathambi wrote: >> Add support for VCNL4035, which is capable of Ambient light >> sensing (ALS) and proximity function. This patch adds support >> only for ALS function >> >> Signed-off-by: Parthiban Nallathambi >> >> Changelog since v1: >> >> 1. Fixed 0-day warning on le16_to_cpu usage >> 2. Persistence value is directly mapped to datasheet's value to >> avoid confusions of usage from sysfs >> >> Changelog in v3: >> - Usage of lock is not needed, removed mutex locking >> - ALS threshold and persistence configuration available >> as events and threshold events are notified to userspace >> - Usage of devm_ is re-ordered and exit handling updated >> - Complexity in timestamp calculation is removed and used >> iio_get_time_ns >> >> Changelog in v4: >> - New white light index is introduced for getting data from >> white spectrum >> - PM enable/disable is called from read_raw accordingly >> - Probe exit handling re-ordered >> >> Changelog in v5: >> - White spectrum is mesaured as "_CLEAR" intesity, so removed >> white spectrum modifier >> - Header re-ordering >> - Trigger init and de-init into separate function >> - Indentation correct and usage of dev_err in place of pr_err >> >> Changelog in v6: >> - Usage of devm_ for trigger probing and cleanups >> - pm_runtime re-order and exit patch corrections >> - IIO_INTENSITY to IIO_LIGHT, lux/steps are fixed at IT cycle >> lux can be calculated based on IT sensitivity >> - _CLEAR to _BOTH although measurement is only WHITE spectrum >> for traditional reasons >> --- > > As Jonathan already pointed out, the changelog should go here. > A tip is to use notes (see `man git-notes`) to add a changelog and then > generate the patches with `git format-patch --notes `. > > I find it really neat. Sure, thanks for the hint about git notes. > >> drivers/iio/light/Kconfig | 12 + >> drivers/iio/light/Makefile | 1 + >> drivers/iio/light/vcnl4035.c | 686 +++++++++++++++++++++++++++++++++++++++++++ >> 3 files changed, 699 insertions(+) >> create mode 100644 drivers/iio/light/vcnl4035.c >> >> diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig >> index c7ef8d1862d6..b7069a4c28a2 100644 >> --- a/drivers/iio/light/Kconfig >> +++ b/drivers/iio/light/Kconfig >> @@ -447,6 +447,18 @@ config VCNL4000 >> To compile this driver as a module, choose M here: the >> module will be called vcnl4000. >> >> +config VCNL4035 >> + tristate "VCNL4035 combined ALS and proximity sensor" >> + select REGMAP_I2C >> + depends on I2C >> + help >> + Say Y here if you want to build a driver for the Vishay VCNL4035, >> + combined ambient light (ALS) and proximity sensor. Currently only ALS >> + function is available. >> + >> + To compile this driver as a module, choose M here: the >> + module will be called vcnl4035. >> + >> config VEML6070 >> tristate "VEML6070 UV A light sensor" >> depends on I2C >> diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile >> index 80943af5d627..dce98511a59b 100644 >> --- a/drivers/iio/light/Makefile >> +++ b/drivers/iio/light/Makefile >> @@ -44,6 +44,7 @@ obj-$(CONFIG_TSL2772) += tsl2772.o >> obj-$(CONFIG_TSL4531) += tsl4531.o >> obj-$(CONFIG_US5182D) += us5182d.o >> obj-$(CONFIG_VCNL4000) += vcnl4000.o >> +obj-$(CONFIG_VCNL4035) += vcnl4035.o >> obj-$(CONFIG_VEML6070) += veml6070.o >> obj-$(CONFIG_VL6180) += vl6180.o >> obj-$(CONFIG_ZOPT2201) += zopt2201.o >> diff --git a/drivers/iio/light/vcnl4035.c b/drivers/iio/light/vcnl4035.c >> new file mode 100644 >> index 000000000000..e9f471d93a15 >> --- /dev/null >> +++ b/drivers/iio/light/vcnl4035.c >> @@ -0,0 +1,686 @@ >> +// SPDX-License-Identifier: GPL-2.0 >> +/* >> + * VCNL4035 Ambient Light and Proximity Sensor - 7-bit I2C slave address 0x60 >> + * >> + * Copyright (c) 2018, DENX Software Engineering GmbH >> + * Author: Parthiban Nallathambi >> + * >> + * >> + * TODO: Proximity >> + */ >> +#include >> +#include >> +#include >> +#include >> +#include >> + >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> + >> +#define VCNL4035_DRV_NAME "vcnl4035" >> +#define VCNL4035_IRQ_NAME "vcnl4035_event" >> +#define VCNL4035_REGMAP_NAME "vcnl4035_regmap" >> + >> +/* Device registers */ >> +#define VCNL4035_ALS_CONF 0x00 >> +#define VCNL4035_ALS_THDH 0x01 >> +#define VCNL4035_ALS_THDL 0x02 >> +#define VCNL4035_ALS_DATA 0x0B >> +#define VCNL4035_WHITE_DATA 0x0C >> +#define VCNL4035_INT_FLAG 0x0D >> +#define VCNL4035_DEV_ID 0x0E >> + >> +/* Register masks */ >> +#define VCNL4035_MODE_ALS_MASK BIT(0) >> +#define VCNL4035_MODE_ALS_WHITE_CHAN BIT(8) >> +#define VCNL4035_MODE_ALS_INT_MASK BIT(1) >> +#define VCNL4035_ALS_IT_MASK GENMASK(7, 5) >> +#define VCNL4035_ALS_PERS_MASK GENMASK(3, 2) >> +#define VCNL4035_INT_ALS_IF_H_MASK BIT(12) >> +#define VCNL4035_INT_ALS_IF_L_MASK BIT(13) >> + >> +/* Default values */ >> +#define VCNL4035_MODE_ALS_ENABLE BIT(0) >> +#define VCNL4035_MODE_ALS_DISABLE 0x00 >> +#define VCNL4035_MODE_ALS_INT_ENABLE BIT(1) >> +#define VCNL4035_MODE_ALS_INT_DISABLE 0 >> +#define VCNL4035_DEV_ID_VAL 0x80 >> +#define VCNL4035_ALS_IT_DEFAULT 0x01 >> +#define VCNL4035_ALS_PERS_DEFAULT 0x00 >> +#define VCNL4035_ALS_THDH_DEFAULT 5000 >> +#define VCNL4035_ALS_THDL_DEFAULT 100 >> +#define VCNL4035_SLEEP_DELAY_MS 2000 >> + >> +struct vcnl4035_data { >> + struct i2c_client *client; >> + struct regmap *regmap; >> + unsigned int als_it_val; >> + unsigned int als_persistence:4; >> + unsigned int als_thresh_low; >> + unsigned int als_thresh_high; >> + struct iio_trigger *drdy_trigger0; >> +}; >> + >> +static inline bool vcnl4035_is_triggered(struct vcnl4035_data *data) >> +{ >> + int ret; >> + int reg; >> + >> + ret = regmap_read(data->regmap, VCNL4035_INT_FLAG, ®); >> + if (ret < 0) >> + return false; >> + >> + return !!(reg & >> + (VCNL4035_INT_ALS_IF_H_MASK | VCNL4035_INT_ALS_IF_L_MASK)); >> +} >> + >> +static irqreturn_t vcnl4035_drdy_irq_thread(int irq, void *private) >> +{ >> + struct iio_dev *indio_dev = private; >> + struct vcnl4035_data *data = iio_priv(indio_dev); >> + >> + if (vcnl4035_is_triggered(data)) { >> + iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_LIGHT, >> + 0, >> + IIO_EV_TYPE_THRESH, >> + IIO_EV_DIR_EITHER), >> + iio_get_time_ns(indio_dev)); >> + iio_trigger_poll_chained(data->drdy_trigger0); >> + return IRQ_HANDLED; >> + } >> + >> + return IRQ_NONE; >> +} >> + >> +/* Triggered buffer */ >> +static irqreturn_t vcnl4035_trigger_consumer_handler(int irq, void *p) >> +{ >> + struct iio_poll_func *pf = p; >> + struct iio_dev *indio_dev = pf->indio_dev; >> + struct vcnl4035_data *data = iio_priv(indio_dev); >> + u8 buffer[ALIGN(sizeof(u16), sizeof(s64)) + sizeof(s64)]; >> + int ret; >> + >> + ret = regmap_read(data->regmap, VCNL4035_ALS_DATA, (int *)buffer); >> + if (ret < 0) { >> + dev_err(&data->client->dev, >> + "Trigger consumer can't read from sensor.\n"); >> + goto fail_read; >> + } >> + iio_push_to_buffers_with_timestamp(indio_dev, buffer, >> + iio_get_time_ns(indio_dev)); >> + >> +fail_read: >> + iio_trigger_notify_done(indio_dev->trig); >> + >> + return IRQ_HANDLED; >> +} >> + >> +static int vcnl4035_als_drdy_set_state(struct iio_trigger *trigger, >> + bool enable_drdy) >> +{ >> + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trigger); >> + struct vcnl4035_data *data = iio_priv(indio_dev); >> + int val = enable_drdy ? VCNL4035_MODE_ALS_INT_ENABLE : >> + VCNL4035_MODE_ALS_INT_DISABLE; >> + >> + return regmap_update_bits(data->regmap, VCNL4035_ALS_CONF, >> + VCNL4035_MODE_ALS_INT_MASK, >> + val); >> +} >> + >> +static const struct iio_trigger_ops vcnl4035_trigger_ops = { >> + .validate_device = iio_trigger_validate_own_device, >> + .set_trigger_state = vcnl4035_als_drdy_set_state, >> +}; >> + >> +static int vcnl4035_set_pm_runtime_state(struct vcnl4035_data *data, bool on) >> +{ >> + int ret; >> + struct device *dev = &data->client->dev; >> + >> + if (on) { >> + ret = pm_runtime_get_sync(dev); >> + if (ret < 0) >> + pm_runtime_put_noidle(dev); >> + } else { >> + pm_runtime_mark_last_busy(dev); >> + ret = pm_runtime_put_autosuspend(dev); >> + } >> + >> + return ret; >> +} >> + >> +/* >> + * Device IT INT Time (ms) Scale (lux/step) >> + * 000 50 0.064 >> + * 001 100 0.032 >> + * 010 200 0.016 >> + * 100 400 0.008 >> + * 101 - 111 800 0.004 >> + * Values are proportional, so ALS INT is selected for input due to >> + * simplicity reason. Integration time value and scaling is >> + * calculated based on device INT value >> + * >> + * Raw value needs to be scaled using ALS steps >> + */ >> +static int vcnl4035_read_raw(struct iio_dev *indio_dev, >> + struct iio_chan_spec const *chan, int *val, >> + int *val2, long mask) >> +{ >> + struct vcnl4035_data *data = iio_priv(indio_dev); >> + int ret; >> + int raw_data; >> + unsigned int reg; >> + >> + switch (mask) { >> + case IIO_CHAN_INFO_RAW: >> + ret = vcnl4035_set_pm_runtime_state(data, true); >> + if (ret < 0) >> + return ret; >> + >> + ret = iio_device_claim_direct_mode(indio_dev); >> + if (!ret) { >> + if (chan->channel) >> + reg = VCNL4035_ALS_DATA; >> + else >> + reg = VCNL4035_WHITE_DATA; >> + ret = regmap_read(data->regmap, reg, &raw_data); >> + iio_device_release_direct_mode(indio_dev); >> + if (!ret) { >> + *val = raw_data; >> + ret = IIO_VAL_INT; >> + } >> + } >> + vcnl4035_set_pm_runtime_state(data, false); >> + return ret; >> + case IIO_CHAN_INFO_INT_TIME: >> + *val = 50; >> + if (data->als_it_val) >> + *val = data->als_it_val * 100; >> + return IIO_VAL_INT; >> + case IIO_CHAN_INFO_SCALE: >> + *val = 64; >> + if (!data->als_it_val) >> + *val2 = 1000; >> + else >> + *val2 = data->als_it_val * 2 * 1000; >> + return IIO_VAL_FRACTIONAL; >> + default: >> + return -EINVAL; >> + } >> +} >> + >> +static int vcnl4035_write_raw(struct iio_dev *indio_dev, >> + struct iio_chan_spec const *chan, >> + int val, int val2, long mask) >> +{ >> + int ret; >> + struct vcnl4035_data *data = iio_priv(indio_dev); >> + >> + switch (mask) { >> + case IIO_CHAN_INFO_INT_TIME: > > Should we check that val2 is 0? > >> + if (val <= 0 || val > 800) >> + return -EINVAL; >> + >> + ret = vcnl4035_set_pm_runtime_state(data, true); >> + if (ret < 0) >> + return ret; >> + >> + ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF, >> + VCNL4035_ALS_IT_MASK, >> + val / 100); >> + if (!ret) >> + data->als_it_val = val / 100; >> + >> + vcnl4035_set_pm_runtime_state(data, false); >> + return ret; >> + default: >> + return -EINVAL; >> + } >> +} >> + >> +/* No direct ABI for persistence and threshold, so eventing */ >> +static int vcnl4035_read_thresh(struct iio_dev *indio_dev, >> + const struct iio_chan_spec *chan, enum iio_event_type type, >> + enum iio_event_direction dir, enum iio_event_info info, >> + int *val, int *val2) >> +{ >> + struct vcnl4035_data *data = iio_priv(indio_dev); >> + >> + switch (info) { >> + case IIO_EV_INFO_VALUE: >> + switch (dir) { >> + case IIO_EV_DIR_RISING: >> + *val = data->als_thresh_high; >> + return IIO_VAL_INT; >> + case IIO_EV_DIR_FALLING: >> + *val = data->als_thresh_low; >> + return IIO_VAL_INT; >> + default: >> + return -EINVAL; >> + } >> + break; >> + case IIO_EV_INFO_PERIOD: >> + *val = data->als_persistence; >> + return IIO_VAL_INT; >> + default: >> + return -EINVAL; >> + } >> + >> +} >> + >> +static int vcnl4035_write_thresh(struct iio_dev *indio_dev, >> + const struct iio_chan_spec *chan, enum iio_event_type type, >> + enum iio_event_direction dir, enum iio_event_info info, int val, >> + int val2) >> +{ >> + struct vcnl4035_data *data = iio_priv(indio_dev); >> + int ret; >> + > > Should we check that val2 is 0? > >> + switch (info) { >> + case IIO_EV_INFO_VALUE: >> + /* 16 bit threshold range 0 - 65535 */ >> + if (val < 0 || val > 65535) >> + return -EINVAL; >> + if (dir == IIO_EV_DIR_RISING) { >> + if (val < data->als_thresh_low) >> + return -EINVAL; >> + ret = regmap_write(data->regmap, VCNL4035_ALS_THDH, >> + val); >> + if (!ret) >> + data->als_thresh_high = val; >> + } else { >> + if (val > data->als_thresh_high) >> + return -EINVAL; >> + ret = regmap_write(data->regmap, VCNL4035_ALS_THDL, >> + val); >> + if (!ret) >> + data->als_thresh_low = val; >> + } >> + return ret; >> + case IIO_EV_INFO_PERIOD: >> + /* allow only 1 2 4 8 as persistence value */ >> + if (val < 0 || val > 8 || __sw_hweight8(val) != 1) >> + return -EINVAL; >> + ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF, >> + VCNL4035_ALS_PERS_MASK, val); >> + if (!ret) >> + data->als_persistence = val; >> + return ret; >> + default: >> + return -EINVAL; >> + } >> +} >> + >> +static IIO_CONST_ATTR_INT_TIME_AVAIL("50 100 200 400 800"); >> + >> +static struct attribute *vcnl4035_attributes[] = { >> + &iio_const_attr_integration_time_available.dev_attr.attr, >> + NULL, >> +}; >> + >> +static const struct attribute_group vcnl4035_attribute_group = { >> + .attrs = vcnl4035_attributes, >> +}; >> + >> +static const struct iio_info vcnl4035_info = { >> + .read_raw = vcnl4035_read_raw, >> + .write_raw = vcnl4035_write_raw, >> + .read_event_value = vcnl4035_read_thresh, >> + .write_event_value = vcnl4035_write_thresh, >> + .attrs = &vcnl4035_attribute_group, >> +}; >> + >> +static const struct iio_event_spec vcnl4035_event_spec[] = { >> + { >> + .type = IIO_EV_TYPE_THRESH, >> + .dir = IIO_EV_DIR_RISING, >> + .mask_separate = BIT(IIO_EV_INFO_VALUE), >> + }, { >> + .type = IIO_EV_TYPE_THRESH, >> + .dir = IIO_EV_DIR_FALLING, >> + .mask_separate = BIT(IIO_EV_INFO_VALUE), >> + }, { >> + .type = IIO_EV_TYPE_THRESH, >> + .dir = IIO_EV_DIR_EITHER, >> + .mask_separate = BIT(IIO_EV_INFO_PERIOD), >> + }, >> +}; >> + >> +enum vcnl4035_scan_index_order { >> + VCNL4035_CHAN_INDEX_LIGHT, >> + VCNL4035_CHAN_INDEX_WHITE_LED, >> +}; >> + >> +static const unsigned long vcnl4035_available_scan_masks[] = { >> + BIT(VCNL4035_CHAN_INDEX_LIGHT), >> + BIT(VCNL4035_CHAN_INDEX_WHITE_LED), >> + 0 >> +}; >> + >> +static const struct iio_chan_spec vcnl4035_channels[] = { >> + { >> + .type = IIO_LIGHT, >> + .channel = 0, >> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | >> + BIT(IIO_CHAN_INFO_INT_TIME) | >> + BIT(IIO_CHAN_INFO_SCALE), >> + .event_spec = vcnl4035_event_spec, >> + .num_event_specs = ARRAY_SIZE(vcnl4035_event_spec), >> + .scan_index = VCNL4035_CHAN_INDEX_LIGHT, >> + .scan_type = { >> + .sign = 'u', >> + .realbits = 16, >> + .storagebits = 16, >> + .endianness = IIO_LE, >> + }, >> + }, >> + { >> + .type = IIO_LIGHT, >> + .channel = 1, >> + .modified = 1, >> + .channel2 = IIO_MOD_LIGHT_BOTH, >> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), >> + .scan_index = VCNL4035_CHAN_INDEX_WHITE_LED, >> + .scan_type = { >> + .sign = 'u', >> + .realbits = 16, >> + .storagebits = 16, >> + .endianness = IIO_LE, >> + }, >> + }, >> +}; >> + >> +static int vcnl4035_set_als_power_state(struct vcnl4035_data *data, u8 status) >> +{ >> + return regmap_update_bits(data->regmap, VCNL4035_ALS_CONF, >> + VCNL4035_MODE_ALS_MASK, >> + status); >> +} >> + >> +static int vcnl4035_init(struct vcnl4035_data *data) >> +{ >> + int ret; >> + int id; >> + >> + ret = regmap_read(data->regmap, VCNL4035_DEV_ID, &id); >> + if (ret < 0) { >> + dev_err(&data->client->dev, "Failed to read DEV_ID register\n"); >> + return ret; >> + } >> + >> + if (id != VCNL4035_DEV_ID_VAL) { >> + dev_err(&data->client->dev, "Wrong id, got %x, expected %x\n", >> + id, VCNL4035_DEV_ID_VAL); >> + return -ENODEV; >> + } >> + >> + ret = vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_ENABLE); >> + if (ret < 0) >> + return ret; >> + >> + /* ALS white channel enable */ >> + ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF, >> + VCNL4035_MODE_ALS_WHITE_CHAN, >> + 1); >> + if (ret) { >> + dev_err(&data->client->dev, "set white channel enable %d\n", >> + ret); >> + return ret; >> + } >> + >> + /* set default integration time - 100 ms for ALS */ >> + ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF, >> + VCNL4035_ALS_IT_MASK, >> + VCNL4035_ALS_IT_DEFAULT); >> + if (ret) { >> + dev_err(&data->client->dev, "set default ALS IT returned %d\n", >> + ret); >> + return ret; >> + } >> + data->als_it_val = VCNL4035_ALS_IT_DEFAULT; >> + >> + /* set default persistence time - 1 for ALS */ >> + ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF, >> + VCNL4035_ALS_PERS_MASK, >> + VCNL4035_ALS_PERS_DEFAULT); >> + if (ret) { >> + dev_err(&data->client->dev, "set default PERS returned %d\n", >> + ret); >> + return ret; >> + } >> + data->als_persistence = VCNL4035_ALS_PERS_DEFAULT; >> + >> + /* set default HIGH threshold for ALS */ >> + ret = regmap_write(data->regmap, VCNL4035_ALS_THDH, >> + VCNL4035_ALS_THDH_DEFAULT); >> + if (ret) { >> + dev_err(&data->client->dev, "set default THDH returned %d\n", >> + ret); >> + return ret; >> + } >> + data->als_thresh_high = VCNL4035_ALS_THDH_DEFAULT; >> + >> + /* set default LOW threshold for ALS */ >> + ret = regmap_write(data->regmap, VCNL4035_ALS_THDL, >> + VCNL4035_ALS_THDL_DEFAULT); >> + if (ret) { >> + dev_err(&data->client->dev, "set default THDL returned %d\n", >> + ret); >> + return ret; >> + } >> + data->als_thresh_low = VCNL4035_ALS_THDL_DEFAULT; >> + >> + return 0; >> +} >> + >> +static bool vcnl4035_is_volatile_reg(struct device *dev, unsigned int reg) >> +{ >> + switch (reg) { >> + case VCNL4035_ALS_CONF: >> + case VCNL4035_DEV_ID: >> + return false; >> + default: >> + return true; >> + } >> +} >> + >> +static const struct regmap_config vcnl4035_regmap_config = { >> + .name = VCNL4035_REGMAP_NAME, >> + .reg_bits = 8, >> + .val_bits = 16, >> + .max_register = VCNL4035_DEV_ID, >> + .cache_type = REGCACHE_RBTREE, >> + .volatile_reg = vcnl4035_is_volatile_reg, >> + .val_format_endian = REGMAP_ENDIAN_LITTLE, >> +}; >> + >> +static int vcnl4035_probe_trigger(struct iio_dev *indio_dev) >> +{ >> + int ret; >> + struct vcnl4035_data *data = iio_priv(indio_dev); >> + >> + data->drdy_trigger0 = devm_iio_trigger_alloc( >> + indio_dev->dev.parent, >> + "%s-dev%d", indio_dev->name, indio_dev->id); >> + if (!data->drdy_trigger0) >> + return -ENOMEM; >> + >> + data->drdy_trigger0->dev.parent = indio_dev->dev.parent; >> + data->drdy_trigger0->ops = &vcnl4035_trigger_ops; >> + iio_trigger_set_drvdata(data->drdy_trigger0, indio_dev); >> + ret = devm_iio_trigger_register(indio_dev->dev.parent, >> + data->drdy_trigger0); >> + if (ret) { >> + dev_err(&data->client->dev, "iio trigger register failed\n"); >> + return ret; >> + } >> + >> + /* Trigger setup */ >> + ret = devm_iio_triggered_buffer_setup(indio_dev->dev.parent, indio_dev, >> + NULL, vcnl4035_trigger_consumer_handler, >> + NULL); >> + if (ret < 0) { >> + dev_err(&data->client->dev, "iio triggered buffer setup failed\n"); >> + return ret; >> + } >> + >> + /* IRQ to trigger mapping */ >> + ret = devm_request_threaded_irq(&data->client->dev, data->client->irq, >> + NULL, vcnl4035_drdy_irq_thread, >> + IRQF_TRIGGER_LOW | IRQF_ONESHOT, >> + VCNL4035_IRQ_NAME, indio_dev); >> + if (ret < 0) { >> + dev_err(&data->client->dev, "request irq %d for trigger0 failed\n", >> + data->client->irq); >> + return ret; >> + } >> + >> + return 0; >> +} >> + >> +static int vcnl4035_probe(struct i2c_client *client, >> + const struct i2c_device_id *id) >> +{ >> + struct vcnl4035_data *data; >> + struct iio_dev *indio_dev; >> + struct regmap *regmap; >> + int ret; >> + >> + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); >> + if (!indio_dev) >> + return -ENOMEM; >> + >> + regmap = devm_regmap_init_i2c(client, &vcnl4035_regmap_config); >> + if (IS_ERR(regmap)) { >> + dev_err(&client->dev, "regmap_init failed!\n"); >> + return PTR_ERR(regmap); >> + } >> + >> + data = iio_priv(indio_dev); >> + i2c_set_clientdata(client, indio_dev); >> + data->client = client; >> + data->regmap = regmap; >> + >> + indio_dev->dev.parent = &client->dev; >> + indio_dev->info = &vcnl4035_info; >> + indio_dev->name = VCNL4035_DRV_NAME; >> + indio_dev->channels = vcnl4035_channels; >> + indio_dev->num_channels = ARRAY_SIZE(vcnl4035_channels); >> + indio_dev->modes = INDIO_DIRECT_MODE; >> + >> + ret = vcnl4035_init(data); >> + if (ret < 0) { >> + dev_err(&client->dev, "vcnl4035 chip init failed\n"); >> + return ret; >> + } >> + >> + if (client->irq > 0) { >> + ret = vcnl4035_probe_trigger(indio_dev); >> + if (ret < 0) { >> + dev_err(&client->dev, "vcnl4035 unable init trigger\n"); >> + goto fail_poweroff; >> + } >> + } >> + >> + ret = iio_device_register(indio_dev); >> + if (ret) >> + goto fail_poweroff; >> + >> + ret = pm_runtime_set_active(&client->dev); >> + if (ret < 0) >> + goto fail_unregister; >> + >> + pm_runtime_enable(&client->dev); >> + pm_runtime_set_autosuspend_delay(&client->dev, VCNL4035_SLEEP_DELAY_MS); >> + pm_runtime_use_autosuspend(&client->dev); >> + >> + return 0; >> + >> +fail_unregister: >> + iio_device_unregister(indio_dev); >> +fail_poweroff: >> + vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_DISABLE); >> + return ret; >> +} >> + >> +static int vcnl4035_remove(struct i2c_client *client) >> +{ >> + struct iio_dev *indio_dev = i2c_get_clientdata(client); >> + >> + iio_device_unregister(indio_dev); >> + >> + pm_runtime_dont_use_autosuspend(&client->dev); >> + pm_runtime_disable(&client->dev); >> + pm_runtime_set_suspended(&client->dev); >> + pm_runtime_put_noidle(&client->dev); >> + >> + return vcnl4035_set_als_power_state(iio_priv(indio_dev), >> + VCNL4035_MODE_ALS_DISABLE); >> +} >> + >> +#ifdef CONFIG_PM >> +static int __maybe_unused vcnl4035_runtime_suspend(struct device *dev) >> +{ >> + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); >> + struct vcnl4035_data *data = iio_priv(indio_dev); >> + int ret; >> + >> + ret = vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_DISABLE); >> + regcache_mark_dirty(data->regmap); >> + >> + return ret; >> +} >> + >> +static int __maybe_unused vcnl4035_runtime_resume(struct device *dev) >> +{ >> + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); >> + struct vcnl4035_data *data = iio_priv(indio_dev); >> + int ret; >> + >> + regcache_sync(data->regmap); >> + ret = vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_ENABLE); >> + if (ret < 0) >> + return ret; >> + >> + /* wait for 1 ALS integration cycle */ >> + msleep(data->als_it_val * 100); >> + >> + return 0; >> +} >> +#endif >> + >> +static const struct dev_pm_ops vcnl4035_pm_ops = { >> + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, >> + pm_runtime_force_resume) >> + SET_RUNTIME_PM_OPS(vcnl4035_runtime_suspend, >> + vcnl4035_runtime_resume, NULL) >> +}; >> + >> +static const struct of_device_id vcnl4035_of_match[] = { >> + { .compatible = "vishay,vcnl4035", }, >> + { } >> +}; >> +MODULE_DEVICE_TABLE(of, vcnl4035_of_match); >> + >> +static struct i2c_driver vcnl4035_driver = { >> + .driver = { >> + .name = VCNL4035_DRV_NAME, >> + .pm = &vcnl4035_pm_ops, >> + .of_match_table = of_match_ptr(vcnl4035_of_match), > > Please correct me if I'm wrong here. > > Use of_match_ptr() and not provide an ACPI match table will not make > this driver work on ACPI systems. > > The reason is that the ACPI match function will fall back on > of_match_table if no acpi_match_table is provided, and the > of_match_ptr() macro will simple evaluate to NULL if not CONFIG_OF > is set. > > In other words, I think we should change this to just > .of_match_table = &vcnl4035_of_match, Yes, I missed that in last revision. Andy pointed me this. I will remove this and also ifdef CONFIG_PM which is not required in next revision. > >> + }, >> + .probe = vcnl4035_probe, >> + .remove = vcnl4035_remove, >> +}; >> + >> +module_i2c_driver(vcnl4035_driver); >> + >> +MODULE_AUTHOR("Parthiban Nallathambi "); >> +MODULE_DESCRIPTION("VCNL4035 Ambient Light Sensor driver"); >> +MODULE_LICENSE("GPL v2"); >> -- >> 2.14.4 >> > > Best regards > Marcus Folkesson > -- Thanks, Parthiban Nallathambi DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de