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/slab.h>
  31#include <linux/delay.h>
  32
  33#include "../w1.h"
  34#include "../w1_int.h"
  35#include "../w1_family.h"
  36
  37MODULE_LICENSE("GPL");
  38MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
  39MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol, temperature family.");
  40MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS18S20));
  41MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS1822));
  42MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS18B20));
  43MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS1825));
  44MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS28EA00));
  45
  46/* Allow the strong pullup to be disabled, but default to enabled.
  47 * If it was disabled a parasite powered device might not get the require
  48 * current to do a temperature conversion.  If it is enabled parasite powered
  49 * devices have a better chance of getting the current required.
  50 * In case the parasite power-detection is not working (seems to be the case
  51 * for some DS18S20) the strong pullup can also be forced, regardless of the
  52 * power state of the devices.
  53 *
  54 * Summary of options:
  55 * - strong_pullup = 0  Disable strong pullup completely
  56 * - strong_pullup = 1  Enable automatic strong pullup detection
  57 * - strong_pullup = 2  Force strong pullup
  58 */
  59static int w1_strong_pullup = 1;
  60module_param_named(strong_pullup, w1_strong_pullup, int, 0);
  61
  62struct w1_therm_family_data {
  63        uint8_t rom[9];
  64        atomic_t refcnt;
  65};
  66
  67/* return the address of the refcnt in the family data */
  68#define THERM_REFCNT(family_data) \
  69        (&((struct w1_therm_family_data*)family_data)->refcnt)
  70
  71static int w1_therm_add_slave(struct w1_slave *sl)
  72{
  73        sl->family_data = kzalloc(sizeof(struct w1_therm_family_data),
  74                GFP_KERNEL);
  75        if (!sl->family_data)
  76                return -ENOMEM;
  77        atomic_set(THERM_REFCNT(sl->family_data), 1);
  78        return 0;
  79}
  80
  81static void w1_therm_remove_slave(struct w1_slave *sl)
  82{
  83        int refcnt = atomic_sub_return(1, THERM_REFCNT(sl->family_data));
  84        while(refcnt) {
  85                msleep(1000);
  86                refcnt = atomic_read(THERM_REFCNT(sl->family_data));
  87        }
  88        kfree(sl->family_data);
  89        sl->family_data = NULL;
  90}
  91
  92static ssize_t w1_slave_show(struct device *device,
  93        struct device_attribute *attr, char *buf);
  94
  95static ssize_t w1_seq_show(struct device *device,
  96        struct device_attribute *attr, char *buf);
  97
  98static DEVICE_ATTR_RO(w1_slave);
  99static DEVICE_ATTR_RO(w1_seq);
 100
 101static struct attribute *w1_therm_attrs[] = {
 102        &dev_attr_w1_slave.attr,
 103        NULL,
 104};
 105
 106static struct attribute *w1_ds28ea00_attrs[] = {
 107        &dev_attr_w1_slave.attr,
 108        &dev_attr_w1_seq.attr,
 109        NULL,
 110};
 111ATTRIBUTE_GROUPS(w1_therm);
 112ATTRIBUTE_GROUPS(w1_ds28ea00);
 113
 114static struct w1_family_ops w1_therm_fops = {
 115        .add_slave      = w1_therm_add_slave,
 116        .remove_slave   = w1_therm_remove_slave,
 117        .groups         = w1_therm_groups,
 118};
 119
 120static struct w1_family_ops w1_ds28ea00_fops = {
 121        .add_slave      = w1_therm_add_slave,
 122        .remove_slave   = w1_therm_remove_slave,
 123        .groups         = w1_ds28ea00_groups,
 124};
 125
 126static struct w1_family w1_therm_family_DS18S20 = {
 127        .fid = W1_THERM_DS18S20,
 128        .fops = &w1_therm_fops,
 129};
 130
 131static struct w1_family w1_therm_family_DS18B20 = {
 132        .fid = W1_THERM_DS18B20,
 133        .fops = &w1_therm_fops,
 134};
 135
 136static struct w1_family w1_therm_family_DS1822 = {
 137        .fid = W1_THERM_DS1822,
 138        .fops = &w1_therm_fops,
 139};
 140
 141static struct w1_family w1_therm_family_DS28EA00 = {
 142        .fid = W1_THERM_DS28EA00,
 143        .fops = &w1_ds28ea00_fops,
 144};
 145
 146static struct w1_family w1_therm_family_DS1825 = {
 147        .fid = W1_THERM_DS1825,
 148        .fops = &w1_therm_fops,
 149};
 150
 151struct w1_therm_family_converter
 152{
 153        u8                      broken;
 154        u16                     reserved;
 155        struct w1_family        *f;
 156        int                     (*convert)(u8 rom[9]);
 157};
 158
 159/* The return value is millidegrees Centigrade. */
 160static inline int w1_DS18B20_convert_temp(u8 rom[9]);
 161static inline int w1_DS18S20_convert_temp(u8 rom[9]);
 162
 163static struct w1_therm_family_converter w1_therm_families[] = {
 164        {
 165                .f              = &w1_therm_family_DS18S20,
 166                .convert        = w1_DS18S20_convert_temp
 167        },
 168        {
 169                .f              = &w1_therm_family_DS1822,
 170                .convert        = w1_DS18B20_convert_temp
 171        },
 172        {
 173                .f              = &w1_therm_family_DS18B20,
 174                .convert        = w1_DS18B20_convert_temp
 175        },
 176        {
 177                .f              = &w1_therm_family_DS28EA00,
 178                .convert        = w1_DS18B20_convert_temp
 179        },
 180        {
 181                .f              = &w1_therm_family_DS1825,
 182                .convert        = w1_DS18B20_convert_temp
 183        }
 184};
 185
 186static inline int w1_DS18B20_convert_temp(u8 rom[9])
 187{
 188        s16 t = le16_to_cpup((__le16 *)rom);
 189        return t*1000/16;
 190}
 191
 192static inline int w1_DS18S20_convert_temp(u8 rom[9])
 193{
 194        int t, h;
 195
 196        if (!rom[7])
 197                return 0;
 198
 199        if (rom[1] == 0)
 200                t = ((s32)rom[0] >> 1)*1000;
 201        else
 202                t = 1000*(-1*(s32)(0x100-rom[0]) >> 1);
 203
 204        t -= 250;
 205        h = 1000*((s32)rom[7] - (s32)rom[6]);
 206        h /= (s32)rom[7];
 207        t += h;
 208
 209        return t;
 210}
 211
 212static inline int w1_convert_temp(u8 rom[9], u8 fid)
 213{
 214        int i;
 215
 216        for (i = 0; i < ARRAY_SIZE(w1_therm_families); ++i)
 217                if (w1_therm_families[i].f->fid == fid)
 218                        return w1_therm_families[i].convert(rom);
 219
 220        return 0;
 221}
 222
 223
 224static ssize_t w1_slave_show(struct device *device,
 225        struct device_attribute *attr, char *buf)
 226{
 227        struct w1_slave *sl = dev_to_w1_slave(device);
 228        struct w1_master *dev = sl->master;
 229        u8 rom[9], crc, verdict, external_power;
 230        int i, ret, max_trying = 10;
 231        ssize_t c = PAGE_SIZE;
 232        u8 *family_data = sl->family_data;
 233
 234        ret = mutex_lock_interruptible(&dev->bus_mutex);
 235        if (ret != 0)
 236                goto post_unlock;
 237
 238        if(!sl->family_data)
 239        {
 240                ret = -ENODEV;
 241                goto pre_unlock;
 242        }
 243
 244        /* prevent the slave from going away in sleep */
 245        atomic_inc(THERM_REFCNT(family_data));
 246        memset(rom, 0, sizeof(rom));
 247
 248        while (max_trying--) {
 249
 250                verdict = 0;
 251                crc = 0;
 252
 253                if (!w1_reset_select_slave(sl)) {
 254                        int count = 0;
 255                        unsigned int tm = 750;
 256                        unsigned long sleep_rem;
 257
 258                        w1_write_8(dev, W1_READ_PSUPPLY);
 259                        external_power = w1_read_8(dev);
 260
 261                        if (w1_reset_select_slave(sl))
 262                                continue;
 263
 264                        /* 750ms strong pullup (or delay) after the convert */
 265                        if (w1_strong_pullup == 2 ||
 266                                        (!external_power && w1_strong_pullup))
 267                                w1_next_pullup(dev, tm);
 268
 269                        w1_write_8(dev, W1_CONVERT_TEMP);
 270
 271                        if (external_power) {
 272                                mutex_unlock(&dev->bus_mutex);
 273
 274                                sleep_rem = msleep_interruptible(tm);
 275                                if (sleep_rem != 0) {
 276                                        ret = -EINTR;
 277                                        goto post_unlock;
 278                                }
 279
 280                                ret = mutex_lock_interruptible(&dev->bus_mutex);
 281                                if (ret != 0)
 282                                        goto post_unlock;
 283                        } else if (!w1_strong_pullup) {
 284                                sleep_rem = msleep_interruptible(tm);
 285                                if (sleep_rem != 0) {
 286                                        ret = -EINTR;
 287                                        goto pre_unlock;
 288                                }
 289                        }
 290
 291                        if (!w1_reset_select_slave(sl)) {
 292
 293                                w1_write_8(dev, W1_READ_SCRATCHPAD);
 294                                if ((count = w1_read_block(dev, rom, 9)) != 9) {
 295                                        dev_warn(device, "w1_read_block() "
 296                                                "returned %u instead of 9.\n",
 297                                                count);
 298                                }
 299
 300                                crc = w1_calc_crc8(rom, 8);
 301
 302                                if (rom[8] == crc)
 303                                        verdict = 1;
 304                        }
 305                }
 306
 307                if (verdict)
 308                        break;
 309        }
 310
 311        for (i = 0; i < 9; ++i)
 312                c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", rom[i]);
 313        c -= snprintf(buf + PAGE_SIZE - c, c, ": crc=%02x %s\n",
 314                           crc, (verdict) ? "YES" : "NO");
 315        if (verdict)
 316                memcpy(family_data, rom, sizeof(rom));
 317        else
 318                dev_warn(device, "Read failed CRC check\n");
 319
 320        for (i = 0; i < 9; ++i)
 321                c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ",
 322                              ((u8 *)family_data)[i]);
 323
 324        c -= snprintf(buf + PAGE_SIZE - c, c, "t=%d\n",
 325                w1_convert_temp(rom, sl->family->fid));
 326        ret = PAGE_SIZE - c;
 327
 328pre_unlock:
 329        mutex_unlock(&dev->bus_mutex);
 330
 331post_unlock:
 332        atomic_dec(THERM_REFCNT(family_data));
 333        return ret;
 334}
 335
 336#define W1_42_CHAIN     0x99
 337#define W1_42_CHAIN_OFF 0x3C
 338#define W1_42_CHAIN_OFF_INV     0xC3
 339#define W1_42_CHAIN_ON  0x5A
 340#define W1_42_CHAIN_ON_INV      0xA5
 341#define W1_42_CHAIN_DONE 0x96
 342#define W1_42_CHAIN_DONE_INV 0x69
 343#define W1_42_COND_READ 0x0F
 344#define W1_42_SUCCESS_CONFIRM_BYTE 0xAA
 345#define W1_42_FINISHED_BYTE 0xFF
 346static ssize_t w1_seq_show(struct device *device,
 347        struct device_attribute *attr, char *buf)
 348{
 349        struct w1_slave *sl = dev_to_w1_slave(device);
 350        ssize_t c = PAGE_SIZE;
 351        int rv;
 352        int i;
 353        u8 ack;
 354        u64 rn;
 355        struct w1_reg_num *reg_num;
 356        int seq = 0;
 357
 358        mutex_lock(&sl->master->bus_mutex);
 359        /* Place all devices in CHAIN state */
 360        if (w1_reset_bus(sl->master))
 361                goto error;
 362        w1_write_8(sl->master, W1_SKIP_ROM);
 363        w1_write_8(sl->master, W1_42_CHAIN);
 364        w1_write_8(sl->master, W1_42_CHAIN_ON);
 365        w1_write_8(sl->master, W1_42_CHAIN_ON_INV);
 366        msleep(sl->master->pullup_duration);
 367
 368        /* check for acknowledgment */
 369        ack = w1_read_8(sl->master);
 370        if (ack != W1_42_SUCCESS_CONFIRM_BYTE)
 371                goto error;
 372
 373        /* In case the bus fails to send 0xFF, limit*/
 374        for (i = 0; i <= 64; i++) {
 375                if (w1_reset_bus(sl->master))
 376                        goto error;
 377
 378                w1_write_8(sl->master, W1_42_COND_READ);
 379                rv = w1_read_block(sl->master, (u8 *)&rn, 8);
 380                reg_num = (struct w1_reg_num *) &rn;
 381                if (reg_num->family == W1_42_FINISHED_BYTE)
 382                        break;
 383                if (sl->reg_num.id == reg_num->id)
 384                        seq = i;
 385
 386                w1_write_8(sl->master, W1_42_CHAIN);
 387                w1_write_8(sl->master, W1_42_CHAIN_DONE);
 388                w1_write_8(sl->master, W1_42_CHAIN_DONE_INV);
 389                w1_read_block(sl->master, &ack, sizeof(ack));
 390
 391                /* check for acknowledgment */
 392                ack = w1_read_8(sl->master);
 393                if (ack != W1_42_SUCCESS_CONFIRM_BYTE)
 394                        goto error;
 395
 396        }
 397
 398        /* Exit from CHAIN state */
 399        if (w1_reset_bus(sl->master))
 400                goto error;
 401        w1_write_8(sl->master, W1_SKIP_ROM);
 402        w1_write_8(sl->master, W1_42_CHAIN);
 403        w1_write_8(sl->master, W1_42_CHAIN_OFF);
 404        w1_write_8(sl->master, W1_42_CHAIN_OFF_INV);
 405
 406        /* check for acknowledgment */
 407        ack = w1_read_8(sl->master);
 408        if (ack != W1_42_SUCCESS_CONFIRM_BYTE)
 409                goto error;
 410        mutex_unlock(&sl->master->bus_mutex);
 411
 412        c -= snprintf(buf + PAGE_SIZE - c, c, "%d\n", seq);
 413        return PAGE_SIZE - c;
 414error:
 415        mutex_unlock(&sl->master->bus_mutex);
 416        return -EIO;
 417}
 418
 419static int __init w1_therm_init(void)
 420{
 421        int err, i;
 422
 423        for (i = 0; i < ARRAY_SIZE(w1_therm_families); ++i) {
 424                err = w1_register_family(w1_therm_families[i].f);
 425                if (err)
 426                        w1_therm_families[i].broken = 1;
 427        }
 428
 429        return 0;
 430}
 431
 432static void __exit w1_therm_fini(void)
 433{
 434        int i;
 435
 436        for (i = 0; i < ARRAY_SIZE(w1_therm_families); ++i)
 437                if (!w1_therm_families[i].broken)
 438                        w1_unregister_family(w1_therm_families[i].f);
 439}
 440
 441module_init(w1_therm_init);
 442module_exit(w1_therm_fini);
 443