linux/drivers/char/ds1620.c
<<
>>
Prefs
   1/*
   2 * linux/drivers/char/ds1620.c: Dallas Semiconductors DS1620
   3 *   thermometer driver (as used in the Rebel.com NetWinder)
   4 */
   5#include <linux/module.h>
   6#include <linux/miscdevice.h>
   7#include <linux/delay.h>
   8#include <linux/proc_fs.h>
   9#include <linux/capability.h>
  10#include <linux/init.h>
  11#include <linux/mutex.h>
  12
  13#include <mach/hardware.h>
  14#include <asm/mach-types.h>
  15#include <asm/uaccess.h>
  16#include <asm/therm.h>
  17
  18#ifdef CONFIG_PROC_FS
  19/* define for /proc interface */
  20#define THERM_USE_PROC
  21#endif
  22
  23/* Definitions for DS1620 chip */
  24#define THERM_START_CONVERT     0xee
  25#define THERM_RESET             0xaf
  26#define THERM_READ_CONFIG       0xac
  27#define THERM_READ_TEMP         0xaa
  28#define THERM_READ_TL           0xa2
  29#define THERM_READ_TH           0xa1
  30#define THERM_WRITE_CONFIG      0x0c
  31#define THERM_WRITE_TL          0x02
  32#define THERM_WRITE_TH          0x01
  33
  34#define CFG_CPU                 2
  35#define CFG_1SHOT               1
  36
  37static DEFINE_MUTEX(ds1620_mutex);
  38static const char *fan_state[] = { "off", "on", "on (hardwired)" };
  39
  40/*
  41 * Start of NetWinder specifics
  42 *  Note!  We have to hold the gpio lock with IRQs disabled over the
  43 *  whole of our transaction to the Dallas chip, since there is a
  44 *  chance that the WaveArtist driver could touch these bits to
  45 *  enable or disable the speaker.
  46 */
  47extern unsigned int system_rev;
  48
  49static inline void netwinder_ds1620_set_clk(int clk)
  50{
  51        nw_gpio_modify_op(GPIO_DSCLK, clk ? GPIO_DSCLK : 0);
  52}
  53
  54static inline void netwinder_ds1620_set_data(int dat)
  55{
  56        nw_gpio_modify_op(GPIO_DATA, dat ? GPIO_DATA : 0);
  57}
  58
  59static inline int netwinder_ds1620_get_data(void)
  60{
  61        return nw_gpio_read() & GPIO_DATA;
  62}
  63
  64static inline void netwinder_ds1620_set_data_dir(int dir)
  65{
  66        nw_gpio_modify_io(GPIO_DATA, dir ? GPIO_DATA : 0);
  67}
  68
  69static inline void netwinder_ds1620_reset(void)
  70{
  71        nw_cpld_modify(CPLD_DS_ENABLE, 0);
  72        nw_cpld_modify(CPLD_DS_ENABLE, CPLD_DS_ENABLE);
  73}
  74
  75static inline void netwinder_lock(unsigned long *flags)
  76{
  77        spin_lock_irqsave(&nw_gpio_lock, *flags);
  78}
  79
  80static inline void netwinder_unlock(unsigned long *flags)
  81{
  82        spin_unlock_irqrestore(&nw_gpio_lock, *flags);
  83}
  84
  85static inline void netwinder_set_fan(int i)
  86{
  87        unsigned long flags;
  88
  89        spin_lock_irqsave(&nw_gpio_lock, flags);
  90        nw_gpio_modify_op(GPIO_FAN, i ? GPIO_FAN : 0);
  91        spin_unlock_irqrestore(&nw_gpio_lock, flags);
  92}
  93
  94static inline int netwinder_get_fan(void)
  95{
  96        if ((system_rev & 0xf000) == 0x4000)
  97                return FAN_ALWAYS_ON;
  98
  99        return (nw_gpio_read() & GPIO_FAN) ? FAN_ON : FAN_OFF;
 100}
 101
 102/*
 103 * End of NetWinder specifics
 104 */
 105
 106static void ds1620_send_bits(int nr, int value)
 107{
 108        int i;
 109
 110        for (i = 0; i < nr; i++) {
 111                netwinder_ds1620_set_data(value & 1);
 112                netwinder_ds1620_set_clk(0);
 113                udelay(1);
 114                netwinder_ds1620_set_clk(1);
 115                udelay(1);
 116
 117                value >>= 1;
 118        }
 119}
 120
 121static unsigned int ds1620_recv_bits(int nr)
 122{
 123        unsigned int value = 0, mask = 1;
 124        int i;
 125
 126        netwinder_ds1620_set_data(0);
 127
 128        for (i = 0; i < nr; i++) {
 129                netwinder_ds1620_set_clk(0);
 130                udelay(1);
 131
 132                if (netwinder_ds1620_get_data())
 133                        value |= mask;
 134
 135                mask <<= 1;
 136
 137                netwinder_ds1620_set_clk(1);
 138                udelay(1);
 139        }
 140
 141        return value;
 142}
 143
 144static void ds1620_out(int cmd, int bits, int value)
 145{
 146        unsigned long flags;
 147
 148        netwinder_lock(&flags);
 149        netwinder_ds1620_set_clk(1);
 150        netwinder_ds1620_set_data_dir(0);
 151        netwinder_ds1620_reset();
 152
 153        udelay(1);
 154
 155        ds1620_send_bits(8, cmd);
 156        if (bits)
 157                ds1620_send_bits(bits, value);
 158
 159        udelay(1);
 160
 161        netwinder_ds1620_reset();
 162        netwinder_unlock(&flags);
 163
 164        msleep(20);
 165}
 166
 167static unsigned int ds1620_in(int cmd, int bits)
 168{
 169        unsigned long flags;
 170        unsigned int value;
 171
 172        netwinder_lock(&flags);
 173        netwinder_ds1620_set_clk(1);
 174        netwinder_ds1620_set_data_dir(0);
 175        netwinder_ds1620_reset();
 176
 177        udelay(1);
 178
 179        ds1620_send_bits(8, cmd);
 180
 181        netwinder_ds1620_set_data_dir(1);
 182        value = ds1620_recv_bits(bits);
 183
 184        netwinder_ds1620_reset();
 185        netwinder_unlock(&flags);
 186
 187        return value;
 188}
 189
 190static int cvt_9_to_int(unsigned int val)
 191{
 192        if (val & 0x100)
 193                val |= 0xfffffe00;
 194
 195        return val;
 196}
 197
 198static void ds1620_write_state(struct therm *therm)
 199{
 200        ds1620_out(THERM_WRITE_CONFIG, 8, CFG_CPU);
 201        ds1620_out(THERM_WRITE_TL, 9, therm->lo);
 202        ds1620_out(THERM_WRITE_TH, 9, therm->hi);
 203        ds1620_out(THERM_START_CONVERT, 0, 0);
 204}
 205
 206static void ds1620_read_state(struct therm *therm)
 207{
 208        therm->lo = cvt_9_to_int(ds1620_in(THERM_READ_TL, 9));
 209        therm->hi = cvt_9_to_int(ds1620_in(THERM_READ_TH, 9));
 210}
 211
 212static int ds1620_open(struct inode *inode, struct file *file)
 213{
 214        return nonseekable_open(inode, file);
 215}
 216
 217static ssize_t
 218ds1620_read(struct file *file, char __user *buf, size_t count, loff_t *ptr)
 219{
 220        signed int cur_temp;
 221        signed char cur_temp_degF;
 222
 223        cur_temp = cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9)) >> 1;
 224
 225        /* convert to Fahrenheit, as per wdt.c */
 226        cur_temp_degF = (cur_temp * 9) / 5 + 32;
 227
 228        if (copy_to_user(buf, &cur_temp_degF, 1))
 229                return -EFAULT;
 230
 231        return 1;
 232}
 233
 234static int
 235ds1620_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 236{
 237        struct therm therm;
 238        union {
 239                struct therm __user *therm;
 240                int __user *i;
 241        } uarg;
 242        int i;
 243
 244        uarg.i = (int __user *)arg;
 245
 246        switch(cmd) {
 247        case CMD_SET_THERMOSTATE:
 248        case CMD_SET_THERMOSTATE2:
 249                if (!capable(CAP_SYS_ADMIN))
 250                        return -EPERM;
 251
 252                if (cmd == CMD_SET_THERMOSTATE) {
 253                        if (get_user(therm.hi, uarg.i))
 254                                return -EFAULT;
 255                        therm.lo = therm.hi - 3;
 256                } else {
 257                        if (copy_from_user(&therm, uarg.therm, sizeof(therm)))
 258                                return -EFAULT;
 259                }
 260
 261                therm.lo <<= 1;
 262                therm.hi <<= 1;
 263
 264                ds1620_write_state(&therm);
 265                break;
 266
 267        case CMD_GET_THERMOSTATE:
 268        case CMD_GET_THERMOSTATE2:
 269                ds1620_read_state(&therm);
 270
 271                therm.lo >>= 1;
 272                therm.hi >>= 1;
 273
 274                if (cmd == CMD_GET_THERMOSTATE) {
 275                        if (put_user(therm.hi, uarg.i))
 276                                return -EFAULT;
 277                } else {
 278                        if (copy_to_user(uarg.therm, &therm, sizeof(therm)))
 279                                return -EFAULT;
 280                }
 281                break;
 282
 283        case CMD_GET_TEMPERATURE:
 284        case CMD_GET_TEMPERATURE2:
 285                i = cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9));
 286
 287                if (cmd == CMD_GET_TEMPERATURE)
 288                        i >>= 1;
 289
 290                return put_user(i, uarg.i) ? -EFAULT : 0;
 291
 292        case CMD_GET_STATUS:
 293                i = ds1620_in(THERM_READ_CONFIG, 8) & 0xe3;
 294
 295                return put_user(i, uarg.i) ? -EFAULT : 0;
 296
 297        case CMD_GET_FAN:
 298                i = netwinder_get_fan();
 299
 300                return put_user(i, uarg.i) ? -EFAULT : 0;
 301
 302        case CMD_SET_FAN:
 303                if (!capable(CAP_SYS_ADMIN))
 304                        return -EPERM;
 305
 306                if (get_user(i, uarg.i))
 307                        return -EFAULT;
 308
 309                netwinder_set_fan(i);
 310                break;
 311                
 312        default:
 313                return -ENOIOCTLCMD;
 314        }
 315
 316        return 0;
 317}
 318
 319static long
 320ds1620_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 321{
 322        int ret;
 323
 324        mutex_lock(&ds1620_mutex);
 325        ret = ds1620_ioctl(file, cmd, arg);
 326        mutex_unlock(&ds1620_mutex);
 327
 328        return ret;
 329}
 330
 331#ifdef THERM_USE_PROC
 332static int
 333proc_therm_ds1620_read(char *buf, char **start, off_t offset,
 334                       int len, int *eof, void *unused)
 335{
 336        struct therm th;
 337        int temp;
 338
 339        ds1620_read_state(&th);
 340        temp =  cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9));
 341
 342        len = sprintf(buf, "Thermostat: HI %i.%i, LOW %i.%i; "
 343                      "temperature: %i.%i C, fan %s\n",
 344                      th.hi >> 1, th.hi & 1 ? 5 : 0,
 345                      th.lo >> 1, th.lo & 1 ? 5 : 0,
 346                      temp  >> 1, temp  & 1 ? 5 : 0,
 347                      fan_state[netwinder_get_fan()]);
 348
 349        return len;
 350}
 351
 352static struct proc_dir_entry *proc_therm_ds1620;
 353#endif
 354
 355static const struct file_operations ds1620_fops = {
 356        .owner          = THIS_MODULE,
 357        .open           = ds1620_open,
 358        .read           = ds1620_read,
 359        .unlocked_ioctl = ds1620_unlocked_ioctl,
 360        .llseek         = no_llseek,
 361};
 362
 363static struct miscdevice ds1620_miscdev = {
 364        TEMP_MINOR,
 365        "temp",
 366        &ds1620_fops
 367};
 368
 369static int __init ds1620_init(void)
 370{
 371        int ret;
 372        struct therm th, th_start;
 373
 374        if (!machine_is_netwinder())
 375                return -ENODEV;
 376
 377        ds1620_out(THERM_RESET, 0, 0);
 378        ds1620_out(THERM_WRITE_CONFIG, 8, CFG_CPU);
 379        ds1620_out(THERM_START_CONVERT, 0, 0);
 380
 381        /*
 382         * Trigger the fan to start by setting
 383         * temperature high point low.  This kicks
 384         * the fan into action.
 385         */
 386        ds1620_read_state(&th);
 387        th_start.lo = 0;
 388        th_start.hi = 1;
 389        ds1620_write_state(&th_start);
 390
 391        msleep(2000);
 392
 393        ds1620_write_state(&th);
 394
 395        ret = misc_register(&ds1620_miscdev);
 396        if (ret < 0)
 397                return ret;
 398
 399#ifdef THERM_USE_PROC
 400        proc_therm_ds1620 = create_proc_entry("therm", 0, NULL);
 401        if (proc_therm_ds1620)
 402                proc_therm_ds1620->read_proc = proc_therm_ds1620_read;
 403        else
 404                printk(KERN_ERR "therm: unable to register /proc/therm\n");
 405#endif
 406
 407        ds1620_read_state(&th);
 408        ret = cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9));
 409
 410        printk(KERN_INFO "Thermostat: high %i.%i, low %i.%i, "
 411               "current %i.%i C, fan %s.\n",
 412               th.hi >> 1, th.hi & 1 ? 5 : 0,
 413               th.lo >> 1, th.lo & 1 ? 5 : 0,
 414               ret   >> 1, ret   & 1 ? 5 : 0,
 415               fan_state[netwinder_get_fan()]);
 416
 417        return 0;
 418}
 419
 420static void __exit ds1620_exit(void)
 421{
 422#ifdef THERM_USE_PROC
 423        remove_proc_entry("therm", NULL);
 424#endif
 425        misc_deregister(&ds1620_miscdev);
 426}
 427
 428module_init(ds1620_init);
 429module_exit(ds1620_exit);
 430
 431MODULE_LICENSE("GPL");
 432