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 
w1_ds2438_get_page(struct w1_slave * sl,int pageno,u8 * buf)52 static 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] = 0x00;
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] = 0x00;
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 
w1_ds2438_get_temperature(struct w1_slave * sl,int16_t * temperature)86 static 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 
124 post_unlock:
125 	return ret;
126 }
127 
w1_ds2438_change_config_bit(struct w1_slave * sl,u8 mask,u8 value)128 static 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 
w1_ds2438_get_voltage(struct w1_slave * sl,int adc_input,uint16_t * voltage)187 static 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 
229 pre_unlock:
230 	mutex_unlock(&sl->master->bus_mutex);
231 
232 post_unlock:
233 	return ret;
234 }
235 
w1_ds2438_get_current(struct w1_slave * sl,int16_t * voltage)236 static 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 
iad_write(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)255 static 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 
iad_read(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)277 static 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 
page0_read(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)298 static 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 
temperature_read(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)328 static 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 
vad_read(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)349 static 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 
vdd_read(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)370 static 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 
391 static BIN_ATTR(iad, S_IRUGO | S_IWUSR | S_IWGRP, iad_read, iad_write, 0);
392 static BIN_ATTR_RO(page0, DS2438_PAGE_SIZE);
393 static BIN_ATTR_RO(temperature, 0/* real length varies */);
394 static BIN_ATTR_RO(vad, 0/* real length varies */);
395 static BIN_ATTR_RO(vdd, 0/* real length varies */);
396 
397 static 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 
406 static const struct attribute_group w1_ds2438_group = {
407 	.bin_attrs = w1_ds2438_bin_attrs,
408 };
409 
410 static const struct attribute_group *w1_ds2438_groups[] = {
411 	&w1_ds2438_group,
412 	NULL,
413 };
414 
415 static const struct w1_family_ops w1_ds2438_fops = {
416 	.groups		= w1_ds2438_groups,
417 };
418 
419 static struct w1_family w1_ds2438_family = {
420 	.fid = W1_FAMILY_DS2438,
421 	.fops = &w1_ds2438_fops,
422 };
423 module_w1_family(w1_ds2438_family);
424 
425 MODULE_LICENSE("GPL");
426 MODULE_AUTHOR("Mariusz Bialonczyk <manio@skyboo.net>");
427 MODULE_DESCRIPTION("1-wire driver for Maxim/Dallas DS2438 Smart Battery Monitor");
428 MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2438));
429