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 static 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 static 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 static 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 static uint16_t w1_ds2438_get_voltage(struct w1_slave *sl, 190 int adc_input, uint16_t *voltage) 191 { 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; 196 int ret; 197 198 mutex_lock(&sl->master->bus_mutex); 199 200 if (w1_ds2438_change_config_bit(sl, DS2438_STATUS_AD, adc_input)) { 201 ret = -1; 202 goto pre_unlock; 203 } 204 205 while (retries--) { 206 if (w1_reset_select_slave(sl)) 207 continue; 208 w1_write_8(sl->master, W1_DS2438_CONVERT_VOLTAGE); 209 210 mutex_unlock(&sl->master->bus_mutex); 211 sleep_rem = msleep_interruptible(tm); 212 if (sleep_rem != 0) { 213 ret = -1; 214 goto post_unlock; 215 } 216 217 if (mutex_lock_interruptible(&sl->master->bus_mutex) != 0) { 218 ret = -1; 219 goto post_unlock; 220 } 221 222 break; 223 } 224 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]); 227 ret = 0; 228 } else 229 ret = -1; 230 231 pre_unlock: 232 mutex_unlock(&sl->master->bus_mutex); 233 234 post_unlock: 235 return ret; 236 } 237 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) 241 { 242 struct w1_slave *sl = kobj_to_w1_slave(kobj); 243 int ret; 244 245 if (count != 1 || off != 0) 246 return -EFAULT; 247 248 mutex_lock(&sl->master->bus_mutex); 249 250 if (w1_ds2438_change_config_bit(sl, DS2438_STATUS_IAD, *buf & 0x01) == 0) 251 ret = 1; 252 else 253 ret = -EIO; 254 255 mutex_unlock(&sl->master->bus_mutex); 256 257 return ret; 258 } 259 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) 263 { 264 struct w1_slave *sl = kobj_to_w1_slave(kobj); 265 int ret; 266 u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/]; 267 268 if (off != 0) 269 return 0; 270 if (!buf) 271 return -EINVAL; 272 273 mutex_lock(&sl->master->bus_mutex); 274 275 if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) { 276 memcpy(buf, &w1_buf, DS2438_PAGE_SIZE); 277 ret = DS2438_PAGE_SIZE; 278 } else 279 ret = -EIO; 280 281 mutex_unlock(&sl->master->bus_mutex); 282 283 return ret; 284 } 285 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) 289 { 290 struct w1_slave *sl = kobj_to_w1_slave(kobj); 291 int ret; 292 ssize_t c = PAGE_SIZE; 293 int16_t temp; 294 295 if (off != 0) 296 return 0; 297 if (!buf) 298 return -EINVAL; 299 300 if (w1_ds2438_get_temperature(sl, &temp) == 0) { 301 c -= snprintf(buf + PAGE_SIZE - c, c, "%d\n", temp); 302 ret = PAGE_SIZE - c; 303 } else 304 ret = -EIO; 305 306 return ret; 307 } 308 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) 312 { 313 struct w1_slave *sl = kobj_to_w1_slave(kobj); 314 int ret; 315 ssize_t c = PAGE_SIZE; 316 uint16_t voltage; 317 318 if (off != 0) 319 return 0; 320 if (!buf) 321 return -EINVAL; 322 323 if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VAD, &voltage) == 0) { 324 c -= snprintf(buf + PAGE_SIZE - c, c, "%d\n", voltage); 325 ret = PAGE_SIZE - c; 326 } else 327 ret = -EIO; 328 329 return ret; 330 } 331 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) 335 { 336 struct w1_slave *sl = kobj_to_w1_slave(kobj); 337 int ret; 338 ssize_t c = PAGE_SIZE; 339 uint16_t voltage; 340 341 if (off != 0) 342 return 0; 343 if (!buf) 344 return -EINVAL; 345 346 if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VDD, &voltage) == 0) { 347 c -= snprintf(buf + PAGE_SIZE - c, c, "%d\n", voltage); 348 ret = PAGE_SIZE - c; 349 } else 350 ret = -EIO; 351 352 return ret; 353 } 354 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 */); 360 361 static struct bin_attribute *w1_ds2438_bin_attrs[] = { 362 &bin_attr_iad, 363 &bin_attr_page0, 364 &bin_attr_temperature, 365 &bin_attr_vad, 366 &bin_attr_vdd, 367 NULL, 368 }; 369 370 static const struct attribute_group w1_ds2438_group = { 371 .bin_attrs = w1_ds2438_bin_attrs, 372 }; 373 374 static const struct attribute_group *w1_ds2438_groups[] = { 375 &w1_ds2438_group, 376 NULL, 377 }; 378 379 static struct w1_family_ops w1_ds2438_fops = { 380 .groups = w1_ds2438_groups, 381 }; 382 383 static struct w1_family w1_ds2438_family = { 384 .fid = W1_FAMILY_DS2438, 385 .fops = &w1_ds2438_fops, 386 }; 387 module_w1_family(w1_ds2438_family); 388 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)); 393