2 * 1-Wire implementation for the ds2438 chip
4 * Copyright (c) 2017 Mariusz Bialonczyk <manio@skyboo.net>
6 * This source code is licensed under the GNU General Public License,
7 * Version 2. See the file COPYING for more details.
10 #include <linux/kernel.h>
11 #include <linux/module.h>
12 #include <linux/device.h>
13 #include <linux/types.h>
14 #include <linux/delay.h>
18 #define W1_FAMILY_DS2438 0x26
20 #define W1_DS2438_RETRIES 3
23 #define W1_DS2438_READ_SCRATCH 0xBE
24 #define W1_DS2438_WRITE_SCRATCH 0x4E
25 #define W1_DS2438_COPY_SCRATCH 0x48
26 #define W1_DS2438_RECALL_MEMORY 0xB8
27 /* Register commands */
28 #define W1_DS2438_CONVERT_TEMP 0x44
29 #define W1_DS2438_CONVERT_VOLTAGE 0xB4
31 #define DS2438_PAGE_SIZE 8
32 #define DS2438_ADC_INPUT_VAD 0
33 #define DS2438_ADC_INPUT_VDD 1
34 #define DS2438_MAX_CONVERSION_TIME 10 /* ms */
36 /* Page #0 definitions */
37 #define DS2438_STATUS_REG 0x00 /* Status/Configuration Register */
38 #define DS2438_STATUS_IAD (1 << 0) /* Current A/D Control Bit */
39 #define DS2438_STATUS_CA (1 << 1) /* Current Accumulator Configuration */
40 #define DS2438_STATUS_EE (1 << 2) /* Current Accumulator Shadow Selector bit */
41 #define DS2438_STATUS_AD (1 << 3) /* Voltage A/D Input Select Bit */
42 #define DS2438_STATUS_TB (1 << 4) /* Temperature Busy Flag */
43 #define DS2438_STATUS_NVB (1 << 5) /* Nonvolatile Memory Busy Flag */
44 #define DS2438_STATUS_ADB (1 << 6) /* A/D Converter Busy Flag */
46 #define DS2438_TEMP_LSB 0x01
47 #define DS2438_TEMP_MSB 0x02
48 #define DS2438_VOLTAGE_LSB 0x03
49 #define DS2438_VOLTAGE_MSB 0x04
50 #define DS2438_CURRENT_LSB 0x05
51 #define DS2438_CURRENT_MSB 0x06
52 #define DS2438_THRESHOLD 0x07
54 static int w1_ds2438_get_page(struct w1_slave
*sl
, int pageno
, u8
*buf
)
56 unsigned int retries
= W1_DS2438_RETRIES
;
64 if (w1_reset_select_slave(sl
))
66 w1_buf
[0] = W1_DS2438_RECALL_MEMORY
;
68 w1_write_block(sl
->master
, w1_buf
, 2);
70 if (w1_reset_select_slave(sl
))
72 w1_buf
[0] = W1_DS2438_READ_SCRATCH
;
74 w1_write_block(sl
->master
, w1_buf
, 2);
76 count
= w1_read_block(sl
->master
, buf
, DS2438_PAGE_SIZE
+ 1);
77 if (count
== DS2438_PAGE_SIZE
+ 1) {
78 crc
= w1_calc_crc8(buf
, DS2438_PAGE_SIZE
);
80 /* check for correct CRC */
81 if ((u8
)buf
[DS2438_PAGE_SIZE
] == crc
)
88 static int w1_ds2438_get_temperature(struct w1_slave
*sl
, int16_t *temperature
)
90 unsigned int retries
= W1_DS2438_RETRIES
;
91 u8 w1_buf
[DS2438_PAGE_SIZE
+ 1 /*for CRC*/];
92 unsigned int tm
= DS2438_MAX_CONVERSION_TIME
;
93 unsigned long sleep_rem
;
96 mutex_lock(&sl
->master
->bus_mutex
);
99 if (w1_reset_select_slave(sl
))
101 w1_write_8(sl
->master
, W1_DS2438_CONVERT_TEMP
);
103 mutex_unlock(&sl
->master
->bus_mutex
);
104 sleep_rem
= msleep_interruptible(tm
);
105 if (sleep_rem
!= 0) {
110 if (mutex_lock_interruptible(&sl
->master
->bus_mutex
) != 0) {
118 if (w1_ds2438_get_page(sl
, 0, w1_buf
) == 0) {
119 *temperature
= (((int16_t) w1_buf
[DS2438_TEMP_MSB
]) << 8) | ((uint16_t) w1_buf
[DS2438_TEMP_LSB
]);
124 mutex_unlock(&sl
->master
->bus_mutex
);
130 static int w1_ds2438_change_config_bit(struct w1_slave
*sl
, u8 mask
, u8 value
)
132 unsigned int retries
= W1_DS2438_RETRIES
;
135 int perform_write
= 0;
138 if (w1_reset_select_slave(sl
))
140 w1_buf
[0] = W1_DS2438_RECALL_MEMORY
;
142 w1_write_block(sl
->master
, w1_buf
, 2);
144 if (w1_reset_select_slave(sl
))
146 w1_buf
[0] = W1_DS2438_READ_SCRATCH
;
148 w1_write_block(sl
->master
, w1_buf
, 2);
150 /* reading one byte of result */
151 status
= w1_read_8(sl
->master
);
153 /* if bit0=1, set a value to a mask for easy compare */
157 if ((status
& mask
) == value
)
158 return 0; /* already set as requested */
168 retries
= W1_DS2438_RETRIES
;
170 if (w1_reset_select_slave(sl
))
172 w1_buf
[0] = W1_DS2438_WRITE_SCRATCH
;
175 w1_write_block(sl
->master
, w1_buf
, 3);
177 if (w1_reset_select_slave(sl
))
179 w1_buf
[0] = W1_DS2438_COPY_SCRATCH
;
181 w1_write_block(sl
->master
, w1_buf
, 2);
189 static uint16_t w1_ds2438_get_voltage(struct w1_slave
*sl
,
190 int adc_input
, uint16_t *voltage
)
192 unsigned int retries
= W1_DS2438_RETRIES
;
193 u8 w1_buf
[DS2438_PAGE_SIZE
+ 1 /*for CRC*/];
194 unsigned int tm
= DS2438_MAX_CONVERSION_TIME
;
195 unsigned long sleep_rem
;
198 mutex_lock(&sl
->master
->bus_mutex
);
200 if (w1_ds2438_change_config_bit(sl
, DS2438_STATUS_AD
, adc_input
)) {
206 if (w1_reset_select_slave(sl
))
208 w1_write_8(sl
->master
, W1_DS2438_CONVERT_VOLTAGE
);
210 mutex_unlock(&sl
->master
->bus_mutex
);
211 sleep_rem
= msleep_interruptible(tm
);
212 if (sleep_rem
!= 0) {
217 if (mutex_lock_interruptible(&sl
->master
->bus_mutex
) != 0) {
225 if (w1_ds2438_get_page(sl
, 0, w1_buf
) == 0) {
226 *voltage
= (((uint16_t) w1_buf
[DS2438_VOLTAGE_MSB
]) << 8) | ((uint16_t) w1_buf
[DS2438_VOLTAGE_LSB
]);
232 mutex_unlock(&sl
->master
->bus_mutex
);
238 static ssize_t
iad_write(struct file
*filp
, struct kobject
*kobj
,
239 struct bin_attribute
*bin_attr
, char *buf
,
240 loff_t off
, size_t count
)
242 struct w1_slave
*sl
= kobj_to_w1_slave(kobj
);
245 if (count
!= 1 || off
!= 0)
248 mutex_lock(&sl
->master
->bus_mutex
);
250 if (w1_ds2438_change_config_bit(sl
, DS2438_STATUS_IAD
, *buf
& 0x01) == 0)
255 mutex_unlock(&sl
->master
->bus_mutex
);
260 static ssize_t
page0_read(struct file
*filp
, struct kobject
*kobj
,
261 struct bin_attribute
*bin_attr
, char *buf
,
262 loff_t off
, size_t count
)
264 struct w1_slave
*sl
= kobj_to_w1_slave(kobj
);
266 u8 w1_buf
[DS2438_PAGE_SIZE
+ 1 /*for CRC*/];
273 mutex_lock(&sl
->master
->bus_mutex
);
275 if (w1_ds2438_get_page(sl
, 0, w1_buf
) == 0) {
276 memcpy(buf
, &w1_buf
, DS2438_PAGE_SIZE
);
277 ret
= DS2438_PAGE_SIZE
;
281 mutex_unlock(&sl
->master
->bus_mutex
);
286 static ssize_t
temperature_read(struct file
*filp
, struct kobject
*kobj
,
287 struct bin_attribute
*bin_attr
, char *buf
,
288 loff_t off
, size_t count
)
290 struct w1_slave
*sl
= kobj_to_w1_slave(kobj
);
292 ssize_t c
= PAGE_SIZE
;
300 if (w1_ds2438_get_temperature(sl
, &temp
) == 0) {
301 c
-= snprintf(buf
+ PAGE_SIZE
- c
, c
, "%d\n", temp
);
309 static ssize_t
vad_read(struct file
*filp
, struct kobject
*kobj
,
310 struct bin_attribute
*bin_attr
, char *buf
,
311 loff_t off
, size_t count
)
313 struct w1_slave
*sl
= kobj_to_w1_slave(kobj
);
315 ssize_t c
= PAGE_SIZE
;
323 if (w1_ds2438_get_voltage(sl
, DS2438_ADC_INPUT_VAD
, &voltage
) == 0) {
324 c
-= snprintf(buf
+ PAGE_SIZE
- c
, c
, "%d\n", voltage
);
332 static ssize_t
vdd_read(struct file
*filp
, struct kobject
*kobj
,
333 struct bin_attribute
*bin_attr
, char *buf
,
334 loff_t off
, size_t count
)
336 struct w1_slave
*sl
= kobj_to_w1_slave(kobj
);
338 ssize_t c
= PAGE_SIZE
;
346 if (w1_ds2438_get_voltage(sl
, DS2438_ADC_INPUT_VDD
, &voltage
) == 0) {
347 c
-= snprintf(buf
+ PAGE_SIZE
- c
, c
, "%d\n", voltage
);
355 static BIN_ATTR(iad
, S_IRUGO
| S_IWUSR
| S_IWGRP
, NULL
, iad_write
, 1);
356 static BIN_ATTR_RO(page0
, DS2438_PAGE_SIZE
);
357 static BIN_ATTR_RO(temperature
, 0/* real length varies */);
358 static BIN_ATTR_RO(vad
, 0/* real length varies */);
359 static BIN_ATTR_RO(vdd
, 0/* real length varies */);
361 static struct bin_attribute
*w1_ds2438_bin_attrs
[] = {
364 &bin_attr_temperature
,
370 static const struct attribute_group w1_ds2438_group
= {
371 .bin_attrs
= w1_ds2438_bin_attrs
,
374 static const struct attribute_group
*w1_ds2438_groups
[] = {
379 static struct w1_family_ops w1_ds2438_fops
= {
380 .groups
= w1_ds2438_groups
,
383 static struct w1_family w1_ds2438_family
= {
384 .fid
= W1_FAMILY_DS2438
,
385 .fops
= &w1_ds2438_fops
,
387 module_w1_family(w1_ds2438_family
);
389 MODULE_LICENSE("GPL");
390 MODULE_AUTHOR("Mariusz Bialonczyk <manio@skyboo.net>");
391 MODULE_DESCRIPTION("1-wire driver for Maxim/Dallas DS2438 Smart Battery Monitor");
392 MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2438
));