The Sensortek STK36C61 is a 3-in-1 ambient light / proximity / RGB
colour sensor. Its register interface is compatible with the feature
set this driver uses. Add its chip ID to the known-ID list and the
device table entries.

Whenever the ALS engine runs, the chip also measures four colour
channels: red, green, blue and clear, laid out directly after the
ALS data as 16-bit big-endian values.

R, G and B are gained by the same ALSCTRL gain field the illuminance
channel uses, clear by its own gain field in the GAINCTRL register,
and the whole colour block integrates over the ALS integration time.
Expose the channels with per-channel scale and a shared integration
time, as suggested by Jonathan Cameron for the green channel.

The scales reuse the driver's existing gain-only table: green equals
the ALS data, so its scale must read identically to the pre-existing
in_illuminance_scale.

Assisted-by: Claude:claude-fable-5
Signed-off-by: Jorijn van der Graaf <[email protected]>
---
 Documentation/ABI/testing/sysfs-bus-iio |   1 +
 drivers/iio/light/stk3310.c             | 101 ++++++++++++++++++++----
 2 files changed, 88 insertions(+), 14 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-bus-iio 
b/Documentation/ABI/testing/sysfs-bus-iio
index a4f5595722ad..08a8de215814 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -507,6 +507,7 @@ What:               
/sys/bus/iio/devices/iio:deviceX/in_intensity_z_scale
 What:          /sys/bus/iio/devices/iio:deviceX/in_intensity_red_scale
 What:          /sys/bus/iio/devices/iio:deviceX/in_intensity_green_scale
 What:          /sys/bus/iio/devices/iio:deviceX/in_intensity_blue_scale
+What:          /sys/bus/iio/devices/iio:deviceX/in_intensity_clear_scale
 What:          /sys/bus/iio/devices/iio:deviceX/in_concentration_co2_scale
 What:          /sys/bus/iio/devices/iio:deviceX/in_volumeflow_scale
 What:          /sys/bus/iio/devices/iio:deviceX/in_volumeflowY_scale
diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c
index f5782a1e19bb..0a16e326e939 100644
--- a/drivers/iio/light/stk3310.c
+++ b/drivers/iio/light/stk3310.c
@@ -40,7 +40,13 @@
 #define STK3310_REG_PS_DATA_LSB                        0x12
 #define STK3310_REG_ALS_DATA_MSB               0x13
 #define STK3310_REG_ALS_DATA_LSB               0x14
+#define STK36C61_REG_RED_DATA_MSB              0x15
+#define STK36C61_REG_GREEN_DATA_MSB            0x17
+#define STK36C61_REG_BLUE_DATA_MSB             0x19
+#define STK36C61_REG_CLEAR_DATA_MSB            0x1B
+#define STK36C61_REG_CLEAR_DATA_LSB            0x1C
 #define STK3310_REG_ID                         0x3E
+#define STK36C61_REG_GAINCTRL                  0x4E
 #define STK3310_MAX_REG                                0x80
 
 #define STK3310_STATE_EN_PS                    BIT(0)
@@ -54,6 +60,7 @@
 #define STK3311S34_CHIP_ID_VAL                 0x1E
 #define STK3311X_CHIP_ID_VAL                   0x12
 #define STK3335_CHIP_ID_VAL                    0x51
+#define STK36C61_CHIP_ID_VAL                   0x95
 #define STK3310_PSINT_EN                       0x01
 #define STK3310_PS_MAX_VAL                     0xFFFF
 
@@ -83,6 +90,8 @@ static const struct reg_field stk3310_reg_field_als_gain =
                                REG_FIELD(STK3310_REG_ALSCTRL, 4, 5);
 static const struct reg_field stk3310_reg_field_ps_gain =
                                REG_FIELD(STK3310_REG_PSCTRL, 4, 5);
+static const struct reg_field stk3310_reg_field_clear_gain =
+                               REG_FIELD(STK36C61_REG_GAINCTRL, 4, 5);
 static const struct reg_field stk3310_reg_field_als_it =
                                REG_FIELD(STK3310_REG_ALSCTRL, 0, 3);
 static const struct reg_field stk3310_reg_field_ps_it =
@@ -102,6 +111,7 @@ static const u8 stk3310_chip_ids[] = {
        STK3311X_CHIP_ID_VAL,
        STK3311_CHIP_ID_VAL,
        STK3335_CHIP_ID_VAL,
+       STK36C61_CHIP_ID_VAL,
 };
 
 /* Estimate maximum proximity values with regard to measurement scale. */
