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>
17 #include "../w1_family.h"
19 #define W1_DS2438_RETRIES 3
22 #define W1_DS2438_READ_SCRATCH 0xBE
23 #define W1_DS2438_WRITE_SCRATCH 0x4E
24 #define W1_DS2438_COPY_SCRATCH 0x48
25 #define W1_DS2438_RECALL_MEMORY 0xB8
26 /* Register commands */
27 #define W1_DS2438_CONVERT_TEMP 0x44
28 #define W1_DS2438_CONVERT_VOLTAGE 0xB4
30 #define DS2438_PAGE_SIZE 8
31 #define DS2438_ADC_INPUT_VAD 0
32 #define DS2438_ADC_INPUT_VDD 1
33 #define DS2438_MAX_CONVERSION_TIME 10 /* ms */
35 /* Page #0 definitions */
36 #define DS2438_STATUS_REG 0x00 /* Status/Configuration Register */
37 #define DS2438_STATUS_IAD (1 << 0) /* Current A/D Control Bit */
38 #define DS2438_STATUS_CA (1 << 1) /* Current Accumulator Configuration */
39 #define DS2438_STATUS_EE (1 << 2) /* Current Accumulator Shadow Selector bit */
40 #define DS2438_STATUS_AD (1 << 3) /* Voltage A/D Input Select Bit */
41 #define DS2438_STATUS_TB (1 << 4) /* Temperature Busy Flag */
42 #define DS2438_STATUS_NVB (1 << 5) /* Nonvolatile Memory Busy Flag */
43 #define DS2438_STATUS_ADB (1 << 6) /* A/D Converter Busy Flag */
45 #define DS2438_TEMP_LSB 0x01
46 #define DS2438_TEMP_MSB 0x02
47 #define DS2438_VOLTAGE_LSB 0x03
48 #define DS2438_VOLTAGE_MSB 0x04
49 #define DS2438_CURRENT_LSB 0x05
50 #define DS2438_CURRENT_MSB 0x06
51 #define DS2438_THRESHOLD 0x07
53 int w1_ds2438_get_page(struct w1_slave *sl, int pageno, u8 *buf)
55 unsigned int retries = W1_DS2438_RETRIES;
63 if (w1_reset_select_slave(sl))
65 w1_buf[0] = W1_DS2438_RECALL_MEMORY;
67 w1_write_block(sl->master, w1_buf, 2);
69 if (w1_reset_select_slave(sl))
71 w1_buf[0] = W1_DS2438_READ_SCRATCH;
73 w1_write_block(sl->master, w1_buf, 2);
75 count = w1_read_block(sl->master, buf, DS2438_PAGE_SIZE + 1);
76 if (count == DS2438_PAGE_SIZE + 1) {
77 crc = w1_calc_crc8(buf, DS2438_PAGE_SIZE);
79 /* check for correct CRC */
80 if ((u8)buf[DS2438_PAGE_SIZE] == crc)
87 int w1_ds2438_get_temperature(struct w1_slave *sl, int16_t *temperature)
89 unsigned int retries = W1_DS2438_RETRIES;
90 u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];
91 unsigned int tm = DS2438_MAX_CONVERSION_TIME;
92 unsigned long sleep_rem;
95 mutex_lock(&sl->master->bus_mutex);
98 if (w1_reset_select_slave(sl))
100 w1_write_8(sl->master, W1_DS2438_CONVERT_TEMP);
102 mutex_unlock(&sl->master->bus_mutex);
103 sleep_rem = msleep_interruptible(tm);
104 if (sleep_rem != 0) {
109 if (mutex_lock_interruptible(&sl->master->bus_mutex) != 0) {
117 if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) {
118 *temperature = (((int16_t) w1_buf[DS2438_TEMP_MSB]) << 8) | ((uint16_t) w1_buf[DS2438_TEMP_LSB]);
123 mutex_unlock(&sl->master->bus_mutex);
129 int w1_ds2438_change_config_bit(struct w1_slave *sl, u8 mask, u8 value)
131 unsigned int retries = W1_DS2438_RETRIES;
134 int perform_write = 0;
137 if (w1_reset_select_slave(sl))
139 w1_buf[0] = W1_DS2438_RECALL_MEMORY;
141 w1_write_block(sl->master, w1_buf, 2);
143 if (w1_reset_select_slave(sl))
145 w1_buf[0] = W1_DS2438_READ_SCRATCH;
147 w1_write_block(sl->master, w1_buf, 2);
149 /* reading one byte of result */
150 status = w1_read_8(sl->master);
152 /* if bit0=1, set a value to a mask for easy compare */
156 if ((status & mask) == value)
157 return 0; /* already set as requested */
167 retries = W1_DS2438_RETRIES;
169 if (w1_reset_select_slave(sl))
171 w1_buf[0] = W1_DS2438_WRITE_SCRATCH;
174 w1_write_block(sl->master, w1_buf, 3);
176 if (w1_reset_select_slave(sl))
178 w1_buf[0] = W1_DS2438_COPY_SCRATCH;
180 w1_write_block(sl->master, w1_buf, 2);
188 uint16_t w1_ds2438_get_voltage(struct w1_slave *sl, int adc_input, uint16_t *voltage)
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;
196 mutex_lock(&sl->master->bus_mutex);
198 if (w1_ds2438_change_config_bit(sl, DS2438_STATUS_AD, adc_input)) {
204 if (w1_reset_select_slave(sl))
206 w1_write_8(sl->master, W1_DS2438_CONVERT_VOLTAGE);
208 mutex_unlock(&sl->master->bus_mutex);
209 sleep_rem = msleep_interruptible(tm);
210 if (sleep_rem != 0) {
215 if (mutex_lock_interruptible(&sl->master->bus_mutex) != 0) {
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]);
230 mutex_unlock(&sl->master->bus_mutex);
236 static ssize_t iad_write(struct file *filp, struct kobject *kobj,
237 struct bin_attribute *bin_attr, char *buf,
238 loff_t off, size_t count)
240 struct w1_slave *sl = kobj_to_w1_slave(kobj);
243 if (count != 1 || off != 0)
246 mutex_lock(&sl->master->bus_mutex);
248 if (w1_ds2438_change_config_bit(sl, DS2438_STATUS_IAD, *buf & 0x01) == 0)
253 mutex_unlock(&sl->master->bus_mutex);
258 static ssize_t page0_read(struct file *filp, struct kobject *kobj,
259 struct bin_attribute *bin_attr, char *buf,
260 loff_t off, size_t count)
262 struct w1_slave *sl = kobj_to_w1_slave(kobj);
264 u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/];
271 mutex_lock(&sl->master->bus_mutex);
273 if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) {
274 memcpy(buf, &w1_buf, DS2438_PAGE_SIZE);
275 ret = DS2438_PAGE_SIZE;
279 mutex_unlock(&sl->master->bus_mutex);
284 static ssize_t temperature_read(struct file *filp, struct kobject *kobj,
285 struct bin_attribute *bin_attr, char *buf,
286 loff_t off, size_t count)
288 struct w1_slave *sl = kobj_to_w1_slave(kobj);
290 ssize_t c = PAGE_SIZE;
298 if (w1_ds2438_get_temperature(sl, &temp) == 0) {
299 c -= snprintf(buf + PAGE_SIZE - c, c, "%d\n", temp);
307 static ssize_t vad_read(struct file *filp, struct kobject *kobj,
308 struct bin_attribute *bin_attr, char *buf,
309 loff_t off, size_t count)
311 struct w1_slave *sl = kobj_to_w1_slave(kobj);
313 ssize_t c = PAGE_SIZE;
321 if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VAD, &voltage) == 0) {
322 c -= snprintf(buf + PAGE_SIZE - c, c, "%d\n", voltage);
330 static ssize_t vdd_read(struct file *filp, struct kobject *kobj,
331 struct bin_attribute *bin_attr, char *buf,
332 loff_t off, size_t count)
334 struct w1_slave *sl = kobj_to_w1_slave(kobj);
336 ssize_t c = PAGE_SIZE;
344 if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VDD, &voltage) == 0) {
345 c -= snprintf(buf + PAGE_SIZE - c, c, "%d\n", voltage);
353 static BIN_ATTR(iad, S_IRUGO | S_IWUSR | S_IWGRP, NULL, iad_write, 1);
354 static BIN_ATTR_RO(page0, DS2438_PAGE_SIZE);
355 static BIN_ATTR_RO(temperature, 0/* real length varies */);
356 static BIN_ATTR_RO(vad, 0/* real length varies */);
357 static BIN_ATTR_RO(vdd, 0/* real length varies */);
359 static struct bin_attribute *w1_ds2438_bin_attrs[] = {
362 &bin_attr_temperature,
368 static const struct attribute_group w1_ds2438_group = {
369 .bin_attrs = w1_ds2438_bin_attrs,
372 static const struct attribute_group *w1_ds2438_groups[] = {
377 static struct w1_family_ops w1_ds2438_fops = {
378 .groups = w1_ds2438_groups,
381 static struct w1_family w1_ds2438_family = {
382 .fid = W1_FAMILY_DS2438,
383 .fops = &w1_ds2438_fops,
385 module_w1_family(w1_ds2438_family);
387 MODULE_LICENSE("GPL");
388 MODULE_AUTHOR("Mariusz Bialonczyk <manio@skyboo.net>");
389 MODULE_DESCRIPTION("1-wire driver for Maxim/Dallas DS2438 Smart Battery Monitor");
390 MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS2438));