root/drivers/char/ds1620.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. netwinder_ds1620_set_clk
  2. netwinder_ds1620_set_data
  3. netwinder_ds1620_get_data
  4. netwinder_ds1620_set_data_dir
  5. netwinder_ds1620_reset
  6. netwinder_lock
  7. netwinder_unlock
  8. netwinder_set_fan
  9. netwinder_get_fan
  10. ds1620_send_bits
  11. ds1620_recv_bits
  12. ds1620_out
  13. ds1620_in
  14. cvt_9_to_int
  15. ds1620_write_state
  16. ds1620_read_state
  17. ds1620_open
  18. ds1620_read
  19. ds1620_ioctl
  20. ds1620_unlocked_ioctl
  21. ds1620_proc_therm_show
  22. ds1620_init
  23. ds1620_exit

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

/* [<][>][^][v][top][bottom][index][help] */