@@ -132,6 +142,7 @@ struct stk3310_data {
        struct regmap_field *reg_state;
        struct regmap_field *reg_als_gain;
        struct regmap_field *reg_ps_gain;
+       struct regmap_field *reg_clear_gain;
        struct regmap_field *reg_als_it;
        struct regmap_field *reg_ps_it;
        struct regmap_field *reg_int_ps;
@@ -202,11 +213,35 @@ static const struct iio_chan_spec_ext_info 
stk3310_ext_info[] = {
        .ext_info = stk3310_ext_info,                   \
 }
 
+#define STK36C61_INTENSITY_CHANNEL(_mod, _reg) {       \
+       .type = IIO_INTENSITY,                          \
+       .address = _reg,                                \
+       .modified = 1,                                  \
+       .channel2 = IIO_MOD_LIGHT_##_mod,               \
+       .info_mask_separate =                           \
+               BIT(IIO_CHAN_INFO_RAW) |                \
+               BIT(IIO_CHAN_INFO_SCALE),               \
+       .info_mask_shared_by_type =                     \
+               BIT(IIO_CHAN_INFO_INT_TIME),            \
+       .info_mask_shared_by_type_available =           \
+               BIT(IIO_CHAN_INFO_SCALE) |              \
+               BIT(IIO_CHAN_INFO_INT_TIME),            \
+}
+
 static const struct iio_chan_spec stk3310_channels[] = {
        STK3310_LIGHT_CHANNEL,
        STK3310_PROXIMITY_CHANNEL,
 };
 
