brintos

brintos / linux-shallow public Read only

0
0
Text · 11.6 KiB · e008c27 Raw
517 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * 1-Wire implementation for the ds2438 chip4 *5 * Copyright (c) 2017 Mariusz Bialonczyk <manio@skyboo.net>6 */7 8#include <linux/kernel.h>9#include <linux/module.h>10#include <linux/device.h>11#include <linux/types.h>12#include <linux/delay.h>13 14#include <linux/w1.h>15 16#define W1_FAMILY_DS2438		0x2617 18#define W1_DS2438_RETRIES		319 20/* Memory commands */21#define W1_DS2438_READ_SCRATCH		0xBE22#define W1_DS2438_WRITE_SCRATCH		0x4E23#define W1_DS2438_COPY_SCRATCH		0x4824#define W1_DS2438_RECALL_MEMORY		0xB825/* Register commands */26#define W1_DS2438_CONVERT_TEMP		0x4427#define W1_DS2438_CONVERT_VOLTAGE	0xB428 29#define DS2438_PAGE_SIZE		830#define DS2438_ADC_INPUT_VAD		031#define DS2438_ADC_INPUT_VDD		132#define DS2438_MAX_CONVERSION_TIME	10		/* ms */33 34/* Page #0 definitions */35#define DS2438_STATUS_REG		0x00		/* Status/Configuration Register */36#define DS2438_STATUS_IAD		(1 << 0)	/* Current A/D Control Bit */37#define DS2438_STATUS_CA		(1 << 1)	/* Current Accumulator Configuration */38#define DS2438_STATUS_EE		(1 << 2)	/* Current Accumulator Shadow Selector bit */39#define DS2438_STATUS_AD		(1 << 3)	/* Voltage A/D Input Select Bit */40#define DS2438_STATUS_TB		(1 << 4)	/* Temperature Busy Flag */41#define DS2438_STATUS_NVB		(1 << 5)	/* Nonvolatile Memory Busy Flag */42#define DS2438_STATUS_ADB		(1 << 6)	/* A/D Converter Busy Flag */43 44#define DS2438_TEMP_LSB			0x0145#define DS2438_TEMP_MSB			0x0246#define DS2438_VOLTAGE_LSB		0x0347#define DS2438_VOLTAGE_MSB		0x0448#define DS2438_CURRENT_LSB		0x0549#define DS2438_CURRENT_MSB		0x0650#define DS2438_THRESHOLD		0x0751 52/* Page #1 definitions */53#define DS2438_ETM_0			0x0054#define DS2438_ETM_1			0x0155#define DS2438_ETM_2			0x0256#define DS2438_ETM_3			0x0357#define DS2438_ICA			0x0458#define DS2438_OFFSET_LSB		0x0559#define DS2438_OFFSET_MSB		0x0660 61static int w1_ds2438_get_page(struct w1_slave *sl, int pageno, u8 *buf)62{63	unsigned int retries = W1_DS2438_RETRIES;64	u8 w1_buf[2];65	u8 crc;66	size_t count;67 68	while (retries--) {69		if (w1_reset_select_slave(sl))70			continue;71		w1_buf[0] = W1_DS2438_RECALL_MEMORY;72		w1_buf[1] = (u8)pageno;73		w1_write_block(sl->master, w1_buf, 2);74 75		if (w1_reset_select_slave(sl))76			continue;77		w1_buf[0] = W1_DS2438_READ_SCRATCH;78		w1_buf[1] = (u8)pageno;79		w1_write_block(sl->master, w1_buf, 2);80 81		count = w1_read_block(sl->master, buf, DS2438_PAGE_SIZE + 1);82		if (count == DS2438_PAGE_SIZE + 1) {83			crc = w1_calc_crc8(buf, DS2438_PAGE_SIZE);84 85			/* check for correct CRC */86			if ((u8)buf[DS2438_PAGE_SIZE] == crc)87				return 0;88		}89	}90	return -1;91}92 93static int w1_ds2438_get_temperature(struct w1_slave *sl, int16_t *temperature)94{95	unsigned int retries = W1_DS2438_RETRIES;96	u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];97	unsigned int tm = DS2438_MAX_CONVERSION_TIME;98	unsigned long sleep_rem;99	int ret;100 101	mutex_lock(&sl->master->bus_mutex);102 103	while (retries--) {104		if (w1_reset_select_slave(sl))105			continue;106		w1_write_8(sl->master, W1_DS2438_CONVERT_TEMP);107 108		mutex_unlock(&sl->master->bus_mutex);109		sleep_rem = msleep_interruptible(tm);110		if (sleep_rem != 0) {111			ret = -1;112			goto post_unlock;113		}114 115		if (mutex_lock_interruptible(&sl->master->bus_mutex) != 0) {116			ret = -1;117			goto post_unlock;118		}119 120		break;121	}122 123	if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) {124		*temperature = (((int16_t) w1_buf[DS2438_TEMP_MSB]) << 8) | ((uint16_t) w1_buf[DS2438_TEMP_LSB]);125		ret = 0;126	} else127		ret = -1;128 129	mutex_unlock(&sl->master->bus_mutex);130 131post_unlock:132	return ret;133}134 135static int w1_ds2438_change_config_bit(struct w1_slave *sl, u8 mask, u8 value)136{137	unsigned int retries = W1_DS2438_RETRIES;138	u8 w1_buf[3];139	u8 status;140	int perform_write = 0;141 142	while (retries--) {143		if (w1_reset_select_slave(sl))144			continue;145		w1_buf[0] = W1_DS2438_RECALL_MEMORY;146		w1_buf[1] = 0x00;147		w1_write_block(sl->master, w1_buf, 2);148 149		if (w1_reset_select_slave(sl))150			continue;151		w1_buf[0] = W1_DS2438_READ_SCRATCH;152		w1_buf[1] = 0x00;153		w1_write_block(sl->master, w1_buf, 2);154 155		/* reading one byte of result */156		status = w1_read_8(sl->master);157 158		/* if bit0=1, set a value to a mask for easy compare */159		if (value)160			value = mask;161 162		if ((status & mask) == value)163			return 0;	/* already set as requested */164 165		/* changing bit */166		status ^= mask;167		perform_write = 1;168 169		break;170	}171 172	if (perform_write) {173		retries = W1_DS2438_RETRIES;174		while (retries--) {175			if (w1_reset_select_slave(sl))176				continue;177			w1_buf[0] = W1_DS2438_WRITE_SCRATCH;178			w1_buf[1] = 0x00;179			w1_buf[2] = status;180			w1_write_block(sl->master, w1_buf, 3);181 182			if (w1_reset_select_slave(sl))183				continue;184			w1_buf[0] = W1_DS2438_COPY_SCRATCH;185			w1_buf[1] = 0x00;186			w1_write_block(sl->master, w1_buf, 2);187 188			return 0;189		}190	}191	return -1;192}193 194static int w1_ds2438_change_offset_register(struct w1_slave *sl, u8 *value)195{196	unsigned int retries = W1_DS2438_RETRIES;197	u8 w1_buf[9];198	u8 w1_page1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];199 200	if (w1_ds2438_get_page(sl, 1, w1_page1_buf) == 0) {201		memcpy(&w1_buf[2], w1_page1_buf, DS2438_PAGE_SIZE - 1); /* last register reserved */202		w1_buf[7] = value[0]; /* change only offset register */203		w1_buf[8] = value[1];204		while (retries--) {205			if (w1_reset_select_slave(sl))206				continue;207			w1_buf[0] = W1_DS2438_WRITE_SCRATCH;208			w1_buf[1] = 0x01; /* write to page 1 */209			w1_write_block(sl->master, w1_buf, 9);210 211			if (w1_reset_select_slave(sl))212				continue;213			w1_buf[0] = W1_DS2438_COPY_SCRATCH;214			w1_buf[1] = 0x01;215			w1_write_block(sl->master, w1_buf, 2);216			return 0;217		}218	}219	return -1;220}221 222static int w1_ds2438_get_voltage(struct w1_slave *sl,223				 int adc_input, uint16_t *voltage)224{225	unsigned int retries = W1_DS2438_RETRIES;226	u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];227	unsigned int tm = DS2438_MAX_CONVERSION_TIME;228	unsigned long sleep_rem;229	int ret;230 231	mutex_lock(&sl->master->bus_mutex);232 233	if (w1_ds2438_change_config_bit(sl, DS2438_STATUS_AD, adc_input)) {234		ret = -1;235		goto pre_unlock;236	}237 238	while (retries--) {239		if (w1_reset_select_slave(sl))240			continue;241		w1_write_8(sl->master, W1_DS2438_CONVERT_VOLTAGE);242 243		mutex_unlock(&sl->master->bus_mutex);244		sleep_rem = msleep_interruptible(tm);245		if (sleep_rem != 0) {246			ret = -1;247			goto post_unlock;248		}249 250		if (mutex_lock_interruptible(&sl->master->bus_mutex) != 0) {251			ret = -1;252			goto post_unlock;253		}254 255		break;256	}257 258	if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) {259		*voltage = (((uint16_t) w1_buf[DS2438_VOLTAGE_MSB]) << 8) | ((uint16_t) w1_buf[DS2438_VOLTAGE_LSB]);260		ret = 0;261	} else262		ret = -1;263 264pre_unlock:265	mutex_unlock(&sl->master->bus_mutex);266 267post_unlock:268	return ret;269}270 271static int w1_ds2438_get_current(struct w1_slave *sl, int16_t *voltage)272{273	u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];274	int ret;275 276	mutex_lock(&sl->master->bus_mutex);277 278	if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) {279		/* The voltage measured across current sense resistor RSENS. */280		*voltage = (((int16_t) w1_buf[DS2438_CURRENT_MSB]) << 8) | ((int16_t) w1_buf[DS2438_CURRENT_LSB]);281		ret = 0;282	} else283		ret = -1;284 285	mutex_unlock(&sl->master->bus_mutex);286 287	return ret;288}289 290static ssize_t iad_write(struct file *filp, struct kobject *kobj,291			 struct bin_attribute *bin_attr, char *buf,292			 loff_t off, size_t count)293{294	struct w1_slave *sl = kobj_to_w1_slave(kobj);295	int ret;296 297	if (count != 1 || off != 0)298		return -EFAULT;299 300	mutex_lock(&sl->master->bus_mutex);301 302	if (w1_ds2438_change_config_bit(sl, DS2438_STATUS_IAD, *buf & 0x01) == 0)303		ret = 1;304	else305		ret = -EIO;306 307	mutex_unlock(&sl->master->bus_mutex);308 309	return ret;310}311 312static ssize_t iad_read(struct file *filp, struct kobject *kobj,313			struct bin_attribute *bin_attr, char *buf,314			loff_t off, size_t count)315{316	struct w1_slave *sl = kobj_to_w1_slave(kobj);317	int ret;318	int16_t voltage;319 320	if (off != 0)321		return 0;322	if (!buf)323		return -EINVAL;324 325	if (w1_ds2438_get_current(sl, &voltage) == 0)326		ret = snprintf(buf, count, "%i\n", voltage);327	else328		ret = -EIO;329 330	return ret;331}332 333static ssize_t page0_read(struct file *filp, struct kobject *kobj,334			  struct bin_attribute *bin_attr, char *buf,335			  loff_t off, size_t count)336{337	struct w1_slave *sl = kobj_to_w1_slave(kobj);338	int ret;339	u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];340 341	if (off != 0)342		return 0;343	if (!buf)344		return -EINVAL;345 346	mutex_lock(&sl->master->bus_mutex);347 348	/* Read no more than page0 size */349	if (count > DS2438_PAGE_SIZE)350		count = DS2438_PAGE_SIZE;351 352	if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) {353		memcpy(buf, &w1_buf, count);354		ret = count;355	} else356		ret = -EIO;357 358	mutex_unlock(&sl->master->bus_mutex);359 360	return ret;361}362 363static ssize_t page1_read(struct file *filp, struct kobject *kobj,364			  struct bin_attribute *bin_attr, char *buf,365			  loff_t off, size_t count)366{367	struct w1_slave *sl = kobj_to_w1_slave(kobj);368	int ret;369	u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];370 371	if (off != 0)372		return 0;373	if (!buf)374		return -EINVAL;375 376	mutex_lock(&sl->master->bus_mutex);377 378	/* Read no more than page1 size */379	if (count > DS2438_PAGE_SIZE)380		count = DS2438_PAGE_SIZE;381 382	if (w1_ds2438_get_page(sl, 1, w1_buf) == 0) {383		memcpy(buf, &w1_buf, count);384		ret = count;385	} else386		ret = -EIO;387 388	mutex_unlock(&sl->master->bus_mutex);389 390	return ret;391}392 393static ssize_t offset_write(struct file *filp, struct kobject *kobj,394			    struct bin_attribute *bin_attr, char *buf,395			    loff_t off, size_t count)396{397	struct w1_slave *sl = kobj_to_w1_slave(kobj);398	int ret;399 400	mutex_lock(&sl->master->bus_mutex);401 402	if (w1_ds2438_change_offset_register(sl, buf) == 0)403		ret = count;404	else405		ret = -EIO;406 407	mutex_unlock(&sl->master->bus_mutex);408 409	return ret;410}411 412static ssize_t temperature_read(struct file *filp, struct kobject *kobj,413				struct bin_attribute *bin_attr, char *buf,414				loff_t off, size_t count)415{416	struct w1_slave *sl = kobj_to_w1_slave(kobj);417	int ret;418	int16_t temp;419 420	if (off != 0)421		return 0;422	if (!buf)423		return -EINVAL;424 425	if (w1_ds2438_get_temperature(sl, &temp) == 0)426		ret = snprintf(buf, count, "%i\n", temp);427	else428		ret = -EIO;429 430	return ret;431}432 433static ssize_t vad_read(struct file *filp, struct kobject *kobj,434			struct bin_attribute *bin_attr, char *buf,435			loff_t off, size_t count)436{437	struct w1_slave *sl = kobj_to_w1_slave(kobj);438	int ret;439	uint16_t voltage;440 441	if (off != 0)442		return 0;443	if (!buf)444		return -EINVAL;445 446	if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VAD, &voltage) == 0)447		ret = snprintf(buf, count, "%u\n", voltage);448	else449		ret = -EIO;450 451	return ret;452}453 454static ssize_t vdd_read(struct file *filp, struct kobject *kobj,455			struct bin_attribute *bin_attr, char *buf,456			loff_t off, size_t count)457{458	struct w1_slave *sl = kobj_to_w1_slave(kobj);459	int ret;460	uint16_t voltage;461 462	if (off != 0)463		return 0;464	if (!buf)465		return -EINVAL;466 467	if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VDD, &voltage) == 0)468		ret = snprintf(buf, count, "%u\n", voltage);469	else470		ret = -EIO;471 472	return ret;473}474 475static BIN_ATTR_RW(iad, 0);476static BIN_ATTR_RO(page0, DS2438_PAGE_SIZE);477static BIN_ATTR_RO(page1, DS2438_PAGE_SIZE);478static BIN_ATTR_WO(offset, 2);479static BIN_ATTR_RO(temperature, 0/* real length varies */);480static BIN_ATTR_RO(vad, 0/* real length varies */);481static BIN_ATTR_RO(vdd, 0/* real length varies */);482 483static struct bin_attribute *w1_ds2438_bin_attrs[] = {484	&bin_attr_iad,485	&bin_attr_page0,486	&bin_attr_page1,487	&bin_attr_offset,488	&bin_attr_temperature,489	&bin_attr_vad,490	&bin_attr_vdd,491	NULL,492};493 494static const struct attribute_group w1_ds2438_group = {495	.bin_attrs = w1_ds2438_bin_attrs,496};497 498static const struct attribute_group *w1_ds2438_groups[] = {499	&w1_ds2438_group,500	NULL,501};502 503static const struct w1_family_ops w1_ds2438_fops = {504	.groups		= w1_ds2438_groups,505};506 507static struct w1_family w1_ds2438_family = {508	.fid = W1_FAMILY_DS2438,509	.fops = &w1_ds2438_fops,510};511module_w1_family(w1_ds2438_family);512 513MODULE_LICENSE("GPL");514MODULE_AUTHOR("Mariusz Bialonczyk <manio@skyboo.net>");515MODULE_DESCRIPTION("1-wire driver for Maxim/Dallas DS2438 Smart Battery Monitor");516MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2438));517