aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/ds1302.c
blob: 170693c93c73d275fceb2c310c41df8a323c9297 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
/*!***************************************************************************
*!
*! FILE NAME  : ds1302.c
*!
*! DESCRIPTION: Implements an interface for the DS1302 RTC
*!
*! Functions exported: ds1302_readreg, ds1302_writereg, ds1302_init, get_rtc_status
*!
*! ---------------------------------------------------------------------------
*!
*! (C) Copyright 1999, 2000, 2001  Axis Communications AB, LUND, SWEDEN
*!
*!***************************************************************************/


#include <linux/fs.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/bcd.h>
#include <linux/smp_lock.h>
#include <linux/uaccess.h>
#include <linux/io.h>

#include <asm/system.h>
#include <asm/rtc.h>
#if defined(CONFIG_M32R)
#include <asm/m32r.h>
#endif

#define RTC_MAJOR_NR 121 /* local major, change later */

static const char ds1302_name[] = "ds1302";

/* Send 8 bits. */
static void
out_byte_rtc(unsigned int reg_addr, unsigned char x)
{
	//RST H
	outw(0x0001,(unsigned long)PLD_RTCRSTODT);
	//write data
	outw(((x<<8)|(reg_addr&0xff)),(unsigned long)PLD_RTCWRDATA);
	//WE
	outw(0x0002,(unsigned long)PLD_RTCCR);
	//wait
	while(inw((unsigned long)PLD_RTCCR));

	//RST L
	outw(0x0000,(unsigned long)PLD_RTCRSTODT);

}

static unsigned char
in_byte_rtc(unsigned int reg_addr)
{
	unsigned char retval;

	//RST H
	outw(0x0001,(unsigned long)PLD_RTCRSTODT);
	//write data
	outw((reg_addr&0xff),(unsigned long)PLD_RTCRDDATA);
	//RE
	outw(0x0001,(unsigned long)PLD_RTCCR);
	//wait
	while(inw((unsigned long)PLD_RTCCR));

	//read data
	retval=(inw((unsigned long)PLD_RTCRDDATA) & 0xff00)>>8;

	//RST L
	outw(0x0000,(unsigned long)PLD_RTCRSTODT);

	return retval;
}

/* Enable writing. */

static void
ds1302_wenable(void)
{
	out_byte_rtc(0x8e,0x00);
}

/* Disable writing. */

static void
ds1302_wdisable(void)
{
	out_byte_rtc(0x8e,0x80);
}



/* Read a byte from the selected register in the DS1302. */

unsigned char
ds1302_readreg(int reg)
{
	unsigned char x;

	x=in_byte_rtc((0x81 | (reg << 1))); /* read register */

	return x;
}

/* Write a byte to the selected register. */

void
ds1302_writereg(int reg, unsigned char val)
{
	ds1302_wenable();
	out_byte_rtc((0x80 | (reg << 1)),val);
	ds1302_wdisable();
}

void
get_rtc_time(struct rtc_time *rtc_tm)
{
	unsigned long flags;

	local_irq_save(flags);

	rtc_tm->tm_sec = CMOS_READ(RTC_SECONDS);
	rtc_tm->tm_min = CMOS_READ(RTC_MINUTES);
	rtc_tm->tm_hour = CMOS_READ(RTC_HOURS);
	rtc_tm->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH);
	rtc_tm->tm_mon = CMOS_READ(RTC_MONTH);
	rtc_tm->tm_year = CMOS_READ(RTC_YEAR);

	local_irq_restore(flags);

	rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec);
	rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min);
	rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour);
	rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday);
	rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon);
	rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);

	/*
	 * Account for differences between how the RTC uses the values
	 * and how they are defined in a struct rtc_time;
	 */

	if (rtc_tm->tm_year <= 69)
		rtc_tm->tm_year += 100;

	rtc_tm->tm_mon--;
}

static unsigned char days_in_mo[] =
    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

/* ioctl that supports RTC_RD_TIME and RTC_SET_TIME (read and set time/date). */