+static const struct iio_chan_spec stk36c61_channels[] = {
+       STK3310_LIGHT_CHANNEL,
+       STK3310_PROXIMITY_CHANNEL,
+       STK36C61_INTENSITY_CHANNEL(RED, STK36C61_REG_RED_DATA_MSB),
+       STK36C61_INTENSITY_CHANNEL(GREEN, STK36C61_REG_GREEN_DATA_MSB),
+       STK36C61_INTENSITY_CHANNEL(BLUE, STK36C61_REG_BLUE_DATA_MSB),
+       STK36C61_INTENSITY_CHANNEL(CLEAR, STK36C61_REG_CLEAR_DATA_MSB),
+};
+
 /**
  * struct stk3310_chip_info - chip-specific data
  * @name: device name reported to the IIO core
@@ -225,6 +260,12 @@ static const struct stk3310_chip_info stk3310_chip_info = {
        .num_channels = ARRAY_SIZE(stk3310_channels),
 };
 
+static const struct stk3310_chip_info stk36c61_chip_info = {
+       .name = "stk36c61",
+       .channels = stk36c61_channels,
+       .num_channels = ARRAY_SIZE(stk36c61_channels),
+};
+
 static IIO_CONST_ATTR(in_illuminance_scale_available, STK3310_SCALE_AVAILABLE);
 
 static IIO_CONST_ATTR(in_proximity_scale_available, STK3310_SCALE_AVAILABLE);
@@ -401,7 +442,8 @@ static int stk3310_read_raw(struct iio_dev *indio_dev,
        struct i2c_client *client = data->client;
        struct regmap *map = data->regmap;
 
-       if (chan->type != IIO_LIGHT && chan->type != IIO_PROXIMITY)
+       if (chan->type != IIO_LIGHT && chan->type != IIO_PROXIMITY &&
+           chan->type != IIO_INTENSITY)
                return -EINVAL;
 
        switch (mask) {
@@ -417,10 +459,10 @@ static int stk3310_read_raw(struct iio_dev *indio_dev,
                mutex_unlock(&data->lock);
                return IIO_VAL_INT;
        case IIO_CHAN_INFO_INT_TIME:
-               if (chan->type == IIO_LIGHT)
-                       ret = regmap_field_read(data->reg_als_it, &index);
-               else
+               if (chan->type == IIO_PROXIMITY)
                        ret = regmap_field_read(data->reg_ps_it, &index);
+               else
+                       ret = regmap_field_read(data->reg_als_it, &index);
                if (ret < 0)
                        return ret;
 
@@ -428,10 +470,12 @@ static int stk3310_read_raw(struct iio_dev *indio_dev,
                *val2 = stk3310_it_table[index][1];
                return IIO_VAL_INT_PLUS_MICRO;
        case IIO_CHAN_INFO_SCALE:
-               if (chan->type == IIO_LIGHT)
-                       ret = regmap_field_read(data->reg_als_gain, &index);
-               else
+               if (chan->type == IIO_PROXIMITY)
                        ret = regmap_field_read(data->reg_ps_gain, &index);
+               else if (chan->channel2 == IIO_MOD_LIGHT_CLEAR)
+                       ret = regmap_field_read(data->reg_clear_gain, &index);
+               else
+                       ret = regmap_field_read(data->reg_als_gain, &index);
                if (ret < 0)
                        return ret;
 
@@ -451,7 +495,8 @@ static int stk3310_write_raw(struct iio_dev *indio_dev,
        int index;
        struct stk3310_data *data = iio_priv(indio_dev);
 
-       if (chan->type != IIO_LIGHT && chan->type != IIO_PROXIMITY)
+       if (chan->type != IIO_LIGHT && chan->type != IIO_PROXIMITY &&
+           chan->type != IIO_INTENSITY)
                return -EINVAL;
 
        switch (mask) {
@@ -462,10 +507,10 @@ static int stk3310_write_raw(struct iio_dev *indio_dev,
                if (index < 0)
                        return -EINVAL;
                mutex_lock(&data->lock);
-               if (chan->type == IIO_LIGHT)
-                       ret = regmap_field_write(data->reg_als_it, index);
-               else
+               if (chan->type == IIO_PROXIMITY)
                        ret = regmap_field_write(data->reg_ps_it, index);
+               else
+                       ret = regmap_field_write(data->reg_als_it, index);
                if (ret < 0)
                        dev_err(&data->client->dev,
                                "sensor configuration failed\n");
@@ -479,10 +524,12 @@ static int stk3310_write_raw(struct iio_dev *indio_dev,
                if (index < 0)
                        return -EINVAL;
                mutex_lock(&data->lock);
-               if (chan->type == IIO_LIGHT)
-                       ret = regmap_field_write(data->reg_als_gain, index);
-               else
+               if (chan->type == IIO_PROXIMITY)
                        ret = regmap_field_write(data->reg_ps_gain, index);
+               else if (chan->channel2 == IIO_MOD_LIGHT_CLEAR)
+                       ret = regmap_field_write(data->reg_clear_gain, index);
+               else
+                       ret = regmap_field_write(data->reg_als_gain, index);
                if (ret < 0)
                        dev_err(&data->client->dev,
                                "sensor configuration failed\n");
@@ -493,9 +540,31 @@ static int stk3310_write_raw(struct iio_dev *indio_dev,
        return -EINVAL;
 }
 
+static int stk3310_read_avail(struct iio_dev *indio_dev,
+                             struct iio_chan_spec const *chan,
+                             const int **vals, int *type, int *length,
+                             long mask)
+{
+       switch (mask) {
+       case IIO_CHAN_INFO_SCALE:
+               *vals = (const int *)stk3310_scale_table;
+               *length = 2 * ARRAY_SIZE(stk3310_scale_table);
+               *type = IIO_VAL_INT_PLUS_MICRO;
+               return IIO_AVAIL_LIST;
+       case IIO_CHAN_INFO_INT_TIME:
+               *vals = (const int *)stk3310_it_table;
+               *length = 2 * ARRAY_SIZE(stk3310_it_table);
+               *type = IIO_VAL_INT_PLUS_MICRO;
+               return IIO_AVAIL_LIST;
+       default:
+               return -EINVAL;
+       }
+}
+
 static const struct iio_info stk3310_info = {
        .read_raw               = stk3310_read_raw,
        .write_raw              = stk3310_write_raw,
+       .read_avail             = stk3310_read_avail,
        .attrs                  = &stk3310_attribute_group,
        .read_event_value       = stk3310_read_event,
        .write_event_value      = stk3310_write_event,
@@ -567,6 +636,7 @@ static bool stk3310_is_volatile_reg(struct device *dev, 
unsigned int reg)
        switch (reg) {
        case STK3310_REG_ALS_DATA_MSB:
        case STK3310_REG_ALS_DATA_LSB:
+       case STK36C61_REG_RED_DATA_MSB ... STK36C61_REG_CLEAR_DATA_LSB:
        case STK3310_REG_PS_DATA_LSB:
        case STK3310_REG_PS_DATA_MSB:
        case STK3310_REG_FLAG:
@@ -601,6 +671,7 @@ static int stk3310_regmap_init(struct stk3310_data *data)
        STK3310_REGFIELD(state);
        STK3310_REGFIELD(als_gain);
        STK3310_REGFIELD(ps_gain);
+       STK3310_REGFIELD(clear_gain);
        STK3310_REGFIELD(als_it);
        STK3310_REGFIELD(ps_it);
        STK3310_REGFIELD(int_ps);
@@ -795,6 +866,7 @@ static const struct i2c_device_id stk3310_i2c_id[] = {
        { .name = "stk3310", .driver_data = (kernel_ulong_t)&stk3310_chip_info 
},
        { .name = "stk3311", .driver_data = (kernel_ulong_t)&stk3310_chip_info 
},
        { .name = "stk3335", .driver_data = (kernel_ulong_t)&stk3310_chip_info 
},
+       { .name = "stk36c61", .driver_data = 
(kernel_ulong_t)&stk36c61_chip_info },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, stk3310_i2c_id);
@@ -812,6 +884,7 @@ static const struct of_device_id stk3310_of_match[] = {
        { .compatible = "sensortek,stk3310", .data = &stk3310_chip_info },
        { .compatible = "sensortek,stk3311", .data = &stk3310_chip_info },
        { .compatible = "sensortek,stk3335", .data = &stk3310_chip_info },
+       { .compatible = "sensortek,stk36c61", .data = &stk36c61_chip_info },
        { }
 };
 MODULE_DEVICE_TABLE(of, stk3310_of_match);
-- 
2.55.0


Reply via email to