698 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Measurements Specialties driver common i2c functions4 *5 * Copyright (c) 2015 Measurement-Specialties6 */7 8#include <linux/module.h>9#include <linux/iio/iio.h>10#include <linux/device.h>11#include <linux/delay.h>12 13#include "ms_sensors_i2c.h"14 15/* Conversion times in us */16static const u16 ms_sensors_ht_t_conversion_time[] = { 50000, 25000,17 13000, 7000 };18static const u16 ms_sensors_ht_h_conversion_time[] = { 16000, 5000,19 3000, 8000 };20static const u16 ms_sensors_tp_conversion_time[] = { 500, 1100, 2100,21 4100, 8220, 16440 };22 23#define MS_SENSORS_SERIAL_READ_MSB 0xFA0F24#define MS_SENSORS_SERIAL_READ_LSB 0xFCC925#define MS_SENSORS_CONFIG_REG_WRITE 0xE626#define MS_SENSORS_CONFIG_REG_READ 0xE727#define MS_SENSORS_HT_T_CONVERSION_START 0xF328#define MS_SENSORS_HT_H_CONVERSION_START 0xF529 30#define MS_SENSORS_TP_PROM_READ 0xA031#define MS_SENSORS_TP_T_CONVERSION_START 0x5032#define MS_SENSORS_TP_P_CONVERSION_START 0x4033#define MS_SENSORS_TP_ADC_READ 0x0034 35#define MS_SENSORS_NO_READ_CMD 0xFF36 37/**38 * ms_sensors_reset() - Reset function39 * @cli: pointer to device client40 * @cmd: reset cmd. Depends on device in use41 * @delay: usleep minimal delay after reset command is issued42 *43 * Generic I2C reset function for Measurement Specialties devices.44 *45 * Return: 0 on success, negative errno otherwise.46 */47int ms_sensors_reset(void *cli, u8 cmd, unsigned int delay)48{49 int ret;50 struct i2c_client *client = cli;51 52 ret = i2c_smbus_write_byte(client, cmd);53 if (ret) {54 dev_err(&client->dev, "Failed to reset device\n");55 return ret;56 }57 usleep_range(delay, delay + 1000);58 59 return 0;60}61EXPORT_SYMBOL_NS(ms_sensors_reset, IIO_MEAS_SPEC_SENSORS);62 63/**64 * ms_sensors_read_prom_word() - PROM word read function65 * @cli: pointer to device client66 * @cmd: PROM read cmd. Depends on device and prom id67 * @word: pointer to word destination value68 *69 * Generic i2c prom word read function for Measurement Specialties devices.70 *71 * Return: 0 on success, negative errno otherwise.72 */73int ms_sensors_read_prom_word(void *cli, int cmd, u16 *word)74{75 int ret;76 struct i2c_client *client = cli;77 78 ret = i2c_smbus_read_word_swapped(client, cmd);79 if (ret < 0) {80 dev_err(&client->dev, "Failed to read prom word\n");81 return ret;82 }83 *word = ret;84 85 return 0;86}87EXPORT_SYMBOL_NS(ms_sensors_read_prom_word, IIO_MEAS_SPEC_SENSORS);88 89/**90 * ms_sensors_convert_and_read() - ADC conversion & read function91 * @cli: pointer to device client92 * @conv: ADC conversion command. Depends on device in use93 * @rd: ADC read command. Depends on device in use94 * @delay: usleep minimal delay after conversion command is issued95 * @adc: pointer to ADC destination value96 *97 * Generic ADC conversion & read function for Measurement Specialties98 * devices.99 * The function will issue conversion command, sleep appopriate delay, and100 * issue command to read ADC.101 *102 * Return: 0 on success, negative errno otherwise.103 */104int ms_sensors_convert_and_read(void *cli, u8 conv, u8 rd,105 unsigned int delay, u32 *adc)106{107 int ret;108 __be32 buf = 0;109 struct i2c_client *client = cli;110 111 /* Trigger conversion */112 ret = i2c_smbus_write_byte(client, conv);113 if (ret)114 goto err;115 usleep_range(delay, delay + 1000);116 117 /* Retrieve ADC value */118 if (rd != MS_SENSORS_NO_READ_CMD)119 ret = i2c_smbus_read_i2c_block_data(client, rd, 3, (u8 *)&buf);120 else121 ret = i2c_master_recv(client, (u8 *)&buf, 3);122 if (ret < 0)123 goto err;124 125 dev_dbg(&client->dev, "ADC raw value : %x\n", be32_to_cpu(buf) >> 8);126 *adc = be32_to_cpu(buf) >> 8;127 128 return 0;129err:130 dev_err(&client->dev, "Unable to make sensor adc conversion\n");131 return ret;132}133EXPORT_SYMBOL_NS(ms_sensors_convert_and_read, IIO_MEAS_SPEC_SENSORS);134 135/**136 * ms_sensors_crc_valid() - CRC check function137 * @value: input and CRC compare value138 *139 * Cyclic Redundancy Check function used in TSYS02D, HTU21, MS8607.140 * This function performs a x^8 + x^5 + x^4 + 1 polynomial CRC.141 * The argument contains CRC value in LSB byte while the bytes 1 and 2142 * are used for CRC computation.143 *144 * Return: 1 if CRC is valid, 0 otherwise.145 */146static bool ms_sensors_crc_valid(u32 value)147{148 u32 polynom = 0x988000; /* x^8 + x^5 + x^4 + 1 */149 u32 msb = 0x800000;150 u32 mask = 0xFF8000;151 u32 result = value & 0xFFFF00;152 u8 crc = value & 0xFF;153 154 while (msb != 0x80) {155 if (result & msb)156 result = ((result ^ polynom) & mask)157 | (result & ~mask);158 msb >>= 1;159 mask >>= 1;160 polynom >>= 1;161 }162 163 return result == crc;164}165 166/**167 * ms_sensors_read_serial() - Serial number read function168 * @client: pointer to i2c client169 * @sn: pointer to 64-bits destination value170 *171 * Generic i2c serial number read function for Measurement Specialties devices.172 * This function is used for TSYS02d, HTU21, MS8607 chipset.173 * Refer to datasheet:174 * http://www.meas-spec.com/downloads/HTU2X_Serial_Number_Reading.pdf175 *176 * Sensor raw MSB serial number format is the following :177 * [ SNB3, CRC, SNB2, CRC, SNB1, CRC, SNB0, CRC]178 * Sensor raw LSB serial number format is the following :179 * [ X, X, SNC1, SNC0, CRC, SNA1, SNA0, CRC]180 * The resulting serial number is following :181 * [ SNA1, SNA0, SNB3, SNB2, SNB1, SNB0, SNC1, SNC0]182 *183 * Return: 0 on success, negative errno otherwise.184 */185int ms_sensors_read_serial(struct i2c_client *client, u64 *sn)186{187 u8 i;188 __be64 rcv_buf = 0;189 u64 rcv_val;190 __be16 send_buf;191 int ret;192 193 struct i2c_msg msg[2] = {194 {195 .addr = client->addr,196 .flags = client->flags,197 .len = 2,198 .buf = (__u8 *)&send_buf,199 },200 {201 .addr = client->addr,202 .flags = client->flags | I2C_M_RD,203 .buf = (__u8 *)&rcv_buf,204 },205 };206 207 /* Read MSB part of serial number */208 send_buf = cpu_to_be16(MS_SENSORS_SERIAL_READ_MSB);209 msg[1].len = 8;210 ret = i2c_transfer(client->adapter, msg, 2);211 if (ret < 0) {212 dev_err(&client->dev, "Unable to read device serial number");213 return ret;214 }215 216 rcv_val = be64_to_cpu(rcv_buf);217 dev_dbg(&client->dev, "Serial MSB raw : %llx\n", rcv_val);218 219 for (i = 0; i < 64; i += 16) {220 if (!ms_sensors_crc_valid((rcv_val >> i) & 0xFFFF))221 return -ENODEV;222 }223 224 *sn = (((rcv_val >> 32) & 0xFF000000) |225 ((rcv_val >> 24) & 0x00FF0000) |226 ((rcv_val >> 16) & 0x0000FF00) |227 ((rcv_val >> 8) & 0x000000FF)) << 16;228 229 /* Read LSB part of serial number */230 send_buf = cpu_to_be16(MS_SENSORS_SERIAL_READ_LSB);231 msg[1].len = 6;232 rcv_buf = 0;233 ret = i2c_transfer(client->adapter, msg, 2);234 if (ret < 0) {235 dev_err(&client->dev, "Unable to read device serial number");236 return ret;237 }238 239 rcv_val = be64_to_cpu(rcv_buf) >> 16;240 dev_dbg(&client->dev, "Serial MSB raw : %llx\n", rcv_val);241 242 for (i = 0; i < 48; i += 24) {243 if (!ms_sensors_crc_valid((rcv_val >> i) & 0xFFFFFF))244 return -ENODEV;245 }246 247 *sn |= (rcv_val & 0xFFFF00) << 40 | (rcv_val >> 32);248 249 return 0;250}251EXPORT_SYMBOL_NS(ms_sensors_read_serial, IIO_MEAS_SPEC_SENSORS);252 253static int ms_sensors_read_config_reg(struct i2c_client *client,254 u8 *config_reg)255{256 int ret;257 258 ret = i2c_smbus_write_byte(client, MS_SENSORS_CONFIG_REG_READ);259 if (ret) {260 dev_err(&client->dev, "Unable to read config register");261 return ret;262 }263 264 ret = i2c_master_recv(client, config_reg, 1);265 if (ret < 0) {266 dev_err(&client->dev, "Unable to read config register");267 return ret;268 }269 dev_dbg(&client->dev, "Config register :%x\n", *config_reg);270 271 return 0;272}273 274/**275 * ms_sensors_write_resolution() - Set resolution function276 * @dev_data: pointer to temperature/humidity device data277 * @i: resolution index to set278 *279 * This function will program the appropriate resolution based on the index280 * provided when user space will set samp_freq channel.281 * This function is used for TSYS02D, HTU21 and MS8607 chipsets.282 *283 * Return: 0 on success, negative errno otherwise.284 */285ssize_t ms_sensors_write_resolution(struct ms_ht_dev *dev_data,286 u8 i)287{288 u8 config_reg;289 int ret;290 291 ret = ms_sensors_read_config_reg(dev_data->client, &config_reg);292 if (ret)293 return ret;294 295 config_reg &= 0x7E;296 config_reg |= ((i & 1) << 7) + ((i & 2) >> 1);297 298 return i2c_smbus_write_byte_data(dev_data->client,299 MS_SENSORS_CONFIG_REG_WRITE,300 config_reg);301}302EXPORT_SYMBOL_NS(ms_sensors_write_resolution, IIO_MEAS_SPEC_SENSORS);303 304/**305 * ms_sensors_show_battery_low() - Show device battery low indicator306 * @dev_data: pointer to temperature/humidity device data307 * @buf: pointer to char buffer to write result308 *309 * This function will read battery indicator value in the device and310 * return 1 if the device voltage is below 2.25V.311 * This function is used for TSYS02D, HTU21 and MS8607 chipsets.312 *313 * Return: length of sprintf on success, negative errno otherwise.314 */315ssize_t ms_sensors_show_battery_low(struct ms_ht_dev *dev_data,316 char *buf)317{318 int ret;319 u8 config_reg;320 321 mutex_lock(&dev_data->lock);322 ret = ms_sensors_read_config_reg(dev_data->client, &config_reg);323 mutex_unlock(&dev_data->lock);324 if (ret)325 return ret;326 327 return sysfs_emit(buf, "%d\n", (config_reg & 0x40) >> 6);328}329EXPORT_SYMBOL_NS(ms_sensors_show_battery_low, IIO_MEAS_SPEC_SENSORS);330 331/**332 * ms_sensors_show_heater() - Show device heater333 * @dev_data: pointer to temperature/humidity device data334 * @buf: pointer to char buffer to write result335 *336 * This function will read heater enable value in the device and337 * return 1 if the heater is enabled.338 * This function is used for HTU21 and MS8607 chipsets.339 *340 * Return: length of sprintf on success, negative errno otherwise.341 */342ssize_t ms_sensors_show_heater(struct ms_ht_dev *dev_data,343 char *buf)344{345 u8 config_reg;346 int ret;347 348 mutex_lock(&dev_data->lock);349 ret = ms_sensors_read_config_reg(dev_data->client, &config_reg);350 mutex_unlock(&dev_data->lock);351 if (ret)352 return ret;353 354 return sysfs_emit(buf, "%d\n", (config_reg & 0x4) >> 2);355}356EXPORT_SYMBOL_NS(ms_sensors_show_heater, IIO_MEAS_SPEC_SENSORS);357 358/**359 * ms_sensors_write_heater() - Write device heater360 * @dev_data: pointer to temperature/humidity device data361 * @buf: pointer to char buffer from user space362 * @len: length of buf363 *364 * This function will write 1 or 0 value in the device365 * to enable or disable heater.366 * This function is used for HTU21 and MS8607 chipsets.367 *368 * Return: length of buffer, negative errno otherwise.369 */370ssize_t ms_sensors_write_heater(struct ms_ht_dev *dev_data,371 const char *buf, size_t len)372{373 u8 val, config_reg;374 int ret;375 376 ret = kstrtou8(buf, 10, &val);377 if (ret)378 return ret;379 380 if (val > 1)381 return -EINVAL;382 383 mutex_lock(&dev_data->lock);384 ret = ms_sensors_read_config_reg(dev_data->client, &config_reg);385 if (ret) {386 mutex_unlock(&dev_data->lock);387 return ret;388 }389 390 config_reg &= 0xFB;391 config_reg |= val << 2;392 393 ret = i2c_smbus_write_byte_data(dev_data->client,394 MS_SENSORS_CONFIG_REG_WRITE,395 config_reg);396 mutex_unlock(&dev_data->lock);397 if (ret) {398 dev_err(&dev_data->client->dev, "Unable to write config register\n");399 return ret;400 }401 402 return len;403}404EXPORT_SYMBOL_NS(ms_sensors_write_heater, IIO_MEAS_SPEC_SENSORS);405 406/**407 * ms_sensors_ht_read_temperature() - Read temperature408 * @dev_data: pointer to temperature/humidity device data409 * @temperature:pointer to temperature destination value410 *411 * This function will get temperature ADC value from the device,412 * check the CRC and compute the temperature value.413 * This function is used for TSYS02D, HTU21 and MS8607 chipsets.414 *415 * Return: 0 on success, negative errno otherwise.416 */417int ms_sensors_ht_read_temperature(struct ms_ht_dev *dev_data,418 s32 *temperature)419{420 int ret;421 u32 adc;422 u16 delay;423 424 mutex_lock(&dev_data->lock);425 delay = ms_sensors_ht_t_conversion_time[dev_data->res_index];426 ret = ms_sensors_convert_and_read(dev_data->client,427 MS_SENSORS_HT_T_CONVERSION_START,428 MS_SENSORS_NO_READ_CMD,429 delay, &adc);430 mutex_unlock(&dev_data->lock);431 if (ret)432 return ret;433 434 if (!ms_sensors_crc_valid(adc)) {435 dev_err(&dev_data->client->dev,436 "Temperature read crc check error\n");437 return -ENODEV;438 }439 440 /* Temperature algorithm */441 *temperature = (((s64)(adc >> 8) * 175720) >> 16) - 46850;442 443 return 0;444}445EXPORT_SYMBOL_NS(ms_sensors_ht_read_temperature, IIO_MEAS_SPEC_SENSORS);446 447/**448 * ms_sensors_ht_read_humidity() - Read humidity449 * @dev_data: pointer to temperature/humidity device data450 * @humidity: pointer to humidity destination value451 *452 * This function will get humidity ADC value from the device,453 * check the CRC and compute the temperature value.454 * This function is used for HTU21 and MS8607 chipsets.455 *456 * Return: 0 on success, negative errno otherwise.457 */458int ms_sensors_ht_read_humidity(struct ms_ht_dev *dev_data,459 u32 *humidity)460{461 int ret;462 u32 adc;463 u16 delay;464 465 mutex_lock(&dev_data->lock);466 delay = ms_sensors_ht_h_conversion_time[dev_data->res_index];467 ret = ms_sensors_convert_and_read(dev_data->client,468 MS_SENSORS_HT_H_CONVERSION_START,469 MS_SENSORS_NO_READ_CMD,470 delay, &adc);471 mutex_unlock(&dev_data->lock);472 if (ret)473 return ret;474 475 if (!ms_sensors_crc_valid(adc)) {476 dev_err(&dev_data->client->dev,477 "Humidity read crc check error\n");478 return -ENODEV;479 }480 481 /* Humidity algorithm */482 *humidity = (((s32)(adc >> 8) * 12500) >> 16) * 10 - 6000;483 if (*humidity >= 100000)484 *humidity = 100000;485 486 return 0;487}488EXPORT_SYMBOL_NS(ms_sensors_ht_read_humidity, IIO_MEAS_SPEC_SENSORS);489 490/**491 * ms_sensors_tp_crc4() - Calculate PROM CRC for492 * Temperature and pressure devices.493 * This function is only used when reading PROM coefficients494 *495 * @prom: pointer to PROM coefficients array496 *497 * Return: CRC.498 */499static u8 ms_sensors_tp_crc4(u16 *prom)500{501 unsigned int cnt, n_bit;502 u16 n_rem = 0x0000;503 504 for (cnt = 0; cnt < MS_SENSORS_TP_PROM_WORDS_NB * 2; cnt++) {505 if (cnt % 2 == 1)506 n_rem ^= prom[cnt >> 1] & 0x00FF;507 else508 n_rem ^= prom[cnt >> 1] >> 8;509 510 for (n_bit = 8; n_bit > 0; n_bit--) {511 if (n_rem & 0x8000)512 n_rem = (n_rem << 1) ^ 0x3000;513 else514 n_rem <<= 1;515 }516 }517 518 return n_rem >> 12;519}520 521/**522 * ms_sensors_tp_crc_valid_112() - CRC check function for523 * Temperature and pressure devices for 112bit PROM.524 * This function is only used when reading PROM coefficients525 *526 * @prom: pointer to PROM coefficients array527 *528 * Return: True if CRC is ok.529 */530static bool ms_sensors_tp_crc_valid_112(u16 *prom)531{532 u16 w0 = prom[0], crc_read = (w0 & 0xF000) >> 12;533 u8 crc;534 535 prom[0] &= 0x0FFF; /* Clear the CRC computation part */536 prom[MS_SENSORS_TP_PROM_WORDS_NB - 1] = 0;537 538 crc = ms_sensors_tp_crc4(prom);539 540 prom[0] = w0;541 542 return crc == crc_read;543}544 545/**546 * ms_sensors_tp_crc_valid_128() - CRC check function for547 * Temperature and pressure devices for 128bit PROM.548 * This function is only used when reading PROM coefficients549 *550 * @prom: pointer to PROM coefficients array551 *552 * Return: True if CRC is ok.553 */554static bool ms_sensors_tp_crc_valid_128(u16 *prom)555{556 u16 w7 = prom[7], crc_read = w7 & 0x000F;557 u8 crc;558 559 prom[7] &= 0xFF00; /* Clear the CRC and LSB part */560 561 crc = ms_sensors_tp_crc4(prom);562 563 prom[7] = w7;564 565 return crc == crc_read;566}567 568/**569 * ms_sensors_tp_read_prom() - prom coeff read function570 * @dev_data: pointer to temperature/pressure device data571 *572 * This function will read prom coefficients and check CRC.573 * This function is used for MS5637 and MS8607 chipsets.574 *575 * Return: 0 on success, negative errno otherwise.576 */577int ms_sensors_tp_read_prom(struct ms_tp_dev *dev_data)578{579 int i, ret;580 bool valid;581 582 for (i = 0; i < dev_data->hw->prom_len; i++) {583 ret = ms_sensors_read_prom_word(584 dev_data->client,585 MS_SENSORS_TP_PROM_READ + (i << 1),586 &dev_data->prom[i]);587 588 if (ret)589 return ret;590 }591 592 if (dev_data->hw->prom_len == 8)593 valid = ms_sensors_tp_crc_valid_128(dev_data->prom);594 else595 valid = ms_sensors_tp_crc_valid_112(dev_data->prom);596 597 if (!valid) {598 dev_err(&dev_data->client->dev,599 "Calibration coefficients crc check error\n");600 return -ENODEV;601 }602 603 return 0;604}605EXPORT_SYMBOL_NS(ms_sensors_tp_read_prom, IIO_MEAS_SPEC_SENSORS);606 607/**608 * ms_sensors_read_temp_and_pressure() - read temp and pressure609 * @dev_data: pointer to temperature/pressure device data610 * @temperature:pointer to temperature destination value611 * @pressure: pointer to pressure destination value612 *613 * This function will read ADC and compute pressure and temperature value.614 * This function is used for MS5637 and MS8607 chipsets.615 *616 * Return: 0 on success, negative errno otherwise.617 */618int ms_sensors_read_temp_and_pressure(struct ms_tp_dev *dev_data,619 int *temperature,620 unsigned int *pressure)621{622 int ret;623 u32 t_adc, p_adc;624 s32 dt, temp;625 s64 off, sens, t2, off2, sens2;626 u16 *prom = dev_data->prom, delay;627 628 mutex_lock(&dev_data->lock);629 delay = ms_sensors_tp_conversion_time[dev_data->res_index];630 631 ret = ms_sensors_convert_and_read(632 dev_data->client,633 MS_SENSORS_TP_T_CONVERSION_START +634 dev_data->res_index * 2,635 MS_SENSORS_TP_ADC_READ,636 delay, &t_adc);637 if (ret) {638 mutex_unlock(&dev_data->lock);639 return ret;640 }641 642 ret = ms_sensors_convert_and_read(643 dev_data->client,644 MS_SENSORS_TP_P_CONVERSION_START +645 dev_data->res_index * 2,646 MS_SENSORS_TP_ADC_READ,647 delay, &p_adc);648 mutex_unlock(&dev_data->lock);649 if (ret)650 return ret;651 652 dt = (s32)t_adc - (prom[5] << 8);653 654 /* Actual temperature = 2000 + dT * TEMPSENS */655 temp = 2000 + (((s64)dt * prom[6]) >> 23);656 657 /* Second order temperature compensation */658 if (temp < 2000) {659 s64 tmp = (s64)temp - 2000;660 661 t2 = (3 * ((s64)dt * (s64)dt)) >> 33;662 off2 = (61 * tmp * tmp) >> 4;663 sens2 = (29 * tmp * tmp) >> 4;664 665 if (temp < -1500) {666 s64 tmp = (s64)temp + 1500;667 668 off2 += 17 * tmp * tmp;669 sens2 += 9 * tmp * tmp;670 }671 } else {672 t2 = (5 * ((s64)dt * (s64)dt)) >> 38;673 off2 = 0;674 sens2 = 0;675 }676 677 /* OFF = OFF_T1 + TCO * dT */678 off = (((s64)prom[2]) << 17) + ((((s64)prom[4]) * (s64)dt) >> 6);679 off -= off2;680 681 /* Sensitivity at actual temperature = SENS_T1 + TCS * dT */682 sens = (((s64)prom[1]) << 16) + (((s64)prom[3] * dt) >> 7);683 sens -= sens2;684 685 /* Temperature compensated pressure = D1 * SENS - OFF */686 *temperature = (temp - t2) * 10;687 *pressure = (u32)(((((s64)p_adc * sens) >> 21) - off) >> 15);688 689 return 0;690}691EXPORT_SYMBOL_NS(ms_sensors_read_temp_and_pressure, IIO_MEAS_SPEC_SENSORS);692 693MODULE_DESCRIPTION("Measurement-Specialties common i2c driver");694MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>");695MODULE_AUTHOR("Ludovic Tancerel <ludovic.tancerel@maplehightech.com>");696MODULE_LICENSE("GPL v2");697 698