static long rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	unsigned long flags;

	switch(cmd) {
		case RTC_RD_TIME:	/* read the time/date from RTC	*/
		{
			struct rtc_time rtc_tm;

			memset(&rtc_tm, 0, sizeof (struct rtc_time));
			lock_kernel();
			get_rtc_time(&rtc_tm);
			unlock_kernel();
			if (copy_to_user((struct rtc_time*)arg, &rtc_tm, sizeof(struct rtc_time)))
				return -EFAULT;
			return 0;
		}

		case RTC_SET_TIME:	/* set the RTC */
		{
			struct rtc_time rtc_tm;
			unsigned char mon, day, hrs, min, sec, leap_yr;
			unsigned int yrs;

			if (!capable(CAP_SYS_TIME))
				return -EPERM;

			if (copy_from_user(&rtc_tm, (struct rtc_time*)arg, sizeof(struct rtc_time)))
				return -EFAULT;

			yrs = rtc_tm.tm_year + 1900;
			mon = rtc_tm.tm_mon + 1;   /* tm_mon starts at zero */
			day = rtc_tm.tm_mday;
			hrs = rtc_tm.tm_hour;
			min = rtc_tm.tm_min;
			sec = rtc_tm.tm_sec;


			if ((yrs < 1970) || (yrs > 2069))
				return -EINVAL;

			leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));

			if ((mon > 12) || (day == 0))
				return -EINVAL;

			if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr)))
				return -EINVAL;

			if ((hrs >= 24) || (min >= 60) || (sec >= 60))
				return -EINVAL;

			if (yrs >= 2000)
				yrs -= 2000;	/* RTC (0, 1, ... 69) */
			else
				yrs -= 1900;	/* RTC (70, 71, ... 99) */

			sec = bin2bcd(sec);
			min = bin2bcd(min);
			hrs = bin2bcd(hrs);
			day = bin2bcd(day);
			mon = bin2bcd(mon);
			yrs = bin2bcd(yrs);

			lock_kernel();
			local_irq_save(flags);
			CMOS_WRITE(yrs, RTC_YEAR);
			CMOS_WRITE(mon, RTC_MONTH);
			CMOS_WRITE(day, RTC_DAY_OF_MONTH);
			CMOS_WRITE(hrs, RTC_HOURS);
			CMOS_WRITE(min, RTC_MINUTES);
			CMOS_WRITE(sec, RTC_SECONDS);
			local_irq_restore(flags);
			unlock_kernel();

			/* Notice that at this point, the RTC is updated but
			 * the kernel is still running with the old time.
			 * You need to set that separately with settimeofday
			 * or adjtimex.
			 */
			return 0;
		}

		case RTC_SET_CHARGE: /* set the RTC TRICKLE CHARGE register */
		{
			int tcs_val;

			if (!capable(CAP_SYS_TIME))
				return -EPERM;

			if(copy_from_user(&tcs_val, (int*)arg, sizeof(int)))
				return -EFAULT;

			lock_kernel();
			tcs_val = RTC_TCR_PATTERN | (tcs_val & 0x0F);
			ds1302_writereg(RTC_TRICKLECHARGER, tcs_val);
			unlock_kernel();
			return 0;
		}
		default:
			return -EINVAL;
	}
}

int
get_rtc_status(char *buf)
{
	char *p;
	struct rtc_time tm;

	p = buf;

	get_rtc_time(&tm);

	/*
	 * There is no way to tell if the luser has the RTC set for local
	 * time or for Universal Standard Time (GMT). Probably local though.
	 */

	p += sprintf(p,
		"rtc_time\t: %02d:%02d:%02d\n"
		"rtc_date\t: %04d-%02d-%02d\n",
		tm.tm_hour, tm.tm_min, tm.tm_sec,
		tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);

	return  p - buf;
}


/* The various file operations we support. */

static const struct file_operations rtc_fops = {
	.owner		= THIS_MODULE,
	.unlocked_ioctl	= rtc_ioctl,
};

/* Probe for the chip by writing something to its RAM and try reading it back. */

#define MAGIC_PATTERN 0x42

static int __init
ds1302_probe(void)
{
	int retval, res, baur;

	baur=(boot_cpu_data.bus_clock/(2*1000*1000));

	printk("%s: Set PLD_RTCBAUR = %d\n", ds1302_name,baur);

	outw(0x0000,(unsigned long)PLD_RTCCR);
	outw(0x0000,(unsigned long)PLD_RTCRSTODT);
	outw(baur,(unsigned long)PLD_RTCBAUR);

	/* Try to talk to timekeeper. */

	ds1302_wenable();
	/* write RAM byte 0 */
	/* write something magic */
	out_byte_rtc(0xc0,MAGIC_PATTERN);

	/* read RAM byte 0 */
	if((res = in_byte_rtc(0xc1)) == MAGIC_PATTERN) {
		char buf[100];
		ds1302_wdisable();
		printk("%s: RTC found.\n", ds1302_name);
		get_rtc_status(buf);
		printk(buf);
		retval = 1;
	} else {
		printk("%s: RTC not found.\n", ds1302_name);
		retval = 0;
	}

	return retval;
}


