18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * linux/drivers/char/ds1620.c: Dallas Semiconductors DS1620
48c2ecf20Sopenharmony_ci *   thermometer driver (as used in the Rebel.com NetWinder)
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci#include <linux/module.h>
78c2ecf20Sopenharmony_ci#include <linux/miscdevice.h>
88c2ecf20Sopenharmony_ci#include <linux/delay.h>
98c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
108c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
118c2ecf20Sopenharmony_ci#include <linux/capability.h>
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/mutex.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <mach/hardware.h>
168c2ecf20Sopenharmony_ci#include <asm/mach-types.h>
178c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
188c2ecf20Sopenharmony_ci#include <asm/therm.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS
218c2ecf20Sopenharmony_ci/* define for /proc interface */
228c2ecf20Sopenharmony_ci#define THERM_USE_PROC
238c2ecf20Sopenharmony_ci#endif
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/* Definitions for DS1620 chip */
268c2ecf20Sopenharmony_ci#define THERM_START_CONVERT	0xee
278c2ecf20Sopenharmony_ci#define THERM_RESET		0xaf
288c2ecf20Sopenharmony_ci#define THERM_READ_CONFIG	0xac
298c2ecf20Sopenharmony_ci#define THERM_READ_TEMP		0xaa
308c2ecf20Sopenharmony_ci#define THERM_READ_TL		0xa2
318c2ecf20Sopenharmony_ci#define THERM_READ_TH		0xa1
328c2ecf20Sopenharmony_ci#define THERM_WRITE_CONFIG	0x0c
338c2ecf20Sopenharmony_ci#define THERM_WRITE_TL		0x02
348c2ecf20Sopenharmony_ci#define THERM_WRITE_TH		0x01
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define CFG_CPU			2
378c2ecf20Sopenharmony_ci#define CFG_1SHOT		1
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(ds1620_mutex);
408c2ecf20Sopenharmony_cistatic const char *fan_state[] = { "off", "on", "on (hardwired)" };
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/*
438c2ecf20Sopenharmony_ci * Start of NetWinder specifics
448c2ecf20Sopenharmony_ci *  Note!  We have to hold the gpio lock with IRQs disabled over the
458c2ecf20Sopenharmony_ci *  whole of our transaction to the Dallas chip, since there is a
468c2ecf20Sopenharmony_ci *  chance that the WaveArtist driver could touch these bits to
478c2ecf20Sopenharmony_ci *  enable or disable the speaker.
488c2ecf20Sopenharmony_ci */
498c2ecf20Sopenharmony_ciextern unsigned int system_rev;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic inline void netwinder_ds1620_set_clk(int clk)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	nw_gpio_modify_op(GPIO_DSCLK, clk ? GPIO_DSCLK : 0);
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic inline void netwinder_ds1620_set_data(int dat)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	nw_gpio_modify_op(GPIO_DATA, dat ? GPIO_DATA : 0);
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic inline int netwinder_ds1620_get_data(void)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	return nw_gpio_read() & GPIO_DATA;
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic inline void netwinder_ds1620_set_data_dir(int dir)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	nw_gpio_modify_io(GPIO_DATA, dir ? GPIO_DATA : 0);
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic inline void netwinder_ds1620_reset(void)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	nw_cpld_modify(CPLD_DS_ENABLE, 0);
748c2ecf20Sopenharmony_ci	nw_cpld_modify(CPLD_DS_ENABLE, CPLD_DS_ENABLE);
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic inline void netwinder_lock(unsigned long *flags)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&nw_gpio_lock, *flags);
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic inline void netwinder_unlock(unsigned long *flags)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&nw_gpio_lock, *flags);
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic inline void netwinder_set_fan(int i)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	unsigned long flags;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&nw_gpio_lock, flags);
928c2ecf20Sopenharmony_ci	nw_gpio_modify_op(GPIO_FAN, i ? GPIO_FAN : 0);
938c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&nw_gpio_lock, flags);
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic inline int netwinder_get_fan(void)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	if ((system_rev & 0xf000) == 0x4000)
998c2ecf20Sopenharmony_ci		return FAN_ALWAYS_ON;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	return (nw_gpio_read() & GPIO_FAN) ? FAN_ON : FAN_OFF;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci/*
1058c2ecf20Sopenharmony_ci * End of NetWinder specifics
1068c2ecf20Sopenharmony_ci */
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic void ds1620_send_bits(int nr, int value)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	int i;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	for (i = 0; i < nr; i++) {
1138c2ecf20Sopenharmony_ci		netwinder_ds1620_set_data(value & 1);
1148c2ecf20Sopenharmony_ci		netwinder_ds1620_set_clk(0);
1158c2ecf20Sopenharmony_ci		udelay(1);
1168c2ecf20Sopenharmony_ci		netwinder_ds1620_set_clk(1);
1178c2ecf20Sopenharmony_ci		udelay(1);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci		value >>= 1;
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic unsigned int ds1620_recv_bits(int nr)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	unsigned int value = 0, mask = 1;
1268c2ecf20Sopenharmony_ci	int i;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	netwinder_ds1620_set_data(0);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	for (i = 0; i < nr; i++) {
1318c2ecf20Sopenharmony_ci		netwinder_ds1620_set_clk(0);
1328c2ecf20Sopenharmony_ci		udelay(1);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci		if (netwinder_ds1620_get_data())
1358c2ecf20Sopenharmony_ci			value |= mask;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci		mask <<= 1;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci		netwinder_ds1620_set_clk(1);
1408c2ecf20Sopenharmony_ci		udelay(1);
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	return value;
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic void ds1620_out(int cmd, int bits, int value)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	unsigned long flags;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	netwinder_lock(&flags);
1518c2ecf20Sopenharmony_ci	netwinder_ds1620_set_clk(1);
1528c2ecf20Sopenharmony_ci	netwinder_ds1620_set_data_dir(0);
1538c2ecf20Sopenharmony_ci	netwinder_ds1620_reset();
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	udelay(1);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	ds1620_send_bits(8, cmd);
1588c2ecf20Sopenharmony_ci	if (bits)
1598c2ecf20Sopenharmony_ci		ds1620_send_bits(bits, value);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	udelay(1);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	netwinder_ds1620_reset();
1648c2ecf20Sopenharmony_ci	netwinder_unlock(&flags);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	msleep(20);
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic unsigned int ds1620_in(int cmd, int bits)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	unsigned long flags;
1728c2ecf20Sopenharmony_ci	unsigned int value;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	netwinder_lock(&flags);
1758c2ecf20Sopenharmony_ci	netwinder_ds1620_set_clk(1);
1768c2ecf20Sopenharmony_ci	netwinder_ds1620_set_data_dir(0);
1778c2ecf20Sopenharmony_ci	netwinder_ds1620_reset();
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	udelay(1);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	ds1620_send_bits(8, cmd);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	netwinder_ds1620_set_data_dir(1);
1848c2ecf20Sopenharmony_ci	value = ds1620_recv_bits(bits);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	netwinder_ds1620_reset();
1878c2ecf20Sopenharmony_ci	netwinder_unlock(&flags);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	return value;
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic int cvt_9_to_int(unsigned int val)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	if (val & 0x100)
1958c2ecf20Sopenharmony_ci		val |= 0xfffffe00;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	return val;
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic void ds1620_write_state(struct therm *therm)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	ds1620_out(THERM_WRITE_CONFIG, 8, CFG_CPU);
2038c2ecf20Sopenharmony_ci	ds1620_out(THERM_WRITE_TL, 9, therm->lo);
2048c2ecf20Sopenharmony_ci	ds1620_out(THERM_WRITE_TH, 9, therm->hi);
2058c2ecf20Sopenharmony_ci	ds1620_out(THERM_START_CONVERT, 0, 0);
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic void ds1620_read_state(struct therm *therm)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	therm->lo = cvt_9_to_int(ds1620_in(THERM_READ_TL, 9));
2118c2ecf20Sopenharmony_ci	therm->hi = cvt_9_to_int(ds1620_in(THERM_READ_TH, 9));
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic int ds1620_open(struct inode *inode, struct file *file)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	return stream_open(inode, file);
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic ssize_t
2208c2ecf20Sopenharmony_cids1620_read(struct file *file, char __user *buf, size_t count, loff_t *ptr)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	signed int cur_temp;
2238c2ecf20Sopenharmony_ci	signed char cur_temp_degF;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	cur_temp = cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9)) >> 1;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	/* convert to Fahrenheit, as per wdt.c */
2288c2ecf20Sopenharmony_ci	cur_temp_degF = (cur_temp * 9) / 5 + 32;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	if (copy_to_user(buf, &cur_temp_degF, 1))
2318c2ecf20Sopenharmony_ci		return -EFAULT;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	return 1;
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic int
2378c2ecf20Sopenharmony_cids1620_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	struct therm therm;
2408c2ecf20Sopenharmony_ci	union {
2418c2ecf20Sopenharmony_ci		struct therm __user *therm;
2428c2ecf20Sopenharmony_ci		int __user *i;
2438c2ecf20Sopenharmony_ci	} uarg;
2448c2ecf20Sopenharmony_ci	int i;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	uarg.i = (int __user *)arg;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	switch(cmd) {
2498c2ecf20Sopenharmony_ci	case CMD_SET_THERMOSTATE:
2508c2ecf20Sopenharmony_ci	case CMD_SET_THERMOSTATE2:
2518c2ecf20Sopenharmony_ci		if (!capable(CAP_SYS_ADMIN))
2528c2ecf20Sopenharmony_ci			return -EPERM;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci		if (cmd == CMD_SET_THERMOSTATE) {
2558c2ecf20Sopenharmony_ci			if (get_user(therm.hi, uarg.i))
2568c2ecf20Sopenharmony_ci				return -EFAULT;
2578c2ecf20Sopenharmony_ci			therm.lo = therm.hi - 3;
2588c2ecf20Sopenharmony_ci		} else {
2598c2ecf20Sopenharmony_ci			if (copy_from_user(&therm, uarg.therm, sizeof(therm)))
2608c2ecf20Sopenharmony_ci				return -EFAULT;
2618c2ecf20Sopenharmony_ci		}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci		therm.lo <<= 1;
2648c2ecf20Sopenharmony_ci		therm.hi <<= 1;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci		ds1620_write_state(&therm);
2678c2ecf20Sopenharmony_ci		break;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	case CMD_GET_THERMOSTATE:
2708c2ecf20Sopenharmony_ci	case CMD_GET_THERMOSTATE2:
2718c2ecf20Sopenharmony_ci		ds1620_read_state(&therm);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci		therm.lo >>= 1;
2748c2ecf20Sopenharmony_ci		therm.hi >>= 1;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci		if (cmd == CMD_GET_THERMOSTATE) {
2778c2ecf20Sopenharmony_ci			if (put_user(therm.hi, uarg.i))
2788c2ecf20Sopenharmony_ci				return -EFAULT;
2798c2ecf20Sopenharmony_ci		} else {
2808c2ecf20Sopenharmony_ci			if (copy_to_user(uarg.therm, &therm, sizeof(therm)))
2818c2ecf20Sopenharmony_ci				return -EFAULT;
2828c2ecf20Sopenharmony_ci		}
2838c2ecf20Sopenharmony_ci		break;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	case CMD_GET_TEMPERATURE:
2868c2ecf20Sopenharmony_ci	case CMD_GET_TEMPERATURE2:
2878c2ecf20Sopenharmony_ci		i = cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9));
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci		if (cmd == CMD_GET_TEMPERATURE)
2908c2ecf20Sopenharmony_ci			i >>= 1;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci		return put_user(i, uarg.i) ? -EFAULT : 0;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	case CMD_GET_STATUS:
2958c2ecf20Sopenharmony_ci		i = ds1620_in(THERM_READ_CONFIG, 8) & 0xe3;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci		return put_user(i, uarg.i) ? -EFAULT : 0;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	case CMD_GET_FAN:
3008c2ecf20Sopenharmony_ci		i = netwinder_get_fan();
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci		return put_user(i, uarg.i) ? -EFAULT : 0;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	case CMD_SET_FAN:
3058c2ecf20Sopenharmony_ci		if (!capable(CAP_SYS_ADMIN))
3068c2ecf20Sopenharmony_ci			return -EPERM;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci		if (get_user(i, uarg.i))
3098c2ecf20Sopenharmony_ci			return -EFAULT;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci		netwinder_set_fan(i);
3128c2ecf20Sopenharmony_ci		break;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	default:
3158c2ecf20Sopenharmony_ci		return -ENOIOCTLCMD;
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	return 0;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic long
3228c2ecf20Sopenharmony_cids1620_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	int ret;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	mutex_lock(&ds1620_mutex);
3278c2ecf20Sopenharmony_ci	ret = ds1620_ioctl(file, cmd, arg);
3288c2ecf20Sopenharmony_ci	mutex_unlock(&ds1620_mutex);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	return ret;
3318c2ecf20Sopenharmony_ci}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci#ifdef THERM_USE_PROC
3348c2ecf20Sopenharmony_cistatic int ds1620_proc_therm_show(struct seq_file *m, void *v)
3358c2ecf20Sopenharmony_ci{
3368c2ecf20Sopenharmony_ci	struct therm th;
3378c2ecf20Sopenharmony_ci	int temp;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	ds1620_read_state(&th);
3408c2ecf20Sopenharmony_ci	temp =  cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9));
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	seq_printf(m, "Thermostat: HI %i.%i, LOW %i.%i; temperature: %i.%i C, fan %s\n",
3438c2ecf20Sopenharmony_ci		   th.hi >> 1, th.hi & 1 ? 5 : 0,
3448c2ecf20Sopenharmony_ci		   th.lo >> 1, th.lo & 1 ? 5 : 0,
3458c2ecf20Sopenharmony_ci		   temp  >> 1, temp  & 1 ? 5 : 0,
3468c2ecf20Sopenharmony_ci		   fan_state[netwinder_get_fan()]);
3478c2ecf20Sopenharmony_ci	return 0;
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci#endif
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cistatic const struct file_operations ds1620_fops = {
3528c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
3538c2ecf20Sopenharmony_ci	.open		= ds1620_open,
3548c2ecf20Sopenharmony_ci	.read		= ds1620_read,
3558c2ecf20Sopenharmony_ci	.unlocked_ioctl	= ds1620_unlocked_ioctl,
3568c2ecf20Sopenharmony_ci	.llseek		= no_llseek,
3578c2ecf20Sopenharmony_ci};
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_cistatic struct miscdevice ds1620_miscdev = {
3608c2ecf20Sopenharmony_ci	TEMP_MINOR,
3618c2ecf20Sopenharmony_ci	"temp",
3628c2ecf20Sopenharmony_ci	&ds1620_fops
3638c2ecf20Sopenharmony_ci};
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_cistatic int __init ds1620_init(void)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	int ret;
3688c2ecf20Sopenharmony_ci	struct therm th, th_start;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	if (!machine_is_netwinder())
3718c2ecf20Sopenharmony_ci		return -ENODEV;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	ds1620_out(THERM_RESET, 0, 0);
3748c2ecf20Sopenharmony_ci	ds1620_out(THERM_WRITE_CONFIG, 8, CFG_CPU);
3758c2ecf20Sopenharmony_ci	ds1620_out(THERM_START_CONVERT, 0, 0);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	/*
3788c2ecf20Sopenharmony_ci	 * Trigger the fan to start by setting
3798c2ecf20Sopenharmony_ci	 * temperature high point low.  This kicks
3808c2ecf20Sopenharmony_ci	 * the fan into action.
3818c2ecf20Sopenharmony_ci	 */
3828c2ecf20Sopenharmony_ci	ds1620_read_state(&th);
3838c2ecf20Sopenharmony_ci	th_start.lo = 0;
3848c2ecf20Sopenharmony_ci	th_start.hi = 1;
3858c2ecf20Sopenharmony_ci	ds1620_write_state(&th_start);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	msleep(2000);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	ds1620_write_state(&th);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	ret = misc_register(&ds1620_miscdev);
3928c2ecf20Sopenharmony_ci	if (ret < 0)
3938c2ecf20Sopenharmony_ci		return ret;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci#ifdef THERM_USE_PROC
3968c2ecf20Sopenharmony_ci	if (!proc_create_single("therm", 0, NULL, ds1620_proc_therm_show))
3978c2ecf20Sopenharmony_ci		printk(KERN_ERR "therm: unable to register /proc/therm\n");
3988c2ecf20Sopenharmony_ci#endif
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	ds1620_read_state(&th);
4018c2ecf20Sopenharmony_ci	ret = cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9));
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	printk(KERN_INFO "Thermostat: high %i.%i, low %i.%i, "
4048c2ecf20Sopenharmony_ci	       "current %i.%i C, fan %s.\n",
4058c2ecf20Sopenharmony_ci	       th.hi >> 1, th.hi & 1 ? 5 : 0,
4068c2ecf20Sopenharmony_ci	       th.lo >> 1, th.lo & 1 ? 5 : 0,
4078c2ecf20Sopenharmony_ci	       ret   >> 1, ret   & 1 ? 5 : 0,
4088c2ecf20Sopenharmony_ci	       fan_state[netwinder_get_fan()]);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	return 0;
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic void __exit ds1620_exit(void)
4148c2ecf20Sopenharmony_ci{
4158c2ecf20Sopenharmony_ci#ifdef THERM_USE_PROC
4168c2ecf20Sopenharmony_ci	remove_proc_entry("therm", NULL);
4178c2ecf20Sopenharmony_ci#endif
4188c2ecf20Sopenharmony_ci	misc_deregister(&ds1620_miscdev);
4198c2ecf20Sopenharmony_ci}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_cimodule_init(ds1620_init);
4228c2ecf20Sopenharmony_cimodule_exit(ds1620_exit);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
425