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