linux/drivers/w1/slaves/w1_therm.c
<<
>>
Prefs
   1/*
   2 *      w1_therm.c
   3 *
   4 * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
   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 <johnpol@2ka.mipt.ru>");
  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 */
  45static int w1_strong_pullup = 1;
  46module_param_named(strong_pullup, w1_strong_pullup, int, 0);
  47
  48static u8 bad_roms[][9] = {
  49                                {0xaa, 0x00, 0x4b, 0x46, 0xff, 0xff, 0x0c, 0x10, 0x87},
  50                                {}
  51                        };
  52
  53static ssize_t w1_therm_read(struct device *device,
  54        struct device_attribute *attr, char *buf);
  55
  56static struct device_attribute w1_therm_attr =
  57        __ATTR(w1_slave, S_IRUGO, w1_therm_read, NULL);
  58
  59static int w1_therm_add_slave(struct w1_slave *sl)
  60{
  61        return device_create_file(&sl->dev, &w1_therm_attr);
  62}
  63
  64static void w1_therm_remove_slave(struct w1_slave *sl)
  65{
  66        device_remove_file(&sl->dev, &w1_therm_attr);
  67}
  68
  69static struct w1_family_ops w1_therm_fops = {
  70        .add_slave      = w1_therm_add_slave,
  71        .remove_slave   = w1_therm_remove_slave,
  72};
  73
  74static struct w1_family w1_therm_family_DS18S20 = {
  75        .fid = W1_THERM_DS18S20,
  76        .fops = &w1_therm_fops,
  77};
  78
  79static struct w1_family w1_therm_family_DS18B20 = {
  80        .fid = W1_THERM_DS18B20,
  81        .fops = &w1_therm_fops,
  82};
  83
  84static struct w1_family w1_therm_family_DS1822 = {
  85        .fid = W1_THERM_DS1822,
  86        .fops = &w1_therm_fops,
  87};
  88
  89struct w1_therm_family_converter
  90{
  91        u8                      broken;
  92        u16                     reserved;
  93        struct w1_family        *f;
  94        int                     (*convert)(u8 rom[9]);
  95};
  96
  97/* The return value is millidegrees Centigrade. */
  98static inline int w1_DS18B20_convert_temp(u8 rom[9]);
  99static inline int w1_DS18S20_convert_temp(u8 rom[9]);
 100
 101static struct w1_therm_family_converter w1_therm_families[] = {
 102        {
 103                .f              = &w1_therm_family_DS18S20,
 104                .convert        = w1_DS18S20_convert_temp
 105        },
 106        {
 107                .f              = &w1_therm_family_DS1822,
 108                .convert        = w1_DS18B20_convert_temp
 109        },
 110        {
 111                .f              = &w1_therm_family_DS18B20,
 112                .convert        = w1_DS18B20_convert_temp
 113        },
 114};
 115
 116static inline int w1_DS18B20_convert_temp(u8 rom[9])
 117{
 118        int t = ((s16)rom[1] << 8) | rom[0];
 119        t = t*1000/16;
 120        return t;
 121}
 122
 123static inline int w1_DS18S20_convert_temp(u8 rom[9])
 124{
 125        int t, h;
 126
 127        if (!rom[7])
 128                return 0;
 129
 130        if (rom[1] == 0)
 131                t = ((s32)rom[0] >> 1)*1000;
 132        else
 133                t = 1000*(-1*(s32)(0x100-rom[0]) >> 1);
 134
 135        t -= 250;
 136        h = 1000*((s32)rom[7] - (s32)rom[6]);
 137        h /= (s32)rom[7];
 138        t += h;
 139
 140        return t;
 141}
 142
 143static inline int w1_convert_temp(u8 rom[9], u8 fid)
 144{
 145        int i;
 146
 147        for (i = 0; i < ARRAY_SIZE(w1_therm_families); ++i)
 148                if (w1_therm_families[i].f->fid == fid)
 149                        return w1_therm_families[i].convert(rom);
 150
 151        return 0;
 152}
 153
 154static int w1_therm_check_rom(u8 rom[9])
 155{
 156        int i;
 157
 158        for (i=0; i<sizeof(bad_roms)/9; ++i)
 159                if (!memcmp(bad_roms[i], rom, 9))
 160                        return 1;
 161
 162        return 0;
 163}
 164
 165static ssize_t w1_therm_read(struct device *device,
 166        struct device_attribute *attr, char *buf)
 167{
 168        struct w1_slave *sl = dev_to_w1_slave(device);
 169        struct w1_master *dev = sl->master;
 170        u8 rom[9], crc, verdict;
 171        int i, max_trying = 10;
 172        ssize_t c = PAGE_SIZE;
 173
 174        mutex_lock(&dev->mutex);
 175
 176        memset(rom, 0, sizeof(rom));
 177
 178        verdict = 0;
 179        crc = 0;
 180
 181        while (max_trying--) {
 182                if (!w1_reset_select_slave(sl)) {
 183                        int count = 0;
 184                        unsigned int tm = 750;
 185
 186                        /* 750ms strong pullup (or delay) after the convert */
 187                        if (w1_strong_pullup)
 188                                w1_next_pullup(dev, tm);
 189                        w1_write_8(dev, W1_CONVERT_TEMP);
 190                        if (!w1_strong_pullup)
 191                                msleep(tm);
 192
 193                        if (!w1_reset_select_slave(sl)) {
 194
 195                                w1_write_8(dev, W1_READ_SCRATCHPAD);
 196                                if ((count = w1_read_block(dev, rom, 9)) != 9) {
 197                                        dev_warn(device, "w1_read_block() "
 198                                                "returned %u instead of 9.\n",
 199                                                count);
 200                                }
 201
 202                                crc = w1_calc_crc8(rom, 8);
 203
 204                                if (rom[8] == crc)
 205                                        verdict = 1;
 206                        }
 207                }
 208
 209                if (!w1_therm_check_rom(rom))
 210                        break;
 211        }
 212
 213        for (i = 0; i < 9; ++i)
 214                c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", rom[i]);
 215        c -= snprintf(buf + PAGE_SIZE - c, c, ": crc=%02x %s\n",
 216                           crc, (verdict) ? "YES" : "NO");
 217        if (verdict)
 218                memcpy(sl->rom, rom, sizeof(sl->rom));
 219        else
 220                dev_warn(device, "18S20 doesn't respond to CONVERT_TEMP.\n");
 221
 222        for (i = 0; i < 9; ++i)
 223                c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", sl->rom[i]);
 224
 225        c -= snprintf(buf + PAGE_SIZE - c, c, "t=%d\n",
 226                w1_convert_temp(rom, sl->family->fid));
 227        mutex_unlock(&dev->mutex);
 228
 229        return PAGE_SIZE - c;
 230}
 231
 232static int __init w1_therm_init(void)
 233{
 234        int err, i;
 235
 236        for (i = 0; i < ARRAY_SIZE(w1_therm_families); ++i) {
 237                err = w1_register_family(w1_therm_families[i].f);
 238                if (err)
 239                        w1_therm_families[i].broken = 1;
 240        }
 241
 242        return 0;
 243}
 244
 245static void __exit w1_therm_fini(void)
 246{
 247        int i;
 248
 249        for (i = 0; i < ARRAY_SIZE(w1_therm_families); ++i)
 250                if (!w1_therm_families[i].broken)
 251                        w1_unregister_family(w1_therm_families[i].f);
 252}
 253
 254module_init(w1_therm_init);
 255module_exit(w1_therm_fini);
 256