Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * linux/drivers/char/ds1620.c: Dallas Semiconductors DS1620
0004  *   thermometer driver (as used in the Rebel.com NetWinder)
0005  */
0006 #include <linux/module.h>
0007 #include <linux/miscdevice.h>
0008 #include <linux/delay.h>
0009 #include <linux/proc_fs.h>
0010 #include <linux/seq_file.h>
0011 #include <linux/capability.h>
0012 #include <linux/init.h>
0013 #include <linux/mutex.h>
0014 
0015 #include <mach/hardware.h>
0016 #include <asm/mach-types.h>
0017 #include <linux/uaccess.h>
0018 #include <asm/therm.h>
0019 
0020 #ifdef CONFIG_PROC_FS
0021 /* define for /proc interface */
0022 #define THERM_USE_PROC
0023 #endif
0024 
0025 /* Definitions for DS1620 chip */
0026 #define THERM_START_CONVERT 0xee
0027 #define THERM_RESET     0xaf
0028 #define THERM_READ_CONFIG   0xac
0029 #define THERM_READ_TEMP     0xaa
0030 #define THERM_READ_TL       0xa2
0031 #define THERM_READ_TH       0xa1
0032 #define THERM_WRITE_CONFIG  0x0c
0033 #define THERM_WRITE_TL      0x02
0034 #define THERM_WRITE_TH      0x01
0035 
0036 #define CFG_CPU         2
0037 #define CFG_1SHOT       1
0038 
0039 static DEFINE_MUTEX(ds1620_mutex);
0040 static const char *fan_state[] = { "off", "on", "on (hardwired)" };
0041 
0042 /*
0043  * Start of NetWinder specifics
0044  *  Note!  We have to hold the gpio lock with IRQs disabled over the
0045  *  whole of our transaction to the Dallas chip, since there is a
0046  *  chance that the WaveArtist driver could touch these bits to
0047  *  enable or disable the speaker.
0048  */
0049 extern unsigned int system_rev;
0050 
0051 static inline void netwinder_ds1620_set_clk(int clk)
0052 {
0053     nw_gpio_modify_op(GPIO_DSCLK, clk ? GPIO_DSCLK : 0);
0054 }
0055 
0056 static inline void netwinder_ds1620_set_data(int dat)
0057 {
0058     nw_gpio_modify_op(GPIO_DATA, dat ? GPIO_DATA : 0);
0059 }
0060 
0061 static inline int netwinder_ds1620_get_data(void)
0062 {
0063     return nw_gpio_read() & GPIO_DATA;
0064 }
0065 
0066 static inline void netwinder_ds1620_set_data_dir(int dir)
0067 {
0068     nw_gpio_modify_io(GPIO_DATA, dir ? GPIO_DATA : 0);
0069 }
0070 
0071 static inline void netwinder_ds1620_reset(void)
0072 {
0073     nw_cpld_modify(CPLD_DS_ENABLE, 0);
0074     nw_cpld_modify(CPLD_DS_ENABLE, CPLD_DS_ENABLE);
0075 }
0076 
0077 static inline void netwinder_lock(unsigned long *flags)
0078 {
0079     raw_spin_lock_irqsave(&nw_gpio_lock, *flags);
0080 }
0081 
0082 static inline void netwinder_unlock(unsigned long *flags)
0083 {
0084     raw_spin_unlock_irqrestore(&nw_gpio_lock, *flags);
0085 }
0086 
0087 static inline void netwinder_set_fan(int i)
0088 {
0089     unsigned long flags;
0090 
0091     raw_spin_lock_irqsave(&nw_gpio_lock, flags);
0092     nw_gpio_modify_op(GPIO_FAN, i ? GPIO_FAN : 0);
0093     raw_spin_unlock_irqrestore(&nw_gpio_lock, flags);
0094 }
0095 
0096 static inline int netwinder_get_fan(void)
0097 {
0098     if ((system_rev & 0xf000) == 0x4000)
0099         return FAN_ALWAYS_ON;
0100 
0101     return (nw_gpio_read() & GPIO_FAN) ? FAN_ON : FAN_OFF;
0102 }
0103 
0104 /*
0105  * End of NetWinder specifics
0106  */
0107 
0108 static void ds1620_send_bits(int nr, int value)
0109 {
0110     int i;
0111 
0112     for (i = 0; i < nr; i++) {
0113         netwinder_ds1620_set_data(value & 1);
0114         netwinder_ds1620_set_clk(0);
0115         udelay(1);
0116         netwinder_ds1620_set_clk(1);
0117         udelay(1);
0118 
0119         value >>= 1;
0120     }
0121 }
0122 
0123 static unsigned int ds1620_recv_bits(int nr)
0124 {
0125     unsigned int value = 0, mask = 1;
0126     int i;
0127 
0128     netwinder_ds1620_set_data(0);
0129 
0130     for (i = 0; i < nr; i++) {
0131         netwinder_ds1620_set_clk(0);
0132         udelay(1);
0133 
0134         if (netwinder_ds1620_get_data())
0135             value |= mask;
0136 
0137         mask <<= 1;
0138 
0139         netwinder_ds1620_set_clk(1);
0140         udelay(1);
0141     }
0142 
0143     return value;
0144 }
0145 
0146 static void ds1620_out(int cmd, int bits, int value)
0147 {
0148     unsigned long flags;
0149 
0150     netwinder_lock(&flags);
0151     netwinder_ds1620_set_clk(1);
0152     netwinder_ds1620_set_data_dir(0);
0153     netwinder_ds1620_reset();
0154 
0155     udelay(1);
0156 
0157     ds1620_send_bits(8, cmd);
0158     if (bits)
0159         ds1620_send_bits(bits, value);
0160 
0161     udelay(1);
0162 
0163     netwinder_ds1620_reset();
0164     netwinder_unlock(&flags);
0165 
0166     msleep(20);
0167 }
0168 
0169 static unsigned int ds1620_in(int cmd, int bits)
0170 {
0171     unsigned long flags;
0172     unsigned int value;
0173 
0174     netwinder_lock(&flags);
0175     netwinder_ds1620_set_clk(1);
0176     netwinder_ds1620_set_data_dir(0);
0177     netwinder_ds1620_reset();
0178 
0179     udelay(1);
0180 
0181     ds1620_send_bits(8, cmd);
0182 
0183     netwinder_ds1620_set_data_dir(1);
0184     value = ds1620_recv_bits(bits);
0185 
0186     netwinder_ds1620_reset();
0187     netwinder_unlock(&flags);
0188 
0189     return value;
0190 }
0191 
0192 static int cvt_9_to_int(unsigned int val)
0193 {
0194     if (val & 0x100)
0195         val |= 0xfffffe00;
0196 
0197     return val;
0198 }
0199 
0200 static void ds1620_write_state(struct therm *therm)
0201 {
0202     ds1620_out(THERM_WRITE_CONFIG, 8, CFG_CPU);
0203     ds1620_out(THERM_WRITE_TL, 9, therm->lo);
0204     ds1620_out(THERM_WRITE_TH, 9, therm->hi);
0205     ds1620_out(THERM_START_CONVERT, 0, 0);
0206 }
0207 
0208 static void ds1620_read_state(struct therm *therm)
0209 {
0210     therm->lo = cvt_9_to_int(ds1620_in(THERM_READ_TL, 9));
0211     therm->hi = cvt_9_to_int(ds1620_in(THERM_READ_TH, 9));
0212 }
0213 
0214 static int ds1620_open(struct inode *inode, struct file *file)
0215 {
0216     return stream_open(inode, file);
0217 }
0218 
0219 static ssize_t
0220 ds1620_read(struct file *file, char __user *buf, size_t count, loff_t *ptr)
0221 {
0222     signed int cur_temp;
0223     signed char cur_temp_degF;
0224 
0225     cur_temp = cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9)) >> 1;
0226 
0227     /* convert to Fahrenheit, as per wdt.c */
0228     cur_temp_degF = (cur_temp * 9) / 5 + 32;
0229 
0230     if (copy_to_user(buf, &cur_temp_degF, 1))
0231         return -EFAULT;
0232 
0233     return 1;
0234 }
0235 
0236 static int
0237 ds1620_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
0238 {
0239     struct therm therm;
0240     union {
0241         struct therm __user *therm;
0242         int __user *i;
0243     } uarg;
0244     int i;
0245 
0246     uarg.i = (int __user *)arg;
0247 
0248     switch(cmd) {
0249     case CMD_SET_THERMOSTATE:
0250     case CMD_SET_THERMOSTATE2:
0251         if (!capable(CAP_SYS_ADMIN))
0252             return -EPERM;
0253 
0254         if (cmd == CMD_SET_THERMOSTATE) {
0255             if (get_user(therm.hi, uarg.i))
0256                 return -EFAULT;
0257             therm.lo = therm.hi - 3;
0258         } else {
0259             if (copy_from_user(&therm, uarg.therm, sizeof(therm)))
0260                 return -EFAULT;
0261         }
0262 
0263         therm.lo <<= 1;
0264         therm.hi <<= 1;
0265 
0266         ds1620_write_state(&therm);
0267         break;
0268 
0269     case CMD_GET_THERMOSTATE:
0270     case CMD_GET_THERMOSTATE2:
0271         ds1620_read_state(&therm);
0272 
0273         therm.lo >>= 1;
0274         therm.hi >>= 1;
0275 
0276         if (cmd == CMD_GET_THERMOSTATE) {
0277             if (put_user(therm.hi, uarg.i))
0278                 return -EFAULT;
0279         } else {
0280             if (copy_to_user(uarg.therm, &therm, sizeof(therm)))
0281                 return -EFAULT;
0282         }
0283         break;
0284 
0285     case CMD_GET_TEMPERATURE:
0286     case CMD_GET_TEMPERATURE2:
0287         i = cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9));
0288 
0289         if (cmd == CMD_GET_TEMPERATURE)
0290             i >>= 1;
0291 
0292         return put_user(i, uarg.i) ? -EFAULT : 0;
0293 
0294     case CMD_GET_STATUS:
0295         i = ds1620_in(THERM_READ_CONFIG, 8) & 0xe3;
0296 
0297         return put_user(i, uarg.i) ? -EFAULT : 0;
0298 
0299     case CMD_GET_FAN:
0300         i = netwinder_get_fan();
0301 
0302         return put_user(i, uarg.i) ? -EFAULT : 0;
0303 
0304     case CMD_SET_FAN:
0305         if (!capable(CAP_SYS_ADMIN))
0306             return -EPERM;
0307 
0308         if (get_user(i, uarg.i))
0309             return -EFAULT;
0310 
0311         netwinder_set_fan(i);
0312         break;
0313         
0314     default:
0315         return -ENOIOCTLCMD;
0316     }
0317 
0318     return 0;
0319 }
0320 
0321 static long
0322 ds1620_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
0323 {
0324     int ret;
0325 
0326     mutex_lock(&ds1620_mutex);
0327     ret = ds1620_ioctl(file, cmd, arg);
0328     mutex_unlock(&ds1620_mutex);
0329 
0330     return ret;
0331 }
0332 
0333 #ifdef THERM_USE_PROC
0334 static int ds1620_proc_therm_show(struct seq_file *m, void *v)
0335 {
0336     struct therm th;
0337     int temp;
0338 
0339     ds1620_read_state(&th);
0340     temp =  cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9));
0341 
0342     seq_printf(m, "Thermostat: HI %i.%i, LOW %i.%i; temperature: %i.%i C, fan %s\n",
0343            th.hi >> 1, th.hi & 1 ? 5 : 0,
0344            th.lo >> 1, th.lo & 1 ? 5 : 0,
0345            temp  >> 1, temp  & 1 ? 5 : 0,
0346            fan_state[netwinder_get_fan()]);
0347     return 0;
0348 }
0349 #endif
0350 
0351 static const struct file_operations ds1620_fops = {
0352     .owner      = THIS_MODULE,
0353     .open       = ds1620_open,
0354     .read       = ds1620_read,
0355     .unlocked_ioctl = ds1620_unlocked_ioctl,
0356     .llseek     = no_llseek,
0357 };
0358 
0359 static struct miscdevice ds1620_miscdev = {
0360     TEMP_MINOR,
0361     "temp",
0362     &ds1620_fops
0363 };
0364 
0365 static int __init ds1620_init(void)
0366 {
0367     int ret;
0368     struct therm th, th_start;
0369 
0370     if (!machine_is_netwinder())
0371         return -ENODEV;
0372 
0373     ds1620_out(THERM_RESET, 0, 0);
0374     ds1620_out(THERM_WRITE_CONFIG, 8, CFG_CPU);
0375     ds1620_out(THERM_START_CONVERT, 0, 0);
0376 
0377     /*
0378      * Trigger the fan to start by setting
0379      * temperature high point low.  This kicks
0380      * the fan into action.
0381      */
0382     ds1620_read_state(&th);
0383     th_start.lo = 0;
0384     th_start.hi = 1;
0385     ds1620_write_state(&th_start);
0386 
0387     msleep(2000);
0388 
0389     ds1620_write_state(&th);
0390 
0391     ret = misc_register(&ds1620_miscdev);
0392     if (ret < 0)
0393         return ret;
0394 
0395 #ifdef THERM_USE_PROC
0396     if (!proc_create_single("therm", 0, NULL, ds1620_proc_therm_show))
0397         printk(KERN_ERR "therm: unable to register /proc/therm\n");
0398 #endif
0399 
0400     ds1620_read_state(&th);
0401     ret = cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9));
0402 
0403     printk(KERN_INFO "Thermostat: high %i.%i, low %i.%i, "
0404            "current %i.%i C, fan %s.\n",
0405            th.hi >> 1, th.hi & 1 ? 5 : 0,
0406            th.lo >> 1, th.lo & 1 ? 5 : 0,
0407            ret   >> 1, ret   & 1 ? 5 : 0,
0408            fan_state[netwinder_get_fan()]);
0409 
0410     return 0;
0411 }
0412 
0413 static void __exit ds1620_exit(void)
0414 {
0415 #ifdef THERM_USE_PROC
0416     remove_proc_entry("therm", NULL);
0417 #endif
0418     misc_deregister(&ds1620_miscdev);
0419 }
0420 
0421 module_init(ds1620_init);
0422 module_exit(ds1620_exit);
0423 
0424 MODULE_LICENSE("GPL");