1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072 |
- // 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 si1133_regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = 0x2C,
- .wr_table = &si1133_write_ranges_table,
- .rd_table = &si1133_read_ranges_table,
- .precious_table = &si1133_precious_table,
- };
- struct si1133_data {
- struct regmap *regmap;
- struct i2c_client *client;
- /* Lock protecting one command at a time can be processed */
- struct mutex mutex;
- int rsp_seq;
- u8 scan_mask;
- u8 adc_sens[6];
- u8 adc_config[6];
- struct completion completion;
- };
- struct si1133_coeff {
- s16 info;
- u16 mag;
- };
- struct si1133_lux_coeff {
- struct si1133_coeff coeff_high[4];
- struct si1133_coeff coeff_low[9];
- };
- static const struct si1133_lux_coeff lux_coeff = {
- {
- { 0, 209},
- { 1665, 93},
- { 2064, 65},
- {-2671, 234}
- },
- {
- { 0, 0},
- { 1921, 29053},
- {-1022, 36363},
- { 2320, 20789},
- { -367, 57909},
- {-1774, 38240},
- { -608, 46775},
- {-1503, 51831},
- {-1886, 58928}
- }
- };
- static int si1133_calculate_polynomial_inner(u32 input, u8 fraction, u16 mag,
- s8 shift)
- {
- return ((input << fraction) / mag) << shift;
- }
- static int si1133_calculate_output(u32 x, u32 y, u8 x_order, u8 y_order,
- u8 input_fraction, s8 sign,
- const struct si1133_coeff *coeffs)
- {
- s8 shift;
- int x1 = 1;
- int x2 = 1;
- int y1 = 1;
- int y2 = 1;
- shift = ((u16)coeffs->info & 0xFF00) >> 8;
- shift ^= 0xFF;
- shift += 1;
- shift = -shift;
- if (x_order > 0) {
- x1 = si1133_calculate_polynomial_inner(x, input_fraction,
- coeffs->mag, shift);
- if (x_order > 1)
- x2 = x1;
- }
- if (y_order > 0) {
- y1 = si1133_calculate_polynomial_inner(y, input_fraction,
- coeffs->mag, shift);
- if (y_order > 1)
- y2 = y1;
- }
- return sign * x1 * x2 * y1 * y2;
- }
- /*
- * The algorithm is from:
- * https://siliconlabs.github.io/Gecko_SDK_Doc/efm32zg/html/si1133_8c_source.html#l00716
- */
- static int si1133_calc_polynomial(u32 x, u32 y, u8 input_fraction, u8 num_coeff,
- const struct si1133_coeff *coeffs)
- {
- u8 x_order, y_order;
- u8 counter;
- s8 sign;
- int output = 0;
- for (counter = 0; counter < num_coeff; counter++) {
- if (coeffs->info < 0)
- sign = -1;
- else
- sign = 1;
- x_order = si1133_get_x_order(coeffs->info);
- y_order = si1133_get_y_order(coeffs->info);
- if ((x_order == 0) && (y_order == 0))
- output +=
- sign * coeffs->mag << SI1133_LUX_OUTPUT_FRACTION;
- else
- output += si1133_calculate_output(x, y, x_order,
- y_order,
- input_fraction, sign,
- coeffs);
- coeffs++;
- }
- return abs(output);
- }
- static int si1133_cmd_reset_sw(struct si1133_data *data)
- {
- struct device *dev = &data->client->dev;
- unsigned int resp;
- unsigned long timeout;
- int err;
- err = regmap_write(data->regmap, SI1133_REG_COMMAND,
- SI1133_CMD_RESET_SW);
- if (err)
- return err;
- timeout = jiffies + msecs_to_jiffies(SI1133_CMD_TIMEOUT_MS);
- while (true) {
- err = regmap_read(data->regmap, SI1133_REG_RESPONSE0, &resp);
- if (err == -ENXIO) {
- usleep_range(SI1133_CMD_MINSLEEP_US_LOW,
- SI1133_CMD_MINSLEEP_US_HIGH);
- continue;
- }
- if ((resp & SI1133_MAX_CMD_CTR) == SI1133_MAX_CMD_CTR)
- break;
- if (time_after(jiffies, timeout)) {
- dev_warn(dev, "Timeout on reset ctr resp: %d\n", resp);
- return -ETIMEDOUT;
- }
- }
- if (!err)
- data->rsp_seq = SI1133_MAX_CMD_CTR;
- return err;
- }
- static int si1133_parse_response_err(struct device *dev, u32 resp, u8 cmd)
- {
- resp &= 0xF;
- switch (resp) {
- case SI1133_ERR_OUTPUT_BUFFER_OVERFLOW:
- dev_warn(dev, "Output buffer overflow: %#02hhx\n", cmd);
- return -EOVERFLOW;
- case SI1133_ERR_SATURATION_ADC_OR_OVERFLOW_ACCUMULATION:
- dev_warn(dev, "Saturation of the ADC or overflow of accumulation: %#02hhx\n",
- cmd);
- return -EOVERFLOW;
- case SI1133_ERR_INVALID_LOCATION_CMD:
- dev_warn(dev,
- "Parameter access to an invalid location: %#02hhx\n",
- cmd);
- return -EINVAL;
- case SI1133_ERR_INVALID_CMD:
- dev_warn(dev, "Invalid command %#02hhx\n", cmd);
- return -EINVAL;
- default:
- dev_warn(dev, "Unknown error %#02hhx\n", cmd);
- return -EINVAL;
- }
- }
- static int si1133_cmd_reset_counter(struct si1133_data *data)
- {
- int err = regmap_write(data->regmap, SI1133_REG_COMMAND,
- SI1133_CMD_RESET_CTR);
- if (err)
- return err;
- data->rsp_seq = 0;
- return 0;
- }
- static int si1133_command(struct si1133_data *data, u8 cmd)
- {
- struct device *dev = &data->client->dev;
- u32 resp;
- int err;
- int expected_seq;
- mutex_lock(&data->mutex);
- expected_seq = (data->rsp_seq + 1) & SI1133_MAX_CMD_CTR;
- if (cmd == SI1133_CMD_FORCE)
- reinit_completion(&data->completion);
- err = regmap_write(data->regmap, SI1133_REG_COMMAND, cmd);
- if (err) {
- dev_warn(dev, "Failed to write command %#02hhx, ret=%d\n", cmd,
- err);
- goto out;
- }
- if (cmd == SI1133_CMD_FORCE) {
- /* wait for irq */
- if (!wait_for_completion_timeout(&data->completion,
- msecs_to_jiffies(SI1133_COMPLETION_TIMEOUT_MS))) {
- err = -ETIMEDOUT;
- goto out;
- }
- err = regmap_read(data->regmap, SI1133_REG_RESPONSE0, &resp);
- if (err)
- goto out;
- } else {
- err = regmap_read_poll_timeout(data->regmap,
- SI1133_REG_RESPONSE0, resp,
- (resp & SI1133_CMD_SEQ_MASK) ==
- expected_seq ||
- (resp & SI1133_CMD_ERR_MASK),
- SI1133_CMD_MINSLEEP_US_LOW,
- SI1133_CMD_TIMEOUT_MS * 1000);
- if (err) {
- dev_warn(dev,
- "Failed to read command %#02hhx, ret=%d\n",
- cmd, err);
- goto out;
- }
- }
- if (resp & SI1133_CMD_ERR_MASK) {
- err = si1133_parse_response_err(dev, resp, cmd);
- si1133_cmd_reset_counter(data);
- } else {
- data->rsp_seq = expected_seq;
- }
- out:
- mutex_unlock(&data->mutex);
- return err;
- }
- static int si1133_param_set(struct si1133_data *data, u8 param, u32 value)
- {
- int err = regmap_write(data->regmap, SI1133_REG_HOSTIN0, value);
- if (err)
- return err;
- return si1133_command(data, SI1133_CMD_PARAM_SET |
- (param & SI1133_CMD_PARAM_MASK));
- }
- static int si1133_param_query(struct si1133_data *data, u8 param, u32 *result)
- {
- int err = si1133_command(data, SI1133_CMD_PARAM_QUERY |
- (param & SI1133_CMD_PARAM_MASK));
- if (err)
- return err;
- return regmap_read(data->regmap, SI1133_REG_RESPONSE1, result);
- }
- #define SI1133_CHANNEL(_ch, _type) \
- .type = _type, \
- .channel = _ch, \
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
- .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | \
- BIT(IIO_CHAN_INFO_SCALE) | \
- BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
- static const struct iio_chan_spec si1133_channels[] = {
- {
- .type = IIO_LIGHT,
- .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
- .channel = 0,
- },
- {
- SI1133_CHANNEL(SI1133_PARAM_ADCMUX_WHITE, IIO_INTENSITY)
- .channel2 = IIO_MOD_LIGHT_BOTH,
- },
- {
- SI1133_CHANNEL(SI1133_PARAM_ADCMUX_LARGE_WHITE, IIO_INTENSITY)
- .channel2 = IIO_MOD_LIGHT_BOTH,
- .extend_name = "large",
- },
- {
- SI1133_CHANNEL(SI1133_PARAM_ADCMUX_SMALL_IR, IIO_INTENSITY)
- .extend_name = "small",
- .modified = 1,
- .channel2 = IIO_MOD_LIGHT_IR,
- },
- {
- SI1133_CHANNEL(SI1133_PARAM_ADCMUX_MED_IR, IIO_INTENSITY)
- .modified = 1,
- .channel2 = IIO_MOD_LIGHT_IR,
- },
- {
- SI1133_CHANNEL(SI1133_PARAM_ADCMUX_LARGE_IR, IIO_INTENSITY)
- .extend_name = "large",
- .modified = 1,
- .channel2 = IIO_MOD_LIGHT_IR,
- },
- {
- SI1133_CHANNEL(SI1133_PARAM_ADCMUX_UV, IIO_UVINDEX)
- },
- {
- SI1133_CHANNEL(SI1133_PARAM_ADCMUX_UV_DEEP, IIO_UVINDEX)
- .modified = 1,
- .channel2 = IIO_MOD_LIGHT_DUV,
- }
- };
- static int si1133_get_int_time_index(int milliseconds, int nanoseconds)
- {
- int i;
- for (i = 0; i < ARRAY_SIZE(si1133_int_time_table); i++) {
- if (milliseconds == si1133_int_time_table[i][0] &&
- nanoseconds == si1133_int_time_table[i][1])
- return i;
- }
- return -EINVAL;
- }
- static int si1133_set_integration_time(struct si1133_data *data, u8 adc,
- int milliseconds, int nanoseconds)
- {
- int index;
- index = si1133_get_int_time_index(milliseconds, nanoseconds);
- if (index < 0)
- return index;
- data->adc_sens[adc] &= 0xF0;
- data->adc_sens[adc] |= index;
- return si1133_param_set(data, SI1133_PARAM_REG_ADCSENS(0),
- data->adc_sens[adc]);
- }
- static int si1133_set_chlist(struct si1133_data *data, u8 scan_mask)
- {
- /* channel list already set, no need to reprogram */
- if (data->scan_mask == scan_mask)
- return 0;
- data->scan_mask = scan_mask;
- return si1133_param_set(data, SI1133_PARAM_REG_CHAN_LIST, scan_mask);
- }
- static int si1133_chan_set_adcconfig(struct si1133_data *data, u8 adc,
- u8 adc_config)
- {
- int err;
- err = si1133_param_set(data, SI1133_PARAM_REG_ADCCONFIG(adc),
- adc_config);
- if (err)
- return err;
- data->adc_config[adc] = adc_config;
- return 0;
- }
- static int si1133_update_adcconfig(struct si1133_data *data, uint8_t adc,
- u8 mask, u8 shift, u8 value)
- {
- u32 adc_config;
- int err;
- err = si1133_param_query(data, SI1133_PARAM_REG_ADCCONFIG(adc),
- &adc_config);
- if (err)
- return err;
- adc_config &= ~mask;
- adc_config |= (value << shift);
- return si1133_chan_set_adcconfig(data, adc, adc_config);
- }
- static int si1133_set_adcmux(struct si1133_data *data, u8 adc, u8 mux)
- {
- if ((mux & data->adc_config[adc]) == mux)
- return 0; /* mux already set to correct value */
- return si1133_update_adcconfig(data, adc, SI1133_ADCMUX_MASK, 0, mux);
- }
- static int si1133_force_measurement(struct si1133_data *data)
- {
- return si1133_command(data, SI1133_CMD_FORCE);
- }
- static int si1133_bulk_read(struct si1133_data *data, u8 start_reg, u8 length,
- u8 *buffer)
- {
- int err;
- err = si1133_force_measurement(data);
- if (err)
- return err;
- return regmap_bulk_read(data->regmap, start_reg, buffer, length);
- }
- static int si1133_measure(struct si1133_data *data,
- struct iio_chan_spec const *chan,
- int *val)
- {
- int err;
- __be16 resp;
- err = si1133_set_adcmux(data, 0, chan->channel);
- if (err)
- return err;
- /* Deactivate lux measurements if they were active */
- err = si1133_set_chlist(data, BIT(0));
- if (err)
- return err;
- err = si1133_bulk_read(data, SI1133_REG_HOSTOUT(0), sizeof(resp),
- (u8 *)&resp);
- if (err)
- return err;
- *val = be16_to_cpu(resp);
- return err;
- }
- static irqreturn_t si1133_threaded_irq_handler(int irq, void *private)
- {
- struct iio_dev *iio_dev = private;
- struct si1133_data *data = iio_priv(iio_dev);
- u32 irq_status;
- int err;
- err = regmap_read(data->regmap, SI1133_REG_IRQ_STATUS, &irq_status);
- if (err) {
- dev_err_ratelimited(&iio_dev->dev, "Error reading IRQ\n");
- goto out;
- }
- if (irq_status != data->scan_mask)
- return IRQ_NONE;
- out:
- complete(&data->completion);
- return IRQ_HANDLED;
- }
- static int si1133_scale_to_swgain(int scale_integer, int scale_fractional)
- {
- scale_integer = find_closest(scale_integer, si1133_scale_available,
- ARRAY_SIZE(si1133_scale_available));
- if (scale_integer < 0 ||
- scale_integer > ARRAY_SIZE(si1133_scale_available) ||
- scale_fractional != 0)
- return -EINVAL;
- return scale_integer;
- }
- static int si1133_chan_set_adcsens(struct si1133_data *data, u8 adc,
- u8 adc_sens)
- {
- int err;
- err = si1133_param_set(data, SI1133_PARAM_REG_ADCSENS(adc), adc_sens);
- if (err)
- return err;
- data->adc_sens[adc] = adc_sens;
- return 0;
- }
- static int si1133_update_adcsens(struct si1133_data *data, u8 mask,
- u8 shift, u8 value)
- {
- int err;
- u32 adc_sens;
- err = si1133_param_query(data, SI1133_PARAM_REG_ADCSENS(0),
- &adc_sens);
- if (err)
- return err;
- adc_sens &= ~mask;
- adc_sens |= (value << shift);
- return si1133_chan_set_adcsens(data, 0, adc_sens);
- }
- static int si1133_get_lux(struct si1133_data *data, int *val)
- {
- int err;
- int lux;
- u32 high_vis;
- u32 low_vis;
- u32 ir;
- u8 buffer[SI1133_LUX_BUFFER_SIZE];
- /* Activate lux channels */
- err = si1133_set_chlist(data, SI1133_LUX_ADC_MASK);
- if (err)
- return err;
- err = si1133_bulk_read(data, SI1133_REG_HOSTOUT(0),
- SI1133_LUX_BUFFER_SIZE, buffer);
- if (err)
- return err;
- high_vis = (buffer[0] << 16) | (buffer[1] << 8) | buffer[2];
- low_vis = (buffer[3] << 16) | (buffer[4] << 8) | buffer[5];
- ir = (buffer[6] << 16) | (buffer[7] << 8) | buffer[8];
- if (high_vis > SI1133_ADC_THRESHOLD || ir > SI1133_ADC_THRESHOLD)
- lux = si1133_calc_polynomial(high_vis, ir,
- SI1133_INPUT_FRACTION_HIGH,
- ARRAY_SIZE(lux_coeff.coeff_high),
- &lux_coeff.coeff_high[0]);
- else
- lux = si1133_calc_polynomial(low_vis, ir,
- SI1133_INPUT_FRACTION_LOW,
- ARRAY_SIZE(lux_coeff.coeff_low),
- &lux_coeff.coeff_low[0]);
- *val = lux >> SI1133_LUX_OUTPUT_FRACTION;
- return err;
- }
- static int si1133_read_raw(struct iio_dev *iio_dev,
- struct iio_chan_spec const *chan,
- int *val, int *val2, long mask)
- {
- struct si1133_data *data = iio_priv(iio_dev);
- u8 adc_sens = data->adc_sens[0];
- int err;
- switch (mask) {
- case IIO_CHAN_INFO_PROCESSED:
- switch (chan->type) {
- case IIO_LIGHT:
- err = si1133_get_lux(data, val);
- if (err)
- return err;
- return IIO_VAL_INT;
- default:
- return -EINVAL;
- }
- case IIO_CHAN_INFO_RAW:
- switch (chan->type) {
- case IIO_INTENSITY:
- case IIO_UVINDEX:
- err = si1133_measure(data, chan, val);
- if (err)
- return err;
- return IIO_VAL_INT;
- default:
- return -EINVAL;
- }
- case IIO_CHAN_INFO_INT_TIME:
- switch (chan->type) {
- case IIO_INTENSITY:
- case IIO_UVINDEX:
- adc_sens &= SI1133_ADCSENS_HW_GAIN_MASK;
- *val = si1133_int_time_table[adc_sens][0];
- *val2 = si1133_int_time_table[adc_sens][1];
- return IIO_VAL_INT_PLUS_MICRO;
- default:
- return -EINVAL;
- }
- case IIO_CHAN_INFO_SCALE:
- switch (chan->type) {
- case IIO_INTENSITY:
- case IIO_UVINDEX:
- adc_sens &= SI1133_ADCSENS_SCALE_MASK;
- adc_sens >>= SI1133_ADCSENS_SCALE_SHIFT;
- *val = BIT(adc_sens);
- return IIO_VAL_INT;
- default:
- return -EINVAL;
- }
- case IIO_CHAN_INFO_HARDWAREGAIN:
- switch (chan->type) {
- case IIO_INTENSITY:
- case IIO_UVINDEX:
- adc_sens >>= SI1133_ADCSENS_HSIG_SHIFT;
- *val = adc_sens;
- return IIO_VAL_INT;
- default:
- return -EINVAL;
- }
- default:
- return -EINVAL;
- }
- }
- static int si1133_write_raw(struct iio_dev *iio_dev,
- struct iio_chan_spec const *chan,
- int val, int val2, long mask)
- {
- struct si1133_data *data = iio_priv(iio_dev);
- switch (mask) {
- case IIO_CHAN_INFO_SCALE:
- switch (chan->type) {
- case IIO_INTENSITY:
- case IIO_UVINDEX:
- val = si1133_scale_to_swgain(val, val2);
- if (val < 0)
- return val;
- return si1133_update_adcsens(data,
- SI1133_ADCSENS_SCALE_MASK,
- SI1133_ADCSENS_SCALE_SHIFT,
- val);
- default:
- return -EINVAL;
- }
- case IIO_CHAN_INFO_INT_TIME:
- return si1133_set_integration_time(data, 0, val, val2);
- case IIO_CHAN_INFO_HARDWAREGAIN:
- switch (chan->type) {
- case IIO_INTENSITY:
- case IIO_UVINDEX:
- if (val != 0 && val != 1)
- return -EINVAL;
- return si1133_update_adcsens(data,
- SI1133_ADCSENS_HSIG_MASK,
- SI1133_ADCSENS_HSIG_SHIFT,
- val);
- default:
- return -EINVAL;
- }
- default:
- return -EINVAL;
- }
- }
- static struct attribute *si1133_attributes[] = {
- &iio_const_attr_integration_time_available.dev_attr.attr,
- &iio_const_attr_scale_available.dev_attr.attr,
- NULL,
- };
- static const struct attribute_group si1133_attribute_group = {
- .attrs = si1133_attributes,
- };
- static const struct iio_info si1133_info = {
- .read_raw = si1133_read_raw,
- .write_raw = si1133_write_raw,
- .attrs = &si1133_attribute_group,
- };
- /*
- * si1133_init_lux_channels - Configure 3 different channels(adc) (1,2 and 3)
- * The channel configuration for the lux measurement was taken from :
- * https://siliconlabs.github.io/Gecko_SDK_Doc/efm32zg/html/si1133_8c_source.html#l00578
- *
- * Reserved the channel 0 for the other raw measurements
- */
- static int si1133_init_lux_channels(struct si1133_data *data)
- {
- int err;
- err = si1133_chan_set_adcconfig(data, 1,
- SI1133_ADCCONFIG_DECIM_RATE(1) |
- SI1133_PARAM_ADCMUX_LARGE_WHITE);
- if (err)
- return err;
- err = si1133_param_set(data, SI1133_PARAM_REG_ADCPOST(1),
- SI1133_ADCPOST_24BIT_EN |
- SI1133_ADCPOST_POSTSHIFT_BITQTY(0));
- if (err)
- return err;
- err = si1133_chan_set_adcsens(data, 1, SI1133_ADCSENS_HSIG_MASK |
- SI1133_ADCSENS_NB_MEAS(64) | _48_8_us);
- if (err)
- return err;
- err = si1133_chan_set_adcconfig(data, 2,
- SI1133_ADCCONFIG_DECIM_RATE(1) |
- SI1133_PARAM_ADCMUX_LARGE_WHITE);
- if (err)
- return err;
- err = si1133_param_set(data, SI1133_PARAM_REG_ADCPOST(2),
- SI1133_ADCPOST_24BIT_EN |
- SI1133_ADCPOST_POSTSHIFT_BITQTY(2));
- if (err)
- return err;
- err = si1133_chan_set_adcsens(data, 2, SI1133_ADCSENS_HSIG_MASK |
- SI1133_ADCSENS_NB_MEAS(1) | _3_120_0_us);
- if (err)
- return err;
- err = si1133_chan_set_adcconfig(data, 3,
- SI1133_ADCCONFIG_DECIM_RATE(1) |
- SI1133_PARAM_ADCMUX_MED_IR);
- if (err)
- return err;
- err = si1133_param_set(data, SI1133_PARAM_REG_ADCPOST(3),
- SI1133_ADCPOST_24BIT_EN |
- SI1133_ADCPOST_POSTSHIFT_BITQTY(2));
- if (err)
- return err;
- return si1133_chan_set_adcsens(data, 3, SI1133_ADCSENS_HSIG_MASK |
- SI1133_ADCSENS_NB_MEAS(64) | _48_8_us);
- }
- static int si1133_initialize(struct si1133_data *data)
- {
- int err;
- err = si1133_cmd_reset_sw(data);
- if (err)
- return err;
- /* Turn off autonomous mode */
- err = si1133_param_set(data, SI1133_REG_MEAS_RATE, 0);
- if (err)
- return err;
- err = si1133_init_lux_channels(data);
- if (err)
- return err;
- return regmap_write(data->regmap, SI1133_REG_IRQ_ENABLE,
- SI1133_IRQ_CHANNEL_ENABLE);
- }
- static int si1133_validate_ids(struct iio_dev *iio_dev)
- {
- struct si1133_data *data = iio_priv(iio_dev);
- unsigned int part_id, rev_id, mfr_id;
- int err;
- err = regmap_read(data->regmap, SI1133_REG_PART_ID, &part_id);
- if (err)
- return err;
- err = regmap_read(data->regmap, SI1133_REG_REV_ID, &rev_id);
- if (err)
- return err;
- err = regmap_read(data->regmap, SI1133_REG_MFR_ID, &mfr_id);
- if (err)
- return err;
- dev_info(&iio_dev->dev,
- "Device ID part %#02hhx rev %#02hhx mfr %#02hhx\n",
- part_id, rev_id, mfr_id);
- if (part_id != SI1133_PART_ID) {
- dev_err(&iio_dev->dev,
- "Part ID mismatch got %#02hhx, expected %#02x\n",
- part_id, SI1133_PART_ID);
- return -ENODEV;
- }
- return 0;
- }
- static int si1133_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
- {
- struct si1133_data *data;
- struct iio_dev *iio_dev;
- int err;
- iio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
- if (!iio_dev)
- return -ENOMEM;
- data = iio_priv(iio_dev);
- init_completion(&data->completion);
- data->regmap = devm_regmap_init_i2c(client, &si1133_regmap_config);
- if (IS_ERR(data->regmap)) {
- err = PTR_ERR(data->regmap);
- dev_err(&client->dev, "Failed to initialise regmap: %d\n", err);
- return err;
- }
- i2c_set_clientdata(client, iio_dev);
- data->client = client;
- iio_dev->dev.parent = &client->dev;
- iio_dev->name = id->name;
- iio_dev->channels = si1133_channels;
- iio_dev->num_channels = ARRAY_SIZE(si1133_channels);
- iio_dev->info = &si1133_info;
- iio_dev->modes = INDIO_DIRECT_MODE;
- mutex_init(&data->mutex);
- err = si1133_validate_ids(iio_dev);
- if (err)
- return err;
- err = si1133_initialize(data);
- if (err) {
- dev_err(&client->dev,
- "Error when initializing chip: %d\n", err);
- return err;
- }
- if (!client->irq) {
- dev_err(&client->dev,
- "Required interrupt not provided, cannot proceed\n");
- return -EINVAL;
- }
- err = devm_request_threaded_irq(&client->dev, client->irq,
- NULL,
- si1133_threaded_irq_handler,
- IRQF_ONESHOT | IRQF_SHARED,
- client->name, iio_dev);
- if (err) {
- dev_warn(&client->dev, "Request irq %d failed: %i\n",
- client->irq, err);
- return err;
- }
- return devm_iio_device_register(&client->dev, iio_dev);
- }
- static const struct i2c_device_id si1133_ids[] = {
- { "si1133", 0 },
- { }
- };
- MODULE_DEVICE_TABLE(i2c, si1133_ids);
- static struct i2c_driver si1133_driver = {
- .driver = {
- .name = "si1133",
- },
- .probe = si1133_probe,
- .id_table = si1133_ids,
- };
- module_i2c_driver(si1133_driver);
- MODULE_AUTHOR("Maxime Roussin-Belanger <maxime.roussinbelanger@gmail.com>");
- MODULE_DESCRIPTION("Silabs SI1133, UV index sensor and ambient light sensor driver");
- MODULE_LICENSE("GPL");
|