brintos

brintos / linux-shallow public Read only

0
0
Text · 8.4 KiB · 44a1cdb Raw
425 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * linux/drivers/char/ds1620.c: Dallas Semiconductors DS16204 *   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_FS21/* define for /proc interface */22#define THERM_USE_PROC23#endif24 25/* Definitions for DS1620 chip */26#define THERM_START_CONVERT	0xee27#define THERM_RESET		0xaf28#define THERM_READ_CONFIG	0xac29#define THERM_READ_TEMP		0xaa30#define THERM_READ_TL		0xa231#define THERM_READ_TH		0xa132#define THERM_WRITE_CONFIG	0x0c33#define THERM_WRITE_TL		0x0234#define THERM_WRITE_TH		0x0135 36#define CFG_CPU			237#define CFG_1SHOT		138 39static DEFINE_MUTEX(ds1620_mutex);40static const char *fan_state[] = { "off", "on", "on (hardwired)" };41 42/*43 * Start of NetWinder specifics44 *  Note!  We have to hold the gpio lock with IRQs disabled over the45 *  whole of our transaction to the Dallas chip, since there is a46 *  chance that the WaveArtist driver could touch these bits to47 *  enable or disable the speaker.48 */49extern unsigned int system_rev;50 51static inline void netwinder_ds1620_set_clk(int clk)52{53	nw_gpio_modify_op(GPIO_DSCLK, clk ? GPIO_DSCLK : 0);54}55 56static inline void netwinder_ds1620_set_data(int dat)57{58	nw_gpio_modify_op(GPIO_DATA, dat ? GPIO_DATA : 0);59}60 61static inline int netwinder_ds1620_get_data(void)62{63	return nw_gpio_read() & GPIO_DATA;64}65 66static inline void netwinder_ds1620_set_data_dir(int dir)67{68	nw_gpio_modify_io(GPIO_DATA, dir ? GPIO_DATA : 0);69}70 71static 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 77static inline void netwinder_lock(unsigned long *flags)78{79	raw_spin_lock_irqsave(&nw_gpio_lock, *flags);80}81 82static inline void netwinder_unlock(unsigned long *flags)83{84	raw_spin_unlock_irqrestore(&nw_gpio_lock, *flags);85}86 87static 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 96static 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 specifics106 */107 108static 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 123static 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 146static 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 169static 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 192static int cvt_9_to_int(unsigned int val)193{194	if (val & 0x100)195		val |= 0xfffffe00;196 197	return val;198}199 200static 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 208static 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 214static int ds1620_open(struct inode *inode, struct file *file)215{216	return stream_open(inode, file);217}218 219static ssize_t220ds1620_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 236static int237ds1620_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 321static long322ds1620_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_PROC334static 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#endif350 351static 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 358static struct miscdevice ds1620_miscdev = {359	TEMP_MINOR,360	"temp",361	&ds1620_fops362};363 364static 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 setting378	 * temperature high point low.  This kicks379	 * 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_PROC395	if (!proc_create_single("therm", 0, NULL, ds1620_proc_therm_show))396		printk(KERN_ERR "therm: unable to register /proc/therm\n");397#endif398 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 412static void __exit ds1620_exit(void)413{414#ifdef THERM_USE_PROC415	remove_proc_entry("therm", NULL);416#endif417	misc_deregister(&ds1620_miscdev);418}419 420module_init(ds1620_init);421module_exit(ds1620_exit);422 423MODULE_DESCRIPTION("Dallas Semiconductor DS1620 thermometer driver");424MODULE_LICENSE("GPL");425