Linux IIO development
 help / color / mirror / Atom feed
From: Linus Walleij <linusw@kernel.org>
To: linux-iio@vger.kernel.org, "Jonathan Cameron" <jic23@kernel.org>,
	"David Lechner" <dlechner@baylibre.com>,
	"Nuno Sá" <nuno.sa@analog.com>,
	"Andy Shevchenko" <andy@kernel.org>,
	"Rob Herring" <robh@kernel.org>,
	"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
	"Conor Dooley" <conor+dt@kernel.org>,
	"Kyungmin Park" <kyungmin.park@samsung.com>
Cc: devicetree@vger.kernel.org, Linus Walleij <linusw@kernel.org>
Subject: [PATCH v2 5/5] iio: light: gp2ap020a00f: Add GP2AP030A00F support
Date: Sun, 08 Nov 2026 22:44:03 +0100	[thread overview]
Message-ID: <20261108-gp2ap030-v2-5-24eaa1b9fe59@kernel.org> (raw)
In-Reply-To: <20261108-gp2ap030-v2-0-24eaa1b9fe59@kernel.org>

The GP2AP030A00F has the same register addresses and interrupt modes
as the GP2AP020A00F, but does not implement its automatic
illuminance calculation or selectable ambient-light cancellation.

Datasheet: https://jp.sharp/products/device/doc/opto/gp2ap030a00f_appl_e.pdf
Signed-off-by: Linus Walleij <linusw@kernel.org>
---
 drivers/iio/light/Kconfig        |   6 +--
 drivers/iio/light/gp2ap020a00f.c | 100 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 101 insertions(+), 5 deletions(-)

diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index 462fff1ddef2..89fe003371b8 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -259,15 +259,15 @@ config GP2AP002
 	  module will be called gp2ap002.
 
 config GP2AP020A00F
-	tristate "Sharp GP2AP020A00F Proximity/ALS sensor"
+	tristate "Sharp GP2AP020A00F and similar Proximity/ALS sensors"
 	depends on I2C
 	select REGMAP_I2C
 	select IIO_BUFFER
 	select IIO_TRIGGERED_BUFFER
 	select IRQ_WORK
 	help
-	  Say Y here if you have a Sharp GP2AP020A00F proximity/ALS combo-chip
-	  hooked to an I2C bus.
+	  Say Y here to enable support for the Sharp GP2AP020A00F and similar
+	  proximity/ALS combo-chips hooked to an I2C bus.
 
 	  To compile this driver as a module, choose M here: the
 	  module will be called gp2ap020a00f.
diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c
index 5464311515c7..813a64ee627c 100644
--- a/drivers/iio/light/gp2ap020a00f.c
+++ b/drivers/iio/light/gp2ap020a00f.c
@@ -54,6 +54,11 @@
 #include <linux/iio/trigger_consumer.h>
 #include <linux/iio/triggered_buffer.h>
 
+/*
+ * The GP2AP030A00F shares the register layout, but has no TYPE or ALC bit.
+ * Its D0 and D1 registers always contain raw clear and infrared counts.
+ */
+
 /* Registers */
 #define GP2AP020A00F_OP_REG	0x00 /* Basic operations */
 #define GP2AP020A00F_ALS_REG	0x01 /* ALS related settings */
@@ -137,6 +142,7 @@
 #define GP2AP020A00F_RES_P_6_25ms_x2	0x28
 #define GP2AP020A00F_RES_P_1_56ms_x2	0x30
 #define GP2AP020A00F_RES_P_0_39ms_x2	0x38
+#define GP2AP030A00F_RES_P_1_56ms_x2	0x18
 #define GP2AP020A00F_RANGE_P_MASK	0x07 /* PS: Max measurable range */
 #define GP2AP020A00F_RANGE_P_x1		0x00
 #define GP2AP020A00F_RANGE_P_x2		0x01
@@ -158,6 +164,7 @@
 #define GP2AP020A00F_IS_27_5mA		0x10
 #define GP2AP020A00F_IS_55mA		0x20
 #define GP2AP020A00F_IS_110mA		0x30
+#define GP2AP030A00F_IS_130mA		0x30
 #define GP2AP020A00F_PIN_MASK		0x0c /* INT terminal setting */
 #define GP2AP020A00F_PIN_ALS_OR_PS	0x00
 #define GP2AP020A00F_PIN_ALS		0x04
