Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/char/ds1620.c
26278 views
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
};
357
358
static struct miscdevice ds1620_miscdev = {
359
TEMP_MINOR,
360
"temp",
361
&ds1620_fops
362
};
363
364
static int __init ds1620_init(void)
365
{
366
int ret;
367
struct therm th, th_start;
368
369
if (!machine_is_netwinder())
370
return -ENODEV;
371
372
ds1620_out(THERM_RESET, 0, 0);
373
ds1620_out(THERM_WRITE_CONFIG, 8, CFG_CPU);
374
ds1620_out(THERM_START_CONVERT, 0, 0);
375
376
/*
377
* Trigger the fan to start by setting
378
* temperature high point low. This kicks
379
* the fan into action.
380
*/
381
ds1620_read_state(&th);
382
th_start.lo = 0;
383
th_start.hi = 1;
384
ds1620_write_state(&th_start);
385
386
msleep(2000);
387
388
ds1620_write_state(&th);
389
390
ret = misc_register(&ds1620_miscdev);
391
if (ret < 0)
392
return ret;
393
394
#ifdef THERM_USE_PROC
395
if (!proc_create_single("therm", 0, NULL, ds1620_proc_therm_show))
396
printk(KERN_ERR "therm: unable to register /proc/therm\n");
397
#endif
398
399
ds1620_read_state(&th);
400
ret = cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9));
401
402
printk(KERN_INFO "Thermostat: high %i.%i, low %i.%i, "
403
"current %i.%i C, fan %s.\n",
404
th.hi >> 1, th.hi & 1 ? 5 : 0,
405
th.lo >> 1, th.lo & 1 ? 5 : 0,
406
ret >> 1, ret & 1 ? 5 : 0,
407
fan_state[netwinder_get_fan()]);
408
409
return 0;
410
}
411
412
static void __exit ds1620_exit(void)
413
{
414
#ifdef THERM_USE_PROC
415
remove_proc_entry("therm", NULL);
416
#endif
417
misc_deregister(&ds1620_miscdev);
418
}
419
420
module_init(ds1620_init);
421
module_exit(ds1620_exit);
422
423
MODULE_DESCRIPTION("Dallas Semiconductor DS1620 thermometer driver");
424
MODULE_LICENSE("GPL");
425
426