cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ds1620.c (8602B)


      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
     39static DEFINE_MUTEX(ds1620_mutex);
     40static 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 */
     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 specifics
    106 */
    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_t
    220ds1620_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 int
    237ds1620_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 long
    322ds1620_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
    334static 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
    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	.llseek		= no_llseek,
    357};
    358
    359static struct miscdevice ds1620_miscdev = {
    360	TEMP_MINOR,
    361	"temp",
    362	&ds1620_fops
    363};
    364
    365static int __init ds1620_init(void)
    366{
    367	int ret;
    368	struct therm th, th_start;
    369
    370	if (!machine_is_netwinder())
    371		return -ENODEV;
    372
    373	ds1620_out(THERM_RESET, 0, 0);
    374	ds1620_out(THERM_WRITE_CONFIG, 8, CFG_CPU);
    375	ds1620_out(THERM_START_CONVERT, 0, 0);
    376
    377	/*
    378	 * Trigger the fan to start by setting
    379	 * temperature high point low.  This kicks
    380	 * the fan into action.
    381	 */
    382	ds1620_read_state(&th);
    383	th_start.lo = 0;
    384	th_start.hi = 1;
    385	ds1620_write_state(&th_start);
    386
    387	msleep(2000);
    388
    389	ds1620_write_state(&th);
    390
    391	ret = misc_register(&ds1620_miscdev);
    392	if (ret < 0)
    393		return ret;
    394
    395#ifdef THERM_USE_PROC
    396	if (!proc_create_single("therm", 0, NULL, ds1620_proc_therm_show))
    397		printk(KERN_ERR "therm: unable to register /proc/therm\n");
    398#endif
    399
    400	ds1620_read_state(&th);
    401	ret = cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9));
    402
    403	printk(KERN_INFO "Thermostat: high %i.%i, low %i.%i, "
    404	       "current %i.%i C, fan %s.\n",
    405	       th.hi >> 1, th.hi & 1 ? 5 : 0,
    406	       th.lo >> 1, th.lo & 1 ? 5 : 0,
    407	       ret   >> 1, ret   & 1 ? 5 : 0,
    408	       fan_state[netwinder_get_fan()]);
    409
    410	return 0;
    411}
    412
    413static void __exit ds1620_exit(void)
    414{
    415#ifdef THERM_USE_PROC
    416	remove_proc_entry("therm", NULL);
    417#endif
    418	misc_deregister(&ds1620_miscdev);
    419}
    420
    421module_init(ds1620_init);
    422module_exit(ds1620_exit);
    423
    424MODULE_LICENSE("GPL");