linux/drivers/w1/slaves/w1_therm.c
<<
>>
Prefs
   1/*
   2 *      w1_therm.c
   3 *
   4 * Copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net>
   5 *
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the therms of the GNU General Public License as published by
   9 * the Free Software Foundation; either version 2 of the License, or
  10 * (at your option) any later version.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program; if not, write to the Free Software
  19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  20 */
  21
  22#include <asm/types.h>
  23
  24#include <linux/kernel.h>
  25#include <linux/module.h>
  26#include <linux/moduleparam.h>
  27#include <linux/sched.h>
  28#include <linux/device.h>
  29#include <linux/types.h>
  30#include <linux/delay.h>
  31
  32#include "../w1.h"
  33#include "../w1_int.h"
  34#include "../w1_family.h"
  35
  36MODULE_LICENSE("GPL");
  37MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
  38MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol, temperature family.");
  39
  40/* Allow the strong pullup to be disabled, but default to enabled.
  41 * If it was disabled a parasite powered device might not get the require
  42 * current to do a temperature conversion.  If it is enabled parasite powered
  43 * devices have a better chance of getting the current required.
  44 * In case the parasite power-detection is not working (seems to be the case
  45 * for some DS18S20) the strong pullup can also be forced, regardless of the
  46 * power state of the devices.
  47 *
  48 * Summary of options:
  49 * - strong_pullup = 0  Disable strong pullup completely
  50 * - strong_pullup = 1  Enable automatic strong pullup detection
  51 * - strong_pullup = 2  Force strong pullup
  52 */
  53static int w1_strong_pullup = 1;
  54module_param_named(strong_pullup, w1_strong_pullup, int, 0);
  55
  56
  57static ssize_t w1_therm_read(struct device *device,
  58        struct device_attribute *attr, char *buf);
  59
  60static struct device_attribute w1_therm_attr =
  61        __ATTR(w1_slave, S_IRUGO, w1_therm_read, NULL);
  62
  63static int w1_therm_add_slave(struct w1_slave *sl)
  64{
  65        return device_create_file(&sl->dev, &w1_therm_attr);
  66}
  67
  68static void w1_therm_remove_slave(struct w1_slave *sl)
  69{
  70        device_remove_file(&sl->dev, &w1_therm_attr);
  71}
  72
  73static struct w1_family_ops w1_therm_fops = {
  74        .add_slave      = w1_therm_add_slave,
  75        .remove_slave   = w1_therm_remove_slave,
  76};
  77
  78static struct w1_family w1_therm_family_DS18S20 = {
  79        .fid = W1_THERM_DS18S20,
  80        .fops = &w1_therm_fops,
  81};
  82
  83static struct w1_family w1_therm_family_DS18B20 = {
  84        .fid = W1_THERM_DS18B20,
  85        .fops = &w1_therm_fops,
  86};
  87
  88static struct w1_family w1_therm_family_DS1822 = {
  89        .fid = W1_THERM_DS1822,
  90        .fops = &w1_therm_fops,
  91};
  92
  93static struct w1_family w1_therm_family_DS28EA00 = {
  94        .fid = W1_THERM_DS28EA00,
  95        .fops = &w1_therm_fops,
  96};
  97
  98static struct w1_family w1_therm_family_DS1825 = {
  99        .fid = W1_THERM_DS1825,
 100        .fops = &w1_therm_fops,
 101};
 102
 103struct w1_therm_family_converter
 104{
 105        u8                      broken;
 106        u16                     reserved;
 107        struct w1_family        *f;
 108        int                     (*convert)(u8 rom[9]);
 109};
 110
 111/* The return value is millidegrees Centigrade. */
 112static inline int w1_DS18B20_convert_temp(u8 rom[9]);
 113static inline int w1_DS18S20_convert_temp(u8 rom[9]);
 114
 115static struct w1_therm_family_converter w1_therm_families[] = {
 116        {
 117                .f              = &w1_therm_family_DS18S20,
 118                .convert        = w1_DS18S20_convert_temp
 119        },
 120        {
 121                .f              = &w1_therm_family_DS1822,
 122                .convert        = w1_DS18B20_convert_temp
 123        },
 124        {
 125                .f              = &w1_therm_family_DS18B20,
 126                .convert        = w1_DS18B20_convert_temp
 127        },
 128        {
 129                .f              = &w1_therm_family_DS28EA00,
 130                .convert        = w1_DS18B20_convert_temp
 131        },
 132        {
 133                .f              = &w1_therm_family_DS1825,
 134                .convert        = w1_DS18B20_convert_temp
 135        }
 136};
 137
 138static inline int w1_DS18B20_convert_temp(u8 rom[9])
 139{
 140        s16 t = le16_to_cpup((__le16 *)rom);
 141        return t*1000/16;
 142}
 143
 144static inline int w1_DS18S20_convert_temp(u8 rom[9])
 145{
 146        int t, h;
 147
 148        if (!rom[7])
 149                return 0;
 150
 151        if (rom[1] == 0)
 152                t = ((s32)rom[0] >> 1)*1000;
 153        else
 154                t = 1000*(-1*(s32)(0x100-rom[0]) >> 1);
 155
 156        t -= 250;
 157        h = 1000*((s32)rom[7] - (s32)rom[6]);
 158        h /= (s32)rom[7];
 159        t += h;
 160
 161        return t;
 162}
 163
 164static inline int w1_convert_temp(u8 rom[9], u8 fid)
 165{
 166        int i;
 167
 168        for (i = 0; i < ARRAY_SIZE(w1_therm_families); ++i)
 169                if (w1_therm_families[i].f->fid == fid)
 170                        return w1_therm_families[i].convert(rom);
 171
 172        return 0;
 173}
 174
 175
 176static ssize_t w1_therm_read(struct device *device,
 177        struct device_attribute *attr, char *buf)
 178{
 179        struct w1_slave *sl = dev_to_w1_slave(device);
 180        struct w1_master *dev = sl->master;
 181        u8 rom[9], crc, verdict, external_power;
 182        int i, max_trying = 10;
 183        ssize_t c = PAGE_SIZE;
 184
 185        i = mutex_lock_interruptible(&dev->bus_mutex);
 186        if (i != 0)
 187                return i;
 188
 189        memset(rom, 0, sizeof(rom));
 190
 191        while (max_trying--) {
 192
 193                verdict = 0;
 194                crc = 0;
 195
 196                if (!w1_reset_select_slave(sl)) {
 197                        int count = 0;
 198                        unsigned int tm = 750;
 199                        unsigned long sleep_rem;
 200
 201                        w1_write_8(dev, W1_READ_PSUPPLY);
 202                        external_power = w1_read_8(dev);
 203
 204                        if (w1_reset_select_slave(sl))
 205                                continue;
 206
 207                        /* 750ms strong pullup (or delay) after the convert */
 208                        if (w1_strong_pullup == 2 ||
 209                                        (!external_power && w1_strong_pullup))
 210                                w1_next_pullup(dev, tm);
 211
 212                        w1_write_8(dev, W1_CONVERT_TEMP);
 213
 214                        if (external_power) {
 215                                mutex_unlock(&dev->bus_mutex);
 216
 217                                sleep_rem = msleep_interruptible(tm);
 218                                if (sleep_rem != 0)
 219                                        return -EINTR;
 220
 221                                i = mutex_lock_interruptible(&dev->bus_mutex);
 222                                if (i != 0)
 223                                        return i;
 224                        } else if (!w1_strong_pullup) {
 225                                sleep_rem = msleep_interruptible(tm);
 226                                if (sleep_rem != 0) {
 227                                        mutex_unlock(&dev->bus_mutex);
 228                                        return -EINTR;
 229                                }
 230                        }
 231
 232                        if (!w1_reset_select_slave(sl)) {
 233
 234                                w1_write_8(dev, W1_READ_SCRATCHPAD);
 235                                if ((count = w1_read_block(dev, rom, 9)) != 9) {
 236                                        dev_warn(device, "w1_read_block() "
 237                                                "returned %u instead of 9.\n",
 238                                                count);
 239                                }
 240
 241                                crc = w1_calc_crc8(rom, 8);
 242
 243                                if (rom[8] == crc)
 244                                        verdict = 1;
 245                        }
 246                }
 247
 248                if (verdict)
 249                        break;
 250        }
 251
 252        for (i = 0; i < 9; ++i)
 253                c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", rom[i]);
 254        c -= snprintf(buf + PAGE_SIZE - c, c, ": crc=%02x %s\n",
 255                           crc, (verdict) ? "YES" : "NO");
 256        if (verdict)
 257                memcpy(sl->rom, rom, sizeof(sl->rom));
 258        else
 259                dev_warn(device, "Read failed CRC check\n");
 260
 261        for (i = 0; i < 9; ++i)
 262                c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", sl->rom[i]);
 263
 264        c -= snprintf(buf + PAGE_SIZE - c, c, "t=%d\n",
 265                w1_convert_temp(rom, sl->family->fid));
 266        mutex_unlock(&dev->bus_mutex);
 267
 268        return PAGE_SIZE - c;
 269}
 270
 271static int __init w1_therm_init(void)
 272{
 273        int err, i;
 274
 275        for (i = 0; i < ARRAY_SIZE(w1_therm_families); ++i) {
 276                err = w1_register_family(w1_therm_families[i].f);
 277                if (err)
 278                        w1_therm_families[i].broken = 1;
 279        }
 280
 281        return 0;
 282}
 283
 284static void __exit w1_therm_fini(void)
 285{
 286        int i;
 287
 288        for (i = 0; i < ARRAY_SIZE(w1_therm_families); ++i)
 289                if (!w1_therm_families[i].broken)
 290                        w1_unregister_family(w1_therm_families[i].f);
 291}
 292
 293module_init(w1_therm_init);
 294module_exit(w1_therm_fini);
 295