162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * linux/drivers/char/ds1620.c: Dallas Semiconductors DS1620
462306a36Sopenharmony_ci *   thermometer driver (as used in the Rebel.com NetWinder)
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include <linux/miscdevice.h>
862306a36Sopenharmony_ci#include <linux/delay.h>
962306a36Sopenharmony_ci#include <linux/proc_fs.h>
1062306a36Sopenharmony_ci#include <linux/seq_file.h>
1162306a36Sopenharmony_ci#include <linux/capability.h>
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <linux/mutex.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <mach/hardware.h>
1662306a36Sopenharmony_ci#include <asm/mach-types.h>
1762306a36Sopenharmony_ci#include <linux/uaccess.h>
1862306a36Sopenharmony_ci#include <asm/therm.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS
2162306a36Sopenharmony_ci/* define for /proc interface */
2262306a36Sopenharmony_ci#define THERM_USE_PROC
2362306a36Sopenharmony_ci#endif
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/* Definitions for DS1620 chip */
2662306a36Sopenharmony_ci#define THERM_START_CONVERT	0xee
2762306a36Sopenharmony_ci#define THERM_RESET		0xaf
2862306a36Sopenharmony_ci#define THERM_READ_CONFIG	0xac
2962306a36Sopenharmony_ci#define THERM_READ_TEMP		0xaa
3062306a36Sopenharmony_ci#define THERM_READ_TL		0xa2
3162306a36Sopenharmony_ci#define THERM_READ_TH		0xa1
3262306a36Sopenharmony_ci#define THERM_WRITE_CONFIG	0x0c
3362306a36Sopenharmony_ci#define THERM_WRITE_TL		0x02
3462306a36Sopenharmony_ci#define THERM_WRITE_TH		0x01
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define CFG_CPU			2
3762306a36Sopenharmony_ci#define CFG_1SHOT		1
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic DEFINE_MUTEX(ds1620_mutex);
4062306a36Sopenharmony_cistatic const char *fan_state[] = { "off", "on", "on (hardwired)" };
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/*
4362306a36Sopenharmony_ci * Start of NetWinder specifics
4462306a36Sopenharmony_ci *  Note!  We have to hold the gpio lock with IRQs disabled over the
4562306a36Sopenharmony_ci *  whole of our transaction to the Dallas chip, since there is a
4662306a36Sopenharmony_ci *  chance that the WaveArtist driver could touch these bits to
4762306a36Sopenharmony_ci *  enable or disable the speaker.
4862306a36Sopenharmony_ci */
4962306a36Sopenharmony_ciextern unsigned int system_rev;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic inline void netwinder_ds1620_set_clk(int clk)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	nw_gpio_modify_op(GPIO_DSCLK, clk ? GPIO_DSCLK : 0);
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic inline void netwinder_ds1620_set_data(int dat)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	nw_gpio_modify_op(GPIO_DATA, dat ? GPIO_DATA : 0);
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic inline int netwinder_ds1620_get_data(void)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	return nw_gpio_read() & GPIO_DATA;
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic inline void netwinder_ds1620_set_data_dir(int dir)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	nw_gpio_modify_io(GPIO_DATA, dir ? GPIO_DATA : 0);
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic inline void netwinder_ds1620_reset(void)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	nw_cpld_modify(CPLD_DS_ENABLE, 0);
7462306a36Sopenharmony_ci	nw_cpld_modify(CPLD_DS_ENABLE, CPLD_DS_ENABLE);
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic inline void netwinder_lock(unsigned long *flags)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	raw_spin_lock_irqsave(&nw_gpio_lock, *flags);
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic inline void netwinder_unlock(unsigned long *flags)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&nw_gpio_lock, *flags);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic inline void netwinder_set_fan(int i)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	unsigned long flags;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	raw_spin_lock_irqsave(&nw_gpio_lock, flags);
9262306a36Sopenharmony_ci	nw_gpio_modify_op(GPIO_FAN, i ? GPIO_FAN : 0);
9362306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&nw_gpio_lock, flags);
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic inline int netwinder_get_fan(void)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	if ((system_rev & 0xf000) == 0x4000)
9962306a36Sopenharmony_ci		return FAN_ALWAYS_ON;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	return (nw_gpio_read() & GPIO_FAN) ? FAN_ON : FAN_OFF;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci/*
10562306a36Sopenharmony_ci * End of NetWinder specifics
10662306a36Sopenharmony_ci */
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic void ds1620_send_bits(int nr, int value)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	int i;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	for (i = 0; i < nr; i++) {
11362306a36Sopenharmony_ci		netwinder_ds1620_set_data(value & 1);
11462306a36Sopenharmony_ci		netwinder_ds1620_set_clk(0);
11562306a36Sopenharmony_ci		udelay(1);
11662306a36Sopenharmony_ci		netwinder_ds1620_set_clk(1);
11762306a36Sopenharmony_ci		udelay(1);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci		value >>= 1;
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic unsigned int ds1620_recv_bits(int nr)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	unsigned int value = 0, mask = 1;
12662306a36Sopenharmony_ci	int i;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	netwinder_ds1620_set_data(0);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	for (i = 0; i < nr; i++) {
13162306a36Sopenharmony_ci		netwinder_ds1620_set_clk(0);
13262306a36Sopenharmony_ci		udelay(1);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci		if (netwinder_ds1620_get_data())
13562306a36Sopenharmony_ci			value |= mask;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci		mask <<= 1;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci		netwinder_ds1620_set_clk(1);
14062306a36Sopenharmony_ci		udelay(1);
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	return value;
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic void ds1620_out(int cmd, int bits, int value)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	unsigned long flags;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	netwinder_lock(&flags);
15162306a36Sopenharmony_ci	netwinder_ds1620_set_clk(1);
15262306a36Sopenharmony_ci	netwinder_ds1620_set_data_dir(0);
15362306a36Sopenharmony_ci	netwinder_ds1620_reset();
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	udelay(1);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	ds1620_send_bits(8, cmd);
15862306a36Sopenharmony_ci	if (bits)
15962306a36Sopenharmony_ci		ds1620_send_bits(bits, value);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	udelay(1);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	netwinder_ds1620_reset();
16462306a36Sopenharmony_ci	netwinder_unlock(&flags);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	msleep(20);
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic unsigned int ds1620_in(int cmd, int bits)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	unsigned long flags;
17262306a36Sopenharmony_ci	unsigned int value;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	netwinder_lock(&flags);
17562306a36Sopenharmony_ci	netwinder_ds1620_set_clk(1);
17662306a36Sopenharmony_ci	netwinder_ds1620_set_data_dir(0);
17762306a36Sopenharmony_ci	netwinder_ds1620_reset();
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	udelay(1);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	ds1620_send_bits(8, cmd);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	netwinder_ds1620_set_data_dir(1);
18462306a36Sopenharmony_ci	value = ds1620_recv_bits(bits);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	netwinder_ds1620_reset();
18762306a36Sopenharmony_ci	netwinder_unlock(&flags);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	return value;
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic int cvt_9_to_int(unsigned int val)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	if (val & 0x100)
19562306a36Sopenharmony_ci		val |= 0xfffffe00;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	return val;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic void ds1620_write_state(struct therm *therm)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	ds1620_out(THERM_WRITE_CONFIG, 8, CFG_CPU);
20362306a36Sopenharmony_ci	ds1620_out(THERM_WRITE_TL, 9, therm->lo);
20462306a36Sopenharmony_ci	ds1620_out(THERM_WRITE_TH, 9, therm->hi);
20562306a36Sopenharmony_ci	ds1620_out(THERM_START_CONVERT, 0, 0);
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic void ds1620_read_state(struct therm *therm)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	therm->lo = cvt_9_to_int(ds1620_in(THERM_READ_TL, 9));
21162306a36Sopenharmony_ci	therm->hi = cvt_9_to_int(ds1620_in(THERM_READ_TH, 9));
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic int ds1620_open(struct inode *inode, struct file *file)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	return stream_open(inode, file);
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic ssize_t
22062306a36Sopenharmony_cids1620_read(struct file *file, char __user *buf, size_t count, loff_t *ptr)
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci	signed int cur_temp;
22362306a36Sopenharmony_ci	signed char cur_temp_degF;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	cur_temp = cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9)) >> 1;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	/* convert to Fahrenheit, as per wdt.c */
22862306a36Sopenharmony_ci	cur_temp_degF = (cur_temp * 9) / 5 + 32;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	if (copy_to_user(buf, &cur_temp_degF, 1))
23162306a36Sopenharmony_ci		return -EFAULT;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	return 1;
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic int
23762306a36Sopenharmony_cids1620_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	struct therm therm;
24062306a36Sopenharmony_ci	union {
24162306a36Sopenharmony_ci		struct therm __user *therm;
24262306a36Sopenharmony_ci		int __user *i;
24362306a36Sopenharmony_ci	} uarg;
24462306a36Sopenharmony_ci	int i;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	uarg.i = (int __user *)arg;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	switch(cmd) {
24962306a36Sopenharmony_ci	case CMD_SET_THERMOSTATE:
25062306a36Sopenharmony_ci	case CMD_SET_THERMOSTATE2:
25162306a36Sopenharmony_ci		if (!capable(CAP_SYS_ADMIN))
25262306a36Sopenharmony_ci			return -EPERM;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci		if (cmd == CMD_SET_THERMOSTATE) {
25562306a36Sopenharmony_ci			if (get_user(therm.hi, uarg.i))
25662306a36Sopenharmony_ci				return -EFAULT;
25762306a36Sopenharmony_ci			therm.lo = therm.hi - 3;
25862306a36Sopenharmony_ci		} else {
25962306a36Sopenharmony_ci			if (copy_from_user(&therm, uarg.therm, sizeof(therm)))
26062306a36Sopenharmony_ci				return -EFAULT;
26162306a36Sopenharmony_ci		}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci		therm.lo <<= 1;
26462306a36Sopenharmony_ci		therm.hi <<= 1;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci		ds1620_write_state(&therm);
26762306a36Sopenharmony_ci		break;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	case CMD_GET_THERMOSTATE:
27062306a36Sopenharmony_ci	case CMD_GET_THERMOSTATE2:
27162306a36Sopenharmony_ci		ds1620_read_state(&therm);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci		therm.lo >>= 1;
27462306a36Sopenharmony_ci		therm.hi >>= 1;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci		if (cmd == CMD_GET_THERMOSTATE) {
27762306a36Sopenharmony_ci			if (put_user(therm.hi, uarg.i))
27862306a36Sopenharmony_ci				return -EFAULT;
27962306a36Sopenharmony_ci		} else {
28062306a36Sopenharmony_ci			if (copy_to_user(uarg.therm, &therm, sizeof(therm)))
28162306a36Sopenharmony_ci				return -EFAULT;
28262306a36Sopenharmony_ci		}
28362306a36Sopenharmony_ci		break;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	case CMD_GET_TEMPERATURE:
28662306a36Sopenharmony_ci	case CMD_GET_TEMPERATURE2:
28762306a36Sopenharmony_ci		i = cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9));
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci		if (cmd == CMD_GET_TEMPERATURE)
29062306a36Sopenharmony_ci			i >>= 1;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci		return put_user(i, uarg.i) ? -EFAULT : 0;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	case CMD_GET_STATUS:
29562306a36Sopenharmony_ci		i = ds1620_in(THERM_READ_CONFIG, 8) & 0xe3;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci		return put_user(i, uarg.i) ? -EFAULT : 0;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	case CMD_GET_FAN:
30062306a36Sopenharmony_ci		i = netwinder_get_fan();
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci		return put_user(i, uarg.i) ? -EFAULT : 0;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	case CMD_SET_FAN:
30562306a36Sopenharmony_ci		if (!capable(CAP_SYS_ADMIN))
30662306a36Sopenharmony_ci			return -EPERM;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci		if (get_user(i, uarg.i))
30962306a36Sopenharmony_ci			return -EFAULT;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci		netwinder_set_fan(i);
31262306a36Sopenharmony_ci		break;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	default:
31562306a36Sopenharmony_ci		return -ENOIOCTLCMD;
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	return 0;
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic long
32262306a36Sopenharmony_cids1620_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	int ret;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	mutex_lock(&ds1620_mutex);
32762306a36Sopenharmony_ci	ret = ds1620_ioctl(file, cmd, arg);
32862306a36Sopenharmony_ci	mutex_unlock(&ds1620_mutex);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	return ret;
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci#ifdef THERM_USE_PROC
33462306a36Sopenharmony_cistatic int ds1620_proc_therm_show(struct seq_file *m, void *v)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	struct therm th;
33762306a36Sopenharmony_ci	int temp;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	ds1620_read_state(&th);
34062306a36Sopenharmony_ci	temp =  cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9));
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	seq_printf(m, "Thermostat: HI %i.%i, LOW %i.%i; temperature: %i.%i C, fan %s\n",
34362306a36Sopenharmony_ci		   th.hi >> 1, th.hi & 1 ? 5 : 0,
34462306a36Sopenharmony_ci		   th.lo >> 1, th.lo & 1 ? 5 : 0,
34562306a36Sopenharmony_ci		   temp  >> 1, temp  & 1 ? 5 : 0,
34662306a36Sopenharmony_ci		   fan_state[netwinder_get_fan()]);
34762306a36Sopenharmony_ci	return 0;
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci#endif
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic const struct file_operations ds1620_fops = {
35262306a36Sopenharmony_ci	.owner		= THIS_MODULE,
35362306a36Sopenharmony_ci	.open		= ds1620_open,
35462306a36Sopenharmony_ci	.read		= ds1620_read,
35562306a36Sopenharmony_ci	.unlocked_ioctl	= ds1620_unlocked_ioctl,
35662306a36Sopenharmony_ci	.llseek		= no_llseek,
35762306a36Sopenharmony_ci};
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic struct miscdevice ds1620_miscdev = {
36062306a36Sopenharmony_ci	TEMP_MINOR,
36162306a36Sopenharmony_ci	"temp",
36262306a36Sopenharmony_ci	&ds1620_fops
36362306a36Sopenharmony_ci};
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cistatic int __init ds1620_init(void)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	int ret;
36862306a36Sopenharmony_ci	struct therm th, th_start;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	if (!machine_is_netwinder())
37162306a36Sopenharmony_ci		return -ENODEV;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	ds1620_out(THERM_RESET, 0, 0);
37462306a36Sopenharmony_ci	ds1620_out(THERM_WRITE_CONFIG, 8, CFG_CPU);
37562306a36Sopenharmony_ci	ds1620_out(THERM_START_CONVERT, 0, 0);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	/*
37862306a36Sopenharmony_ci	 * Trigger the fan to start by setting
37962306a36Sopenharmony_ci	 * temperature high point low.  This kicks
38062306a36Sopenharmony_ci	 * the fan into action.
38162306a36Sopenharmony_ci	 */
38262306a36Sopenharmony_ci	ds1620_read_state(&th);
38362306a36Sopenharmony_ci	th_start.lo = 0;
38462306a36Sopenharmony_ci	th_start.hi = 1;
38562306a36Sopenharmony_ci	ds1620_write_state(&th_start);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	msleep(2000);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	ds1620_write_state(&th);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	ret = misc_register(&ds1620_miscdev);
39262306a36Sopenharmony_ci	if (ret < 0)
39362306a36Sopenharmony_ci		return ret;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci#ifdef THERM_USE_PROC
39662306a36Sopenharmony_ci	if (!proc_create_single("therm", 0, NULL, ds1620_proc_therm_show))
39762306a36Sopenharmony_ci		printk(KERN_ERR "therm: unable to register /proc/therm\n");
39862306a36Sopenharmony_ci#endif
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	ds1620_read_state(&th);
40162306a36Sopenharmony_ci	ret = cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9));
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	printk(KERN_INFO "Thermostat: high %i.%i, low %i.%i, "
40462306a36Sopenharmony_ci	       "current %i.%i C, fan %s.\n",
40562306a36Sopenharmony_ci	       th.hi >> 1, th.hi & 1 ? 5 : 0,
40662306a36Sopenharmony_ci	       th.lo >> 1, th.lo & 1 ? 5 : 0,
40762306a36Sopenharmony_ci	       ret   >> 1, ret   & 1 ? 5 : 0,
40862306a36Sopenharmony_ci	       fan_state[netwinder_get_fan()]);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	return 0;
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic void __exit ds1620_exit(void)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci#ifdef THERM_USE_PROC
41662306a36Sopenharmony_ci	remove_proc_entry("therm", NULL);
41762306a36Sopenharmony_ci#endif
41862306a36Sopenharmony_ci	misc_deregister(&ds1620_miscdev);
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cimodule_init(ds1620_init);
42262306a36Sopenharmony_cimodule_exit(ds1620_exit);
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
425