1 // SPDX-License-Identifier: GPL-2.0-only
3 * 1-Wire implementation for the ds2438 chip
5 * Copyright (c) 2017 Mariusz Bialonczyk <manio@skyboo.net>
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>
16 #define W1_FAMILY_DS2438 0x26
18 #define W1_DS2438_RETRIES 3
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
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 */
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 */
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
52 /* Page #1 definitions */
53 #define DS2438_ETM_0 0x00
54 #define DS2438_ETM_1 0x01
55 #define DS2438_ETM_2 0x02
56 #define DS2438_ETM_3 0x03
57 #define DS2438_ICA 0x04
58 #define DS2438_OFFSET_LSB 0x05
59 #define DS2438_OFFSET_MSB 0x06
61 static int w1_ds2438_get_page(struct w1_slave
*sl
, int pageno
, u8
*buf
)
63 unsigned int retries
= W1_DS2438_RETRIES
;
69 if (w1_reset_select_slave(sl
))
71 w1_buf
[0] = W1_DS2438_RECALL_MEMORY
;
72 w1_buf
[1] = (u8
)pageno
;
73 w1_write_block(sl
->master
, w1_buf
, 2);
75 if (w1_reset_select_slave(sl
))
77 w1_buf
[0] = W1_DS2438_READ_SCRATCH
;
78 w1_buf
[1] = (u8
)pageno
;
79 w1_write_block(sl
->master
, w1_buf
, 2);
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
);
85 /* check for correct CRC */
86 if ((u8
)buf
[DS2438_PAGE_SIZE
] == crc
)
93 static int w1_ds2438_get_temperature(struct w1_slave
*sl
, int16_t *temperature
)
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
;
101 mutex_lock(&sl
->master
->bus_mutex
);
104 if (w1_reset_select_slave(sl
))
106 w1_write_8(sl
->master
, W1_DS2438_CONVERT_TEMP
);
108 mutex_unlock(&sl
->master
->bus_mutex
);
109 sleep_rem
= msleep_interruptible(tm
);
110 if (sleep_rem
!= 0) {
115 if (mutex_lock_interruptible(&sl
->master
->bus_mutex
) != 0) {
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
]);
129 mutex_unlock(&sl
->master
->bus_mutex
);
135 static int w1_ds2438_change_config_bit(struct w1_slave
*sl
, u8 mask
, u8 value
)
137 unsigned int retries
= W1_DS2438_RETRIES
;
140 int perform_write
= 0;
143 if (w1_reset_select_slave(sl
))
145 w1_buf
[0] = W1_DS2438_RECALL_MEMORY
;
147 w1_write_block(sl
->master
, w1_buf
, 2);
149 if (w1_reset_select_slave(sl
))
151 w1_buf
[0] = W1_DS2438_READ_SCRATCH
;
153 w1_write_block(sl
->master
, w1_buf
, 2);
155 /* reading one byte of result */
156 status
= w1_read_8(sl
->master
);
158 /* if bit0=1, set a value to a mask for easy compare */
162 if ((status
& mask
) == value
)
163 return 0; /* already set as requested */
173 retries
= W1_DS2438_RETRIES
;
175 if (w1_reset_select_slave(sl
))
177 w1_buf
[0] = W1_DS2438_WRITE_SCRATCH
;
180 w1_write_block(sl
->master
, w1_buf
, 3);
182 if (w1_reset_select_slave(sl
))
184 w1_buf
[0] = W1_DS2438_COPY_SCRATCH
;
186 w1_write_block(sl
->master
, w1_buf
, 2);
194 static int w1_ds2438_change_offset_register(struct w1_slave
*sl
, u8
*value
)
196 unsigned int retries
= W1_DS2438_RETRIES
;
198 u8 w1_page1_buf
[DS2438_PAGE_SIZE
+ 1 /*for CRC*/];
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];
205 if (w1_reset_select_slave(sl
))
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);
211 if (w1_reset_select_slave(sl
))
213 w1_buf
[0] = W1_DS2438_COPY_SCRATCH
;
215 w1_write_block(sl
->master
, w1_buf
, 2);
222 static int w1_ds2438_get_voltage(struct w1_slave
*sl
,
223 int adc_input
, uint16_t *voltage
)
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
;
231 mutex_lock(&sl
->master
->bus_mutex
);
233 if (w1_ds2438_change_config_bit(sl
, DS2438_STATUS_AD
, adc_input
)) {
239 if (w1_reset_select_slave(sl
))
241 w1_write_8(sl
->master
, W1_DS2438_CONVERT_VOLTAGE
);
243 mutex_unlock(&sl
->master
->bus_mutex
);
244 sleep_rem
= msleep_interruptible(tm
);
245 if (sleep_rem
!= 0) {
250 if (mutex_lock_interruptible(&sl
->master
->bus_mutex
) != 0) {
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
]);
265 mutex_unlock(&sl
->master
->bus_mutex
);
271 static int w1_ds2438_get_current(struct w1_slave
*sl
, int16_t *voltage
)
273 u8 w1_buf
[DS2438_PAGE_SIZE
+ 1 /*for CRC*/];
276 mutex_lock(&sl
->master
->bus_mutex
);
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
]);
285 mutex_unlock(&sl
->master
->bus_mutex
);
290 static 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
)
294 struct w1_slave
*sl
= kobj_to_w1_slave(kobj
);
297 if (count
!= 1 || off
!= 0)
300 mutex_lock(&sl
->master
->bus_mutex
);
302 if (w1_ds2438_change_config_bit(sl
, DS2438_STATUS_IAD
, *buf
& 0x01) == 0)
307 mutex_unlock(&sl
->master
->bus_mutex
);
312 static 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
)
316 struct w1_slave
*sl
= kobj_to_w1_slave(kobj
);
325 if (w1_ds2438_get_current(sl
, &voltage
) == 0)
326 ret
= snprintf(buf
, count
, "%i\n", voltage
);
333 static 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
)
337 struct w1_slave
*sl
= kobj_to_w1_slave(kobj
);
339 u8 w1_buf
[DS2438_PAGE_SIZE
+ 1 /*for CRC*/];
346 mutex_lock(&sl
->master
->bus_mutex
);
348 /* Read no more than page0 size */
349 if (count
> DS2438_PAGE_SIZE
)
350 count
= DS2438_PAGE_SIZE
;
352 if (w1_ds2438_get_page(sl
, 0, w1_buf
) == 0) {
353 memcpy(buf
, &w1_buf
, count
);
358 mutex_unlock(&sl
->master
->bus_mutex
);
363 static 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
)
367 struct w1_slave
*sl
= kobj_to_w1_slave(kobj
);
369 u8 w1_buf
[DS2438_PAGE_SIZE
+ 1 /*for CRC*/];
376 mutex_lock(&sl
->master
->bus_mutex
);
378 /* Read no more than page1 size */
379 if (count
> DS2438_PAGE_SIZE
)
380 count
= DS2438_PAGE_SIZE
;
382 if (w1_ds2438_get_page(sl
, 1, w1_buf
) == 0) {
383 memcpy(buf
, &w1_buf
, count
);
388 mutex_unlock(&sl
->master
->bus_mutex
);
393 static 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
)
397 struct w1_slave
*sl
= kobj_to_w1_slave(kobj
);
400 mutex_lock(&sl
->master
->bus_mutex
);
402 if (w1_ds2438_change_offset_register(sl
, buf
) == 0)
407 mutex_unlock(&sl
->master
->bus_mutex
);
412 static 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
)
416 struct w1_slave
*sl
= kobj_to_w1_slave(kobj
);
425 if (w1_ds2438_get_temperature(sl
, &temp
) == 0)
426 ret
= snprintf(buf
, count
, "%i\n", temp
);
433 static 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
)
437 struct w1_slave
*sl
= kobj_to_w1_slave(kobj
);
446 if (w1_ds2438_get_voltage(sl
, DS2438_ADC_INPUT_VAD
, &voltage
) == 0)
447 ret
= snprintf(buf
, count
, "%u\n", voltage
);
454 static 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
)
458 struct w1_slave
*sl
= kobj_to_w1_slave(kobj
);
467 if (w1_ds2438_get_voltage(sl
, DS2438_ADC_INPUT_VDD
, &voltage
) == 0)
468 ret
= snprintf(buf
, count
, "%u\n", voltage
);
475 static BIN_ATTR_RW(iad
, 0);
476 static BIN_ATTR_RO(page0
, DS2438_PAGE_SIZE
);
477 static BIN_ATTR_RO(page1
, DS2438_PAGE_SIZE
);
478 static BIN_ATTR_WO(offset
, 2);
479 static BIN_ATTR_RO(temperature
, 0/* real length varies */);
480 static BIN_ATTR_RO(vad
, 0/* real length varies */);
481 static BIN_ATTR_RO(vdd
, 0/* real length varies */);
483 static struct bin_attribute
*w1_ds2438_bin_attrs
[] = {
488 &bin_attr_temperature
,
494 static const struct attribute_group w1_ds2438_group
= {
495 .bin_attrs
= w1_ds2438_bin_attrs
,
498 static const struct attribute_group
*w1_ds2438_groups
[] = {
503 static const struct w1_family_ops w1_ds2438_fops
= {
504 .groups
= w1_ds2438_groups
,
507 static struct w1_family w1_ds2438_family
= {
508 .fid
= W1_FAMILY_DS2438
,
509 .fops
= &w1_ds2438_fops
,
511 module_w1_family(w1_ds2438_family
);
513 MODULE_LICENSE("GPL");
514 MODULE_AUTHOR("Mariusz Bialonczyk <manio@skyboo.net>");
515 MODULE_DESCRIPTION("1-wire driver for Maxim/Dallas DS2438 Smart Battery Monitor");
516 MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2438
));