1076 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * si1133.c - Support for Silabs SI1133 combined ambient4 * light and UV index sensors5 *6 * Copyright 2018 Maxime Roussin-Belanger <maxime.roussinbelanger@gmail.com>7 */8 9#include <linux/delay.h>10#include <linux/i2c.h>11#include <linux/interrupt.h>12#include <linux/module.h>13#include <linux/regmap.h>14 15#include <linux/iio/iio.h>16#include <linux/iio/sysfs.h>17 18#include <linux/util_macros.h>19 20#include <linux/unaligned.h>21 22#define SI1133_REG_PART_ID 0x0023#define SI1133_REG_REV_ID 0x0124#define SI1133_REG_MFR_ID 0x0225#define SI1133_REG_INFO0 0x0326#define SI1133_REG_INFO1 0x0427 28#define SI1133_PART_ID 0x3329 30#define SI1133_REG_HOSTIN0 0x0A31#define SI1133_REG_COMMAND 0x0B32#define SI1133_REG_IRQ_ENABLE 0x0F33#define SI1133_REG_RESPONSE1 0x1034#define SI1133_REG_RESPONSE0 0x1135#define SI1133_REG_IRQ_STATUS 0x1236#define SI1133_REG_MEAS_RATE 0x1A37 38#define SI1133_IRQ_CHANNEL_ENABLE 0xF39 40#define SI1133_CMD_RESET_CTR 0x0041#define SI1133_CMD_RESET_SW 0x0142#define SI1133_CMD_FORCE 0x1143#define SI1133_CMD_START_AUTONOMOUS 0x1344#define SI1133_CMD_PARAM_SET 0x8045#define SI1133_CMD_PARAM_QUERY 0x4046#define SI1133_CMD_PARAM_MASK 0x3F47 48#define SI1133_CMD_ERR_MASK BIT(4)49#define SI1133_CMD_SEQ_MASK 0xF50#define SI1133_MAX_CMD_CTR 0xF51 52#define SI1133_PARAM_REG_CHAN_LIST 0x0153#define SI1133_PARAM_REG_ADCCONFIG(x) ((x) * 4) + 254#define SI1133_PARAM_REG_ADCSENS(x) ((x) * 4) + 355#define SI1133_PARAM_REG_ADCPOST(x) ((x) * 4) + 456 57#define SI1133_ADCMUX_MASK 0x1F58 59#define SI1133_ADCCONFIG_DECIM_RATE(x) (x) << 560 61#define SI1133_ADCSENS_SCALE_MASK 0x7062#define SI1133_ADCSENS_SCALE_SHIFT 463#define SI1133_ADCSENS_HSIG_MASK BIT(7)64#define SI1133_ADCSENS_HSIG_SHIFT 765#define SI1133_ADCSENS_HW_GAIN_MASK 0xF66#define SI1133_ADCSENS_NB_MEAS(x) fls(x) << SI1133_ADCSENS_SCALE_SHIFT67 68#define SI1133_ADCPOST_24BIT_EN BIT(6)69#define SI1133_ADCPOST_POSTSHIFT_BITQTY(x) (x & GENMASK(2, 0)) << 370 71#define SI1133_PARAM_ADCMUX_SMALL_IR 0x072#define SI1133_PARAM_ADCMUX_MED_IR 0x173#define SI1133_PARAM_ADCMUX_LARGE_IR 0x274#define SI1133_PARAM_ADCMUX_WHITE 0xB75#define SI1133_PARAM_ADCMUX_LARGE_WHITE 0xD76#define SI1133_PARAM_ADCMUX_UV 0x1877#define SI1133_PARAM_ADCMUX_UV_DEEP 0x1978 79#define SI1133_ERR_INVALID_CMD 0x080#define SI1133_ERR_INVALID_LOCATION_CMD 0x181#define SI1133_ERR_SATURATION_ADC_OR_OVERFLOW_ACCUMULATION 0x282#define SI1133_ERR_OUTPUT_BUFFER_OVERFLOW 0x383 84#define SI1133_COMPLETION_TIMEOUT_MS 50085 86#define SI1133_CMD_MINSLEEP_US_LOW 500087#define SI1133_CMD_MINSLEEP_US_HIGH 750088#define SI1133_CMD_TIMEOUT_MS 2589#define SI1133_CMD_LUX_TIMEOUT_MS 500090#define SI1133_CMD_TIMEOUT_US SI1133_CMD_TIMEOUT_MS * 100091 92#define SI1133_REG_HOSTOUT(x) (x) + 0x1393 94#define SI1133_MEASUREMENT_FREQUENCY 125095 96#define SI1133_X_ORDER_MASK 0x007097#define SI1133_Y_ORDER_MASK 0x000798#define si1133_get_x_order(m) ((m) & SI1133_X_ORDER_MASK) >> 499#define si1133_get_y_order(m) ((m) & SI1133_Y_ORDER_MASK)100 101#define SI1133_LUX_ADC_MASK 0xE102#define SI1133_ADC_THRESHOLD 16000103#define SI1133_INPUT_FRACTION_HIGH 7104#define SI1133_INPUT_FRACTION_LOW 15105#define SI1133_LUX_OUTPUT_FRACTION 12106#define SI1133_LUX_BUFFER_SIZE 9107#define SI1133_MEASURE_BUFFER_SIZE 3108 109static const int si1133_scale_available[] = {110 1, 2, 4, 8, 16, 32, 64, 128};111 112static IIO_CONST_ATTR(scale_available, "1 2 4 8 16 32 64 128");113 114static IIO_CONST_ATTR_INT_TIME_AVAIL("0.0244 0.0488 0.0975 0.195 0.390 0.780 "115 "1.560 3.120 6.24 12.48 25.0 50.0");116 117/* A.K.A. HW_GAIN in datasheet */118enum si1133_int_time {119 _24_4_us = 0,120 _48_8_us = 1,121 _97_5_us = 2,122 _195_0_us = 3,123 _390_0_us = 4,124 _780_0_us = 5,125 _1_560_0_us = 6,126 _3_120_0_us = 7,127 _6_240_0_us = 8,128 _12_480_0_us = 9,129 _25_ms = 10,130 _50_ms = 11,131};132 133/* Integration time in milliseconds, nanoseconds */134static const int si1133_int_time_table[][2] = {135 [_24_4_us] = {0, 24400},136 [_48_8_us] = {0, 48800},137 [_97_5_us] = {0, 97500},138 [_195_0_us] = {0, 195000},139 [_390_0_us] = {0, 390000},140 [_780_0_us] = {0, 780000},141 [_1_560_0_us] = {1, 560000},142 [_3_120_0_us] = {3, 120000},143 [_6_240_0_us] = {6, 240000},144 [_12_480_0_us] = {12, 480000},145 [_25_ms] = {25, 000000},146 [_50_ms] = {50, 000000},147};148 149static const struct regmap_range si1133_reg_ranges[] = {150 regmap_reg_range(0x00, 0x02),151 regmap_reg_range(0x0A, 0x0B),152 regmap_reg_range(0x0F, 0x0F),153 regmap_reg_range(0x10, 0x12),154 regmap_reg_range(0x13, 0x2C),155};156 157static const struct regmap_range si1133_reg_ro_ranges[] = {158 regmap_reg_range(0x00, 0x02),159 regmap_reg_range(0x10, 0x2C),160};161 162static const struct regmap_range si1133_precious_ranges[] = {163 regmap_reg_range(0x12, 0x12),164};165 166static const struct regmap_access_table si1133_write_ranges_table = {167 .yes_ranges = si1133_reg_ranges,168 .n_yes_ranges = ARRAY_SIZE(si1133_reg_ranges),169 .no_ranges = si1133_reg_ro_ranges,170 .n_no_ranges = ARRAY_SIZE(si1133_reg_ro_ranges),171};172 173static const struct regmap_access_table si1133_read_ranges_table = {174 .yes_ranges = si1133_reg_ranges,175 .n_yes_ranges = ARRAY_SIZE(si1133_reg_ranges),176};177 178static const struct regmap_access_table si1133_precious_table = {179 .yes_ranges = si1133_precious_ranges,180 .n_yes_ranges = ARRAY_SIZE(si1133_precious_ranges),181};182 183static const struct regmap_config si1133_regmap_config = {184 .reg_bits = 8,185 .val_bits = 8,186 187 .max_register = 0x2C,188 189 .wr_table = &si1133_write_ranges_table,190 .rd_table = &si1133_read_ranges_table,191 192 .precious_table = &si1133_precious_table,193};194 195struct si1133_data {196 struct regmap *regmap;197 struct i2c_client *client;198 199 /* Lock protecting one command at a time can be processed */200 struct mutex mutex;201 202 int rsp_seq;203 u8 scan_mask;204 u8 adc_sens[6];205 u8 adc_config[6];206 207 struct completion completion;208};209 210struct si1133_coeff {211 s16 info;212 u16 mag;213};214 215struct si1133_lux_coeff {216 struct si1133_coeff coeff_high[4];217 struct si1133_coeff coeff_low[9];218};219 220static const struct si1133_lux_coeff lux_coeff = {221 {222 { 0, 209},223 { 1665, 93},224 { 2064, 65},225 {-2671, 234}226 },227 {228 { 0, 0},229 { 1921, 29053},230 {-1022, 36363},231 { 2320, 20789},232 { -367, 57909},233 {-1774, 38240},234 { -608, 46775},235 {-1503, 51831},236 {-1886, 58928}237 }238};239 240static int si1133_calculate_polynomial_inner(s32 input, u8 fraction, u16 mag,241 s8 shift)242{243 return ((input << fraction) / mag) << shift;244}245 246static int si1133_calculate_output(s32 x, s32 y, u8 x_order, u8 y_order,247 u8 input_fraction, s8 sign,248 const struct si1133_coeff *coeffs)249{250 s8 shift;251 int x1 = 1;252 int x2 = 1;253 int y1 = 1;254 int y2 = 1;255 256 shift = ((u16)coeffs->info & 0xFF00) >> 8;257 shift ^= 0xFF;258 shift += 1;259 shift = -shift;260 261 if (x_order > 0) {262 x1 = si1133_calculate_polynomial_inner(x, input_fraction,263 coeffs->mag, shift);264 if (x_order > 1)265 x2 = x1;266 }267 268 if (y_order > 0) {269 y1 = si1133_calculate_polynomial_inner(y, input_fraction,270 coeffs->mag, shift);271 if (y_order > 1)272 y2 = y1;273 }274 275 return sign * x1 * x2 * y1 * y2;276}277 278/*279 * The algorithm is from:280 * https://siliconlabs.github.io/Gecko_SDK_Doc/efm32zg/html/si1133_8c_source.html#l00716281 */282static int si1133_calc_polynomial(s32 x, s32 y, u8 input_fraction, u8 num_coeff,283 const struct si1133_coeff *coeffs)284{285 u8 x_order, y_order;286 u8 counter;287 s8 sign;288 int output = 0;289 290 for (counter = 0; counter < num_coeff; counter++) {291 if (coeffs->info < 0)292 sign = -1;293 else294 sign = 1;295 296 x_order = si1133_get_x_order(coeffs->info);297 y_order = si1133_get_y_order(coeffs->info);298 299 if ((x_order == 0) && (y_order == 0))300 output +=301 sign * coeffs->mag << SI1133_LUX_OUTPUT_FRACTION;302 else303 output += si1133_calculate_output(x, y, x_order,304 y_order,305 input_fraction, sign,306 coeffs);307 coeffs++;308 }309 310 return abs(output);311}312 313static int si1133_cmd_reset_sw(struct si1133_data *data)314{315 struct device *dev = &data->client->dev;316 unsigned int resp;317 unsigned long timeout;318 int err;319 320 err = regmap_write(data->regmap, SI1133_REG_COMMAND,321 SI1133_CMD_RESET_SW);322 if (err)323 return err;324 325 timeout = jiffies + msecs_to_jiffies(SI1133_CMD_TIMEOUT_MS);326 while (true) {327 err = regmap_read(data->regmap, SI1133_REG_RESPONSE0, &resp);328 if (err == -ENXIO) {329 usleep_range(SI1133_CMD_MINSLEEP_US_LOW,330 SI1133_CMD_MINSLEEP_US_HIGH);331 continue;332 }333 334 if ((resp & SI1133_MAX_CMD_CTR) == SI1133_MAX_CMD_CTR)335 break;336 337 if (time_after(jiffies, timeout)) {338 dev_warn(dev, "Timeout on reset ctr resp: %d\n", resp);339 return -ETIMEDOUT;340 }341 }342 343 if (!err)344 data->rsp_seq = SI1133_MAX_CMD_CTR;345 346 return err;347}348 349static int si1133_parse_response_err(struct device *dev, u32 resp, u8 cmd)350{351 resp &= 0xF;352 353 switch (resp) {354 case SI1133_ERR_OUTPUT_BUFFER_OVERFLOW:355 dev_warn(dev, "Output buffer overflow: 0x%02x\n", cmd);356 return -EOVERFLOW;357 case SI1133_ERR_SATURATION_ADC_OR_OVERFLOW_ACCUMULATION:358 dev_warn(dev, "Saturation of the ADC or overflow of accumulation: 0x%02x\n",359 cmd);360 return -EOVERFLOW;361 case SI1133_ERR_INVALID_LOCATION_CMD:362 dev_warn(dev,363 "Parameter access to an invalid location: 0x%02x\n",364 cmd);365 return -EINVAL;366 case SI1133_ERR_INVALID_CMD:367 dev_warn(dev, "Invalid command 0x%02x\n", cmd);368 return -EINVAL;369 default:370 dev_warn(dev, "Unknown error 0x%02x\n", cmd);371 return -EINVAL;372 }373}374 375static int si1133_cmd_reset_counter(struct si1133_data *data)376{377 int err = regmap_write(data->regmap, SI1133_REG_COMMAND,378 SI1133_CMD_RESET_CTR);379 if (err)380 return err;381 382 data->rsp_seq = 0;383 384 return 0;385}386 387static int si1133_command(struct si1133_data *data, u8 cmd)388{389 struct device *dev = &data->client->dev;390 u32 resp;391 int err;392 int expected_seq;393 394 mutex_lock(&data->mutex);395 396 expected_seq = (data->rsp_seq + 1) & SI1133_MAX_CMD_CTR;397 398 if (cmd == SI1133_CMD_FORCE)399 reinit_completion(&data->completion);400 401 err = regmap_write(data->regmap, SI1133_REG_COMMAND, cmd);402 if (err) {403 dev_warn(dev, "Failed to write command 0x%02x, ret=%d\n", cmd,404 err);405 goto out;406 }407 408 if (cmd == SI1133_CMD_FORCE) {409 /* wait for irq */410 if (!wait_for_completion_timeout(&data->completion,411 msecs_to_jiffies(SI1133_COMPLETION_TIMEOUT_MS))) {412 err = -ETIMEDOUT;413 goto out;414 }415 err = regmap_read(data->regmap, SI1133_REG_RESPONSE0, &resp);416 if (err)417 goto out;418 } else {419 err = regmap_read_poll_timeout(data->regmap,420 SI1133_REG_RESPONSE0, resp,421 (resp & SI1133_CMD_SEQ_MASK) ==422 expected_seq ||423 (resp & SI1133_CMD_ERR_MASK),424 SI1133_CMD_MINSLEEP_US_LOW,425 SI1133_CMD_TIMEOUT_MS * 1000);426 if (err) {427 dev_warn(dev,428 "Failed to read command 0x%02x, ret=%d\n",429 cmd, err);430 goto out;431 }432 }433 434 if (resp & SI1133_CMD_ERR_MASK) {435 err = si1133_parse_response_err(dev, resp, cmd);436 si1133_cmd_reset_counter(data);437 } else {438 data->rsp_seq = expected_seq;439 }440 441out:442 mutex_unlock(&data->mutex);443 444 return err;445}446 447static int si1133_param_set(struct si1133_data *data, u8 param, u32 value)448{449 int err = regmap_write(data->regmap, SI1133_REG_HOSTIN0, value);450 451 if (err)452 return err;453 454 return si1133_command(data, SI1133_CMD_PARAM_SET |455 (param & SI1133_CMD_PARAM_MASK));456}457 458static int si1133_param_query(struct si1133_data *data, u8 param, u32 *result)459{460 int err = si1133_command(data, SI1133_CMD_PARAM_QUERY |461 (param & SI1133_CMD_PARAM_MASK));462 if (err)463 return err;464 465 return regmap_read(data->regmap, SI1133_REG_RESPONSE1, result);466}467 468#define SI1133_CHANNEL(_ch, _type) \469 .type = _type, \470 .channel = _ch, \471 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \472 .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | \473 BIT(IIO_CHAN_INFO_SCALE) | \474 BIT(IIO_CHAN_INFO_HARDWAREGAIN), \475 476static const struct iio_chan_spec si1133_channels[] = {477 {478 .type = IIO_LIGHT,479 .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),480 .channel = 0,481 },482 {483 SI1133_CHANNEL(SI1133_PARAM_ADCMUX_WHITE, IIO_INTENSITY)484 .channel2 = IIO_MOD_LIGHT_BOTH,485 },486 {487 SI1133_CHANNEL(SI1133_PARAM_ADCMUX_LARGE_WHITE, IIO_INTENSITY)488 .channel2 = IIO_MOD_LIGHT_BOTH,489 .extend_name = "large",490 },491 {492 SI1133_CHANNEL(SI1133_PARAM_ADCMUX_SMALL_IR, IIO_INTENSITY)493 .extend_name = "small",494 .modified = 1,495 .channel2 = IIO_MOD_LIGHT_IR,496 },497 {498 SI1133_CHANNEL(SI1133_PARAM_ADCMUX_MED_IR, IIO_INTENSITY)499 .modified = 1,500 .channel2 = IIO_MOD_LIGHT_IR,501 },502 {503 SI1133_CHANNEL(SI1133_PARAM_ADCMUX_LARGE_IR, IIO_INTENSITY)504 .extend_name = "large",505 .modified = 1,506 .channel2 = IIO_MOD_LIGHT_IR,507 },508 {509 SI1133_CHANNEL(SI1133_PARAM_ADCMUX_UV, IIO_UVINDEX)510 },511 {512 SI1133_CHANNEL(SI1133_PARAM_ADCMUX_UV_DEEP, IIO_UVINDEX)513 .modified = 1,514 .channel2 = IIO_MOD_LIGHT_DUV,515 }516};517 518static int si1133_get_int_time_index(int milliseconds, int nanoseconds)519{520 int i;521 522 for (i = 0; i < ARRAY_SIZE(si1133_int_time_table); i++) {523 if (milliseconds == si1133_int_time_table[i][0] &&524 nanoseconds == si1133_int_time_table[i][1])525 return i;526 }527 return -EINVAL;528}529 530static int si1133_set_integration_time(struct si1133_data *data, u8 adc,531 int milliseconds, int nanoseconds)532{533 int index;534 535 index = si1133_get_int_time_index(milliseconds, nanoseconds);536 if (index < 0)537 return index;538 539 data->adc_sens[adc] &= 0xF0;540 data->adc_sens[adc] |= index;541 542 return si1133_param_set(data, SI1133_PARAM_REG_ADCSENS(0),543 data->adc_sens[adc]);544}545 546static int si1133_set_chlist(struct si1133_data *data, u8 scan_mask)547{548 /* channel list already set, no need to reprogram */549 if (data->scan_mask == scan_mask)550 return 0;551 552 data->scan_mask = scan_mask;553 554 return si1133_param_set(data, SI1133_PARAM_REG_CHAN_LIST, scan_mask);555}556 557static int si1133_chan_set_adcconfig(struct si1133_data *data, u8 adc,558 u8 adc_config)559{560 int err;561 562 err = si1133_param_set(data, SI1133_PARAM_REG_ADCCONFIG(adc),563 adc_config);564 if (err)565 return err;566 567 data->adc_config[adc] = adc_config;568 569 return 0;570}571 572static int si1133_update_adcconfig(struct si1133_data *data, uint8_t adc,573 u8 mask, u8 shift, u8 value)574{575 u32 adc_config;576 int err;577 578 err = si1133_param_query(data, SI1133_PARAM_REG_ADCCONFIG(adc),579 &adc_config);580 if (err)581 return err;582 583 adc_config &= ~mask;584 adc_config |= (value << shift);585 586 return si1133_chan_set_adcconfig(data, adc, adc_config);587}588 589static int si1133_set_adcmux(struct si1133_data *data, u8 adc, u8 mux)590{591 if ((mux & data->adc_config[adc]) == mux)592 return 0; /* mux already set to correct value */593 594 return si1133_update_adcconfig(data, adc, SI1133_ADCMUX_MASK, 0, mux);595}596 597static int si1133_force_measurement(struct si1133_data *data)598{599 return si1133_command(data, SI1133_CMD_FORCE);600}601 602static int si1133_bulk_read(struct si1133_data *data, u8 start_reg, u8 length,603 u8 *buffer)604{605 int err;606 607 err = si1133_force_measurement(data);608 if (err)609 return err;610 611 return regmap_bulk_read(data->regmap, start_reg, buffer, length);612}613 614static int si1133_measure(struct si1133_data *data,615 struct iio_chan_spec const *chan,616 int *val)617{618 int err;619 620 u8 buffer[SI1133_MEASURE_BUFFER_SIZE];621 622 err = si1133_set_adcmux(data, 0, chan->channel);623 if (err)624 return err;625 626 /* Deactivate lux measurements if they were active */627 err = si1133_set_chlist(data, BIT(0));628 if (err)629 return err;630 631 err = si1133_bulk_read(data, SI1133_REG_HOSTOUT(0), sizeof(buffer),632 buffer);633 if (err)634 return err;635 636 *val = sign_extend32(get_unaligned_be24(&buffer[0]), 23);637 638 return err;639}640 641static irqreturn_t si1133_threaded_irq_handler(int irq, void *private)642{643 struct iio_dev *iio_dev = private;644 struct si1133_data *data = iio_priv(iio_dev);645 u32 irq_status;646 int err;647 648 err = regmap_read(data->regmap, SI1133_REG_IRQ_STATUS, &irq_status);649 if (err) {650 dev_err_ratelimited(&iio_dev->dev, "Error reading IRQ\n");651 goto out;652 }653 654 if (irq_status != data->scan_mask)655 return IRQ_NONE;656 657out:658 complete(&data->completion);659 660 return IRQ_HANDLED;661}662 663static int si1133_scale_to_swgain(int scale_integer, int scale_fractional)664{665 scale_integer = find_closest(scale_integer, si1133_scale_available,666 ARRAY_SIZE(si1133_scale_available));667 if (scale_integer < 0 ||668 scale_integer > ARRAY_SIZE(si1133_scale_available) ||669 scale_fractional != 0)670 return -EINVAL;671 672 return scale_integer;673}674 675static int si1133_chan_set_adcsens(struct si1133_data *data, u8 adc,676 u8 adc_sens)677{678 int err;679 680 err = si1133_param_set(data, SI1133_PARAM_REG_ADCSENS(adc), adc_sens);681 if (err)682 return err;683 684 data->adc_sens[adc] = adc_sens;685 686 return 0;687}688 689static int si1133_update_adcsens(struct si1133_data *data, u8 mask,690 u8 shift, u8 value)691{692 int err;693 u32 adc_sens;694 695 err = si1133_param_query(data, SI1133_PARAM_REG_ADCSENS(0),696 &adc_sens);697 if (err)698 return err;699 700 adc_sens &= ~mask;701 adc_sens |= (value << shift);702 703 return si1133_chan_set_adcsens(data, 0, adc_sens);704}705 706static int si1133_get_lux(struct si1133_data *data, int *val)707{708 int err;709 int lux;710 s32 high_vis;711 s32 low_vis;712 s32 ir;713 u8 buffer[SI1133_LUX_BUFFER_SIZE];714 715 /* Activate lux channels */716 err = si1133_set_chlist(data, SI1133_LUX_ADC_MASK);717 if (err)718 return err;719 720 err = si1133_bulk_read(data, SI1133_REG_HOSTOUT(0),721 SI1133_LUX_BUFFER_SIZE, buffer);722 if (err)723 return err;724 725 high_vis = sign_extend32(get_unaligned_be24(&buffer[0]), 23);726 727 low_vis = sign_extend32(get_unaligned_be24(&buffer[3]), 23);728 729 ir = sign_extend32(get_unaligned_be24(&buffer[6]), 23);730 731 if (high_vis > SI1133_ADC_THRESHOLD || ir > SI1133_ADC_THRESHOLD)732 lux = si1133_calc_polynomial(high_vis, ir,733 SI1133_INPUT_FRACTION_HIGH,734 ARRAY_SIZE(lux_coeff.coeff_high),735 &lux_coeff.coeff_high[0]);736 else737 lux = si1133_calc_polynomial(low_vis, ir,738 SI1133_INPUT_FRACTION_LOW,739 ARRAY_SIZE(lux_coeff.coeff_low),740 &lux_coeff.coeff_low[0]);741 742 *val = lux >> SI1133_LUX_OUTPUT_FRACTION;743 744 return err;745}746 747static int si1133_read_raw(struct iio_dev *iio_dev,748 struct iio_chan_spec const *chan,749 int *val, int *val2, long mask)750{751 struct si1133_data *data = iio_priv(iio_dev);752 u8 adc_sens = data->adc_sens[0];753 int err;754 755 switch (mask) {756 case IIO_CHAN_INFO_PROCESSED:757 switch (chan->type) {758 case IIO_LIGHT:759 err = si1133_get_lux(data, val);760 if (err)761 return err;762 763 return IIO_VAL_INT;764 default:765 return -EINVAL;766 }767 case IIO_CHAN_INFO_RAW:768 switch (chan->type) {769 case IIO_INTENSITY:770 case IIO_UVINDEX:771 err = si1133_measure(data, chan, val);772 if (err)773 return err;774 775 return IIO_VAL_INT;776 default:777 return -EINVAL;778 }779 case IIO_CHAN_INFO_INT_TIME:780 switch (chan->type) {781 case IIO_INTENSITY:782 case IIO_UVINDEX:783 adc_sens &= SI1133_ADCSENS_HW_GAIN_MASK;784 785 *val = si1133_int_time_table[adc_sens][0];786 *val2 = si1133_int_time_table[adc_sens][1];787 return IIO_VAL_INT_PLUS_MICRO;788 default:789 return -EINVAL;790 }791 case IIO_CHAN_INFO_SCALE:792 switch (chan->type) {793 case IIO_INTENSITY:794 case IIO_UVINDEX:795 adc_sens &= SI1133_ADCSENS_SCALE_MASK;796 adc_sens >>= SI1133_ADCSENS_SCALE_SHIFT;797 798 *val = BIT(adc_sens);799 800 return IIO_VAL_INT;801 default:802 return -EINVAL;803 }804 case IIO_CHAN_INFO_HARDWAREGAIN:805 switch (chan->type) {806 case IIO_INTENSITY:807 case IIO_UVINDEX:808 adc_sens >>= SI1133_ADCSENS_HSIG_SHIFT;809 810 *val = adc_sens;811 812 return IIO_VAL_INT;813 default:814 return -EINVAL;815 }816 default:817 return -EINVAL;818 }819}820 821static int si1133_write_raw(struct iio_dev *iio_dev,822 struct iio_chan_spec const *chan,823 int val, int val2, long mask)824{825 struct si1133_data *data = iio_priv(iio_dev);826 827 switch (mask) {828 case IIO_CHAN_INFO_SCALE:829 switch (chan->type) {830 case IIO_INTENSITY:831 case IIO_UVINDEX:832 val = si1133_scale_to_swgain(val, val2);833 if (val < 0)834 return val;835 836 return si1133_update_adcsens(data,837 SI1133_ADCSENS_SCALE_MASK,838 SI1133_ADCSENS_SCALE_SHIFT,839 val);840 default:841 return -EINVAL;842 }843 case IIO_CHAN_INFO_INT_TIME:844 return si1133_set_integration_time(data, 0, val, val2);845 case IIO_CHAN_INFO_HARDWAREGAIN:846 switch (chan->type) {847 case IIO_INTENSITY:848 case IIO_UVINDEX:849 if (val != 0 && val != 1)850 return -EINVAL;851 852 return si1133_update_adcsens(data,853 SI1133_ADCSENS_HSIG_MASK,854 SI1133_ADCSENS_HSIG_SHIFT,855 val);856 default:857 return -EINVAL;858 }859 default:860 return -EINVAL;861 }862}863 864static struct attribute *si1133_attributes[] = {865 &iio_const_attr_integration_time_available.dev_attr.attr,866 &iio_const_attr_scale_available.dev_attr.attr,867 NULL,868};869 870static const struct attribute_group si1133_attribute_group = {871 .attrs = si1133_attributes,872};873 874static const struct iio_info si1133_info = {875 .read_raw = si1133_read_raw,876 .write_raw = si1133_write_raw,877 .attrs = &si1133_attribute_group,878};879 880/*881 * si1133_init_lux_channels - Configure 3 different channels(adc) (1,2 and 3)882 * The channel configuration for the lux measurement was taken from :883 * https://siliconlabs.github.io/Gecko_SDK_Doc/efm32zg/html/si1133_8c_source.html#l00578884 *885 * Reserved the channel 0 for the other raw measurements886 */887static int si1133_init_lux_channels(struct si1133_data *data)888{889 int err;890 891 err = si1133_chan_set_adcconfig(data, 1,892 SI1133_ADCCONFIG_DECIM_RATE(1) |893 SI1133_PARAM_ADCMUX_LARGE_WHITE);894 if (err)895 return err;896 897 err = si1133_param_set(data, SI1133_PARAM_REG_ADCPOST(1),898 SI1133_ADCPOST_24BIT_EN |899 SI1133_ADCPOST_POSTSHIFT_BITQTY(0));900 if (err)901 return err;902 err = si1133_chan_set_adcsens(data, 1, SI1133_ADCSENS_HSIG_MASK |903 SI1133_ADCSENS_NB_MEAS(64) | _48_8_us);904 if (err)905 return err;906 907 err = si1133_chan_set_adcconfig(data, 2,908 SI1133_ADCCONFIG_DECIM_RATE(1) |909 SI1133_PARAM_ADCMUX_LARGE_WHITE);910 if (err)911 return err;912 913 err = si1133_param_set(data, SI1133_PARAM_REG_ADCPOST(2),914 SI1133_ADCPOST_24BIT_EN |915 SI1133_ADCPOST_POSTSHIFT_BITQTY(2));916 if (err)917 return err;918 919 err = si1133_chan_set_adcsens(data, 2, SI1133_ADCSENS_HSIG_MASK |920 SI1133_ADCSENS_NB_MEAS(1) | _3_120_0_us);921 if (err)922 return err;923 924 err = si1133_chan_set_adcconfig(data, 3,925 SI1133_ADCCONFIG_DECIM_RATE(1) |926 SI1133_PARAM_ADCMUX_MED_IR);927 if (err)928 return err;929 930 err = si1133_param_set(data, SI1133_PARAM_REG_ADCPOST(3),931 SI1133_ADCPOST_24BIT_EN |932 SI1133_ADCPOST_POSTSHIFT_BITQTY(2));933 if (err)934 return err;935 936 return si1133_chan_set_adcsens(data, 3, SI1133_ADCSENS_HSIG_MASK |937 SI1133_ADCSENS_NB_MEAS(64) | _48_8_us);938}939 940static int si1133_initialize(struct si1133_data *data)941{942 int err;943 944 err = si1133_cmd_reset_sw(data);945 if (err)946 return err;947 948 /* Turn off autonomous mode */949 err = si1133_param_set(data, SI1133_REG_MEAS_RATE, 0);950 if (err)951 return err;952 953 err = si1133_init_lux_channels(data);954 if (err)955 return err;956 957 return regmap_write(data->regmap, SI1133_REG_IRQ_ENABLE,958 SI1133_IRQ_CHANNEL_ENABLE);959}960 961static int si1133_validate_ids(struct iio_dev *iio_dev)962{963 struct si1133_data *data = iio_priv(iio_dev);964 965 unsigned int part_id, rev_id, mfr_id;966 int err;967 968 err = regmap_read(data->regmap, SI1133_REG_PART_ID, &part_id);969 if (err)970 return err;971 972 err = regmap_read(data->regmap, SI1133_REG_REV_ID, &rev_id);973 if (err)974 return err;975 976 err = regmap_read(data->regmap, SI1133_REG_MFR_ID, &mfr_id);977 if (err)978 return err;979 980 dev_info(&iio_dev->dev,981 "Device ID part 0x%02x rev 0x%02x mfr 0x%02x\n",982 part_id, rev_id, mfr_id);983 if (part_id != SI1133_PART_ID) {984 dev_err(&iio_dev->dev,985 "Part ID mismatch got 0x%02x, expected 0x%02x\n",986 part_id, SI1133_PART_ID);987 return -ENODEV;988 }989 990 return 0;991}992 993static int si1133_probe(struct i2c_client *client)994{995 const struct i2c_device_id *id = i2c_client_get_device_id(client);996 struct si1133_data *data;997 struct iio_dev *iio_dev;998 int err;999 1000 iio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));1001 if (!iio_dev)1002 return -ENOMEM;1003 1004 data = iio_priv(iio_dev);1005 1006 init_completion(&data->completion);1007 1008 data->regmap = devm_regmap_init_i2c(client, &si1133_regmap_config);1009 if (IS_ERR(data->regmap)) {1010 err = PTR_ERR(data->regmap);1011 dev_err(&client->dev, "Failed to initialise regmap: %d\n", err);1012 return err;1013 }1014 1015 i2c_set_clientdata(client, iio_dev);1016 data->client = client;1017 1018 iio_dev->name = id->name;1019 iio_dev->channels = si1133_channels;1020 iio_dev->num_channels = ARRAY_SIZE(si1133_channels);1021 iio_dev->info = &si1133_info;1022 iio_dev->modes = INDIO_DIRECT_MODE;1023 1024 mutex_init(&data->mutex);1025 1026 err = si1133_validate_ids(iio_dev);1027 if (err)1028 return err;1029 1030 err = si1133_initialize(data);1031 if (err) {1032 dev_err(&client->dev,1033 "Error when initializing chip: %d\n", err);1034 return err;1035 }1036 1037 if (!client->irq) {1038 dev_err(&client->dev,1039 "Required interrupt not provided, cannot proceed\n");1040 return -EINVAL;1041 }1042 1043 err = devm_request_threaded_irq(&client->dev, client->irq,1044 NULL,1045 si1133_threaded_irq_handler,1046 IRQF_ONESHOT | IRQF_SHARED,1047 client->name, iio_dev);1048 if (err) {1049 dev_warn(&client->dev, "Request irq %d failed: %i\n",1050 client->irq, err);1051 return err;1052 }1053 1054 return devm_iio_device_register(&client->dev, iio_dev);1055}1056 1057static const struct i2c_device_id si1133_ids[] = {1058 { "si1133" },1059 { }1060};1061MODULE_DEVICE_TABLE(i2c, si1133_ids);1062 1063static struct i2c_driver si1133_driver = {1064 .driver = {1065 .name = "si1133",1066 },1067 .probe = si1133_probe,1068 .id_table = si1133_ids,1069};1070 1071module_i2c_driver(si1133_driver);1072 1073MODULE_AUTHOR("Maxime Roussin-Belanger <maxime.roussinbelanger@gmail.com>");1074MODULE_DESCRIPTION("Silabs SI1133, UV index sensor and ambient light sensor driver");1075MODULE_LICENSE("GPL");1076