File: /usr/src/linux/drivers/char/ds1620.c

1     /*
2      * linux/drivers/char/ds1620.c: Dallas Semiconductors DS1620
3      *   thermometer driver (as used in the Rebel.com NetWinder)
4      */
5     #include <linux/config.h>
6     #include <linux/module.h>
7     #include <linux/sched.h>
8     #include <linux/miscdevice.h>
9     #include <linux/smp_lock.h>
10     #include <linux/delay.h>
11     #include <linux/proc_fs.h>
12     #include <linux/capability.h>
13     #include <linux/init.h>
14     
15     #include <asm/hardware.h>
16     #include <asm/mach-types.h>
17     #include <asm/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 const char *fan_state[] = { "off", "on", "on (hardwired)" };
40     
41     /*
42      * Start of NetWinder specifics
43      *  Note!  We have to hold the gpio lock with IRQs disabled over the
44      *  whole of our transaction to the Dallas chip, since there is a
45      *  chance that the WaveArtist driver could touch these bits to
46      *  enable or disable the speaker.
47      */
48     extern spinlock_t gpio_lock;
49     extern unsigned int system_rev;
50     
51     static inline void netwinder_ds1620_set_clk(int clk)
52     {
53     	gpio_modify_op(GPIO_DSCLK, clk ? GPIO_DSCLK : 0);
54     }
55     
56     static inline void netwinder_ds1620_set_data(int dat)
57     {
58     	gpio_modify_op(GPIO_DATA, dat ? GPIO_DATA : 0);
59     }
60     
61     static inline int netwinder_ds1620_get_data(void)
62     {
63     	return gpio_read() & GPIO_DATA;
64     }
65     
66     static inline void netwinder_ds1620_set_data_dir(int dir)
67     {
68     	gpio_modify_io(GPIO_DATA, dir ? GPIO_DATA : 0);
69     }
70     
71     static inline void netwinder_ds1620_reset(void)
72     {
73     	cpld_modify(CPLD_DS_ENABLE, 0);
74     	cpld_modify(CPLD_DS_ENABLE, CPLD_DS_ENABLE);
75     }
76     
77     static inline void netwinder_lock(unsigned long *flags)
78     {
79     	spin_lock_irqsave(&gpio_lock, *flags);
80     }
81     
82     static inline void netwinder_unlock(unsigned long *flags)
83     {
84     	spin_unlock_irqrestore(&gpio_lock, *flags);
85     }
86     
87     static inline void netwinder_set_fan(int i)
88     {
89     	unsigned long flags;
90     
91     	spin_lock_irqsave(&gpio_lock, flags);
92     	gpio_modify_op(GPIO_FAN, i ? GPIO_FAN : 0);
93     	spin_unlock_irqrestore(&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 (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     	set_current_state(TASK_INTERRUPTIBLE);
167     	schedule_timeout(2);
168     }
169     
170     static unsigned int ds1620_in(int cmd, int bits)
171     {
172     	unsigned long flags;
173     	unsigned int value;
174     
175     	netwinder_lock(&flags);
176     	netwinder_ds1620_set_clk(1);
177     	netwinder_ds1620_set_data_dir(0);
178     	netwinder_ds1620_reset();
179     
180     	udelay(1);
181     
182     	ds1620_send_bits(8, cmd);
183     
184     	netwinder_ds1620_set_data_dir(1);
185     	value = ds1620_recv_bits(bits);
186     
187     	netwinder_ds1620_reset();
188     	netwinder_unlock(&flags);
189     
190     	return value;
191     }
192     
193     static int cvt_9_to_int(unsigned int val)
194     {
195     	if (val & 0x100)
196     		val |= 0xfffffe00;
197     
198     	return val;
199     }
200     
201     static void ds1620_write_state(struct therm *therm)
202     {
203     	ds1620_out(THERM_WRITE_CONFIG, 8, CFG_CPU);
204     	ds1620_out(THERM_WRITE_TL, 9, therm->lo);
205     	ds1620_out(THERM_WRITE_TH, 9, therm->hi);
206     	ds1620_out(THERM_START_CONVERT, 0, 0);
207     }
208     
209     static void ds1620_read_state(struct therm *therm)
210     {
211     	therm->lo = cvt_9_to_int(ds1620_in(THERM_READ_TL, 9));
212     	therm->hi = cvt_9_to_int(ds1620_in(THERM_READ_TH, 9));
213     }
214     
215     static ssize_t
216     ds1620_read(struct file *file, char *buf, size_t count, loff_t *ptr)
217     {
218     	signed int cur_temp;
219     	signed char cur_temp_degF;
220     
221     	/* Can't seek (pread) on this device */
222     	if (ptr != &file->f_pos)
223     		return -ESPIPE;
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 inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
238     {
239     	struct therm therm;
240     	int i;
241     
242     	switch(cmd) {
243     	case CMD_SET_THERMOSTATE:
244     	case CMD_SET_THERMOSTATE2:
245     		if (!capable(CAP_SYS_ADMIN))
246     			return -EPERM;
247     
248     		if (cmd == CMD_SET_THERMOSTATE) {
249     			if (get_user(therm.hi, (int *)arg))
250     				return -EFAULT;
251     			therm.lo = therm.hi - 3;
252     		} else {
253     			if (copy_from_user(&therm, (void *)arg, sizeof(therm)))
254     				return -EFAULT;
255     		}
256     
257     		therm.lo <<= 1;
258     		therm.hi <<= 1;
259     
260     		ds1620_write_state(&therm);
261     		break;
262     
263     	case CMD_GET_THERMOSTATE:
264     	case CMD_GET_THERMOSTATE2:
265     		ds1620_read_state(&therm);
266     
267     		therm.lo >>= 1;
268     		therm.hi >>= 1;
269     
270     		if (cmd == CMD_GET_THERMOSTATE) {
271     			if (put_user(therm.hi, (int *)arg))
272     				return -EFAULT;
273     		} else {
274     			if (copy_to_user((void *)arg, &therm, sizeof(therm)))
275     				return -EFAULT;
276     		}
277     		break;
278     
279     	case CMD_GET_TEMPERATURE:
280     	case CMD_GET_TEMPERATURE2:
281     		i = cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9));
282     
283     		if (cmd == CMD_GET_TEMPERATURE)
284     			i >>= 1;
285     
286     		return put_user(i, (int *)arg) ? -EFAULT : 0;
287     
288     	case CMD_GET_STATUS:
289     		i = ds1620_in(THERM_READ_CONFIG, 8) & 0xe3;
290     
291     		return put_user(i, (int *)arg) ? -EFAULT : 0;
292     
293     	case CMD_GET_FAN:
294     		i = netwinder_get_fan();
295     
296     		return put_user(i, (int *)arg) ? -EFAULT : 0;
297     
298     	case CMD_SET_FAN:
299     		if (!capable(CAP_SYS_ADMIN))
300     			return -EPERM;
301     
302     		if (get_user(i, (int *)arg))
303     			return -EFAULT;
304     
305     		netwinder_set_fan(i);
306     		break;
307     		
308     	default:
309     		return -ENOIOCTLCMD;
310     	}
311     
312     	return 0;
313     }
314     
315     #ifdef THERM_USE_PROC
316     static int
317     proc_therm_ds1620_read(char *buf, char **start, off_t offset,
318     		       int len, int *eof, void *unused)
319     {
320     	struct therm th;
321     	int temp;
322     
323     	ds1620_read_state(&th);
324     	temp =  cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9));
325     
326     	len = sprintf(buf, "Thermostat: HI %i.%i, LOW %i.%i; "
327     		      "temperature: %i.%i C, fan %s\n",
328     		      th.hi >> 1, th.hi & 1 ? 5 : 0,
329     		      th.lo >> 1, th.lo & 1 ? 5 : 0,
330     		      temp  >> 1, temp  & 1 ? 5 : 0,
331     		      fan_state[netwinder_get_fan()]);
332     
333     	return len;
334     }
335     
336     static struct proc_dir_entry *proc_therm_ds1620;
337     #endif
338     
339     static struct file_operations ds1620_fops = {
340     	owner:		THIS_MODULE,
341     	read:		ds1620_read,
342     	ioctl:		ds1620_ioctl,
343     };
344     
345     static struct miscdevice ds1620_miscdev = {
346     	TEMP_MINOR,
347     	"temp",
348     	&ds1620_fops
349     };
350     
351     int __init ds1620_init(void)
352     {
353     	int ret;
354     	struct therm th, th_start;
355     
356     	if (!machine_is_netwinder())
357     		return -ENODEV;
358     
359     	ds1620_out(THERM_RESET, 0, 0);
360     	ds1620_out(THERM_WRITE_CONFIG, 8, CFG_CPU);
361     	ds1620_out(THERM_START_CONVERT, 0, 0);
362     
363     	/*
364     	 * Trigger the fan to start by setting
365     	 * temperature high point low.  This kicks
366     	 * the fan into action.
367     	 */
368     	ds1620_read_state(&th);
369     	th_start.lo = 0;
370     	th_start.hi = 1;
371     	ds1620_write_state(&th_start);
372     
373     	set_current_state(TASK_INTERRUPTIBLE);
374     	schedule_timeout(2*HZ);
375     
376     	ds1620_write_state(&th);
377     
378     	ret = misc_register(&ds1620_miscdev);
379     	if (ret < 0)
380     		return ret;
381     
382     #ifdef THERM_USE_PROC
383     	proc_therm_ds1620 = create_proc_entry("therm", 0, 0);
384     	if (proc_therm_ds1620)
385     		proc_therm_ds1620->read_proc = proc_therm_ds1620_read;
386     	else
387     		printk(KERN_ERR "therm: unable to register /proc/therm\n");
388     #endif
389     
390     	ds1620_read_state(&th);
391     	ret = cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9));
392     
393     	printk(KERN_INFO "Thermostat: high %i.%i, low %i.%i, "
394     	       "current %i.%i C, fan %s.\n",
395     	       th.hi >> 1, th.hi & 1 ? 5 : 0,
396     	       th.lo >> 1, th.lo & 1 ? 5 : 0,
397     	       ret   >> 1, ret   & 1 ? 5 : 0,
398     	       fan_state[netwinder_get_fan()]);
399     
400     	return 0;
401     }
402     
403     void __exit ds1620_exit(void)
404     {
405     #ifdef THERM_USE_PROC
406     	remove_proc_entry("therm", NULL);
407     #endif
408     	misc_deregister(&ds1620_miscdev);
409     }
410     
411     module_init(ds1620_init);
412     module_exit(ds1620_exit);
413     
414     MODULE_LICENSE("GPL");
415