brintos

brintos / linux-shallow public Read only

0
0
Text · 18.0 KiB · 9c9bc77 Raw
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