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 "../w1.h" 17 #include "../w1_family.h" 18 19 #define W1_DS2438_RETRIES 3 20 21 /* Memory commands */ 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 29 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 */ 34 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 */ 44 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 52 53 int w1_ds2438_get_page(struct w1_slave *sl, int pageno, u8 *buf) 54 { 55 unsigned int retries = W1_DS2438_RETRIES; 56 u8 w1_buf[2]; 57 u8 crc; 58 size_t count; 59 60 while (retries--) { 61 crc = 0; 62 63 if (w1_reset_select_slave(sl)) 64 continue; 65 w1_buf[0] = W1_DS2438_RECALL_MEMORY; 66 w1_buf[1] = 0x00; 67 w1_write_block(sl->master, w1_buf, 2); 68 69 if (w1_reset_select_slave(sl)) 70 continue; 71 w1_buf[0] = W1_DS2438_READ_SCRATCH; 72 w1_buf[1] = 0x00; 73 w1_write_block(sl->master, w1_buf, 2); 74 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); 78 79 /* check for correct CRC */ 80 if ((u8)buf[DS2438_PAGE_SIZE] == crc) 81 return 0; 82 } 83 } 84 return -1; 85 } 86 87 int w1_ds2438_get_temperature(struct w1_slave *sl, int16_t *temperature) 88 { 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; 93 int ret; 94 95 mutex_lock(&sl->master->bus_mutex); 96 97 while (retries--) { 98 if (w1_reset_select_slave(sl)) 99 continue; 100 w1_write_8(sl->master, W1_DS2438_CONVERT_TEMP); 101 102 mutex_unlock(&sl->master->bus_mutex); 103 sleep_rem = msleep_interruptible(tm); 104 if (sleep_rem != 0) { 105 ret = -1; 106 goto post_unlock; 107 } 108 109 if (mutex_lock_interruptible(&sl->master->bus_mutex) != 0) { 110 ret = -1; 111 goto post_unlock; 112 } 113 114 break; 115 } 116 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]); 119 ret = 0; 120 } else 121 ret = -1; 122 123 mutex_unlock(&sl->master->bus_mutex); 124 125 post_unlock: 126 return ret; 127 } 128 129 int w1_ds2438_change_config_bit(struct w1_slave *sl, u8 mask, u8 value) 130 { 131 unsigned int retries = W1_DS2438_RETRIES; 132 u8 w1_buf[3]; 133 u8 status; 134 int perform_write = 0; 135 136 while (retries--) { 137 if (w1_reset_select_slave(sl)) 138 continue; 139 w1_buf[0] = W1_DS2438_RECALL_MEMORY; 140 w1_buf[1] = 0x00; 141 w1_write_block(sl->master, w1_buf, 2); 142 143 if (w1_reset_select_slave(sl)) 144 continue; 145 w1_buf[0] = W1_DS2438_READ_SCRATCH; 146 w1_buf[1] = 0x00; 147 w1_write_block(sl->master, w1_buf, 2); 148 149 /* reading one byte of result */ 150 status = w1_read_8(sl->master); 151 152 /* if bit0=1, set a value to a mask for easy compare */ 153 if (value) 154 value = mask; 155 156 if ((status & mask) == value) 157 return 0; /* already set as requested */ 158 else { 159 /* changing bit */ 160 status ^= mask; 161 perform_write = 1; 162 } 163 break; 164 } 165 166 if (perform_write) { 167 retries = W1_DS2438_RETRIES; 168 while (retries--) { 169 if (w1_reset_select_slave(sl)) 170 continue; 171 w1_buf[0] = W1_DS2438_WRITE_SCRATCH; 172 w1_buf[1] = 0x00; 173 w1_buf[2] = status; 174 w1_write_block(sl->master, w1_buf, 3); 175 176 if (w1_reset_select_slave(sl)) 177 continue; 178 w1_buf[0] = W1_DS2438_COPY_SCRATCH; 179 w1_buf[1] = 0x00; 180 w1_write_block(sl->master, w1_buf, 2); 181 182 return 0; 183 } 184 } 185 return -1; 186 } 187 188 uint16_t w1_ds2438_get_voltage(struct w1_slave *sl, 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 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) 239 { 240 struct w1_slave *sl = kobj_to_w1_slave(kobj); 241 int ret; 242 243 if (count != 1 || off != 0) 244 return -EFAULT; 245 246 mutex_lock(&sl->master->bus_mutex); 247 248 if (w1_ds2438_change_config_bit(sl, DS2438_STATUS_IAD, *buf & 0x01) == 0) 249 ret = 1; 250 else 251 ret = -EIO; 252 253 mutex_unlock(&sl->master->bus_mutex); 254 255 return ret; 256 } 257 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) 261 { 262 struct w1_slave *sl = kobj_to_w1_slave(kobj); 263 int ret; 264 u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/]; 265 266 if (off != 0) 267 return 0; 268 if (!buf) 269 return -EINVAL; 270 271 mutex_lock(&sl->master->bus_mutex); 272 273 if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) { 274 memcpy(buf, &w1_buf, DS2438_PAGE_SIZE); 275 ret = DS2438_PAGE_SIZE; 276 } else 277 ret = -EIO; 278 279 mutex_unlock(&sl->master->bus_mutex); 280 281 return ret; 282 } 283 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) 287 { 288 struct w1_slave *sl = kobj_to_w1_slave(kobj); 289 int ret; 290 ssize_t c = PAGE_SIZE; 291 int16_t temp; 292 293 if (off != 0) 294 return 0; 295 if (!buf) 296 return -EINVAL; 297 298 if (w1_ds2438_get_temperature(sl, &temp) == 0) { 299 c -= snprintf(buf + PAGE_SIZE - c, c, "%d\n", temp); 300 ret = PAGE_SIZE - c; 301 } else 302 ret = -EIO; 303 304 return ret; 305 } 306 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) 310 { 311 struct w1_slave *sl = kobj_to_w1_slave(kobj); 312 int ret; 313 ssize_t c = PAGE_SIZE; 314 uint16_t voltage; 315 316 if (off != 0) 317 return 0; 318 if (!buf) 319 return -EINVAL; 320 321 if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VAD, &voltage) == 0) { 322 c -= snprintf(buf + PAGE_SIZE - c, c, "%d\n", voltage); 323 ret = PAGE_SIZE - c; 324 } else 325 ret = -EIO; 326 327 return ret; 328 } 329 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) 333 { 334 struct w1_slave *sl = kobj_to_w1_slave(kobj); 335 int ret; 336 ssize_t c = PAGE_SIZE; 337 uint16_t voltage; 338 339 if (off != 0) 340 return 0; 341 if (!buf) 342 return -EINVAL; 343 344 if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VDD, &voltage) == 0) { 345 c -= snprintf(buf + PAGE_SIZE - c, c, "%d\n", voltage); 346 ret = PAGE_SIZE - c; 347 } else 348 ret = -EIO; 349 350 return ret; 351 } 352 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 */); 358 359 static struct bin_attribute *w1_ds2438_bin_attrs[] = { 360 &bin_attr_iad, 361 &bin_attr_page0, 362 &bin_attr_temperature, 363 &bin_attr_vad, 364 &bin_attr_vdd, 365 NULL, 366 }; 367 368 static const struct attribute_group w1_ds2438_group = { 369 .bin_attrs = w1_ds2438_bin_attrs, 370 }; 371 372 static const struct attribute_group *w1_ds2438_groups[] = { 373 &w1_ds2438_group, 374 NULL, 375 }; 376 377 static struct w1_family_ops w1_ds2438_fops = { 378 .groups = w1_ds2438_groups, 379 }; 380 381 static struct w1_family w1_ds2438_family = { 382 .fid = W1_FAMILY_DS2438, 383 .fops = &w1_ds2438_fops, 384 }; 385 module_w1_family(w1_ds2438_family); 386 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)); 391