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

