1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * 1-Wire implementation for the ds2438 chip
4 *
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		0x26
17
18#define W1_DS2438_RETRIES		3
19
20/* Memory commands */
21#define W1_DS2438_READ_SCRATCH		0xBE
22#define W1_DS2438_WRITE_SCRATCH		0x4E
23#define W1_DS2438_COPY_SCRATCH		0x48
24#define W1_DS2438_RECALL_MEMORY		0xB8
25/* Register commands */
26#define W1_DS2438_CONVERT_TEMP		0x44
27#define W1_DS2438_CONVERT_VOLTAGE	0xB4
28
29#define DS2438_PAGE_SIZE		8
30#define DS2438_ADC_INPUT_VAD		0
31#define DS2438_ADC_INPUT_VDD		1
32#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			0x01
45#define DS2438_TEMP_MSB			0x02
46#define DS2438_VOLTAGE_LSB		0x03
47#define DS2438_VOLTAGE_MSB		0x04
48#define DS2438_CURRENT_LSB		0x05
49#define DS2438_CURRENT_MSB		0x06
50#define DS2438_THRESHOLD		0x07
51
52static int w1_ds2438_get_page(struct w1_slave *sl, int pageno, u8 *buf)
53{
54	unsigned int retries = W1_DS2438_RETRIES;
55	u8 w1_buf[2];
56	u8 crc;
57	size_t count;
58
59	while (retries--) {
60		crc = 0;
61
62		if (w1_reset_select_slave(sl))
63			continue;
64		w1_buf[0] = W1_DS2438_RECALL_MEMORY;
65		w1_buf[1] = (u8)pageno;
66		w1_write_block(sl->master, w1_buf, 2);
67
68		if (w1_reset_select_slave(sl))
69			continue;
70		w1_buf[0] = W1_DS2438_READ_SCRATCH;
71		w1_buf[1] = (u8)pageno;
72		w1_write_block(sl->master, w1_buf, 2);
73
74		count = w1_read_block(sl->master, buf, DS2438_PAGE_SIZE + 1);
75		if (count == DS2438_PAGE_SIZE + 1) {
76			crc = w1_calc_crc8(buf, DS2438_PAGE_SIZE);
77
78			/* check for correct CRC */
79			if ((u8)buf[DS2438_PAGE_SIZE] == crc)
80				return 0;
81		}
82	}
83	return -1;
84}
85
86static int w1_ds2438_get_temperature(struct w1_slave *sl, int16_t *temperature)
87{
88	unsigned int retries = W1_DS2438_RETRIES;
89	u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];
90	unsigned int tm = DS2438_MAX_CONVERSION_TIME;
91	unsigned long sleep_rem;
92	int ret;
93
94	mutex_lock(&sl->master->bus_mutex);
95
96	while (retries--) {
97		if (w1_reset_select_slave(sl))
98			continue;
99		w1_write_8(sl->master, W1_DS2438_CONVERT_TEMP);
100
101		mutex_unlock(&sl->master->bus_mutex);
102		sleep_rem = msleep_interruptible(tm);
103		if (sleep_rem != 0) {
104			ret = -1;
105			goto post_unlock;
106		}
107
108		if (mutex_lock_interruptible(&sl->master->bus_mutex) != 0) {
109			ret = -1;
110			goto post_unlock;
111		}
112
113		break;
114	}
115
116	if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) {
117		*temperature = (((int16_t) w1_buf[DS2438_TEMP_MSB]) << 8) | ((uint16_t) w1_buf[DS2438_TEMP_LSB]);
118		ret = 0;
119	} else
120		ret = -1;
121
122	mutex_unlock(&sl->master->bus_mutex);
123
124post_unlock:
125	return ret;
126}
127
128static int w1_ds2438_change_config_bit(struct w1_slave *sl, u8 mask, u8 value)
129{
130	unsigned int retries = W1_DS2438_RETRIES;
131	u8 w1_buf[3];
132	u8 status;
133	int perform_write = 0;
134
135	while (retries--) {
136		if (w1_reset_select_slave(sl))
137			continue;
138		w1_buf[0] = W1_DS2438_RECALL_MEMORY;
139		w1_buf[1] = 0x00;
140		w1_write_block(sl->master, w1_buf, 2);
141
142		if (w1_reset_select_slave(sl))
143			continue;
144		w1_buf[0] = W1_DS2438_READ_SCRATCH;
145		w1_buf[1] = 0x00;
146		w1_write_block(sl->master, w1_buf, 2);
147
148		/* reading one byte of result */
149		status = w1_read_8(sl->master);
150
151		/* if bit0=1, set a value to a mask for easy compare */
152		if (value)
153			value = mask;
154
155		if ((status & mask) == value)
156			return 0;	/* already set as requested */
157		else {
158			/* changing bit */
159			status ^= mask;
160			perform_write = 1;
161		}
162		break;
163	}
164
165	if (perform_write) {
166		retries = W1_DS2438_RETRIES;
167		while (retries--) {
168			if (w1_reset_select_slave(sl))
169				continue;
170			w1_buf[0] = W1_DS2438_WRITE_SCRATCH;
171			w1_buf[1] = 0x00;
172			w1_buf[2] = status;
173			w1_write_block(sl->master, w1_buf, 3);
174
175			if (w1_reset_select_slave(sl))
176				continue;
177			w1_buf[0] = W1_DS2438_COPY_SCRATCH;
178			w1_buf[1] = 0x00;
179			w1_write_block(sl->master, w1_buf, 2);
180
181			return 0;
182		}
183	}
184	return -1;
185}
186
187static int w1_ds2438_get_voltage(struct w1_slave *sl,
188				 int adc_input, uint16_t *voltage)
189{
190	unsigned int retries = W1_DS2438_RETRIES;
191	u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];
192	unsigned int tm = DS2438_MAX_CONVERSION_TIME;
193	unsigned long sleep_rem;
194	int ret;
195
196	mutex_lock(&sl->master->bus_mutex);
197
198	if (w1_ds2438_change_config_bit(sl, DS2438_STATUS_AD, adc_input)) {
199		ret = -1;
200		goto pre_unlock;
201	}
202
203	while (retries--) {
204		if (w1_reset_select_slave(sl))
205			continue;
206		w1_write_8(sl->master, W1_DS2438_CONVERT_VOLTAGE);
207
208		mutex_unlock(&sl->master->bus_mutex);
209		sleep_rem = msleep_interruptible(tm);
210		if (sleep_rem != 0) {
211			ret = -1;
212			goto post_unlock;
213		}
214
215		if (mutex_lock_interruptible(&sl->master->bus_mutex) != 0) {
216			ret = -1;
217			goto post_unlock;
218		}
219
220		break;
221	}
222
223	if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) {
224		*voltage = (((uint16_t) w1_buf[DS2438_VOLTAGE_MSB]) << 8) | ((uint16_t) w1_buf[DS2438_VOLTAGE_LSB]);
225		ret = 0;
226	} else
227		ret = -1;
228
229pre_unlock:
230	mutex_unlock(&sl->master->bus_mutex);
231
232post_unlock:
233	return ret;
234}
235
236static int w1_ds2438_get_current(struct w1_slave *sl, int16_t *voltage)
237{
238	u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];
239	int ret;
240
241	mutex_lock(&sl->master->bus_mutex);
242
243	if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) {
244		/* The voltage measured across current sense resistor RSENS. */
245		*voltage = (((int16_t) w1_buf[DS2438_CURRENT_MSB]) << 8) | ((int16_t) w1_buf[DS2438_CURRENT_LSB]);
246		ret = 0;
247	} else
248		ret = -1;
249
250	mutex_unlock(&sl->master->bus_mutex);
251
252	return ret;
253}
254
255static ssize_t iad_write(struct file *filp, struct kobject *kobj,
256			 struct bin_attribute *bin_attr, char *buf,
257			 loff_t off, size_t count)
258{
259	struct w1_slave *sl = kobj_to_w1_slave(kobj);
260	int ret;
261
262	if (count != 1 || off != 0)
263		return -EFAULT;
264
265	mutex_lock(&sl->master->bus_mutex);
266
267	if (w1_ds2438_change_config_bit(sl, DS2438_STATUS_IAD, *buf & 0x01) == 0)
268		ret = 1;
269	else
270		ret = -EIO;
271
272	mutex_unlock(&sl->master->bus_mutex);
273
274	return ret;
275}
276
277static ssize_t iad_read(struct file *filp, struct kobject *kobj,
278			struct bin_attribute *bin_attr, char *buf,
279			loff_t off, size_t count)
280{
281	struct w1_slave *sl = kobj_to_w1_slave(kobj);
282	int ret;
283	int16_t voltage;
284
285	if (off != 0)
286		return 0;
287	if (!buf)
288		return -EINVAL;
289
290	if (w1_ds2438_get_current(sl, &voltage) == 0) {
291		ret = snprintf(buf, count, "%i\n", voltage);
292	} else
293		ret = -EIO;
294
295	return ret;
296}
297
298static ssize_t page0_read(struct file *filp, struct kobject *kobj,
299			  struct bin_attribute *bin_attr, char *buf,
300			  loff_t off, size_t count)
301{
302	struct w1_slave *sl = kobj_to_w1_slave(kobj);
303	int ret;
304	u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];
305
306	if (off != 0)
307		return 0;
308	if (!buf)
309		return -EINVAL;
310
311	mutex_lock(&sl->master->bus_mutex);
312
313	/* Read no more than page0 size */
314	if (count > DS2438_PAGE_SIZE)
315		count = DS2438_PAGE_SIZE;
316
317	if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) {
318		memcpy(buf, &w1_buf, count);
319		ret = count;
320	} else
321		ret = -EIO;
322
323	mutex_unlock(&sl->master->bus_mutex);
324
325	return ret;
326}
327
328static ssize_t temperature_read(struct file *filp, struct kobject *kobj,
329				struct bin_attribute *bin_attr, char *buf,
330				loff_t off, size_t count)
331{
332	struct w1_slave *sl = kobj_to_w1_slave(kobj);
333	int ret;
334	int16_t temp;
335
336	if (off != 0)
337		return 0;
338	if (!buf)
339		return -EINVAL;
340
341	if (w1_ds2438_get_temperature(sl, &temp) == 0) {
342		ret = snprintf(buf, count, "%i\n", temp);
343	} else
344		ret = -EIO;
345
346	return ret;
347}
348
349static ssize_t vad_read(struct file *filp, struct kobject *kobj,
350			struct bin_attribute *bin_attr, char *buf,
351			loff_t off, size_t count)
352{
353	struct w1_slave *sl = kobj_to_w1_slave(kobj);
354	int ret;
355	uint16_t voltage;
356
357	if (off != 0)
358		return 0;
359	if (!buf)
360		return -EINVAL;
361
362	if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VAD, &voltage) == 0) {
363		ret = snprintf(buf, count, "%u\n", voltage);
364	} else
365		ret = -EIO;
366
367	return ret;
368}
369
370static ssize_t vdd_read(struct file *filp, struct kobject *kobj,
371			struct bin_attribute *bin_attr, char *buf,
372			loff_t off, size_t count)
373{
374	struct w1_slave *sl = kobj_to_w1_slave(kobj);
375	int ret;
376	uint16_t voltage;
377
378	if (off != 0)
379		return 0;
380	if (!buf)
381		return -EINVAL;
382
383	if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VDD, &voltage) == 0) {
384		ret = snprintf(buf, count, "%u\n", voltage);
385	} else
386		ret = -EIO;
387
388	return ret;
389}
390
391static BIN_ATTR(iad, S_IRUGO | S_IWUSR | S_IWGRP, iad_read, iad_write, 0);
392static BIN_ATTR_RO(page0, DS2438_PAGE_SIZE);
393static BIN_ATTR_RO(temperature, 0/* real length varies */);
394static BIN_ATTR_RO(vad, 0/* real length varies */);
395static BIN_ATTR_RO(vdd, 0/* real length varies */);
396
397static struct bin_attribute *w1_ds2438_bin_attrs[] = {
398	&bin_attr_iad,
399	&bin_attr_page0,
400	&bin_attr_temperature,
401	&bin_attr_vad,
402	&bin_attr_vdd,
403	NULL,
404};
405
406static const struct attribute_group w1_ds2438_group = {
407	.bin_attrs = w1_ds2438_bin_attrs,
408};
409
410static const struct attribute_group *w1_ds2438_groups[] = {
411	&w1_ds2438_group,
412	NULL,
413};
414
415static const struct w1_family_ops w1_ds2438_fops = {
416	.groups		= w1_ds2438_groups,
417};
418
419static struct w1_family w1_ds2438_family = {
420	.fid = W1_FAMILY_DS2438,
421	.fops = &w1_ds2438_fops,
422};
423module_w1_family(w1_ds2438_family);
424
425MODULE_LICENSE("GPL");
426MODULE_AUTHOR("Mariusz Bialonczyk <manio@skyboo.net>");
427MODULE_DESCRIPTION("1-wire driver for Maxim/Dallas DS2438 Smart Battery Monitor");
428MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2438));
429