From nobody Wed Dec 17 01:07:37 2025 Received: from mail-pg1-f169.google.com (mail-pg1-f169.google.com [209.85.215.169]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8B88A1B86FB; Thu, 18 Jul 2024 10:50:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1721299827; cv=none; b=ESeWmG49ambjFa6XSyAmcrUpyUMmgEJlWBa/9wTp7fwwuwGPpjYQCGtS7KKmFH59vUMm3ytRY0/kTHpfeEV3kZELkt609+MighhHuml1kkbFmfNLq1TG2jeTdPBx+TZmQ2sYEQJAQCim5EUzPHBQrkUoL1XLsgGsXr3fXm3hqdo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1721299827; c=relaxed/simple; bh=vEjFRBWsSwAIUZLWB+DrzyigB9DIGSgo2Bnboz2zqeQ=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=Y0hHRoASjQB//gUm+Vyxu3sOjD+wqbGk4xMfBeVnFsfzNcYquBvkTXDGBKXkSs/+roDjBsrz5Ygp0htCEt10lYgw1//5uBhs+S8vv/SD4BHVa2SoJ6rZcaQi8+Us5C6rUcopjtiqHFjjeAHWzri6/QtSXi/hQ6lA+3FIGP8ExgQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=gGAPKHqe; arc=none smtp.client-ip=209.85.215.169 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="gGAPKHqe" Received: by mail-pg1-f169.google.com with SMTP id 41be03b00d2f7-78135be2d46so411700a12.0; Thu, 18 Jul 2024 03:50:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1721299823; x=1721904623; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=+eCSYPvaq7B66pgnJLs9WEsLtzq9JpmQfKw7c3QRAWo=; b=gGAPKHqeXYRbMlcDRXMzJRcYuuLmU9gEygjt9YfwFbD981pl+zJNCtI0ZhvL12ITI6 EDCDIYo6xyEPVbPEofJxfqxoYTQ/Gosyoi7vB/D1aX1WlGgNrai/SZuon2Kh46m2LJi5 X7IzsEiC9kz8QE3jpKAz00N0NIqQs3pA+tidMk6NsTeRBsmQFSqZSKlQD1ddwUCSeI3x OY0trQrnSUGruAQA7LRtM/twHIPP6YVfYJXDjekvSQF+6ugi6BouNgeRsIQi5BK2F+wA 6rY5a2jY8aDUylxCPc9aIEvoUVpj2iLElFFtys1sPqX25gZFsyJF0FM7PP2BKBNskbs/ FarQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1721299823; x=1721904623; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=+eCSYPvaq7B66pgnJLs9WEsLtzq9JpmQfKw7c3QRAWo=; b=vWpB5GLq31cOdqh78QoY++uf/bQC8hxo7hoNTSNWG55MvWszP/SwnkfR5kVSJAndFc ZZwobmgWGNOZ/uOM2639ztM9yrM4Q1VbZ4RTVZfGi6yW/h8ogtkiBQObUFKZY+sopoQS 5Jx8VqOiJOW/syRPx6sC+Ea2dezVjd0EArsakHNgwBT/NCKdgZMjgu4614V9ydyVJl3u BgVzCbDrWXYwDg/iKGozWNQoFzzfR0+/8Ybgy1NFQTJKP93/+CkgVaxaTc3lv5GtP1vE sshdLxcjJVnyHtPUI0vgRSDOp8BMDrQVjjp4iBGHCIJL1MDjs6iaQR4eb2Aoa+nDLLIH CMQg== X-Forwarded-Encrypted: i=1; AJvYcCXpeHMDlfrG3dekooTJicuFsKznNXV/pnahD7d4ed1rwBrWmWg2UWN/7PjXMrpU9asm5SATNeN4tUEVIqWhW2vTlyz/3vXz6vEZHHTG X-Gm-Message-State: AOJu0YzuNc7nRWY1C0enDlgc4hc5pRYd4EOI5FffKteRfQ2E2iebqwOn r6gJFVe4WjFmbQFasIY986QgoLDm0xwnUtaDtfpXEQQP+E+OZRBNMF8MwxO3A4VoIg== X-Google-Smtp-Source: AGHT+IFaXR2RtJ3pBrYJc2vTjIu0AooOWZLuE6CPwbh+wMKWgPCeEUd/i9/Qa4uykcV5cFvFctJO0g== X-Received: by 2002:a05:6a21:670b:b0:1c0:bd6c:aaa0 with SMTP id adf61e73a8af0-1c3fdcf4ba7mr4879444637.28.1721299822961; Thu, 18 Jul 2024 03:50:22 -0700 (PDT) Received: from localhost.localdomain ([136.233.9.100]) by smtp.googlemail.com with ESMTPSA id 98e67ed59e1d1-2cb778c6fe6sm330366a91.10.2024.07.18.03.50.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 18 Jul 2024 03:50:22 -0700 (PDT) From: Abhash Jha To: linux-iio@vger.kernel.org Cc: anshulusr@gmail.com, jic23@kernel.org, lars@metafoo.de, linux-kernel@vger.kernel.org, Abhash Jha Subject: [PATCH] iio: light: ltr390: Add configurable gain, resolution and ALS reading Date: Thu, 18 Jul 2024 16:19:45 +0530 Message-ID: <20240718104947.7384-1-abhashkumarjha123@gmail.com> X-Mailer: git-send-email 2.43.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 1) Add support for configuring the gain and resolution(integration time) for the sensor. 2) Add a channel for ALS and provide support for reading the raw and scale values. 3) Add automatic mode switching between UVS and ALS based on the channel type. 4) Calculate 'counts_per_uvi' based on the current gain and integration time. Signed-off-by: Abhash Jha --- drivers/iio/light/ltr390.c | 256 ++++++++++++++++++++++++++++++++++--- 1 file changed, 238 insertions(+), 18 deletions(-) diff --git a/drivers/iio/light/ltr390.c b/drivers/iio/light/ltr390.c index fff1e8990..56f3c74ae 100644 --- a/drivers/iio/light/ltr390.c +++ b/drivers/iio/light/ltr390.c @@ -25,19 +25,33 @@ #include =20 #include - +#include #include =20 #define LTR390_MAIN_CTRL 0x00 #define LTR390_PART_ID 0x06 #define LTR390_UVS_DATA 0x10 =20 +#define LTR390_ALS_DATA 0x0D +#define LTR390_ALS_UVS_GAIN 0x05 +#define LTR390_ALS_UVS_MEAS_RATE 0x04 +#define LTR390_INT_CFG 0x19 + #define LTR390_SW_RESET BIT(4) #define LTR390_UVS_MODE BIT(3) #define LTR390_SENSOR_ENABLE BIT(1) =20 #define LTR390_PART_NUMBER_ID 0xb =20 +#define LTR390_ALS_UVS_GAIN_MASK 0x07 +#define LTR390_ALS_UVS_INT_TIME_MASK 0x70 +#define LTR390_ALS_UVS_INT_TIME_MASK_SHIFT 4 + +#define LTR390_SET_ALS_MODE 1 +#define LTR390_SET_UVS_MODE 2 + +#define LTR390_FRACTIONAL_PRECISION 100 + /* * At 20-bit resolution (integration time: 400ms) and 18x gain, 2300 count= s of * the sensor are equal to 1 UV Index [Datasheet Page#8]. @@ -60,6 +74,9 @@ struct ltr390_data { struct i2c_client *client; /* Protects device from simulataneous reads */ struct mutex lock; + int mode; + int gain; + int int_time_us; }; =20 static const struct regmap_config ltr390_regmap_config =3D { @@ -87,36 +104,232 @@ static int ltr390_register_read(struct ltr390_data *d= ata, u8 register_address) return get_unaligned_le24(recieve_buffer); } =20 + +static int ltr390_set_mode(struct ltr390_data *data, int mode) +{ + if (data->mode =3D=3D mode) + return 0; + + if (mode =3D=3D LTR390_SET_ALS_MODE) { + regmap_clear_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_UVS_MODE); + data->mode =3D LTR390_SET_ALS_MODE; + } else if (mode =3D=3D LTR390_SET_UVS_MODE) { + regmap_set_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_UVS_MODE); + data->mode =3D LTR390_SET_UVS_MODE; + } else { + return -EINVAL; + } + + return 0; +} + +static int ltr390_counts_per_uvi(struct ltr390_data *data) +{ + int orig_gain =3D 18; + int orig_int_time =3D 400; + int divisor =3D orig_gain * orig_int_time; + int gain =3D data->gain; + + int int_time_ms =3D DIV_ROUND_CLOSEST(data->int_time_us, 1000); + int uvi =3D DIV_ROUND_CLOSEST(2300*gain*int_time_ms, divisor); + + return uvi; +} + static int ltr390_read_raw(struct iio_dev *iio_device, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { - int ret; struct ltr390_data *data =3D iio_priv(iio_device); + int ret; =20 switch (mask) { case IIO_CHAN_INFO_RAW: - ret =3D ltr390_register_read(data, LTR390_UVS_DATA); - if (ret < 0) - return ret; + switch (chan->type) { + case IIO_UVINDEX: + ret =3D ltr390_set_mode(data, LTR390_SET_UVS_MODE); + if (ret < 0) + return ret; + + ret =3D ltr390_register_read(data, LTR390_UVS_DATA); + if (ret < 0) + return ret; + + break; + + case IIO_INTENSITY: + ret =3D ltr390_set_mode(data, LTR390_SET_ALS_MODE); + if (ret < 0) + return ret; + + ret =3D ltr390_register_read(data, LTR390_ALS_DATA); + if (ret < 0) + return ret; + break; + + default: + ret =3D -EINVAL; + } + *val =3D ret; - return IIO_VAL_INT; + ret =3D IIO_VAL_INT; + break; + case IIO_CHAN_INFO_SCALE: - *val =3D LTR390_WINDOW_FACTOR; - *val2 =3D LTR390_COUNTS_PER_UVI; - return IIO_VAL_FRACTIONAL; + mutex_lock(&data->lock); + + switch (chan->type) { + case IIO_UVINDEX: + ret =3D ltr390_set_mode(data, LTR390_SET_UVS_MODE); + if (ret < 0) + return ret; + + *val =3D LTR390_WINDOW_FACTOR * LTR390_FRACTIONAL_PRECISION; + *val2 =3D ltr390_counts_per_uvi(data); + ret =3D IIO_VAL_FRACTIONAL; + break; + + case IIO_INTENSITY: + ret =3D ltr390_set_mode(data, LTR390_SET_ALS_MODE); + if (ret < 0) + return ret; + + *val =3D LTR390_WINDOW_FACTOR; + *val2 =3D data->gain; + + ret =3D IIO_VAL_FRACTIONAL; + break; + + default: + ret =3D -EINVAL; + } + + mutex_unlock(&data->lock); + break; + + case IIO_CHAN_INFO_INT_TIME: + mutex_lock(&data->lock); + *val =3D data->int_time_us; + mutex_unlock(&data->lock); + ret =3D IIO_VAL_INT; + break; + default: - return -EINVAL; + ret =3D -EINVAL; } + + return ret; } =20 -static const struct iio_info ltr390_info =3D { - .read_raw =3D ltr390_read_raw, +/* integration time in us */ +static const int ltr390_int_time_map_us[] =3D {400000, 200000, 100000, 500= 00, 25000, 12500}; +static const int ltr390_gain_map[] =3D {1, 3, 6, 9, 18}; + +static IIO_CONST_ATTR_INT_TIME_AVAIL("400000 200000 100000 50000 25000 125= 00"); +static IIO_CONST_ATTR(gain_available, "1 3 6 9 18"); + +static struct attribute *ltr390_attributes[] =3D { + &iio_const_attr_integration_time_available.dev_attr.attr, + &iio_const_attr_gain_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ltr390_attribute_group =3D { + .attrs =3D ltr390_attributes, }; =20 -static const struct iio_chan_spec ltr390_channel =3D { +static const struct iio_chan_spec ltr390_channels[] =3D { + /* UV sensor */ + { .type =3D IIO_UVINDEX, - .info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) + .scan_index =3D 0, + .info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all =3D BIT(IIO_CHAN_INFO_INT_TIME) + }, + /* ALS sensor */ + { + .type =3D IIO_INTENSITY, + .scan_index =3D 1, + .info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all =3D BIT(IIO_CHAN_INFO_INT_TIME) + }, +}; + +static int ltr390_set_gain(struct ltr390_data *data, int val) +{ + int ret, idx; + + for (idx =3D 0; idx < ARRAY_SIZE(ltr390_gain_map); idx++) { + if (ltr390_gain_map[idx] =3D=3D val) { + mutex_lock(&data->lock); + ret =3D regmap_update_bits(data->regmap, + LTR390_ALS_UVS_GAIN, + LTR390_ALS_UVS_GAIN_MASK, idx); + if (!ret) + data->gain =3D ltr390_gain_map[idx]; + + mutex_unlock(&data->lock); + break; + } + } + + return 0; +} + +static int ltr390_set_int_time(struct ltr390_data *data, int val) +{ + int ret, idx; + + for (idx =3D 0; idx < ARRAY_SIZE(ltr390_int_time_map_us); idx++) { + if (ltr390_int_time_map_us[idx] =3D=3D val) { + mutex_lock(&data->lock); + ret =3D regmap_update_bits(data->regmap, + LTR390_ALS_UVS_MEAS_RATE, + LTR390_ALS_UVS_INT_TIME_MASK, + idx<int_time_us =3D ltr390_int_time_map_us[idx]; + + mutex_unlock(&data->lock); + break; + } + } + + return 0; +} + +static int ltr390_write_raw(struct iio_dev *indio_dev, struct iio_chan_spe= c const *chan, + int val, int val2, long mask) +{ + struct ltr390_data *data =3D iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (val2 !=3D 0) + ret =3D -EINVAL; + + ret =3D ltr390_set_gain(data, val); + break; + + case IIO_CHAN_INFO_INT_TIME: + if (val2 !=3D 0) + ret =3D -EINVAL; + + ret =3D ltr390_set_int_time(data, val); + break; + + default: + ret =3D -EINVAL; + } + + return ret; +} + +static const struct iio_info ltr390_info =3D { + .attrs =3D <r390_attribute_group, + .read_raw =3D ltr390_read_raw, + .write_raw =3D ltr390_write_raw, }; =20 static int ltr390_probe(struct i2c_client *client) @@ -139,11 +352,18 @@ static int ltr390_probe(struct i2c_client *client) "regmap initialization failed\n"); =20 data->client =3D client; + /* default value of int time from pg: 15 of the datasheet */ + data->int_time_us =3D 100000; + /* default value of gain from pg: 16 of the datasheet */ + data->gain =3D 3; + /* default mode for ltr390 is ALS mode */ + data->mode =3D LTR390_SET_ALS_MODE; + mutex_init(&data->lock); =20 indio_dev->info =3D <r390_info; - indio_dev->channels =3D <r390_channel; - indio_dev->num_channels =3D 1; + indio_dev->channels =3D ltr390_channels; + indio_dev->num_channels =3D ARRAY_SIZE(ltr390_channels); indio_dev->name =3D "ltr390"; =20 ret =3D regmap_read(data->regmap, LTR390_PART_ID, &part_number); @@ -161,8 +381,7 @@ static int ltr390_probe(struct i2c_client *client) /* Wait for the registers to reset before proceeding */ usleep_range(1000, 2000); =20 - ret =3D regmap_set_bits(data->regmap, LTR390_MAIN_CTRL, - LTR390_SENSOR_ENABLE | LTR390_UVS_MODE); + ret =3D regmap_set_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_SENSOR_ENA= BLE); if (ret) return dev_err_probe(dev, ret, "failed to enable the sensor\n"); =20 @@ -189,6 +408,7 @@ static struct i2c_driver ltr390_driver =3D { .probe =3D ltr390_probe, .id_table =3D ltr390_id, }; + module_i2c_driver(ltr390_driver); =20 MODULE_AUTHOR("Anshul Dalal "); --=20 2.43.0