// SPDX-License-Identifier: GPL-2.0+
/*
* si1133.c - Support for Silabs SI1133 combined ambient
* light and UV index sensors
*
* Copyright 2018 Maxime Roussin-Belanger <maxime.roussinbelanger@gmail.com>
*/
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/util_macros.h>
#define SI1133_REG_PART_ID 0x00
#define SI1133_REG_REV_ID 0x01
#define SI1133_REG_MFR_ID 0x02
#define SI1133_REG_INFO0 0x03
#define SI1133_REG_INFO1 0x04
#define SI1133_PART_ID 0x33
#define SI1133_REG_HOSTIN0 0x0A
#define SI1133_REG_COMMAND 0x0B
#define SI1133_REG_IRQ_ENABLE 0x0F
#define SI1133_REG_RESPONSE1 0x10
#define SI1133_REG_RESPONSE0 0x11
#define SI1133_REG_IRQ_STATUS 0x12
#define SI1133_REG_MEAS_RATE 0x1A
#define SI1133_IRQ_CHANNEL_ENABLE 0xF
#define SI1133_CMD_RESET_CTR 0x00
#define SI1133_CMD_RESET_SW 0x01
#define SI1133_CMD_FORCE 0x11
#define SI1133_CMD_START_AUTONOMOUS 0x13
#define SI1133_CMD_PARAM_SET 0x80
#define SI1133_CMD_PARAM_QUERY 0x40
#define SI1133_CMD_PARAM_MASK 0x3F
#define SI1133_CMD_ERR_MASK BIT(4)
#define SI1133_CMD_SEQ_MASK 0xF
#define SI1133_MAX_CMD_CTR 0xF
#define SI1133_PARAM_REG_CHAN_LIST 0x01
#define SI1133_PARAM_REG_ADCCONFIG(x) ((x) * 4) + 2
#define SI1133_PARAM_REG_ADCSENS(x) ((x) * 4) + 3
#define SI1133_PARAM_REG_ADCPOST(x) ((x) * 4) + 4
#define SI1133_ADCMUX_MASK 0x1F
#define SI1133_ADCCONFIG_DECIM_RATE(x) (x) << 5
#define SI1133_ADCSENS_SCALE_MASK 0x70
#define SI1133_ADCSENS_SCALE_SHIFT 4
#define SI1133_ADCSENS_HSIG_MASK BIT(7)
#define SI1133_ADCSENS_HSIG_SHIFT 7
#define SI1133_ADCSENS_HW_GAIN_MASK 0xF
#define SI1133_ADCSENS_NB_MEAS(x) fls(x) << SI1133_ADCSENS_SCALE_SHIFT
#define SI1133_ADCPOST_24BIT_EN BIT(6)
#define SI1133_ADCPOST_POSTSHIFT_BITQTY(x) (x & GENMASK(2, 0)) << 3
#define SI1133_PARAM_ADCMUX_SMALL_IR 0x0
#define SI1133_PARAM_ADCMUX_MED_IR 0x1
#define SI1133_PARAM_ADCMUX_LARGE_IR 0x2
#define SI1133_PARAM_ADCMUX_WHITE 0xB
#define SI1133_PARAM_ADCMUX_LARGE_WHITE 0xD
#define SI1133_PARAM_ADCMUX_UV 0x18
#define SI1133_PARAM_ADCMUX_UV_DEEP 0x19
#define SI1133_ERR_INVALID_CMD 0x0
#define SI1133_ERR_INVALID_LOCATION_CMD 0x1
#define SI1133_ERR_SATURATION_ADC_OR_OVERFLOW_ACCUMULATION 0x2
#define SI1133_ERR_OUTPUT_BUFFER_OVERFLOW 0x3
#define SI1133_COMPLETION_TIMEOUT_MS 500
#define SI1133_CMD_MINSLEEP_US_LOW 5000
#define SI1133_CMD_MINSLEEP_US_HIGH 7500
#define SI1133_CMD_TIMEOUT_MS 25
#define SI1133_CMD_LUX_TIMEOUT_MS 5000
#define SI1133_CMD_TIMEOUT_US SI1133_CMD_TIMEOUT_MS * 1000
#define SI1133_REG_HOSTOUT(x) (x) + 0x13
#define SI1133_MEASUREMENT_FREQUENCY 1250
#define SI1133_X_ORDER_MASK 0x0070
#define SI1133_Y_ORDER_MASK 0x0007
#define si1133_get_x_order(m) ((m) & SI1133_X_ORDER_MASK) >> 4
#define si1133_get_y_order(m) ((m) & SI1133_Y_ORDER_MASK)
#define SI1133_LUX_ADC_MASK 0xE
#define SI1133_ADC_THRESHOLD 16000
#define SI1133_INPUT_FRACTION_HIGH 7
#define SI1133_INPUT_FRACTION_LOW 15
#define SI1133_LUX_OUTPUT_FRACTION 12
#define SI1133_LUX_BUFFER_SIZE 9
static const int si1133_scale_available[] = {
1, 2, 4, 8, 16, 32, 64, 128};
static IIO_CONST_ATTR(scale_available, "1 2 4 8 16 32 64 128");
static IIO_CONST_ATTR_INT_TIME_AVAIL("0.0244 0.0488 0.0975 0.195 0.390 0.780 "
"1.560 3.120 6.24 12.48 25.0 50.0");
/* A.K.A. HW_GAIN in datasheet */
enum si1133_int_time {
_24_4_us = 0,
_48_8_us = 1,
_97_5_us = 2,
_195_0_us = 3,
_390_0_us = 4,
_780_0_us = 5,
_1_560_0_us = 6,
_3_120_0_us = 7,
_6_240_0_us = 8,
_12_480_0_us = 9,
_25_ms = 10,
_50_ms = 11,
};
/* Integration time in milliseconds, nanoseconds */
static const int si1133_int_time_table[][2] = {
[_24_4_us] = {0, 24400},
[_48_8_us] = {0, 48800},
[_97_5_us] = {0, 97500},
[_195_0_us] = {0, 195000},
[_390_0_us] = {0, 390000},
[_780_0_us] = {0, 780000},
[_1_560_0_us] = {1, 560000},
[_3_120_0_us] = {3, 120000},
[_6_240_0_us] = {6, 240000},
[_12_480_0_us] = {12, 480000},
[_25_ms] = {25, 000000},
[_50_ms] = {50, 000000},
};
static const struct regmap_range si1133_reg_ranges[] = {
regmap_reg_range(0x00, 0x02),
regmap_reg_range(0x0A, 0x0B),
regmap_reg_range(0x0F, 0x0F),
regmap_reg_range(0x10, 0x12),
regmap_reg_range(0x13, 0x2C),
};
static const struct regmap_range si1133_reg_ro_ranges[] = {
regmap_reg_range(0x00, 0x02),
regmap_reg_range(0x10, 0x2C),
};
static const struct regmap_range si1133_precious_ranges[] = {
regmap_reg_range(0x12, 0x12),
};
static const struct regmap_access_table si1133_write_ranges_table = {
.yes_ranges = si1133_reg_ranges,
.n_yes_ranges = ARRAY_SIZE(si1133_reg_ranges),
.no_ranges = si1133_reg_ro_ranges,
.n_no_ranges = ARRAY_SIZE(si1133_reg_ro_ranges),
};
static const struct regmap_access_table si1133_read_ranges_table = {
.yes_ranges = si1133_reg_ranges,
.n_yes_ranges = ARRAY_SIZE(si1133_reg_ranges),
};
static const struct regmap_access_table si1133_precious_table = {
.yes_ranges = si1133_precious_ranges,
.n_yes_ranges = ARRAY_SIZE(si1133_precious_ranges),
};
static const struct regmap_config