/* Just probe for the RTC and register the device to handle the ioctl needed. */

int __init
ds1302_init(void)
{
	if (!ds1302_probe()) {
		return -1;
  	}
	return 0;
}

static int __init ds1302_register(void)
{
	ds1302_init();
	if (register_chrdev(RTC_MAJOR_NR, ds1302_name, &rtc_fops)) {
		printk(KERN_INFO "%s: unable to get major %d for rtc\n",
		       ds1302_name, RTC_MAJOR_NR);
		return -1;
	}
	return 0;
}

module_init(ds1302_register);
); } /* These constants are a guess, consistent w/ w83781d */ #define ASB100_TEMP_MIN (-128000) #define ASB100_TEMP_MAX ( 127000) /* TEMP: 0.001C/bit (-128C to +127C) REG: 1C/bit, two's complement */ static u8 TEMP_TO_REG(long temp) { int ntemp = SENSORS_LIMIT(temp, ASB100_TEMP_MIN, ASB100_TEMP_MAX); ntemp += (ntemp<0 ? -500 : 500); return (u8)(ntemp / 1000); } static int TEMP_FROM_REG(u8 reg) { return (s8)reg * 1000; } /* PWM: 0 - 255 per sensors documentation REG: (6.25% duty cycle per bit) */ static u8 ASB100_PWM_TO_REG(int pwm) { pwm = SENSORS_LIMIT(pwm, 0, 255); return (u8)(pwm / 16); } static int ASB100_PWM_FROM_REG(u8 reg) { return reg * 16; } #define DIV_FROM_REG(val) (1 << (val)) /* FAN DIV: 1, 2, 4, or 8 (defaults to 2) REG: 0, 1, 2, or 3 (respectively) (defaults to 1) */ static u8 DIV_TO_REG(long val) { return val==8 ? 3 : val==4 ? 2 : val==1 ? 0 : 1; } /* For each registered client, we need to keep some data in memory. That data is pointed to by client->data. The structure itself is dynamically allocated, at the same time the client itself is allocated. */ struct asb100_data { struct device *hwmon_dev; struct mutex lock; struct mutex update_lock; unsigned long last_updated; /* In jiffies */ /* array of 2 pointers to subclients */ struct i2c_client *lm75[2]; char valid; /* !=0 if following fields are valid */ u8 in[7]; /* Register value */ u8 in_max[7]; /* Register value */ u8 in_min[7]; /* Register value */ u8 fan[3]; /* Register value */ u8 fan_min[3]; /* Register value */ u16 temp[4]; /* Register value (0 and 3 are u8 only) */ u16 temp_max[4]; /* Register value (0 and 3 are u8 only) */ u16 temp_hyst[4]; /* Register value (0 and 3 are u8 only) */ u8 fan_div[3]; /* Register encoding, right justified */ u8 pwm; /* Register encoding */ u8 vid; /* Register encoding, combined */ u32 alarms; /* Register encoding, combined */ u8 vrm; }; static int asb100_read_value(struct i2c_client *client, u16 reg); static void asb100_write_value(struct i2c_client *client, u16 reg, u16 val); static int asb100_probe(struct i2c_client *client, const struct i2c_device_id *id); static int asb100_detect(struct i2c_client *client, int kind, struct i2c_board_info *info); static int asb100_remove(struct i2c_client *client); static struct asb100_data *asb100_update_device(struct device *dev); static void asb100_init_client(struct i2c_client *client); static const struct i2c_device_id asb100_id[] = { { "asb100", asb100 }, { } }; MODULE_DEVICE_TABLE(i2c, asb100_id); static struct i2c_driver asb100_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "asb100", }, .probe = asb100_probe, .remove = asb100_remove, .id_table = asb100_id, .detect = asb100_detect, .address_data = &addr_data, }; /* 7 Voltages */ #define show_in_reg(reg) \ static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \ char *buf) \ { \ int nr = to_sensor_dev_attr(attr)->index; \ struct asb100_data *data = asb100_update_device(dev); \ return sprintf(buf, "%d\n", IN_FROM_REG(data->reg[nr])); \ } show_in_reg(in) show_in_reg(in_min) show_in_reg(in_max) #define set_in_reg(REG, reg) \ static ssize_t set_in_##reg(struct device *dev, struct device_attribute *attr, \ const char *buf, size_t count) \ { \ int nr = to_sensor_dev_attr(attr)->index; \ struct i2c_client *client = to_i2c_client(dev); \ struct asb100_data *data = i2c_get_clientdata(client); \ unsigned long val = simple_strtoul(buf, NULL, 10); \ \ mutex_lock(&data->update_lock); \ data->in_##reg[nr] = IN_TO_REG(val); \ asb100_write_value(client, ASB100_REG_IN_##REG(nr), \ data->in_##reg[nr]); \ mutex_unlock(&data->update_lock); \ return count; \ } set_in_reg(MIN, min) set_in_reg(MAX, max) #define sysfs_in(offset) \ static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, \ show_in, NULL, offset); \ static SENSOR_DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ show_in_min, set_in_min, offset); \ static SENSOR_DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ show_in_max, set_in_max, offset) sysfs_in(0); sysfs_in(1); sysfs_in(2); sysfs_in(3); sysfs_in(4); sysfs_in(5); sysfs_in(6); /* 3 Fans */ static ssize_t show_fan(struct device *dev, struct device_attribute *attr, char *buf) { int nr = to_sensor_dev_attr(attr)->index; struct asb100_data *data = asb100_update_device(dev); return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr], DIV_FROM_REG(data->fan_div[nr]))); } static ssize_t show_fan_min(struct device *dev, struct device_attribute *attr, char *buf) { int nr = to_sensor_dev_attr(attr)->index; struct asb100_data *data = asb100_update_device(dev); return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr]))); } static ssize_t show_fan_div(struct device *dev, struct device_attribute *attr, char *buf) { int nr = to_sensor_dev_attr(attr)->index; struct asb100_data *data = asb100_update_device(dev); return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr])); } static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct asb100_data *data = i2c_get_clientdata(client); u32 val = simple_strtoul(buf, NULL, 10); mutex_lock(&data->update_lock); data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); asb100_write_value(client, ASB100_REG_FAN_MIN(nr), data->fan_min[nr]); mutex_unlock(&data->update_lock); return count; } /* Note: we save and restore the fan minimum here, because its value is determined in part by the fan divisor. This follows the principle of least surprise; the user doesn't expect the fan minimum to change just because the divisor changed. */ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct asb100_data *data = i2c_get_clientdata(client); unsigned long min; unsigned long val = simple_strtoul(buf, NULL, 10); int reg; mutex_lock(&data->update_lock); min = FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr])); data->fan_div[nr] = DIV_TO_REG(val); switch (nr) { case 0: /* fan 1 */ reg = asb100_read_value(client, ASB100_REG_VID_FANDIV); reg = (reg & 0xcf) | (data->fan_div[0] << 4); asb100_write_value(client, ASB100_REG_VID_FANDIV, reg); break; case 1: /* fan 2 */ reg = asb100_read_value(client, ASB100_REG_VID_FANDIV); reg = (reg & 0x3f) | (data->fan_div[1] << 6); asb100_write_value(client, ASB100_REG_VID_FANDIV, reg); break; case 2: /* fan 3 */ reg = asb100_read_value(client, ASB100_REG_PIN); reg = (reg & 0x3f) | (data->fan_div[2] << 6); asb100_write_value(client, ASB100_REG_PIN, reg); break; } data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); asb100_write_value(client, ASB100_REG_FAN_MIN(nr), data->fan_min[nr]); mutex_unlock(&data->update_lock); return count; } #define sysfs_fan(offset) \ static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, \ show_fan, NULL, offset - 1); \ static SENSOR_DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ show_fan_min, set_fan_min, offset - 1); \ static SENSOR_DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \ show_fan_div, set_fan_div, offset - 1) sysfs_fan(1); sysfs_fan(2); sysfs_fan(3); /* 4 Temp. Sensors */ static int sprintf_temp_from_reg(u16 reg, char *buf, int nr) { int ret = 0; switch (nr) { case 1: case 2: ret = sprintf(buf, "%d\n", LM75_TEMP_FROM_REG(reg)); break; case 0: case 3: default: ret = sprintf(buf, "%d\n", TEMP_FROM_REG(reg)); break; } return ret; } #define show_temp_reg(reg) \ static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \ char *buf) \ { \ int nr = to_sensor_dev_attr(attr)->index; \ struct asb100_data *data = asb100_update_device(dev); \ return sprintf_temp_from_reg(data->reg[nr], buf, nr); \ } show_temp_reg(temp); show_temp_reg(temp_max); show_temp_reg(temp_hyst); #define set_temp_reg(REG, reg) \ static ssize_t set_##reg(struct device *dev, struct device_attribute *attr, \ const char *buf, size_t count) \ { \ int nr = to_sensor_dev_attr(attr)->index; \ struct i2c_client *client = to_i2c_client(dev); \ struct asb100_data *data = i2c_get_clientdata(client); \ long val = simple_strtol(buf, NULL, 10); \ \ mutex_lock(&data->update_lock); \ switch (nr) { \ case 1: case 2: \ data->reg[nr] = LM75_TEMP_TO_REG(val); \ break; \ case 0: case 3: default: \ data->reg[nr] = TEMP_TO_REG(val); \ break; \ } \ asb100_write_value(client, ASB100_REG_TEMP_##REG(nr+1), \ data->reg[nr]); \ mutex_unlock(&data->update_lock); \ return count; \ } set_temp_reg(MAX, temp_max); set_temp_reg(HYST, temp_hyst); #define sysfs_temp(num) \ static SENSOR_DEVICE_ATTR(temp##num##_input, S_IRUGO, \ show_temp, NULL, num - 1); \ static SENSOR_DEVICE_ATTR(temp##num##_max, S_IRUGO | S_IWUSR, \ show_temp_max, set_temp_max, num - 1); \ static SENSOR_DEVICE_ATTR(temp##num##_max_hyst, S_IRUGO | S_IWUSR, \ show_temp_hyst, set_temp_hyst, num - 1) sysfs_temp(1); sysfs_temp(2); sysfs_temp(3); sysfs_temp(4); /* VID */ static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf) { struct asb100_data *data = asb100_update_device(dev); return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); } static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); /* VRM */ static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, char *buf) { struct asb100_data *data = dev_get_drvdata(dev); return sprintf(buf, "%d\n", data->vrm); } static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct asb100_data *data = dev_get_drvdata(dev); data->vrm = simple_strtoul(buf, NULL, 10); return count; } /* Alarms */ static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm); static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) { struct asb100_data *data = asb100_update_device(dev); return sprintf(buf, "%u\n", data->alarms); } static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, char *buf) { int bitnr = to_sensor_dev_attr(attr)->index; struct asb100_data *data = asb100_update_device(dev); return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1); } static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0); static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1); static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2); static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3); static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 8); static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 6); static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7); static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 11); static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4); static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 5); static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13); /* 1 PWM */ static ssize_t show_pwm1(struct device *dev, struct device_attribute *attr, char *buf) { struct asb100_data *data = asb100_update_device(dev); return sprintf(buf, "%d\n", ASB100_PWM_FROM_REG(data->pwm & 0x0f)); } static ssize_t set_pwm1(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); struct asb100_data *data = i2c_get_clientdata(client); unsigned long val = simple_strtoul(buf, NULL, 10); mutex_lock(&data->update_lock); data->pwm &= 0x80; /* keep the enable bit */ data->pwm |= (0x0f & ASB100_PWM_TO_REG(val)); asb100_write_value(client, ASB100_REG_PWM1, data->pwm); mutex_unlock(&data->update_lock); return count; } static ssize_t show_pwm_enable1(struct device *dev, struct device_attribute *attr, char *buf) { struct asb100_data *data = asb100_update_device(dev); return sprintf(buf, "%d\n", (data->pwm & 0x80) ? 1 : 0); } static ssize_t set_pwm_enable1(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); struct asb100_data *data = i2c_get_clientdata(client); unsigned long val = simple_strtoul(buf, NULL, 10); mutex_lock(&data->update_lock); data->pwm &= 0x0f; /* keep the duty cycle bits */ data->pwm |= (val ? 0x80 : 0x00); asb100_write_value(client, ASB100_REG_PWM1, data->pwm); mutex_unlock(&data->update_lock); return count; } static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm1, set_pwm1); static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, show_pwm_enable1, set_pwm_enable1); static struct attribute *asb100_attributes[] = { &sensor_dev_attr_in0_input.dev_attr.attr, &sensor_dev_attr_in0_min.dev_attr.attr, &sensor_dev_attr_in0_max.dev_attr.attr, &sensor_dev_attr_in1_input.dev_attr.attr, &sensor_dev_attr_in1_min.dev_attr.attr, &sensor_dev_attr_in1_max.dev_attr.attr, &sensor_dev_attr_in2_input.dev_attr.attr, &sensor_dev_attr_in2_min.dev_attr.attr, &sensor_dev_attr_in2_max.dev_attr.attr, &sensor_dev_attr_in3_input.dev_attr.attr, &sensor_dev_attr_in3_min.dev_attr.attr, &sensor_dev_attr_in3_max.dev_attr.attr, &sensor_dev_attr_in4_input.dev_attr.attr, &sensor_dev_attr_in4_min.dev_attr.attr, &sensor_dev_attr_in4_max.dev_attr.attr, &sensor_dev_attr_in5_input.dev_attr.attr, &sensor_dev_attr_in5_min.dev_attr.attr, &sensor_dev_attr_in5_max.dev_attr.attr, &sensor_dev_attr_in6_input.dev_attr.attr, &sensor_dev_attr_in6_min.dev_attr.attr, &sensor_dev_attr_in6_max.dev_attr.attr, &sensor_dev_attr_fan1_input.dev_attr.attr, &sensor_dev_attr_fan1_min.dev_attr.attr, &sensor_dev_attr_fan1_div.dev_attr.attr, &sensor_dev_attr_fan2_input.dev_attr.attr, &sensor_dev_attr_fan2_min.dev_attr.attr, &sensor_dev_attr_fan2_div.dev_attr.attr, &sensor_dev_attr_fan3_input.dev_attr.attr, &sensor_dev_attr_fan3_min.dev_attr.attr, &sensor_dev_attr_fan3_div.dev_attr.attr, &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, &sensor_dev_attr_temp2_input.dev_attr.attr, &sensor_dev_attr_temp2_max.dev_attr.attr, &sensor_dev_attr_temp2_max_hyst.dev_attr.attr, &sensor_dev_attr_temp3_input.dev_attr.attr, &sensor_dev_attr_temp3_max.dev_attr.attr, &sensor_dev_attr_temp3_max_hyst.dev_attr.attr, &sensor_dev_attr_temp4_input.dev_attr.attr, &sensor_dev_attr_temp4_max.dev_attr.attr, &sensor_dev_attr_temp4_max_hyst.dev_attr.attr, &sensor_dev_attr_in0_alarm.dev_attr.attr, &sensor_dev_attr_in1_alarm.dev_attr.attr, &sensor_dev_attr_in2_alarm.dev_attr.attr, &sensor_dev_attr_in3_alarm.dev_attr.attr, &sensor_dev_attr_in4_alarm.dev_attr.attr, &sensor_dev_attr_fan1_alarm.dev_attr.attr, &sensor_dev_attr_fan2_alarm.dev_attr.attr, &sensor_dev_attr_fan3_alarm.dev_attr.attr, &sensor_dev_attr_temp1_alarm.dev_attr.attr, &sensor_dev_attr_temp2_alarm.dev_attr.attr, &sensor_dev_attr_temp3_alarm.dev_attr.attr, &dev_attr_cpu0_vid.attr, &dev_attr_vrm.attr, &dev_attr_alarms.attr, &dev_attr_pwm1.attr, &dev_attr_pwm1_enable.attr, NULL }; static const struct attribute_group asb100_group = { .attrs = asb100_attributes, }; static int asb100_detect_subclients(struct i2c_client *client) { int i, id, err; int address = client->addr; unsigned short sc_addr[2]; struct asb100_data *data = i2c_get_clientdata(client); struct i2c_adapter *adapter = client->adapter; id = i2c_adapter_id(adapter); if (force_subclients[0] == id && force_subclients[1] == address) { for (i = 2; i <= 3; i++) { if (force_subclients[i] < 0x48 || force_subclients[i] > 0x4f) { dev_err(&client->dev, "invalid subclient " "address %d; must be 0x48-0x4f\n", force_subclients[i]); err = -ENODEV; goto ERROR_SC_2; } } asb100_write_value(client, ASB100_REG_I2C_SUBADDR, (force_subclients[2] & 0x07) | ((force_subclients[3] & 0x07) << 4)); sc_addr[0] = force_subclients[2]; sc_addr[1] = force_subclients[3]; } else { int val = asb100_read_value(client, ASB100_REG_I2C_SUBADDR); sc_addr[0] = 0x48 + (val & 0x07); sc_addr[1] = 0x48 + ((val >> 4) & 0x07); } if (sc_addr[0] == sc_addr[1]) { dev_err(&client->dev, "duplicate addresses 0x%x " "for subclients\n", sc_addr[0]); err = -ENODEV; goto ERROR_SC_2; } data->lm75[0] = i2c_new_dummy(adapter, sc_addr[0]); if (!data->lm75[0]) { dev_err(&client->dev, "subclient %d registration " "at address 0x%x failed.\n", 1, sc_addr[0]); err = -ENOMEM; goto ERROR_SC_2; } data->lm75[1] = i2c_new_dummy(adapter, sc_addr[1]); if (!data->lm75[1]) { dev_err(&client->dev, "subclient %d registration " "at address 0x%x failed.\n", 2, sc_addr[1]); err = -ENOMEM; goto ERROR_SC_3; } return 0; /* Undo inits in case of errors */ ERROR_SC_3: i2c_unregister_device(data->lm75[0]); ERROR_SC_2: return err; } /* Return 0 if detection is successful, -ENODEV otherwise */ static int asb100_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { pr_debug("asb100.o: detect failed, " "smbus byte data not supported!\n"); return -ENODEV; } /* The chip may be stuck in some other bank than bank 0. This may make reading other information impossible. Specify a force=... or force_*=... parameter, and the chip will be reset to the right bank. */ if (kind < 0) { int val1 = i2c_smbus_read_byte_data(client, ASB100_REG_BANK); int val2 = i2c_smbus_read_byte_data(client, ASB100_REG_CHIPMAN); /* If we're in bank 0 */ if ((!(val1 & 0x07)) && /* Check for ASB100 ID (low byte) */ (((!(val1 & 0x80)) && (val2 != 0x94)) || /* Check for ASB100 ID (high byte ) */ ((val1 & 0x80) && (val2 != 0x06)))) { pr_debug("asb100.o: detect failed, " "bad chip id 0x%02x!\n", val2); return -ENODEV; } } /* kind < 0 */ /* We have either had a force parameter, or we have already detected Winbond. Put it now into bank 0 and Vendor ID High Byte */ i2c_smbus_write_byte_data(client, ASB100_REG_BANK, (i2c_smbus_read_byte_data(client, ASB100_REG_BANK) & 0x78) | 0x80); /* Determine the chip type. */ if (kind <= 0) { int val1 = i2c_smbus_read_byte_data(client, ASB100_REG_WCHIPID); int val2 = i2c_smbus_read_byte_data(client, ASB100_REG_CHIPMAN); if ((val1 == 0x31) && (val2 == 0x06)) kind = asb100; else { if (kind == 0) dev_warn(&adapter->dev, "ignoring " "'force' parameter for unknown chip " "at adapter %d, address 0x%02x.\n", i2c_adapter_id(adapter), client->addr); return -ENODEV; } } strlcpy(info->type, "asb100", I2C_NAME_SIZE); return 0; } static int asb100_probe(struct i2c_client *client, const struct i2c_device_id *id) { int err; struct asb100_data *data; data = kzalloc(sizeof(struct asb100_data), GFP_KERNEL); if (!data) { pr_debug("asb100.o: probe failed, kzalloc failed!\n"); err = -ENOMEM; goto ERROR0; } i2c_set_clientdata(client, data); mutex_init(&data->lock); mutex_init(&data->update_lock); /* Attach secondary lm75 clients */ err = asb100_detect_subclients(client); if (err) goto ERROR1; /* Initialize the chip */ asb100_init_client(client); /* A few vars need to be filled upon startup */ data->fan_min[0] = asb100_read_value(client, ASB100_REG_FAN_MIN(0)); data->fan_min[1] = asb100_read_value(client, ASB100_REG_FAN_MIN(1)); data->fan_min[2] = asb100_read_value(client, ASB100_REG_FAN_MIN(2)); /* Register sysfs hooks */ if ((err = sysfs_create_group(&client->dev.kobj, &asb100_group))) goto ERROR3; data->hwmon_dev = hwmon_device_register(&client->dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); goto ERROR4; } return 0; ERROR4: sysfs_remove_group(&client->dev.kobj, &asb100_group); ERROR3: i2c_unregister_device(data->lm75[1]); i2c_unregister_device(data->lm75[0]); ERROR1: kfree(data); ERROR0: return err; } static int asb100_remove(struct i2c_client *client) { struct asb100_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &asb100_group); i2c_unregister_device(data->lm75[1]); i2c_unregister_device(data->lm75[0]); kfree(data); return 0; } /* The SMBus locks itself, usually, but nothing may access the chip between bank switches. */ static int asb100_read_value(struct i2c_client *client, u16 reg) { struct asb100_data *data = i2c_get_clientdata(client); struct i2c_client *cl; int res, bank; mutex_lock(&data->lock); bank = (reg >> 8) & 0x0f; if (bank > 2) /* switch banks */ i2c_smbus_write_byte_data(client, ASB100_REG_BANK, bank); if (bank == 0 || bank > 2) { res = i2c_smbus_read_byte_data(client, reg & 0xff); } else { /* switch to subclient */ cl = data->lm75[bank - 1]; /* convert from ISA to LM75 I2C addresses */ switch (reg & 0xff) { case 0x50: /* TEMP */ res = swab16(i2c_smbus_read_word_data(cl, 0)); break; case 0x52: /* CONFIG */ res = i2c_smbus_read_byte_data(cl, 1); break; case 0x53: /* HYST */ res = swab16(i2c_smbus_read_word_data(cl, 2)); break; case 0x55: /* MAX */ default: res = swab16(i2c_smbus_read_word_data(cl, 3)); break; } } if (bank > 2) i2c_smbus_write_byte_data(client, ASB100_REG_BANK, 0); mutex_unlock(&data->lock); return res; } static void asb100_write_value(struct i2c_client *client, u16 reg, u16 value) { struct asb100_data *data = i2c_get_clientdata(client); struct i2c_client *cl; int bank; mutex_lock(&data->lock); bank = (reg >> 8) & 0x0f; if (bank > 2) /* switch banks */ i2c_smbus_write_byte_data(client, ASB100_REG_BANK, bank); if (bank == 0 || bank > 2) { i2c_smbus_write_byte_data(client, reg & 0xff, value & 0xff); } else { /* switch to subclient */ cl = data->lm75[bank - 1]; /* convert from ISA to LM75 I2C addresses */ switch (reg & 0xff) { case 0x52: /* CONFIG */ i2c_smbus_write_byte_data(cl, 1, value & 0xff); break; case 0x53: /* HYST */ i2c_smbus_write_word_data(cl, 2, swab16(value)); break; case 0x55: /* MAX */ i2c_smbus_write_word_data(cl, 3, swab16(value)); break; } } if (bank > 2) i2c_smbus_write_byte_data(client, ASB100_REG_BANK, 0); mutex_unlock(&data->lock); } static void asb100_init_client(struct i2c_client *client) { struct asb100_data *data = i2c_get_clientdata(client); data->vrm = vid_which_vrm(); /* Start monitoring */ asb100_write_value(client, ASB100_REG_CONFIG, (asb100_read_value(client, ASB100_REG_CONFIG) & 0xf7) | 0x01); } static struct asb100_data *asb100_update_device(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct asb100_data *data = i2c_get_clientdata(client); int i; mutex_lock(&data->update_lock); if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || !data->valid) { dev_dbg(&client->dev, "starting device update...\n"); /* 7 voltage inputs */ for (i = 0; i < 7; i++) { data->in[i] = asb100_read_value(client, ASB100_REG_IN(i)); data->in_min[i] = asb100_read_value(client, ASB100_REG_IN_MIN(i)); data->in_max[i] = asb100_read_value(client, ASB100_REG_IN_MAX(i)); } /* 3 fan inputs */ for (i = 0; i < 3; i++) { data->fan[i] = asb100_read_value(client, ASB100_REG_FAN(i)); data->fan_min[i] = asb100_read_value(client, ASB100_REG_FAN_MIN(i)); } /* 4 temperature inputs */ for (i = 1; i <= 4; i++) { data->temp[i-1] = asb100_read_value(client, ASB100_REG_TEMP(i)); data->temp_max[i-1] = asb100_read_value(client, ASB100_REG_TEMP_MAX(i)); data->temp_hyst[i-1] = asb100_read_value(client, ASB100_REG_TEMP_HYST(i)); } /* VID and fan divisors */ i = asb100_read_value(client, ASB100_REG_VID_FANDIV); data->vid = i & 0x0f; data->vid |= (asb100_read_value(client, ASB100_REG_CHIPID) & 0x01) << 4; data->fan_div[0] = (i >> 4) & 0x03; data->fan_div[1] = (i >> 6) & 0x03; data->fan_div[2] = (asb100_read_value(client, ASB100_REG_PIN) >> 6) & 0x03; /* PWM */ data->pwm = asb100_read_value(client, ASB100_REG_PWM1); /* alarms */ data->alarms = asb100_read_value(client, ASB100_REG_ALARM1) + (asb100_read_value(client, ASB100_REG_ALARM2) << 8); data->last_updated = jiffies; data->valid = 1; dev_dbg(&client->dev, "... device update complete\n"); } mutex_unlock(&data->update_lock); return data; } static int __init asb100_init(void) { return i2c_add_driver(&asb100_driver); } static void __exit asb100_exit(void) { i2c_del_driver(&asb100_driver); } MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>"); MODULE_DESCRIPTION("ASB100 Bach driver"); MODULE_LICENSE("GPL"); module_init(asb100_init); module_exit(asb100_exit);