@@ -239,6 +246,7 @@ struct gp2ap020a00f_chip_info {
 	const u8 *reg_init_tab;
 	const struct iio_chan_spec *channels;
 	unsigned int num_channels;
+	bool has_als_auto;
 };
 
 struct gp2ap020a00f_data {
@@ -276,6 +284,17 @@ static const u8 gp2ap020a00f_reg_init_tab[] = {
 	[GP2AP020A00F_PH_H_REG] = 0,
 };
 
+static const u8 gp2ap030a00f_reg_init_tab[ARRAY_SIZE(gp2ap020a00f_reg_init_tab)] = {
+	[GP2AP020A00F_OP_REG] = GP2AP020A00F_OP3_SHUTDOWN,
+	[GP2AP020A00F_ALS_REG] = GP2AP020A00F_RES_A_100ms |
+				 GP2AP020A00F_RANGE_A_x8,
+	[GP2AP020A00F_PS_REG] = GP2AP030A00F_RES_P_1_56ms_x2 |
+				GP2AP020A00F_RANGE_P_x4,
+	[GP2AP020A00F_LED_REG] = GP2AP020A00F_INTVAL_0 |
+				 GP2AP030A00F_IS_130mA |
+				 GP2AP020A00F_FREQ_327_5kHz,
+};
+
 static bool gp2ap020a00f_is_volatile_reg(struct device *dev, unsigned int reg)
 {
 	switch (reg) {
@@ -371,6 +390,7 @@ static const struct gp2ap020a00f_mutable_config_regs {
 static int gp2ap020a00f_set_operation_mode(struct gp2ap020a00f_data *data,
 					enum gp2ap020a00f_opmode op)
 {
+	u8 op_reg = opmode_regs_settings[op].op_reg;
 	unsigned int op_reg_val;
 	int err;
 
@@ -409,12 +429,16 @@ static int gp2ap020a00f_set_operation_mode(struct gp2ap020a00f_data *data,
 			return err;
 	}
 
+	if (!data->chip_info->has_als_auto)
+		op_reg = (op_reg & ~GP2AP020A00F_TYPE_MASK) |
+			 GP2AP020A00F_TYPE_MANUAL_CALC;
+
 	/* Set OP_REG and apply operation mode (power on / off) */
 	err = regmap_update_bits(data->regmap,
 				 GP2AP020A00F_OP_REG,
 				 GP2AP020A00F_OP_MASK | GP2AP020A00F_OP2_MASK |
 				 GP2AP020A00F_OP3_MASK | GP2AP020A00F_TYPE_MASK,
-				 opmode_regs_settings[op].op_reg);
+				 op_reg);
 	if (err < 0)
 		return err;
 
@@ -446,6 +470,8 @@ static int gp2ap020a00f_write_event_threshold(struct gp2ap020a00f_data *data,
 
 	if (!enable)
 		thresh_reg_val = 0;
+	else if (!data->chip_info->has_als_auto)
+		thresh_reg_val = min(data->thresh_val[th_val_id], 65535U);
 	else if (test_bit(GP2AP020A00F_FLAG_LUX_MODE_HI, &data->flags) &&
 		 th_val_id != GP2AP020A00F_THRESH_PL &&
 		 th_val_id != GP2AP020A00F_THRESH_PH)
@@ -709,6 +735,10 @@ static bool gp2ap020a00f_adjust_lux_mode(struct gp2ap020a00f_data *data,
 	u8 new_range = 0xff;
 	int err;
 
+	/* GP2AP030A00F thresholds and samples are raw counts at a fixed range. */
+	if (!data->chip_info->has_als_auto)
+		return false;
+
 	if (!test_bit(GP2AP020A00F_FLAG_LUX_MODE_HI, &data->flags)) {
 		if (output_val > 16000) {
 			set_bit(GP2AP020A00F_FLAG_LUX_MODE_HI, &data->flags);
@@ -1317,11 +1347,71 @@ static const struct iio_chan_spec gp2ap020a00f_channels[] = {
 	IIO_CHAN_SOFT_TIMESTAMP(GP2AP020A00F_CHAN_TIMESTAMP),
 };
 
+static const struct iio_chan_spec gp2ap030a00f_channels[] = {
+	{
+		.type = IIO_LIGHT,
+		.channel2 = IIO_MOD_LIGHT_CLEAR,
+		.modified = 1,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+		.scan_type = {
+			.sign = 'u',
+			.realbits = 16,
+			.shift = 0,
+			.storagebits = 32,
+			.endianness = IIO_LE,
+		},
+		.scan_index = GP2AP020A00F_SCAN_MODE_LIGHT_CLEAR,
+		.address = GP2AP020A00F_D0_L_REG,
+		.event_spec = gp2ap020a00f_event_spec_light,
+		.num_event_specs = ARRAY_SIZE(gp2ap020a00f_event_spec_light),
+	},
+	{
+		.type = IIO_LIGHT,
+		.channel2 = IIO_MOD_LIGHT_IR,
+		.modified = 1,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+		.scan_type = {
+			.sign = 'u',
+			.realbits = 16,
+			.shift = 0,
+			.storagebits = 32,
+			.endianness = IIO_LE,
+		},
+		.scan_index = GP2AP020A00F_SCAN_MODE_LIGHT_IR,
+		.address = GP2AP020A00F_D1_L_REG,
+	},
+	{
+		.type = IIO_PROXIMITY,
+		.modified = 0,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+		.scan_type = {
+			.sign = 'u',
+			.realbits = 16,
+			.shift = 0,
+			.storagebits = 16,
+			.endianness = IIO_LE,
+		},
+		.scan_index = GP2AP020A00F_SCAN_MODE_PROXIMITY,
+		.address = GP2AP020A00F_D2_L_REG,
+		.event_spec = gp2ap020a00f_event_spec_prox,
+		.num_event_specs = ARRAY_SIZE(gp2ap020a00f_event_spec_prox),
+	},
+	IIO_CHAN_SOFT_TIMESTAMP(GP2AP020A00F_CHAN_TIMESTAMP),
+};
+
 static const struct gp2ap020a00f_chip_info gp2ap020a00f_chip_info = {
 	.name = "gp2ap020a00f",
 	.reg_init_tab = gp2ap020a00f_reg_init_tab,
 	.channels = gp2ap020a00f_channels,
 	.num_channels = ARRAY_SIZE(gp2ap020a00f_channels),
+	.has_als_auto = true,
+};
+
+static const struct gp2ap020a00f_chip_info gp2ap030a00f_chip_info = {
+	.name = "gp2ap030a00f",
+	.reg_init_tab = gp2ap030a00f_reg_init_tab,
+	.channels = gp2ap030a00f_channels,
+	.num_channels = ARRAY_SIZE(gp2ap030a00f_channels),
 };
 
 static const struct iio_info gp2ap020a00f_info = {
@@ -1450,6 +1540,9 @@ static int gp2ap020a00f_probe(struct i2c_client *client)
 		goto error_regulator_disable;
 	}
 
+	if (!data->chip_info->has_als_auto)
+		fsleep(1000);
+
 	/* Initialize device registers */
 	err = regmap_bulk_write(regmap, GP2AP020A00F_OP_REG,
 			data->chip_info->reg_init_tab,
@@ -1549,12 +1642,15 @@ static void gp2ap020a00f_remove(struct i2c_client *client)
 static const struct i2c_device_id gp2ap020a00f_id[] = {
 	{ .name = "gp2ap020a00f",
 	  .driver_data = (kernel_ulong_t)&gp2ap020a00f_chip_info },
+	{ .name = "gp2ap030a00f",
+	  .driver_data = (kernel_ulong_t)&gp2ap030a00f_chip_info },
 	{ }
 };
 MODULE_DEVICE_TABLE(i2c, gp2ap020a00f_id);
 
 static const struct of_device_id gp2ap020a00f_of_match[] = {
 	{ .compatible = "sharp,gp2ap020a00f", .data = &gp2ap020a00f_chip_info },
+	{ .compatible = "sharp,gp2ap030a00f", .data = &gp2ap030a00f_chip_info },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, gp2ap020a00f_of_match);
@@ -1571,5 +1667,5 @@ static struct i2c_driver gp2ap020a00f_driver = {
 module_i2c_driver(gp2ap020a00f_driver);
 
 MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>");
-MODULE_DESCRIPTION("Sharp GP2AP020A00F Proximity/ALS sensor driver");
+MODULE_DESCRIPTION("Sharp GP2AP020A00F and similar proximity/ALS sensor driver");
 MODULE_LICENSE("GPL v2");

-- 
2.56.0


  parent reply	other threads:[~2026-11-08 21:44 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-08 21:43 [PATCH v2 0/5] iio: light: gp2ap020a00f: Add VCC supply and GP2AP030A00F support Linus Walleij
2026-11-08 21:43 ` [PATCH v2 1/5] dt-bindings: iio: light: sharp,gp2ap020a00f: Add VCC supply Linus Walleij
2026-11-09 12:54   ` Krzysztof Kozlowski
2026-11-08 21:44 ` [PATCH v2 2/5] dt-bindings: iio: light: sharp,gp2ap020a00f: Add GP2AP030A00F Linus Walleij
2026-11-09 12:54   ` Krzysztof Kozlowski
2026-11-08 21:44 ` [PATCH v2 3/5] iio: light: gp2ap020a00f: Enable the VCC supply Linus Walleij
2026-11-09  9:21   ` Joshua Crofts
2026-11-08 21:44 ` [PATCH v2 4/5] iio: light: gp2ap020a00f: Prepare for chip variants Linus Walleij
2026-11-09  9:20   ` Joshua Crofts
2026-11-09 23:14   ` Andy Shevchenko
2026-11-08 21:44 ` Linus Walleij [this message]
2026-11-09  9:25   ` [PATCH v2 5/5] iio: light: gp2ap020a00f: Add GP2AP030A00F support Joshua Crofts
2026-11-09 23:17   ` Andy Shevchenko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261108-gp2ap030-v2-5-24eaa1b9fe59@kernel.org \
    --to=linusw@kernel.org \
    --cc=andy@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=dlechner@baylibre.com \
    --cc=jic23@kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=kyungmin.park@samsung.com \
    --cc=linux-iio@vger.kernel.org \
    --cc=nuno.sa@analog.com \
    --cc=robh@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox