xref: /openbmc/linux/drivers/char/ds1620.c (revision 58e16d792a6a8c6b750f637a4649967fcac853dc)
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  
netwinder_ds1620_set_clk(int clk)51  static inline void netwinder_ds1620_set_clk(int clk)
52  {
53  	nw_gpio_modify_op(GPIO_DSCLK, clk ? GPIO_DSCLK : 0);
54  }
55  
netwinder_ds1620_set_data(int dat)56  static inline void netwinder_ds1620_set_data(int dat)
57  {
58  	nw_gpio_modify_op(GPIO_DATA, dat ? GPIO_DATA : 0);
59  }
60  
netwinder_ds1620_get_data(void)61  static inline int netwinder_ds1620_get_data(void)
62  {
63  	return nw_gpio_read() & GPIO_DATA;
64  }
65  
netwinder_ds1620_set_data_dir(int dir)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  
netwinder_ds1620_reset(void)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  
netwinder_lock(unsigned long * flags)77  static inline void netwinder_lock(unsigned long *flags)
78  {
79  	raw_spin_lock_irqsave(&nw_gpio_lock, *flags);
80  }
81  
netwinder_unlock(unsigned long * flags)82  static inline void netwinder_unlock(unsigned long *flags)
83  {
84  	raw_spin_unlock_irqrestore(&nw_gpio_lock, *flags);
85  }
86  
netwinder_set_fan(int i)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  
netwinder_get_fan(void)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  
ds1620_send_bits(int nr,int value)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  
ds1620_recv_bits(int nr)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  
ds1620_out(int cmd,int bits,int value)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  
ds1620_in(int cmd,int bits)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  
cvt_9_to_int(unsigned int val)192  static int cvt_9_to_int(unsigned int val)
193  {
194  	if (val & 0x100)
195  		val |= 0xfffffe00;
196  
197  	return val;
198  }
199  
ds1620_write_state(struct therm * therm)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  
ds1620_read_state(struct therm * therm)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  
ds1620_open(struct inode * inode,struct file * file)214  static int ds1620_open(struct inode *inode, struct file *file)
215  {
216  	return stream_open(inode, file);
217  }
218  
219  static ssize_t
ds1620_read(struct file * file,char __user * buf,size_t count,loff_t * ptr)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
ds1620_ioctl(struct file * file,unsigned int cmd,unsigned long arg)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
ds1620_unlocked_ioctl(struct file * file,unsigned int cmd,unsigned long arg)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
ds1620_proc_therm_show(struct seq_file * m,void * v)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  
ds1620_init(void)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  
ds1620_exit(void)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");
425