| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |     w83781d.c - Part of lm_sensors, Linux kernel modules for hardware | 
 | 3 |                 monitoring | 
 | 4 |     Copyright (c) 1998 - 2001  Frodo Looijaard <frodol@dds.nl>, | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 5 |                                Philip Edelbrock <phil@netroedge.com>, | 
 | 6 |                                and Mark Studebaker <mdsxyz123@yahoo.com> | 
 | 7 |     Copyright (c) 2007         Jean Delvare <khali@linux-fr.org> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 8 |  | 
 | 9 |     This program is free software; you can redistribute it and/or modify | 
 | 10 |     it under the terms of the GNU General Public License as published by | 
 | 11 |     the Free Software Foundation; either version 2 of the License, or | 
 | 12 |     (at your option) any later version. | 
 | 13 |  | 
 | 14 |     This program is distributed in the hope that it will be useful, | 
 | 15 |     but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | 16 |     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 | 17 |     GNU General Public License for more details. | 
 | 18 |  | 
 | 19 |     You should have received a copy of the GNU General Public License | 
 | 20 |     along with this program; if not, write to the Free Software | 
 | 21 |     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 
 | 22 | */ | 
 | 23 |  | 
 | 24 | /* | 
 | 25 |     Supports following chips: | 
 | 26 |  | 
 | 27 |     Chip	#vin	#fanin	#pwm	#temp	wchipid	vendid	i2c	ISA | 
 | 28 |     as99127f	7	3	0	3	0x31	0x12c3	yes	no | 
 | 29 |     as99127f rev.2 (type_name = as99127f)	0x31	0x5ca3	yes	no | 
 | 30 |     w83781d	7	3	0	3	0x10-1	0x5ca3	yes	yes | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 31 |     w83782d	9	3	2-4	3	0x30	0x5ca3	yes	yes | 
 | 32 |     w83783s	5-6	3	2	1-2	0x40	0x5ca3	yes	no | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 33 |  | 
 | 34 | */ | 
 | 35 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 36 | #include <linux/module.h> | 
 | 37 | #include <linux/init.h> | 
 | 38 | #include <linux/slab.h> | 
 | 39 | #include <linux/jiffies.h> | 
 | 40 | #include <linux/i2c.h> | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 41 | #include <linux/platform_device.h> | 
 | 42 | #include <linux/ioport.h> | 
| Mark M. Hoffman | 943b083 | 2005-07-15 21:39:18 -0400 | [diff] [blame] | 43 | #include <linux/hwmon.h> | 
| Jean Delvare | 303760b | 2005-07-31 21:52:01 +0200 | [diff] [blame] | 44 | #include <linux/hwmon-vid.h> | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 45 | #include <linux/hwmon-sysfs.h> | 
| Jim Cromie | 311ce2e | 2006-09-24 21:22:52 +0200 | [diff] [blame] | 46 | #include <linux/sysfs.h> | 
| Mark M. Hoffman | 943b083 | 2005-07-15 21:39:18 -0400 | [diff] [blame] | 47 | #include <linux/err.h> | 
| Ingo Molnar | 9a61bf6 | 2006-01-18 23:19:26 +0100 | [diff] [blame] | 48 | #include <linux/mutex.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 49 | #include <asm/io.h> | 
 | 50 | #include "lm75.h" | 
 | 51 |  | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 52 | /* ISA device, if found */ | 
 | 53 | static struct platform_device *pdev; | 
 | 54 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 55 | /* Addresses to scan */ | 
| Mark M. Hoffman | 25e9c86 | 2008-02-17 22:28:03 -0500 | [diff] [blame] | 56 | static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, | 
 | 57 | 						0x2e, 0x2f, I2C_CLIENT_END }; | 
| Jean Delvare | 2d8672c | 2005-07-19 23:56:35 +0200 | [diff] [blame] | 58 | static unsigned short isa_address = 0x290; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 59 |  | 
 | 60 | /* Insmod parameters */ | 
| Jean Delvare | 0566336 | 2007-11-30 23:51:24 +0100 | [diff] [blame] | 61 | I2C_CLIENT_INSMOD_4(w83781d, w83782d, w83783s, as99127f); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 62 | I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: " | 
 | 63 | 		    "{bus, clientaddr, subclientaddr1, subclientaddr2}"); | 
 | 64 |  | 
| Jean Delvare | fabddcd | 2006-02-05 23:26:51 +0100 | [diff] [blame] | 65 | static int reset; | 
 | 66 | module_param(reset, bool, 0); | 
 | 67 | MODULE_PARM_DESC(reset, "Set to one to reset chip on load"); | 
 | 68 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 69 | static int init = 1; | 
 | 70 | module_param(init, bool, 0); | 
 | 71 | MODULE_PARM_DESC(init, "Set to zero to bypass chip initialization"); | 
 | 72 |  | 
 | 73 | /* Constants specified below */ | 
 | 74 |  | 
 | 75 | /* Length of ISA address segment */ | 
 | 76 | #define W83781D_EXTENT			8 | 
 | 77 |  | 
 | 78 | /* Where are the ISA address/data registers relative to the base address */ | 
 | 79 | #define W83781D_ADDR_REG_OFFSET		5 | 
 | 80 | #define W83781D_DATA_REG_OFFSET		6 | 
 | 81 |  | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 82 | /* The device registers */ | 
 | 83 | /* in nr from 0 to 8 */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 84 | #define W83781D_REG_IN_MAX(nr)		((nr < 7) ? (0x2b + (nr) * 2) : \ | 
 | 85 | 						    (0x554 + (((nr) - 7) * 2))) | 
 | 86 | #define W83781D_REG_IN_MIN(nr)		((nr < 7) ? (0x2c + (nr) * 2) : \ | 
 | 87 | 						    (0x555 + (((nr) - 7) * 2))) | 
 | 88 | #define W83781D_REG_IN(nr)		((nr < 7) ? (0x20 + (nr)) : \ | 
 | 89 | 						    (0x550 + (nr) - 7)) | 
 | 90 |  | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 91 | /* fan nr from 0 to 2 */ | 
 | 92 | #define W83781D_REG_FAN_MIN(nr)		(0x3b + (nr)) | 
 | 93 | #define W83781D_REG_FAN(nr)		(0x28 + (nr)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 94 |  | 
 | 95 | #define W83781D_REG_BANK		0x4E | 
 | 96 | #define W83781D_REG_TEMP2_CONFIG	0x152 | 
 | 97 | #define W83781D_REG_TEMP3_CONFIG	0x252 | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 98 | /* temp nr from 1 to 3 */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 99 | #define W83781D_REG_TEMP(nr)		((nr == 3) ? (0x0250) : \ | 
 | 100 | 					((nr == 2) ? (0x0150) : \ | 
 | 101 | 						     (0x27))) | 
 | 102 | #define W83781D_REG_TEMP_HYST(nr)	((nr == 3) ? (0x253) : \ | 
 | 103 | 					((nr == 2) ? (0x153) : \ | 
 | 104 | 						     (0x3A))) | 
 | 105 | #define W83781D_REG_TEMP_OVER(nr)	((nr == 3) ? (0x255) : \ | 
 | 106 | 					((nr == 2) ? (0x155) : \ | 
 | 107 | 						     (0x39))) | 
 | 108 |  | 
 | 109 | #define W83781D_REG_CONFIG		0x40 | 
| Jean Delvare | c7f5d7e | 2006-02-05 23:13:48 +0100 | [diff] [blame] | 110 |  | 
 | 111 | /* Interrupt status (W83781D, AS99127F) */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 112 | #define W83781D_REG_ALARM1		0x41 | 
 | 113 | #define W83781D_REG_ALARM2		0x42 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 114 |  | 
| Jean Delvare | 0566336 | 2007-11-30 23:51:24 +0100 | [diff] [blame] | 115 | /* Real-time status (W83782D, W83783S) */ | 
| Jean Delvare | c7f5d7e | 2006-02-05 23:13:48 +0100 | [diff] [blame] | 116 | #define W83782D_REG_ALARM1		0x459 | 
 | 117 | #define W83782D_REG_ALARM2		0x45A | 
 | 118 | #define W83782D_REG_ALARM3		0x45B | 
 | 119 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 120 | #define W83781D_REG_BEEP_CONFIG		0x4D | 
 | 121 | #define W83781D_REG_BEEP_INTS1		0x56 | 
 | 122 | #define W83781D_REG_BEEP_INTS2		0x57 | 
 | 123 | #define W83781D_REG_BEEP_INTS3		0x453	/* not on W83781D */ | 
 | 124 |  | 
 | 125 | #define W83781D_REG_VID_FANDIV		0x47 | 
 | 126 |  | 
 | 127 | #define W83781D_REG_CHIPID		0x49 | 
 | 128 | #define W83781D_REG_WCHIPID		0x58 | 
 | 129 | #define W83781D_REG_CHIPMAN		0x4F | 
 | 130 | #define W83781D_REG_PIN			0x4B | 
 | 131 |  | 
 | 132 | /* 782D/783S only */ | 
 | 133 | #define W83781D_REG_VBAT		0x5D | 
 | 134 |  | 
 | 135 | /* PWM 782D (1-4) and 783S (1-2) only */ | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 136 | static const u8 W83781D_REG_PWM[] = { 0x5B, 0x5A, 0x5E, 0x5F }; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 137 | #define W83781D_REG_PWMCLK12		0x5C | 
 | 138 | #define W83781D_REG_PWMCLK34		0x45C | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 139 |  | 
 | 140 | #define W83781D_REG_I2C_ADDR		0x48 | 
 | 141 | #define W83781D_REG_I2C_SUBADDR		0x4A | 
 | 142 |  | 
 | 143 | /* The following are undocumented in the data sheets however we | 
 | 144 |    received the information in an email from Winbond tech support */ | 
 | 145 | /* Sensor selection - not on 781d */ | 
 | 146 | #define W83781D_REG_SCFG1		0x5D | 
 | 147 | static const u8 BIT_SCFG1[] = { 0x02, 0x04, 0x08 }; | 
 | 148 |  | 
 | 149 | #define W83781D_REG_SCFG2		0x59 | 
 | 150 | static const u8 BIT_SCFG2[] = { 0x10, 0x20, 0x40 }; | 
 | 151 |  | 
 | 152 | #define W83781D_DEFAULT_BETA		3435 | 
 | 153 |  | 
| Jean Delvare | 474d00a | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 154 | /* Conversions */ | 
 | 155 | #define IN_TO_REG(val)			SENSORS_LIMIT(((val) + 8) / 16, 0, 255) | 
 | 156 | #define IN_FROM_REG(val)		((val) * 16) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 157 |  | 
 | 158 | static inline u8 | 
 | 159 | FAN_TO_REG(long rpm, int div) | 
 | 160 | { | 
 | 161 | 	if (rpm == 0) | 
 | 162 | 		return 255; | 
 | 163 | 	rpm = SENSORS_LIMIT(rpm, 1, 1000000); | 
 | 164 | 	return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254); | 
 | 165 | } | 
 | 166 |  | 
| Jean Delvare | 474d00a | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 167 | static inline long | 
 | 168 | FAN_FROM_REG(u8 val, int div) | 
 | 169 | { | 
 | 170 | 	if (val == 0) | 
 | 171 | 		return -1; | 
 | 172 | 	if (val == 255) | 
 | 173 | 		return 0; | 
 | 174 | 	return 1350000 / (val * div); | 
 | 175 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 176 |  | 
| Jean Delvare | 474d00a | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 177 | #define TEMP_TO_REG(val)		SENSORS_LIMIT((val) / 1000, -127, 128) | 
 | 178 | #define TEMP_FROM_REG(val)		((val) * 1000) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 179 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 180 | #define BEEP_MASK_FROM_REG(val,type)	((type) == as99127f ? \ | 
 | 181 | 					 (val) ^ 0x7fff : (val)) | 
 | 182 | #define BEEP_MASK_TO_REG(val,type)	((type) == as99127f ? \ | 
 | 183 | 					 (~(val)) & 0x7fff : (val) & 0xffffff) | 
 | 184 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 185 | #define DIV_FROM_REG(val)		(1 << (val)) | 
 | 186 |  | 
 | 187 | static inline u8 | 
 | 188 | DIV_TO_REG(long val, enum chips type) | 
 | 189 | { | 
 | 190 | 	int i; | 
 | 191 | 	val = SENSORS_LIMIT(val, 1, | 
 | 192 | 			    ((type == w83781d | 
 | 193 | 			      || type == as99127f) ? 8 : 128)) >> 1; | 
| Grant Coady | abc0192 | 2005-05-12 13:41:51 +1000 | [diff] [blame] | 194 | 	for (i = 0; i < 7; i++) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 195 | 		if (val == 0) | 
 | 196 | 			break; | 
 | 197 | 		val >>= 1; | 
 | 198 | 	} | 
| Jean Delvare | 474d00a | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 199 | 	return i; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 200 | } | 
 | 201 |  | 
 | 202 | /* There are some complications in a module like this. First off, W83781D chips | 
 | 203 |    may be both present on the SMBus and the ISA bus, and we have to handle | 
 | 204 |    those cases separately at some places. Second, there might be several | 
 | 205 |    W83781D chips available (well, actually, that is probably never done; but | 
 | 206 |    it is a clean illustration of how to handle a case like that). Finally, | 
 | 207 |    a specific chip may be attached to *both* ISA and SMBus, and we would | 
 | 208 |    not like to detect it double. Fortunately, in the case of the W83781D at | 
 | 209 |    least, a register tells us what SMBus address we are on, so that helps | 
 | 210 |    a bit - except if there could be more than one SMBus. Groan. No solution | 
 | 211 |    for this yet. */ | 
 | 212 |  | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 213 | /* For ISA chips, we abuse the i2c_client addr and name fields. We also use | 
 | 214 |    the driver field to differentiate between I2C and ISA chips. */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 215 | struct w83781d_data { | 
 | 216 | 	struct i2c_client client; | 
| Tony Jones | 1beeffe | 2007-08-20 13:46:20 -0700 | [diff] [blame] | 217 | 	struct device *hwmon_dev; | 
| Ingo Molnar | 9a61bf6 | 2006-01-18 23:19:26 +0100 | [diff] [blame] | 218 | 	struct mutex lock; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 219 | 	enum chips type; | 
 | 220 |  | 
| Ingo Molnar | 9a61bf6 | 2006-01-18 23:19:26 +0100 | [diff] [blame] | 221 | 	struct mutex update_lock; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 222 | 	char valid;		/* !=0 if following fields are valid */ | 
 | 223 | 	unsigned long last_updated;	/* In jiffies */ | 
 | 224 |  | 
 | 225 | 	struct i2c_client *lm75[2];	/* for secondary I2C addresses */ | 
 | 226 | 	/* array of 2 pointers to subclients */ | 
 | 227 |  | 
 | 228 | 	u8 in[9];		/* Register value - 8 & 9 for 782D only */ | 
 | 229 | 	u8 in_max[9];		/* Register value - 8 & 9 for 782D only */ | 
 | 230 | 	u8 in_min[9];		/* Register value - 8 & 9 for 782D only */ | 
 | 231 | 	u8 fan[3];		/* Register value */ | 
 | 232 | 	u8 fan_min[3];		/* Register value */ | 
| Jean Delvare | 474d00a | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 233 | 	s8 temp;		/* Register value */ | 
 | 234 | 	s8 temp_max;		/* Register value */ | 
 | 235 | 	s8 temp_max_hyst;	/* Register value */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 236 | 	u16 temp_add[2];	/* Register value */ | 
 | 237 | 	u16 temp_max_add[2];	/* Register value */ | 
 | 238 | 	u16 temp_max_hyst_add[2];	/* Register value */ | 
 | 239 | 	u8 fan_div[3];		/* Register encoding, shifted right */ | 
 | 240 | 	u8 vid;			/* Register encoding, combined */ | 
 | 241 | 	u32 alarms;		/* Register encoding, combined */ | 
 | 242 | 	u32 beep_mask;		/* Register encoding, combined */ | 
 | 243 | 	u8 beep_enable;		/* Boolean */ | 
 | 244 | 	u8 pwm[4];		/* Register value */ | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 245 | 	u8 pwm2_enable;		/* Boolean */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 246 | 	u16 sens[3];		/* 782D/783S only. | 
 | 247 | 				   1 = pentium diode; 2 = 3904 diode; | 
| Jean Delvare | b26f933 | 2007-08-16 14:30:01 +0200 | [diff] [blame] | 248 | 				   4 = thermistor */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 249 | 	u8 vrm; | 
 | 250 | }; | 
 | 251 |  | 
 | 252 | static int w83781d_attach_adapter(struct i2c_adapter *adapter); | 
 | 253 | static int w83781d_detect(struct i2c_adapter *adapter, int address, int kind); | 
 | 254 | static int w83781d_detach_client(struct i2c_client *client); | 
 | 255 |  | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 256 | static int __devinit w83781d_isa_probe(struct platform_device *pdev); | 
 | 257 | static int __devexit w83781d_isa_remove(struct platform_device *pdev); | 
 | 258 |  | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 259 | static int w83781d_read_value(struct w83781d_data *data, u16 reg); | 
 | 260 | static int w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 261 | static struct w83781d_data *w83781d_update_device(struct device *dev); | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 262 | static void w83781d_init_device(struct device *dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 263 |  | 
 | 264 | static struct i2c_driver w83781d_driver = { | 
| Laurent Riffard | cdaf793 | 2005-11-26 20:37:41 +0100 | [diff] [blame] | 265 | 	.driver = { | 
| Laurent Riffard | cdaf793 | 2005-11-26 20:37:41 +0100 | [diff] [blame] | 266 | 		.name = "w83781d", | 
 | 267 | 	}, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 268 | 	.attach_adapter = w83781d_attach_adapter, | 
 | 269 | 	.detach_client = w83781d_detach_client, | 
 | 270 | }; | 
 | 271 |  | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 272 | static struct platform_driver w83781d_isa_driver = { | 
| Laurent Riffard | cdaf793 | 2005-11-26 20:37:41 +0100 | [diff] [blame] | 273 | 	.driver = { | 
| Jean Delvare | 8721884 | 2006-09-03 22:36:14 +0200 | [diff] [blame] | 274 | 		.owner = THIS_MODULE, | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 275 | 		.name = "w83781d", | 
| Laurent Riffard | cdaf793 | 2005-11-26 20:37:41 +0100 | [diff] [blame] | 276 | 	}, | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 277 | 	.probe = w83781d_isa_probe, | 
 | 278 | 	.remove = w83781d_isa_remove, | 
| Jean Delvare | fde0950 | 2005-07-19 23:51:07 +0200 | [diff] [blame] | 279 | }; | 
 | 280 |  | 
 | 281 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 282 | /* following are the sysfs callback functions */ | 
 | 283 | #define show_in_reg(reg) \ | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 284 | static ssize_t show_##reg (struct device *dev, struct device_attribute *da, \ | 
 | 285 | 		char *buf) \ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 286 | { \ | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 287 | 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 288 | 	struct w83781d_data *data = w83781d_update_device(dev); \ | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 289 | 	return sprintf(buf, "%ld\n", \ | 
 | 290 | 		       (long)IN_FROM_REG(data->reg[attr->index])); \ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 291 | } | 
 | 292 | show_in_reg(in); | 
 | 293 | show_in_reg(in_min); | 
 | 294 | show_in_reg(in_max); | 
 | 295 |  | 
 | 296 | #define store_in_reg(REG, reg) \ | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 297 | static ssize_t store_in_##reg (struct device *dev, struct device_attribute \ | 
 | 298 | 		*da, const char *buf, size_t count) \ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 299 | { \ | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 300 | 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \ | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 301 | 	struct w83781d_data *data = dev_get_drvdata(dev); \ | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 302 | 	int nr = attr->index; \ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 303 | 	u32 val; \ | 
 | 304 | 	 \ | 
| Jean Delvare | 474d00a | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 305 | 	val = simple_strtoul(buf, NULL, 10); \ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 306 | 	 \ | 
| Ingo Molnar | 9a61bf6 | 2006-01-18 23:19:26 +0100 | [diff] [blame] | 307 | 	mutex_lock(&data->update_lock); \ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 308 | 	data->in_##reg[nr] = IN_TO_REG(val); \ | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 309 | 	w83781d_write_value(data, W83781D_REG_IN_##REG(nr), data->in_##reg[nr]); \ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 310 | 	 \ | 
| Ingo Molnar | 9a61bf6 | 2006-01-18 23:19:26 +0100 | [diff] [blame] | 311 | 	mutex_unlock(&data->update_lock); \ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 312 | 	return count; \ | 
 | 313 | } | 
 | 314 | store_in_reg(MIN, min); | 
 | 315 | store_in_reg(MAX, max); | 
 | 316 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 317 | #define sysfs_in_offsets(offset) \ | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 318 | static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, \ | 
 | 319 | 		show_in, NULL, offset); \ | 
 | 320 | static SENSOR_DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ | 
 | 321 | 		show_in_min, store_in_min, offset); \ | 
 | 322 | static SENSOR_DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ | 
 | 323 | 		show_in_max, store_in_max, offset) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 324 |  | 
 | 325 | sysfs_in_offsets(0); | 
 | 326 | sysfs_in_offsets(1); | 
 | 327 | sysfs_in_offsets(2); | 
 | 328 | sysfs_in_offsets(3); | 
 | 329 | sysfs_in_offsets(4); | 
 | 330 | sysfs_in_offsets(5); | 
 | 331 | sysfs_in_offsets(6); | 
 | 332 | sysfs_in_offsets(7); | 
 | 333 | sysfs_in_offsets(8); | 
 | 334 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 335 | #define show_fan_reg(reg) \ | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 336 | static ssize_t show_##reg (struct device *dev, struct device_attribute *da, \ | 
 | 337 | 		char *buf) \ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 338 | { \ | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 339 | 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 340 | 	struct w83781d_data *data = w83781d_update_device(dev); \ | 
 | 341 | 	return sprintf(buf,"%ld\n", \ | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 342 | 		FAN_FROM_REG(data->reg[attr->index], \ | 
 | 343 | 			DIV_FROM_REG(data->fan_div[attr->index]))); \ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 344 | } | 
 | 345 | show_fan_reg(fan); | 
 | 346 | show_fan_reg(fan_min); | 
 | 347 |  | 
 | 348 | static ssize_t | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 349 | store_fan_min(struct device *dev, struct device_attribute *da, | 
 | 350 | 		const char *buf, size_t count) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 351 | { | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 352 | 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da); | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 353 | 	struct w83781d_data *data = dev_get_drvdata(dev); | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 354 | 	int nr = attr->index; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 355 | 	u32 val; | 
 | 356 |  | 
 | 357 | 	val = simple_strtoul(buf, NULL, 10); | 
 | 358 |  | 
| Ingo Molnar | 9a61bf6 | 2006-01-18 23:19:26 +0100 | [diff] [blame] | 359 | 	mutex_lock(&data->update_lock); | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 360 | 	data->fan_min[nr] = | 
 | 361 | 	    FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 362 | 	w83781d_write_value(data, W83781D_REG_FAN_MIN(nr), | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 363 | 			    data->fan_min[nr]); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 364 |  | 
| Ingo Molnar | 9a61bf6 | 2006-01-18 23:19:26 +0100 | [diff] [blame] | 365 | 	mutex_unlock(&data->update_lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 366 | 	return count; | 
 | 367 | } | 
 | 368 |  | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 369 | static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); | 
 | 370 | static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO | S_IWUSR, | 
 | 371 | 		show_fan_min, store_fan_min, 0); | 
 | 372 | static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1); | 
 | 373 | static SENSOR_DEVICE_ATTR(fan2_min, S_IRUGO | S_IWUSR, | 
 | 374 | 		show_fan_min, store_fan_min, 1); | 
 | 375 | static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2); | 
 | 376 | static SENSOR_DEVICE_ATTR(fan3_min, S_IRUGO | S_IWUSR, | 
 | 377 | 		show_fan_min, store_fan_min, 2); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 378 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 379 | #define show_temp_reg(reg) \ | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 380 | static ssize_t show_##reg (struct device *dev, struct device_attribute *da, \ | 
 | 381 | 		char *buf) \ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 382 | { \ | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 383 | 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 384 | 	struct w83781d_data *data = w83781d_update_device(dev); \ | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 385 | 	int nr = attr->index; \ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 386 | 	if (nr >= 2) {	/* TEMP2 and TEMP3 */ \ | 
 | 387 | 		return sprintf(buf,"%d\n", \ | 
 | 388 | 			LM75_TEMP_FROM_REG(data->reg##_add[nr-2])); \ | 
 | 389 | 	} else {	/* TEMP1 */ \ | 
 | 390 | 		return sprintf(buf,"%ld\n", (long)TEMP_FROM_REG(data->reg)); \ | 
 | 391 | 	} \ | 
 | 392 | } | 
 | 393 | show_temp_reg(temp); | 
 | 394 | show_temp_reg(temp_max); | 
 | 395 | show_temp_reg(temp_max_hyst); | 
 | 396 |  | 
 | 397 | #define store_temp_reg(REG, reg) \ | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 398 | static ssize_t store_temp_##reg (struct device *dev, \ | 
 | 399 | 		struct device_attribute *da, const char *buf, size_t count) \ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 400 | { \ | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 401 | 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \ | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 402 | 	struct w83781d_data *data = dev_get_drvdata(dev); \ | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 403 | 	int nr = attr->index; \ | 
| Christian Hohnstaedt | 5bfedac | 2007-08-16 11:40:10 +0200 | [diff] [blame] | 404 | 	long val; \ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 405 | 	 \ | 
 | 406 | 	val = simple_strtol(buf, NULL, 10); \ | 
 | 407 | 	 \ | 
| Ingo Molnar | 9a61bf6 | 2006-01-18 23:19:26 +0100 | [diff] [blame] | 408 | 	mutex_lock(&data->update_lock); \ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 409 | 	 \ | 
 | 410 | 	if (nr >= 2) {	/* TEMP2 and TEMP3 */ \ | 
 | 411 | 		data->temp_##reg##_add[nr-2] = LM75_TEMP_TO_REG(val); \ | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 412 | 		w83781d_write_value(data, W83781D_REG_TEMP_##REG(nr), \ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 413 | 				data->temp_##reg##_add[nr-2]); \ | 
 | 414 | 	} else {	/* TEMP1 */ \ | 
 | 415 | 		data->temp_##reg = TEMP_TO_REG(val); \ | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 416 | 		w83781d_write_value(data, W83781D_REG_TEMP_##REG(nr), \ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 417 | 			data->temp_##reg); \ | 
 | 418 | 	} \ | 
 | 419 | 	 \ | 
| Ingo Molnar | 9a61bf6 | 2006-01-18 23:19:26 +0100 | [diff] [blame] | 420 | 	mutex_unlock(&data->update_lock); \ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 421 | 	return count; \ | 
 | 422 | } | 
 | 423 | store_temp_reg(OVER, max); | 
 | 424 | store_temp_reg(HYST, max_hyst); | 
 | 425 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 426 | #define sysfs_temp_offsets(offset) \ | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 427 | static SENSOR_DEVICE_ATTR(temp##offset##_input, S_IRUGO, \ | 
 | 428 | 		show_temp, NULL, offset); \ | 
 | 429 | static SENSOR_DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \ | 
 | 430 | 		show_temp_max, store_temp_max, offset); \ | 
 | 431 | static SENSOR_DEVICE_ATTR(temp##offset##_max_hyst, S_IRUGO | S_IWUSR, \ | 
 | 432 | 		show_temp_max_hyst, store_temp_max_hyst, offset); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 433 |  | 
 | 434 | sysfs_temp_offsets(1); | 
 | 435 | sysfs_temp_offsets(2); | 
 | 436 | sysfs_temp_offsets(3); | 
 | 437 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 438 | static ssize_t | 
| Yani Ioannou | e404e27 | 2005-05-17 06:42:58 -0400 | [diff] [blame] | 439 | show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 440 | { | 
 | 441 | 	struct w83781d_data *data = w83781d_update_device(dev); | 
 | 442 | 	return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm)); | 
 | 443 | } | 
 | 444 |  | 
| Jim Cromie | 311ce2e | 2006-09-24 21:22:52 +0200 | [diff] [blame] | 445 | static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL); | 
 | 446 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 447 | static ssize_t | 
| Yani Ioannou | e404e27 | 2005-05-17 06:42:58 -0400 | [diff] [blame] | 448 | show_vrm_reg(struct device *dev, struct device_attribute *attr, char *buf) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 449 | { | 
| Jean Delvare | 90d6619 | 2007-10-08 18:24:35 +0200 | [diff] [blame] | 450 | 	struct w83781d_data *data = dev_get_drvdata(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 451 | 	return sprintf(buf, "%ld\n", (long) data->vrm); | 
 | 452 | } | 
 | 453 |  | 
 | 454 | static ssize_t | 
| Yani Ioannou | e404e27 | 2005-05-17 06:42:58 -0400 | [diff] [blame] | 455 | store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 456 | { | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 457 | 	struct w83781d_data *data = dev_get_drvdata(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 458 | 	u32 val; | 
 | 459 |  | 
 | 460 | 	val = simple_strtoul(buf, NULL, 10); | 
 | 461 | 	data->vrm = val; | 
 | 462 |  | 
 | 463 | 	return count; | 
 | 464 | } | 
 | 465 |  | 
| Jim Cromie | 311ce2e | 2006-09-24 21:22:52 +0200 | [diff] [blame] | 466 | static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg); | 
 | 467 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 468 | static ssize_t | 
| Yani Ioannou | e404e27 | 2005-05-17 06:42:58 -0400 | [diff] [blame] | 469 | show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 470 | { | 
 | 471 | 	struct w83781d_data *data = w83781d_update_device(dev); | 
| Jean Delvare | 68188ba | 2005-05-16 18:52:38 +0200 | [diff] [blame] | 472 | 	return sprintf(buf, "%u\n", data->alarms); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 473 | } | 
 | 474 |  | 
| Jim Cromie | 311ce2e | 2006-09-24 21:22:52 +0200 | [diff] [blame] | 475 | static DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL); | 
 | 476 |  | 
| Jean Delvare | 7d4a137 | 2007-10-08 18:29:43 +0200 | [diff] [blame] | 477 | static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, | 
 | 478 | 		char *buf) | 
 | 479 | { | 
 | 480 | 	struct w83781d_data *data = w83781d_update_device(dev); | 
 | 481 | 	int bitnr = to_sensor_dev_attr(attr)->index; | 
 | 482 | 	return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1); | 
 | 483 | } | 
 | 484 |  | 
 | 485 | /* The W83781D has a single alarm bit for temp2 and temp3 */ | 
 | 486 | static ssize_t show_temp3_alarm(struct device *dev, | 
 | 487 | 		struct device_attribute *attr, char *buf) | 
 | 488 | { | 
 | 489 | 	struct w83781d_data *data = w83781d_update_device(dev); | 
 | 490 | 	int bitnr = (data->type == w83781d) ? 5 : 13; | 
 | 491 | 	return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1); | 
 | 492 | } | 
 | 493 |  | 
 | 494 | static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0); | 
 | 495 | static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1); | 
 | 496 | static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2); | 
 | 497 | static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3); | 
 | 498 | static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 8); | 
 | 499 | static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 9); | 
 | 500 | static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 10); | 
 | 501 | static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 16); | 
 | 502 | static SENSOR_DEVICE_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 17); | 
 | 503 | static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 6); | 
 | 504 | static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7); | 
 | 505 | static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 11); | 
 | 506 | static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4); | 
 | 507 | static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 5); | 
 | 508 | static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_temp3_alarm, NULL, 0); | 
 | 509 |  | 
| Yani Ioannou | e404e27 | 2005-05-17 06:42:58 -0400 | [diff] [blame] | 510 | static ssize_t show_beep_mask (struct device *dev, struct device_attribute *attr, char *buf) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 511 | { | 
 | 512 | 	struct w83781d_data *data = w83781d_update_device(dev); | 
 | 513 | 	return sprintf(buf, "%ld\n", | 
 | 514 | 		       (long)BEEP_MASK_FROM_REG(data->beep_mask, data->type)); | 
 | 515 | } | 
| Yani Ioannou | e404e27 | 2005-05-17 06:42:58 -0400 | [diff] [blame] | 516 | static ssize_t show_beep_enable (struct device *dev, struct device_attribute *attr, char *buf) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 517 | { | 
 | 518 | 	struct w83781d_data *data = w83781d_update_device(dev); | 
| Jean Delvare | 474d00a | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 519 | 	return sprintf(buf, "%ld\n", (long)data->beep_enable); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 520 | } | 
 | 521 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 522 | static ssize_t | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 523 | store_beep_mask(struct device *dev, struct device_attribute *attr, | 
 | 524 | 		const char *buf, size_t count) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 525 | { | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 526 | 	struct w83781d_data *data = dev_get_drvdata(dev); | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 527 | 	u32 val; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 528 |  | 
 | 529 | 	val = simple_strtoul(buf, NULL, 10); | 
 | 530 |  | 
| Ingo Molnar | 9a61bf6 | 2006-01-18 23:19:26 +0100 | [diff] [blame] | 531 | 	mutex_lock(&data->update_lock); | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 532 | 	data->beep_mask = BEEP_MASK_TO_REG(val, data->type); | 
 | 533 | 	w83781d_write_value(data, W83781D_REG_BEEP_INTS1, | 
 | 534 | 			    data->beep_mask & 0xff); | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 535 | 	w83781d_write_value(data, W83781D_REG_BEEP_INTS2, | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 536 | 			    ((data->beep_mask >> 8) & 0x7f) | 
 | 537 | 			    | data->beep_enable << 7); | 
 | 538 | 	if (data->type != w83781d && data->type != as99127f) { | 
 | 539 | 		w83781d_write_value(data, W83781D_REG_BEEP_INTS3, | 
 | 540 | 				    ((data->beep_mask) >> 16) & 0xff); | 
 | 541 | 	} | 
| Ingo Molnar | 9a61bf6 | 2006-01-18 23:19:26 +0100 | [diff] [blame] | 542 | 	mutex_unlock(&data->update_lock); | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 543 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 544 | 	return count; | 
 | 545 | } | 
 | 546 |  | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 547 | static ssize_t | 
 | 548 | store_beep_enable(struct device *dev, struct device_attribute *attr, | 
 | 549 | 		const char *buf, size_t count) | 
 | 550 | { | 
 | 551 | 	struct w83781d_data *data = dev_get_drvdata(dev); | 
 | 552 | 	u32 val; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 553 |  | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 554 | 	val = simple_strtoul(buf, NULL, 10); | 
 | 555 | 	if (val != 0 && val != 1) | 
 | 556 | 		return -EINVAL; | 
 | 557 |  | 
 | 558 | 	mutex_lock(&data->update_lock); | 
 | 559 | 	data->beep_enable = val; | 
 | 560 | 	val = w83781d_read_value(data, W83781D_REG_BEEP_INTS2) & 0x7f; | 
 | 561 | 	val |= data->beep_enable << 7; | 
 | 562 | 	w83781d_write_value(data, W83781D_REG_BEEP_INTS2, val); | 
 | 563 | 	mutex_unlock(&data->update_lock); | 
 | 564 |  | 
 | 565 | 	return count; | 
 | 566 | } | 
 | 567 |  | 
 | 568 | static DEVICE_ATTR(beep_mask, S_IRUGO | S_IWUSR, | 
 | 569 | 		show_beep_mask, store_beep_mask); | 
 | 570 | static DEVICE_ATTR(beep_enable, S_IRUGO | S_IWUSR, | 
 | 571 | 		show_beep_enable, store_beep_enable); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 572 |  | 
| Jean Delvare | 7d4a137 | 2007-10-08 18:29:43 +0200 | [diff] [blame] | 573 | static ssize_t show_beep(struct device *dev, struct device_attribute *attr, | 
 | 574 | 		char *buf) | 
 | 575 | { | 
 | 576 | 	struct w83781d_data *data = w83781d_update_device(dev); | 
 | 577 | 	int bitnr = to_sensor_dev_attr(attr)->index; | 
 | 578 | 	return sprintf(buf, "%u\n", (data->beep_mask >> bitnr) & 1); | 
 | 579 | } | 
 | 580 |  | 
 | 581 | static ssize_t | 
 | 582 | store_beep(struct device *dev, struct device_attribute *attr, | 
 | 583 | 		const char *buf, size_t count) | 
 | 584 | { | 
 | 585 | 	struct w83781d_data *data = dev_get_drvdata(dev); | 
 | 586 | 	int bitnr = to_sensor_dev_attr(attr)->index; | 
 | 587 | 	unsigned long bit; | 
 | 588 | 	u8 reg; | 
 | 589 |  | 
 | 590 | 	bit = simple_strtoul(buf, NULL, 10); | 
 | 591 | 	if (bit & ~1) | 
 | 592 | 		return -EINVAL; | 
 | 593 |  | 
 | 594 | 	mutex_lock(&data->update_lock); | 
 | 595 | 	if (bit) | 
 | 596 | 		data->beep_mask |= (1 << bitnr); | 
 | 597 | 	else | 
 | 598 | 		data->beep_mask &= ~(1 << bitnr); | 
 | 599 |  | 
 | 600 | 	if (bitnr < 8) { | 
 | 601 | 		reg = w83781d_read_value(data, W83781D_REG_BEEP_INTS1); | 
 | 602 | 		if (bit) | 
 | 603 | 			reg |= (1 << bitnr); | 
 | 604 | 		else | 
 | 605 | 			reg &= ~(1 << bitnr); | 
 | 606 | 		w83781d_write_value(data, W83781D_REG_BEEP_INTS1, reg); | 
 | 607 | 	} else if (bitnr < 16) { | 
 | 608 | 		reg = w83781d_read_value(data, W83781D_REG_BEEP_INTS2); | 
 | 609 | 		if (bit) | 
 | 610 | 			reg |= (1 << (bitnr - 8)); | 
 | 611 | 		else | 
 | 612 | 			reg &= ~(1 << (bitnr - 8)); | 
 | 613 | 		w83781d_write_value(data, W83781D_REG_BEEP_INTS2, reg); | 
 | 614 | 	} else { | 
 | 615 | 		reg = w83781d_read_value(data, W83781D_REG_BEEP_INTS3); | 
 | 616 | 		if (bit) | 
 | 617 | 			reg |= (1 << (bitnr - 16)); | 
 | 618 | 		else | 
 | 619 | 			reg &= ~(1 << (bitnr - 16)); | 
 | 620 | 		w83781d_write_value(data, W83781D_REG_BEEP_INTS3, reg); | 
 | 621 | 	} | 
 | 622 | 	mutex_unlock(&data->update_lock); | 
 | 623 |  | 
 | 624 | 	return count; | 
 | 625 | } | 
 | 626 |  | 
 | 627 | /* The W83781D has a single beep bit for temp2 and temp3 */ | 
 | 628 | static ssize_t show_temp3_beep(struct device *dev, | 
 | 629 | 		struct device_attribute *attr, char *buf) | 
 | 630 | { | 
 | 631 | 	struct w83781d_data *data = w83781d_update_device(dev); | 
 | 632 | 	int bitnr = (data->type == w83781d) ? 5 : 13; | 
 | 633 | 	return sprintf(buf, "%u\n", (data->beep_mask >> bitnr) & 1); | 
 | 634 | } | 
 | 635 |  | 
 | 636 | static SENSOR_DEVICE_ATTR(in0_beep, S_IRUGO | S_IWUSR, | 
 | 637 | 			show_beep, store_beep, 0); | 
 | 638 | static SENSOR_DEVICE_ATTR(in1_beep, S_IRUGO | S_IWUSR, | 
 | 639 | 			show_beep, store_beep, 1); | 
 | 640 | static SENSOR_DEVICE_ATTR(in2_beep, S_IRUGO | S_IWUSR, | 
 | 641 | 			show_beep, store_beep, 2); | 
 | 642 | static SENSOR_DEVICE_ATTR(in3_beep, S_IRUGO | S_IWUSR, | 
 | 643 | 			show_beep, store_beep, 3); | 
 | 644 | static SENSOR_DEVICE_ATTR(in4_beep, S_IRUGO | S_IWUSR, | 
 | 645 | 			show_beep, store_beep, 8); | 
 | 646 | static SENSOR_DEVICE_ATTR(in5_beep, S_IRUGO | S_IWUSR, | 
 | 647 | 			show_beep, store_beep, 9); | 
 | 648 | static SENSOR_DEVICE_ATTR(in6_beep, S_IRUGO | S_IWUSR, | 
 | 649 | 			show_beep, store_beep, 10); | 
 | 650 | static SENSOR_DEVICE_ATTR(in7_beep, S_IRUGO | S_IWUSR, | 
 | 651 | 			show_beep, store_beep, 16); | 
 | 652 | static SENSOR_DEVICE_ATTR(in8_beep, S_IRUGO | S_IWUSR, | 
 | 653 | 			show_beep, store_beep, 17); | 
 | 654 | static SENSOR_DEVICE_ATTR(fan1_beep, S_IRUGO | S_IWUSR, | 
 | 655 | 			show_beep, store_beep, 6); | 
 | 656 | static SENSOR_DEVICE_ATTR(fan2_beep, S_IRUGO | S_IWUSR, | 
 | 657 | 			show_beep, store_beep, 7); | 
 | 658 | static SENSOR_DEVICE_ATTR(fan3_beep, S_IRUGO | S_IWUSR, | 
 | 659 | 			show_beep, store_beep, 11); | 
 | 660 | static SENSOR_DEVICE_ATTR(temp1_beep, S_IRUGO | S_IWUSR, | 
 | 661 | 			show_beep, store_beep, 4); | 
 | 662 | static SENSOR_DEVICE_ATTR(temp2_beep, S_IRUGO | S_IWUSR, | 
 | 663 | 			show_beep, store_beep, 5); | 
 | 664 | static SENSOR_DEVICE_ATTR(temp3_beep, S_IRUGO, | 
 | 665 | 			show_temp3_beep, store_beep, 13); | 
 | 666 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 667 | static ssize_t | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 668 | show_fan_div(struct device *dev, struct device_attribute *da, char *buf) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 669 | { | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 670 | 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 671 | 	struct w83781d_data *data = w83781d_update_device(dev); | 
 | 672 | 	return sprintf(buf, "%ld\n", | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 673 | 		       (long) DIV_FROM_REG(data->fan_div[attr->index])); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 674 | } | 
 | 675 |  | 
 | 676 | /* Note: we save and restore the fan minimum here, because its value is | 
 | 677 |    determined in part by the fan divisor.  This follows the principle of | 
| Andreas Mohr | d6e05ed | 2006-06-26 18:35:02 +0200 | [diff] [blame] | 678 |    least surprise; the user doesn't expect the fan minimum to change just | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 679 |    because the divisor changed. */ | 
 | 680 | static ssize_t | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 681 | store_fan_div(struct device *dev, struct device_attribute *da, | 
 | 682 | 		const char *buf, size_t count) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 683 | { | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 684 | 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da); | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 685 | 	struct w83781d_data *data = dev_get_drvdata(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 686 | 	unsigned long min; | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 687 | 	int nr = attr->index; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 688 | 	u8 reg; | 
 | 689 | 	unsigned long val = simple_strtoul(buf, NULL, 10); | 
 | 690 |  | 
| Ingo Molnar | 9a61bf6 | 2006-01-18 23:19:26 +0100 | [diff] [blame] | 691 | 	mutex_lock(&data->update_lock); | 
| Jean Delvare | 293c099 | 2007-11-30 23:52:44 +0100 | [diff] [blame] | 692 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 693 | 	/* Save fan_min */ | 
 | 694 | 	min = FAN_FROM_REG(data->fan_min[nr], | 
 | 695 | 			   DIV_FROM_REG(data->fan_div[nr])); | 
 | 696 |  | 
 | 697 | 	data->fan_div[nr] = DIV_TO_REG(val, data->type); | 
 | 698 |  | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 699 | 	reg = (w83781d_read_value(data, nr==2 ? W83781D_REG_PIN : W83781D_REG_VID_FANDIV) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 700 | 	       & (nr==0 ? 0xcf : 0x3f)) | 
 | 701 | 	    | ((data->fan_div[nr] & 0x03) << (nr==0 ? 4 : 6)); | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 702 | 	w83781d_write_value(data, nr==2 ? W83781D_REG_PIN : W83781D_REG_VID_FANDIV, reg); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 703 |  | 
 | 704 | 	/* w83781d and as99127f don't have extended divisor bits */ | 
 | 705 | 	if (data->type != w83781d && data->type != as99127f) { | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 706 | 		reg = (w83781d_read_value(data, W83781D_REG_VBAT) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 707 | 		       & ~(1 << (5 + nr))) | 
 | 708 | 		    | ((data->fan_div[nr] & 0x04) << (3 + nr)); | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 709 | 		w83781d_write_value(data, W83781D_REG_VBAT, reg); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 710 | 	} | 
 | 711 |  | 
 | 712 | 	/* Restore fan_min */ | 
 | 713 | 	data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 714 | 	w83781d_write_value(data, W83781D_REG_FAN_MIN(nr), data->fan_min[nr]); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 715 |  | 
| Ingo Molnar | 9a61bf6 | 2006-01-18 23:19:26 +0100 | [diff] [blame] | 716 | 	mutex_unlock(&data->update_lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 717 | 	return count; | 
 | 718 | } | 
 | 719 |  | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 720 | static SENSOR_DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR, | 
 | 721 | 		show_fan_div, store_fan_div, 0); | 
 | 722 | static SENSOR_DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR, | 
 | 723 | 		show_fan_div, store_fan_div, 1); | 
 | 724 | static SENSOR_DEVICE_ATTR(fan3_div, S_IRUGO | S_IWUSR, | 
 | 725 | 		show_fan_div, store_fan_div, 2); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 726 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 727 | static ssize_t | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 728 | show_pwm(struct device *dev, struct device_attribute *da, char *buf) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 729 | { | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 730 | 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 731 | 	struct w83781d_data *data = w83781d_update_device(dev); | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 732 | 	return sprintf(buf, "%d\n", (int)data->pwm[attr->index]); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 733 | } | 
 | 734 |  | 
 | 735 | static ssize_t | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 736 | show_pwm2_enable(struct device *dev, struct device_attribute *da, char *buf) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 737 | { | 
 | 738 | 	struct w83781d_data *data = w83781d_update_device(dev); | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 739 | 	return sprintf(buf, "%d\n", (int)data->pwm2_enable); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 740 | } | 
 | 741 |  | 
 | 742 | static ssize_t | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 743 | store_pwm(struct device *dev, struct device_attribute *da, const char *buf, | 
 | 744 | 		size_t count) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 745 | { | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 746 | 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da); | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 747 | 	struct w83781d_data *data = dev_get_drvdata(dev); | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 748 | 	int nr = attr->index; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 749 | 	u32 val; | 
 | 750 |  | 
 | 751 | 	val = simple_strtoul(buf, NULL, 10); | 
 | 752 |  | 
| Ingo Molnar | 9a61bf6 | 2006-01-18 23:19:26 +0100 | [diff] [blame] | 753 | 	mutex_lock(&data->update_lock); | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 754 | 	data->pwm[nr] = SENSORS_LIMIT(val, 0, 255); | 
 | 755 | 	w83781d_write_value(data, W83781D_REG_PWM[nr], data->pwm[nr]); | 
| Ingo Molnar | 9a61bf6 | 2006-01-18 23:19:26 +0100 | [diff] [blame] | 756 | 	mutex_unlock(&data->update_lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 757 | 	return count; | 
 | 758 | } | 
 | 759 |  | 
 | 760 | static ssize_t | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 761 | store_pwm2_enable(struct device *dev, struct device_attribute *da, | 
 | 762 | 		const char *buf, size_t count) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 763 | { | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 764 | 	struct w83781d_data *data = dev_get_drvdata(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 765 | 	u32 val, reg; | 
 | 766 |  | 
 | 767 | 	val = simple_strtoul(buf, NULL, 10); | 
 | 768 |  | 
| Ingo Molnar | 9a61bf6 | 2006-01-18 23:19:26 +0100 | [diff] [blame] | 769 | 	mutex_lock(&data->update_lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 770 |  | 
 | 771 | 	switch (val) { | 
 | 772 | 	case 0: | 
 | 773 | 	case 1: | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 774 | 		reg = w83781d_read_value(data, W83781D_REG_PWMCLK12); | 
 | 775 | 		w83781d_write_value(data, W83781D_REG_PWMCLK12, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 776 | 				    (reg & 0xf7) | (val << 3)); | 
 | 777 |  | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 778 | 		reg = w83781d_read_value(data, W83781D_REG_BEEP_CONFIG); | 
 | 779 | 		w83781d_write_value(data, W83781D_REG_BEEP_CONFIG, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 780 | 				    (reg & 0xef) | (!val << 4)); | 
 | 781 |  | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 782 | 		data->pwm2_enable = val; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 783 | 		break; | 
 | 784 |  | 
 | 785 | 	default: | 
| Ingo Molnar | 9a61bf6 | 2006-01-18 23:19:26 +0100 | [diff] [blame] | 786 | 		mutex_unlock(&data->update_lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 787 | 		return -EINVAL; | 
 | 788 | 	} | 
 | 789 |  | 
| Ingo Molnar | 9a61bf6 | 2006-01-18 23:19:26 +0100 | [diff] [blame] | 790 | 	mutex_unlock(&data->update_lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 791 | 	return count; | 
 | 792 | } | 
 | 793 |  | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 794 | static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, store_pwm, 0); | 
 | 795 | static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, show_pwm, store_pwm, 1); | 
 | 796 | static SENSOR_DEVICE_ATTR(pwm3, S_IRUGO | S_IWUSR, show_pwm, store_pwm, 2); | 
 | 797 | static SENSOR_DEVICE_ATTR(pwm4, S_IRUGO | S_IWUSR, show_pwm, store_pwm, 3); | 
 | 798 | /* only PWM2 can be enabled/disabled */ | 
 | 799 | static DEVICE_ATTR(pwm2_enable, S_IRUGO | S_IWUSR, | 
 | 800 | 		show_pwm2_enable, store_pwm2_enable); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 801 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 802 | static ssize_t | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 803 | show_sensor(struct device *dev, struct device_attribute *da, char *buf) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 804 | { | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 805 | 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 806 | 	struct w83781d_data *data = w83781d_update_device(dev); | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 807 | 	return sprintf(buf, "%d\n", (int)data->sens[attr->index]); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 808 | } | 
 | 809 |  | 
 | 810 | static ssize_t | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 811 | store_sensor(struct device *dev, struct device_attribute *da, | 
 | 812 | 		const char *buf, size_t count) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 813 | { | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 814 | 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da); | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 815 | 	struct w83781d_data *data = dev_get_drvdata(dev); | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 816 | 	int nr = attr->index; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 817 | 	u32 val, tmp; | 
 | 818 |  | 
 | 819 | 	val = simple_strtoul(buf, NULL, 10); | 
 | 820 |  | 
| Ingo Molnar | 9a61bf6 | 2006-01-18 23:19:26 +0100 | [diff] [blame] | 821 | 	mutex_lock(&data->update_lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 822 |  | 
 | 823 | 	switch (val) { | 
 | 824 | 	case 1:		/* PII/Celeron diode */ | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 825 | 		tmp = w83781d_read_value(data, W83781D_REG_SCFG1); | 
 | 826 | 		w83781d_write_value(data, W83781D_REG_SCFG1, | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 827 | 				    tmp | BIT_SCFG1[nr]); | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 828 | 		tmp = w83781d_read_value(data, W83781D_REG_SCFG2); | 
 | 829 | 		w83781d_write_value(data, W83781D_REG_SCFG2, | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 830 | 				    tmp | BIT_SCFG2[nr]); | 
 | 831 | 		data->sens[nr] = val; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 832 | 		break; | 
 | 833 | 	case 2:		/* 3904 */ | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 834 | 		tmp = w83781d_read_value(data, W83781D_REG_SCFG1); | 
 | 835 | 		w83781d_write_value(data, W83781D_REG_SCFG1, | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 836 | 				    tmp | BIT_SCFG1[nr]); | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 837 | 		tmp = w83781d_read_value(data, W83781D_REG_SCFG2); | 
 | 838 | 		w83781d_write_value(data, W83781D_REG_SCFG2, | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 839 | 				    tmp & ~BIT_SCFG2[nr]); | 
 | 840 | 		data->sens[nr] = val; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 841 | 		break; | 
| Jean Delvare | b26f933 | 2007-08-16 14:30:01 +0200 | [diff] [blame] | 842 | 	case W83781D_DEFAULT_BETA: | 
 | 843 | 		dev_warn(dev, "Sensor type %d is deprecated, please use 4 " | 
 | 844 | 			 "instead\n", W83781D_DEFAULT_BETA); | 
 | 845 | 		/* fall through */ | 
 | 846 | 	case 4:		/* thermistor */ | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 847 | 		tmp = w83781d_read_value(data, W83781D_REG_SCFG1); | 
 | 848 | 		w83781d_write_value(data, W83781D_REG_SCFG1, | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 849 | 				    tmp & ~BIT_SCFG1[nr]); | 
 | 850 | 		data->sens[nr] = val; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 851 | 		break; | 
 | 852 | 	default: | 
| Jean Delvare | b26f933 | 2007-08-16 14:30:01 +0200 | [diff] [blame] | 853 | 		dev_err(dev, "Invalid sensor type %ld; must be 1, 2, or 4\n", | 
 | 854 | 		       (long) val); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 855 | 		break; | 
 | 856 | 	} | 
 | 857 |  | 
| Ingo Molnar | 9a61bf6 | 2006-01-18 23:19:26 +0100 | [diff] [blame] | 858 | 	mutex_unlock(&data->update_lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 859 | 	return count; | 
 | 860 | } | 
 | 861 |  | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 862 | static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO | S_IWUSR, | 
 | 863 | 	show_sensor, store_sensor, 0); | 
 | 864 | static SENSOR_DEVICE_ATTR(temp2_type, S_IRUGO | S_IWUSR, | 
| Mark M. Hoffman | 393cdad | 2007-08-09 08:12:46 -0400 | [diff] [blame] | 865 | 	show_sensor, store_sensor, 1); | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 866 | static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO | S_IWUSR, | 
| Mark M. Hoffman | 393cdad | 2007-08-09 08:12:46 -0400 | [diff] [blame] | 867 | 	show_sensor, store_sensor, 2); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 868 |  | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 869 | /* I2C devices get this name attribute automatically, but for ISA devices | 
 | 870 |    we must create it by ourselves. */ | 
 | 871 | static ssize_t | 
 | 872 | show_name(struct device *dev, struct device_attribute *devattr, char *buf) | 
 | 873 | { | 
 | 874 | 	struct w83781d_data *data = dev_get_drvdata(dev); | 
 | 875 | 	return sprintf(buf, "%s\n", data->client.name); | 
 | 876 | } | 
 | 877 | static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); | 
 | 878 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 879 | /* This function is called when: | 
 | 880 |      * w83781d_driver is inserted (when this module is loaded), for each | 
 | 881 |        available adapter | 
 | 882 |      * when a new adapter is inserted (and w83781d_driver is still present) */ | 
 | 883 | static int | 
 | 884 | w83781d_attach_adapter(struct i2c_adapter *adapter) | 
 | 885 | { | 
 | 886 | 	if (!(adapter->class & I2C_CLASS_HWMON)) | 
 | 887 | 		return 0; | 
| Jean Delvare | 2ed2dc3 | 2005-07-31 21:42:02 +0200 | [diff] [blame] | 888 | 	return i2c_probe(adapter, &addr_data, w83781d_detect); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 889 | } | 
 | 890 |  | 
 | 891 | /* Assumes that adapter is of I2C, not ISA variety. | 
 | 892 |  * OTHERWISE DON'T CALL THIS | 
 | 893 |  */ | 
 | 894 | static int | 
 | 895 | w83781d_detect_subclients(struct i2c_adapter *adapter, int address, int kind, | 
 | 896 | 		struct i2c_client *new_client) | 
 | 897 | { | 
 | 898 | 	int i, val1 = 0, id; | 
 | 899 | 	int err; | 
 | 900 | 	const char *client_name = ""; | 
 | 901 | 	struct w83781d_data *data = i2c_get_clientdata(new_client); | 
 | 902 |  | 
| Deepak Saxena | ba9c2e8 | 2005-10-17 23:08:32 +0200 | [diff] [blame] | 903 | 	data->lm75[0] = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 904 | 	if (!(data->lm75[0])) { | 
 | 905 | 		err = -ENOMEM; | 
 | 906 | 		goto ERROR_SC_0; | 
 | 907 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 908 |  | 
 | 909 | 	id = i2c_adapter_id(adapter); | 
 | 910 |  | 
 | 911 | 	if (force_subclients[0] == id && force_subclients[1] == address) { | 
 | 912 | 		for (i = 2; i <= 3; i++) { | 
 | 913 | 			if (force_subclients[i] < 0x48 || | 
 | 914 | 			    force_subclients[i] > 0x4f) { | 
 | 915 | 				dev_err(&new_client->dev, "Invalid subclient " | 
 | 916 | 					"address %d; must be 0x48-0x4f\n", | 
 | 917 | 					force_subclients[i]); | 
 | 918 | 				err = -EINVAL; | 
 | 919 | 				goto ERROR_SC_1; | 
 | 920 | 			} | 
 | 921 | 		} | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 922 | 		w83781d_write_value(data, W83781D_REG_I2C_SUBADDR, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 923 | 				(force_subclients[2] & 0x07) | | 
 | 924 | 				((force_subclients[3] & 0x07) << 4)); | 
 | 925 | 		data->lm75[0]->addr = force_subclients[2]; | 
 | 926 | 	} else { | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 927 | 		val1 = w83781d_read_value(data, W83781D_REG_I2C_SUBADDR); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 928 | 		data->lm75[0]->addr = 0x48 + (val1 & 0x07); | 
 | 929 | 	} | 
 | 930 |  | 
 | 931 | 	if (kind != w83783s) { | 
| Deepak Saxena | ba9c2e8 | 2005-10-17 23:08:32 +0200 | [diff] [blame] | 932 | 		data->lm75[1] = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 933 | 		if (!(data->lm75[1])) { | 
 | 934 | 			err = -ENOMEM; | 
 | 935 | 			goto ERROR_SC_1; | 
 | 936 | 		} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 937 |  | 
 | 938 | 		if (force_subclients[0] == id && | 
 | 939 | 		    force_subclients[1] == address) { | 
 | 940 | 			data->lm75[1]->addr = force_subclients[3]; | 
 | 941 | 		} else { | 
 | 942 | 			data->lm75[1]->addr = 0x48 + ((val1 >> 4) & 0x07); | 
 | 943 | 		} | 
 | 944 | 		if (data->lm75[0]->addr == data->lm75[1]->addr) { | 
 | 945 | 			dev_err(&new_client->dev, | 
 | 946 | 			       "Duplicate addresses 0x%x for subclients.\n", | 
 | 947 | 			       data->lm75[0]->addr); | 
 | 948 | 			err = -EBUSY; | 
 | 949 | 			goto ERROR_SC_2; | 
 | 950 | 		} | 
 | 951 | 	} | 
 | 952 |  | 
 | 953 | 	if (kind == w83781d) | 
 | 954 | 		client_name = "w83781d subclient"; | 
 | 955 | 	else if (kind == w83782d) | 
 | 956 | 		client_name = "w83782d subclient"; | 
 | 957 | 	else if (kind == w83783s) | 
 | 958 | 		client_name = "w83783s subclient"; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 959 | 	else if (kind == as99127f) | 
 | 960 | 		client_name = "as99127f subclient"; | 
 | 961 |  | 
 | 962 | 	for (i = 0; i <= 1; i++) { | 
 | 963 | 		/* store all data in w83781d */ | 
 | 964 | 		i2c_set_clientdata(data->lm75[i], NULL); | 
 | 965 | 		data->lm75[i]->adapter = adapter; | 
 | 966 | 		data->lm75[i]->driver = &w83781d_driver; | 
 | 967 | 		data->lm75[i]->flags = 0; | 
 | 968 | 		strlcpy(data->lm75[i]->name, client_name, | 
 | 969 | 			I2C_NAME_SIZE); | 
 | 970 | 		if ((err = i2c_attach_client(data->lm75[i]))) { | 
 | 971 | 			dev_err(&new_client->dev, "Subclient %d " | 
 | 972 | 				"registration at address 0x%x " | 
 | 973 | 				"failed.\n", i, data->lm75[i]->addr); | 
 | 974 | 			if (i == 1) | 
 | 975 | 				goto ERROR_SC_3; | 
 | 976 | 			goto ERROR_SC_2; | 
 | 977 | 		} | 
 | 978 | 		if (kind == w83783s) | 
 | 979 | 			break; | 
 | 980 | 	} | 
 | 981 |  | 
 | 982 | 	return 0; | 
 | 983 |  | 
 | 984 | /* Undo inits in case of errors */ | 
 | 985 | ERROR_SC_3: | 
 | 986 | 	i2c_detach_client(data->lm75[0]); | 
 | 987 | ERROR_SC_2: | 
| Jesper Juhl | 6044ec8 | 2005-11-07 01:01:32 -0800 | [diff] [blame] | 988 | 	kfree(data->lm75[1]); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 989 | ERROR_SC_1: | 
| Jesper Juhl | 6044ec8 | 2005-11-07 01:01:32 -0800 | [diff] [blame] | 990 | 	kfree(data->lm75[0]); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 991 | ERROR_SC_0: | 
 | 992 | 	return err; | 
 | 993 | } | 
 | 994 |  | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 995 | #define IN_UNIT_ATTRS(X)					\ | 
 | 996 | 	&sensor_dev_attr_in##X##_input.dev_attr.attr,		\ | 
 | 997 | 	&sensor_dev_attr_in##X##_min.dev_attr.attr,		\ | 
| Jean Delvare | 293c099 | 2007-11-30 23:52:44 +0100 | [diff] [blame] | 998 | 	&sensor_dev_attr_in##X##_max.dev_attr.attr,		\ | 
| Jean Delvare | 7d4a137 | 2007-10-08 18:29:43 +0200 | [diff] [blame] | 999 | 	&sensor_dev_attr_in##X##_alarm.dev_attr.attr,		\ | 
 | 1000 | 	&sensor_dev_attr_in##X##_beep.dev_attr.attr | 
| Jim Cromie | 311ce2e | 2006-09-24 21:22:52 +0200 | [diff] [blame] | 1001 |  | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1002 | #define FAN_UNIT_ATTRS(X)					\ | 
 | 1003 | 	&sensor_dev_attr_fan##X##_input.dev_attr.attr,		\ | 
 | 1004 | 	&sensor_dev_attr_fan##X##_min.dev_attr.attr,		\ | 
| Jean Delvare | 7d4a137 | 2007-10-08 18:29:43 +0200 | [diff] [blame] | 1005 | 	&sensor_dev_attr_fan##X##_div.dev_attr.attr,		\ | 
 | 1006 | 	&sensor_dev_attr_fan##X##_alarm.dev_attr.attr,		\ | 
 | 1007 | 	&sensor_dev_attr_fan##X##_beep.dev_attr.attr | 
| Jim Cromie | 311ce2e | 2006-09-24 21:22:52 +0200 | [diff] [blame] | 1008 |  | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1009 | #define TEMP_UNIT_ATTRS(X)					\ | 
 | 1010 | 	&sensor_dev_attr_temp##X##_input.dev_attr.attr,		\ | 
 | 1011 | 	&sensor_dev_attr_temp##X##_max.dev_attr.attr,		\ | 
| Jean Delvare | 7d4a137 | 2007-10-08 18:29:43 +0200 | [diff] [blame] | 1012 | 	&sensor_dev_attr_temp##X##_max_hyst.dev_attr.attr,	\ | 
 | 1013 | 	&sensor_dev_attr_temp##X##_alarm.dev_attr.attr,		\ | 
 | 1014 | 	&sensor_dev_attr_temp##X##_beep.dev_attr.attr | 
| Jim Cromie | 311ce2e | 2006-09-24 21:22:52 +0200 | [diff] [blame] | 1015 |  | 
 | 1016 | static struct attribute* w83781d_attributes[] = { | 
 | 1017 | 	IN_UNIT_ATTRS(0), | 
 | 1018 | 	IN_UNIT_ATTRS(2), | 
 | 1019 | 	IN_UNIT_ATTRS(3), | 
 | 1020 | 	IN_UNIT_ATTRS(4), | 
 | 1021 | 	IN_UNIT_ATTRS(5), | 
 | 1022 | 	IN_UNIT_ATTRS(6), | 
 | 1023 | 	FAN_UNIT_ATTRS(1), | 
 | 1024 | 	FAN_UNIT_ATTRS(2), | 
 | 1025 | 	FAN_UNIT_ATTRS(3), | 
 | 1026 | 	TEMP_UNIT_ATTRS(1), | 
 | 1027 | 	TEMP_UNIT_ATTRS(2), | 
 | 1028 | 	&dev_attr_cpu0_vid.attr, | 
 | 1029 | 	&dev_attr_vrm.attr, | 
 | 1030 | 	&dev_attr_alarms.attr, | 
 | 1031 | 	&dev_attr_beep_mask.attr, | 
 | 1032 | 	&dev_attr_beep_enable.attr, | 
 | 1033 | 	NULL | 
 | 1034 | }; | 
 | 1035 | static const struct attribute_group w83781d_group = { | 
 | 1036 | 	.attrs = w83781d_attributes, | 
 | 1037 | }; | 
 | 1038 |  | 
 | 1039 | static struct attribute *w83781d_attributes_opt[] = { | 
 | 1040 | 	IN_UNIT_ATTRS(1), | 
 | 1041 | 	IN_UNIT_ATTRS(7), | 
 | 1042 | 	IN_UNIT_ATTRS(8), | 
 | 1043 | 	TEMP_UNIT_ATTRS(3), | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1044 | 	&sensor_dev_attr_pwm1.dev_attr.attr, | 
 | 1045 | 	&sensor_dev_attr_pwm2.dev_attr.attr, | 
 | 1046 | 	&sensor_dev_attr_pwm3.dev_attr.attr, | 
 | 1047 | 	&sensor_dev_attr_pwm4.dev_attr.attr, | 
| Jim Cromie | 311ce2e | 2006-09-24 21:22:52 +0200 | [diff] [blame] | 1048 | 	&dev_attr_pwm2_enable.attr, | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1049 | 	&sensor_dev_attr_temp1_type.dev_attr.attr, | 
 | 1050 | 	&sensor_dev_attr_temp2_type.dev_attr.attr, | 
 | 1051 | 	&sensor_dev_attr_temp3_type.dev_attr.attr, | 
| Jim Cromie | 311ce2e | 2006-09-24 21:22:52 +0200 | [diff] [blame] | 1052 | 	NULL | 
 | 1053 | }; | 
 | 1054 | static const struct attribute_group w83781d_group_opt = { | 
 | 1055 | 	.attrs = w83781d_attributes_opt, | 
 | 1056 | }; | 
 | 1057 |  | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1058 | /* No clean up is done on error, it's up to the caller */ | 
 | 1059 | static int | 
 | 1060 | w83781d_create_files(struct device *dev, int kind, int is_isa) | 
 | 1061 | { | 
 | 1062 | 	int err; | 
 | 1063 |  | 
 | 1064 | 	if ((err = sysfs_create_group(&dev->kobj, &w83781d_group))) | 
 | 1065 | 		return err; | 
 | 1066 |  | 
 | 1067 | 	if (kind != w83783s) { | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1068 | 		if ((err = device_create_file(dev, | 
 | 1069 | 				&sensor_dev_attr_in1_input.dev_attr)) | 
 | 1070 | 		    || (err = device_create_file(dev, | 
 | 1071 | 				&sensor_dev_attr_in1_min.dev_attr)) | 
 | 1072 | 		    || (err = device_create_file(dev, | 
| Jean Delvare | 7d4a137 | 2007-10-08 18:29:43 +0200 | [diff] [blame] | 1073 | 				&sensor_dev_attr_in1_max.dev_attr)) | 
 | 1074 | 		    || (err = device_create_file(dev, | 
 | 1075 | 				&sensor_dev_attr_in1_alarm.dev_attr)) | 
 | 1076 | 		    || (err = device_create_file(dev, | 
 | 1077 | 				&sensor_dev_attr_in1_beep.dev_attr))) | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1078 | 			return err; | 
 | 1079 | 	} | 
 | 1080 | 	if (kind != as99127f && kind != w83781d && kind != w83783s) { | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1081 | 		if ((err = device_create_file(dev, | 
 | 1082 | 				&sensor_dev_attr_in7_input.dev_attr)) | 
 | 1083 | 		    || (err = device_create_file(dev, | 
 | 1084 | 				&sensor_dev_attr_in7_min.dev_attr)) | 
 | 1085 | 		    || (err = device_create_file(dev, | 
 | 1086 | 				&sensor_dev_attr_in7_max.dev_attr)) | 
 | 1087 | 		    || (err = device_create_file(dev, | 
| Jean Delvare | 7d4a137 | 2007-10-08 18:29:43 +0200 | [diff] [blame] | 1088 | 				&sensor_dev_attr_in7_alarm.dev_attr)) | 
 | 1089 | 		    || (err = device_create_file(dev, | 
 | 1090 | 				&sensor_dev_attr_in7_beep.dev_attr)) | 
 | 1091 | 		    || (err = device_create_file(dev, | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1092 | 				&sensor_dev_attr_in8_input.dev_attr)) | 
 | 1093 | 		    || (err = device_create_file(dev, | 
 | 1094 | 				&sensor_dev_attr_in8_min.dev_attr)) | 
 | 1095 | 		    || (err = device_create_file(dev, | 
| Jean Delvare | 7d4a137 | 2007-10-08 18:29:43 +0200 | [diff] [blame] | 1096 | 				&sensor_dev_attr_in8_max.dev_attr)) | 
 | 1097 | 		    || (err = device_create_file(dev, | 
 | 1098 | 				&sensor_dev_attr_in8_alarm.dev_attr)) | 
 | 1099 | 		    || (err = device_create_file(dev, | 
 | 1100 | 				&sensor_dev_attr_in8_beep.dev_attr))) | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1101 | 			return err; | 
 | 1102 | 	} | 
 | 1103 | 	if (kind != w83783s) { | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1104 | 		if ((err = device_create_file(dev, | 
 | 1105 | 				&sensor_dev_attr_temp3_input.dev_attr)) | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1106 | 		    || (err = device_create_file(dev, | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1107 | 				&sensor_dev_attr_temp3_max.dev_attr)) | 
 | 1108 | 		    || (err = device_create_file(dev, | 
| Jean Delvare | 7d4a137 | 2007-10-08 18:29:43 +0200 | [diff] [blame] | 1109 | 				&sensor_dev_attr_temp3_max_hyst.dev_attr)) | 
 | 1110 | 		    || (err = device_create_file(dev, | 
 | 1111 | 				&sensor_dev_attr_temp3_alarm.dev_attr)) | 
 | 1112 | 		    || (err = device_create_file(dev, | 
 | 1113 | 				&sensor_dev_attr_temp3_beep.dev_attr))) | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1114 | 			return err; | 
| Jean Delvare | 7d4a137 | 2007-10-08 18:29:43 +0200 | [diff] [blame] | 1115 |  | 
| Jean Delvare | 7768aa7 | 2007-10-25 13:11:01 +0200 | [diff] [blame] | 1116 | 		if (kind != w83781d) { | 
| Jean Delvare | 7d4a137 | 2007-10-08 18:29:43 +0200 | [diff] [blame] | 1117 | 			err = sysfs_chmod_file(&dev->kobj, | 
 | 1118 | 				&sensor_dev_attr_temp3_alarm.dev_attr.attr, | 
 | 1119 | 				S_IRUGO | S_IWUSR); | 
 | 1120 | 			if (err) | 
 | 1121 | 				return err; | 
| Jean Delvare | 7768aa7 | 2007-10-25 13:11:01 +0200 | [diff] [blame] | 1122 | 		} | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1123 | 	} | 
 | 1124 |  | 
 | 1125 | 	if (kind != w83781d && kind != as99127f) { | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1126 | 		if ((err = device_create_file(dev, | 
 | 1127 | 				&sensor_dev_attr_pwm1.dev_attr)) | 
 | 1128 | 		    || (err = device_create_file(dev, | 
 | 1129 | 				&sensor_dev_attr_pwm2.dev_attr)) | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1130 | 		    || (err = device_create_file(dev, &dev_attr_pwm2_enable))) | 
 | 1131 | 			return err; | 
 | 1132 | 	} | 
 | 1133 | 	if (kind == w83782d && !is_isa) { | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1134 | 		if ((err = device_create_file(dev, | 
 | 1135 | 				&sensor_dev_attr_pwm3.dev_attr)) | 
 | 1136 | 		    || (err = device_create_file(dev, | 
 | 1137 | 				&sensor_dev_attr_pwm4.dev_attr))) | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1138 | 			return err; | 
 | 1139 | 	} | 
 | 1140 |  | 
 | 1141 | 	if (kind != as99127f && kind != w83781d) { | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1142 | 		if ((err = device_create_file(dev, | 
 | 1143 | 				&sensor_dev_attr_temp1_type.dev_attr)) | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1144 | 		    || (err = device_create_file(dev, | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1145 | 				&sensor_dev_attr_temp2_type.dev_attr))) | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1146 | 			return err; | 
 | 1147 | 		if (kind != w83783s) { | 
 | 1148 | 			if ((err = device_create_file(dev, | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1149 | 					&sensor_dev_attr_temp3_type.dev_attr))) | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1150 | 				return err; | 
 | 1151 | 		} | 
 | 1152 | 	} | 
 | 1153 |  | 
 | 1154 | 	if (is_isa) { | 
 | 1155 | 		err = device_create_file(&pdev->dev, &dev_attr_name); | 
 | 1156 | 		if (err) | 
 | 1157 | 			return err; | 
 | 1158 | 	} | 
 | 1159 |  | 
 | 1160 | 	return 0; | 
 | 1161 | } | 
 | 1162 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1163 | static int | 
 | 1164 | w83781d_detect(struct i2c_adapter *adapter, int address, int kind) | 
 | 1165 | { | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1166 | 	int val1 = 0, val2; | 
| Jim Cromie | 311ce2e | 2006-09-24 21:22:52 +0200 | [diff] [blame] | 1167 | 	struct i2c_client *client; | 
 | 1168 | 	struct device *dev; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1169 | 	struct w83781d_data *data; | 
 | 1170 | 	int err; | 
 | 1171 | 	const char *client_name = ""; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1172 | 	enum vendor { winbond, asus } vendid; | 
 | 1173 |  | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1174 | 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1175 | 		err = -EINVAL; | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1176 | 		goto ERROR1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1177 | 	} | 
 | 1178 |  | 
 | 1179 | 	/* OK. For now, we presume we have a valid client. We now create the | 
 | 1180 | 	   client structure, even though we cannot fill it completely yet. | 
 | 1181 | 	   But it allows us to access w83781d_{read,write}_value. */ | 
 | 1182 |  | 
| Deepak Saxena | ba9c2e8 | 2005-10-17 23:08:32 +0200 | [diff] [blame] | 1183 | 	if (!(data = kzalloc(sizeof(struct w83781d_data), GFP_KERNEL))) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1184 | 		err = -ENOMEM; | 
 | 1185 | 		goto ERROR1; | 
 | 1186 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1187 |  | 
| Jim Cromie | 311ce2e | 2006-09-24 21:22:52 +0200 | [diff] [blame] | 1188 | 	client = &data->client; | 
 | 1189 | 	i2c_set_clientdata(client, data); | 
 | 1190 | 	client->addr = address; | 
| Ingo Molnar | 9a61bf6 | 2006-01-18 23:19:26 +0100 | [diff] [blame] | 1191 | 	mutex_init(&data->lock); | 
| Jim Cromie | 311ce2e | 2006-09-24 21:22:52 +0200 | [diff] [blame] | 1192 | 	client->adapter = adapter; | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1193 | 	client->driver = &w83781d_driver; | 
| Jim Cromie | 311ce2e | 2006-09-24 21:22:52 +0200 | [diff] [blame] | 1194 | 	dev = &client->dev; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1195 |  | 
 | 1196 | 	/* Now, we do the remaining detection. */ | 
 | 1197 |  | 
 | 1198 | 	/* The w8378?d may be stuck in some other bank than bank 0. This may | 
 | 1199 | 	   make reading other information impossible. Specify a force=... or | 
 | 1200 | 	   force_*=... parameter, and the Winbond will be reset to the right | 
 | 1201 | 	   bank. */ | 
 | 1202 | 	if (kind < 0) { | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1203 | 		if (w83781d_read_value(data, W83781D_REG_CONFIG) & 0x80) { | 
| Jean Delvare | bd452e6 | 2006-10-13 17:03:42 +0200 | [diff] [blame] | 1204 | 			dev_dbg(&adapter->dev, "Detection of w83781d chip " | 
 | 1205 | 				"failed at step 3\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1206 | 			err = -ENODEV; | 
 | 1207 | 			goto ERROR2; | 
 | 1208 | 		} | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1209 | 		val1 = w83781d_read_value(data, W83781D_REG_BANK); | 
 | 1210 | 		val2 = w83781d_read_value(data, W83781D_REG_CHIPMAN); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1211 | 		/* Check for Winbond or Asus ID if in bank 0 */ | 
 | 1212 | 		if ((!(val1 & 0x07)) && | 
 | 1213 | 		    (((!(val1 & 0x80)) && (val2 != 0xa3) && (val2 != 0xc3)) | 
 | 1214 | 		     || ((val1 & 0x80) && (val2 != 0x5c) && (val2 != 0x12)))) { | 
| Jean Delvare | bd452e6 | 2006-10-13 17:03:42 +0200 | [diff] [blame] | 1215 | 			dev_dbg(&adapter->dev, "Detection of w83781d chip " | 
 | 1216 | 				"failed at step 4\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1217 | 			err = -ENODEV; | 
 | 1218 | 			goto ERROR2; | 
 | 1219 | 		} | 
 | 1220 | 		/* If Winbond SMBus, check address at 0x48. | 
 | 1221 | 		   Asus doesn't support, except for as99127f rev.2 */ | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1222 | 		if ((!(val1 & 0x80) && (val2 == 0xa3)) || | 
 | 1223 | 		    ((val1 & 0x80) && (val2 == 0x5c))) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1224 | 			if (w83781d_read_value | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1225 | 			    (data, W83781D_REG_I2C_ADDR) != address) { | 
| Jean Delvare | bd452e6 | 2006-10-13 17:03:42 +0200 | [diff] [blame] | 1226 | 				dev_dbg(&adapter->dev, "Detection of w83781d " | 
 | 1227 | 					"chip failed at step 5\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1228 | 				err = -ENODEV; | 
 | 1229 | 				goto ERROR2; | 
 | 1230 | 			} | 
 | 1231 | 		} | 
 | 1232 | 	} | 
 | 1233 |  | 
 | 1234 | 	/* We have either had a force parameter, or we have already detected the | 
 | 1235 | 	   Winbond. Put it now into bank 0 and Vendor ID High Byte */ | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1236 | 	w83781d_write_value(data, W83781D_REG_BANK, | 
 | 1237 | 			    (w83781d_read_value(data, W83781D_REG_BANK) | 
| Jim Cromie | 311ce2e | 2006-09-24 21:22:52 +0200 | [diff] [blame] | 1238 | 			     & 0x78) | 0x80); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1239 |  | 
 | 1240 | 	/* Determine the chip type. */ | 
 | 1241 | 	if (kind <= 0) { | 
 | 1242 | 		/* get vendor ID */ | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1243 | 		val2 = w83781d_read_value(data, W83781D_REG_CHIPMAN); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1244 | 		if (val2 == 0x5c) | 
 | 1245 | 			vendid = winbond; | 
 | 1246 | 		else if (val2 == 0x12) | 
 | 1247 | 			vendid = asus; | 
 | 1248 | 		else { | 
| Jean Delvare | bd452e6 | 2006-10-13 17:03:42 +0200 | [diff] [blame] | 1249 | 			dev_dbg(&adapter->dev, "w83781d chip vendor is " | 
 | 1250 | 				"neither Winbond nor Asus\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1251 | 			err = -ENODEV; | 
 | 1252 | 			goto ERROR2; | 
 | 1253 | 		} | 
 | 1254 |  | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1255 | 		val1 = w83781d_read_value(data, W83781D_REG_WCHIPID); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1256 | 		if ((val1 == 0x10 || val1 == 0x11) && vendid == winbond) | 
 | 1257 | 			kind = w83781d; | 
 | 1258 | 		else if (val1 == 0x30 && vendid == winbond) | 
 | 1259 | 			kind = w83782d; | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1260 | 		else if (val1 == 0x40 && vendid == winbond && address == 0x2d) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1261 | 			kind = w83783s; | 
| Jean Delvare | 6722fea | 2007-10-07 12:25:46 +0200 | [diff] [blame] | 1262 | 		else if (val1 == 0x31) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1263 | 			kind = as99127f; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1264 | 		else { | 
 | 1265 | 			if (kind == 0) | 
| Jean Delvare | bd452e6 | 2006-10-13 17:03:42 +0200 | [diff] [blame] | 1266 | 				dev_warn(&adapter->dev, "Ignoring 'force' " | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1267 | 					 "parameter for unknown chip at " | 
| Jean Delvare | bd452e6 | 2006-10-13 17:03:42 +0200 | [diff] [blame] | 1268 | 					 "address 0x%02x\n", address); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1269 | 			err = -EINVAL; | 
 | 1270 | 			goto ERROR2; | 
 | 1271 | 		} | 
 | 1272 | 	} | 
 | 1273 |  | 
 | 1274 | 	if (kind == w83781d) { | 
 | 1275 | 		client_name = "w83781d"; | 
 | 1276 | 	} else if (kind == w83782d) { | 
 | 1277 | 		client_name = "w83782d"; | 
 | 1278 | 	} else if (kind == w83783s) { | 
 | 1279 | 		client_name = "w83783s"; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1280 | 	} else if (kind == as99127f) { | 
 | 1281 | 		client_name = "as99127f"; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1282 | 	} | 
 | 1283 |  | 
 | 1284 | 	/* Fill in the remaining client fields and put into the global list */ | 
| Jim Cromie | 311ce2e | 2006-09-24 21:22:52 +0200 | [diff] [blame] | 1285 | 	strlcpy(client->name, client_name, I2C_NAME_SIZE); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1286 | 	data->type = kind; | 
 | 1287 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1288 | 	/* Tell the I2C layer a new client has arrived */ | 
| Jim Cromie | 311ce2e | 2006-09-24 21:22:52 +0200 | [diff] [blame] | 1289 | 	if ((err = i2c_attach_client(client))) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1290 | 		goto ERROR2; | 
 | 1291 |  | 
 | 1292 | 	/* attach secondary i2c lm75-like clients */ | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1293 | 	if ((err = w83781d_detect_subclients(adapter, address, | 
 | 1294 | 			kind, client))) | 
 | 1295 | 		goto ERROR3; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1296 |  | 
 | 1297 | 	/* Initialize the chip */ | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1298 | 	w83781d_init_device(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1299 |  | 
 | 1300 | 	/* Register sysfs hooks */ | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1301 | 	err = w83781d_create_files(dev, kind, 0); | 
 | 1302 | 	if (err) | 
| Jim Cromie | 311ce2e | 2006-09-24 21:22:52 +0200 | [diff] [blame] | 1303 | 		goto ERROR4; | 
 | 1304 |  | 
| Tony Jones | 1beeffe | 2007-08-20 13:46:20 -0700 | [diff] [blame] | 1305 | 	data->hwmon_dev = hwmon_device_register(dev); | 
 | 1306 | 	if (IS_ERR(data->hwmon_dev)) { | 
 | 1307 | 		err = PTR_ERR(data->hwmon_dev); | 
| Mark M. Hoffman | 943b083 | 2005-07-15 21:39:18 -0400 | [diff] [blame] | 1308 | 		goto ERROR4; | 
 | 1309 | 	} | 
 | 1310 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1311 | 	return 0; | 
 | 1312 |  | 
| Mark M. Hoffman | 943b083 | 2005-07-15 21:39:18 -0400 | [diff] [blame] | 1313 | ERROR4: | 
| Jim Cromie | 311ce2e | 2006-09-24 21:22:52 +0200 | [diff] [blame] | 1314 | 	sysfs_remove_group(&dev->kobj, &w83781d_group); | 
 | 1315 | 	sysfs_remove_group(&dev->kobj, &w83781d_group_opt); | 
 | 1316 |  | 
| Mark M. Hoffman | 943b083 | 2005-07-15 21:39:18 -0400 | [diff] [blame] | 1317 | 	if (data->lm75[1]) { | 
 | 1318 | 		i2c_detach_client(data->lm75[1]); | 
 | 1319 | 		kfree(data->lm75[1]); | 
 | 1320 | 	} | 
 | 1321 | 	if (data->lm75[0]) { | 
 | 1322 | 		i2c_detach_client(data->lm75[0]); | 
 | 1323 | 		kfree(data->lm75[0]); | 
 | 1324 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1325 | ERROR3: | 
| Jim Cromie | 311ce2e | 2006-09-24 21:22:52 +0200 | [diff] [blame] | 1326 | 	i2c_detach_client(client); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1327 | ERROR2: | 
 | 1328 | 	kfree(data); | 
 | 1329 | ERROR1: | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1330 | 	return err; | 
 | 1331 | } | 
 | 1332 |  | 
 | 1333 | static int | 
 | 1334 | w83781d_detach_client(struct i2c_client *client) | 
 | 1335 | { | 
| Mark M. Hoffman | 943b083 | 2005-07-15 21:39:18 -0400 | [diff] [blame] | 1336 | 	struct w83781d_data *data = i2c_get_clientdata(client); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1337 | 	int err; | 
 | 1338 |  | 
| Mark M. Hoffman | 943b083 | 2005-07-15 21:39:18 -0400 | [diff] [blame] | 1339 | 	/* main client */ | 
| Jim Cromie | 311ce2e | 2006-09-24 21:22:52 +0200 | [diff] [blame] | 1340 | 	if (data) { | 
| Tony Jones | 1beeffe | 2007-08-20 13:46:20 -0700 | [diff] [blame] | 1341 | 		hwmon_device_unregister(data->hwmon_dev); | 
| Jim Cromie | 311ce2e | 2006-09-24 21:22:52 +0200 | [diff] [blame] | 1342 | 		sysfs_remove_group(&client->dev.kobj, &w83781d_group); | 
 | 1343 | 		sysfs_remove_group(&client->dev.kobj, &w83781d_group_opt); | 
 | 1344 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1345 |  | 
| Jean Delvare | 7bef559 | 2005-07-27 22:14:49 +0200 | [diff] [blame] | 1346 | 	if ((err = i2c_detach_client(client))) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1347 | 		return err; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1348 |  | 
| Mark M. Hoffman | 943b083 | 2005-07-15 21:39:18 -0400 | [diff] [blame] | 1349 | 	/* main client */ | 
 | 1350 | 	if (data) | 
 | 1351 | 		kfree(data); | 
 | 1352 |  | 
 | 1353 | 	/* subclient */ | 
 | 1354 | 	else | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1355 | 		kfree(client); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1356 |  | 
 | 1357 | 	return 0; | 
 | 1358 | } | 
 | 1359 |  | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1360 | static int __devinit | 
 | 1361 | w83781d_isa_probe(struct platform_device *pdev) | 
 | 1362 | { | 
 | 1363 | 	int err, reg; | 
 | 1364 | 	struct w83781d_data *data; | 
 | 1365 | 	struct resource *res; | 
 | 1366 | 	const char *name; | 
 | 1367 |  | 
 | 1368 | 	/* Reserve the ISA region */ | 
 | 1369 | 	res = platform_get_resource(pdev, IORESOURCE_IO, 0); | 
| Jean Delvare | 2961cb2 | 2008-03-09 13:34:28 +0100 | [diff] [blame] | 1370 | 	if (!request_region(res->start + W83781D_ADDR_REG_OFFSET, 2, | 
 | 1371 | 			    "w83781d")) { | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1372 | 		err = -EBUSY; | 
 | 1373 | 		goto exit; | 
 | 1374 | 	} | 
 | 1375 |  | 
 | 1376 | 	if (!(data = kzalloc(sizeof(struct w83781d_data), GFP_KERNEL))) { | 
 | 1377 | 		err = -ENOMEM; | 
 | 1378 | 		goto exit_release_region; | 
 | 1379 | 	} | 
 | 1380 | 	mutex_init(&data->lock); | 
 | 1381 | 	data->client.addr = res->start; | 
 | 1382 | 	i2c_set_clientdata(&data->client, data); | 
 | 1383 | 	platform_set_drvdata(pdev, data); | 
 | 1384 |  | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1385 | 	reg = w83781d_read_value(data, W83781D_REG_WCHIPID); | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1386 | 	switch (reg) { | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1387 | 	case 0x30: | 
 | 1388 | 		data->type = w83782d; | 
 | 1389 | 		name = "w83782d"; | 
 | 1390 | 		break; | 
 | 1391 | 	default: | 
 | 1392 | 		data->type = w83781d; | 
 | 1393 | 		name = "w83781d"; | 
 | 1394 | 	} | 
 | 1395 | 	strlcpy(data->client.name, name, I2C_NAME_SIZE); | 
 | 1396 |  | 
 | 1397 | 	/* Initialize the W83781D chip */ | 
 | 1398 | 	w83781d_init_device(&pdev->dev); | 
 | 1399 |  | 
 | 1400 | 	/* Register sysfs hooks */ | 
 | 1401 | 	err = w83781d_create_files(&pdev->dev, data->type, 1); | 
 | 1402 | 	if (err) | 
 | 1403 | 		goto exit_remove_files; | 
 | 1404 |  | 
| Tony Jones | 1beeffe | 2007-08-20 13:46:20 -0700 | [diff] [blame] | 1405 | 	data->hwmon_dev = hwmon_device_register(&pdev->dev); | 
 | 1406 | 	if (IS_ERR(data->hwmon_dev)) { | 
 | 1407 | 		err = PTR_ERR(data->hwmon_dev); | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1408 | 		goto exit_remove_files; | 
 | 1409 | 	} | 
 | 1410 |  | 
 | 1411 | 	return 0; | 
 | 1412 |  | 
 | 1413 |  exit_remove_files: | 
 | 1414 | 	sysfs_remove_group(&pdev->dev.kobj, &w83781d_group); | 
 | 1415 | 	sysfs_remove_group(&pdev->dev.kobj, &w83781d_group_opt); | 
 | 1416 | 	device_remove_file(&pdev->dev, &dev_attr_name); | 
 | 1417 | 	kfree(data); | 
 | 1418 |  exit_release_region: | 
| Jean Delvare | 2961cb2 | 2008-03-09 13:34:28 +0100 | [diff] [blame] | 1419 | 	release_region(res->start + W83781D_ADDR_REG_OFFSET, 2); | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1420 |  exit: | 
 | 1421 | 	return err; | 
 | 1422 | } | 
 | 1423 |  | 
 | 1424 | static int __devexit | 
 | 1425 | w83781d_isa_remove(struct platform_device *pdev) | 
 | 1426 | { | 
 | 1427 | 	struct w83781d_data *data = platform_get_drvdata(pdev); | 
 | 1428 |  | 
| Tony Jones | 1beeffe | 2007-08-20 13:46:20 -0700 | [diff] [blame] | 1429 | 	hwmon_device_unregister(data->hwmon_dev); | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1430 | 	sysfs_remove_group(&pdev->dev.kobj, &w83781d_group); | 
 | 1431 | 	sysfs_remove_group(&pdev->dev.kobj, &w83781d_group_opt); | 
 | 1432 | 	device_remove_file(&pdev->dev, &dev_attr_name); | 
| Jean Delvare | 2961cb2 | 2008-03-09 13:34:28 +0100 | [diff] [blame] | 1433 | 	release_region(data->client.addr + W83781D_ADDR_REG_OFFSET, 2); | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1434 | 	kfree(data); | 
 | 1435 |  | 
 | 1436 | 	return 0; | 
 | 1437 | } | 
 | 1438 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1439 | /* The SMBus locks itself, usually, but nothing may access the Winbond between | 
| Jean Delvare | 293c099 | 2007-11-30 23:52:44 +0100 | [diff] [blame] | 1440 |    bank switches. ISA access must always be locked explicitly! | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1441 |    We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks, | 
| Jean Delvare | 293c099 | 2007-11-30 23:52:44 +0100 | [diff] [blame] | 1442 |    would slow down the W83781D access and should not be necessary. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1443 |    There are some ugly typecasts here, but the good news is - they should | 
 | 1444 |    nowhere else be necessary! */ | 
 | 1445 | static int | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1446 | w83781d_read_value(struct w83781d_data *data, u16 reg) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1447 | { | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1448 | 	struct i2c_client *client = &data->client; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1449 | 	int res, word_sized, bank; | 
 | 1450 | 	struct i2c_client *cl; | 
 | 1451 |  | 
| Ingo Molnar | 9a61bf6 | 2006-01-18 23:19:26 +0100 | [diff] [blame] | 1452 | 	mutex_lock(&data->lock); | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1453 | 	if (!client->driver) { /* ISA device */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1454 | 		word_sized = (((reg & 0xff00) == 0x100) | 
 | 1455 | 			      || ((reg & 0xff00) == 0x200)) | 
 | 1456 | 		    && (((reg & 0x00ff) == 0x50) | 
 | 1457 | 			|| ((reg & 0x00ff) == 0x53) | 
 | 1458 | 			|| ((reg & 0x00ff) == 0x55)); | 
 | 1459 | 		if (reg & 0xff00) { | 
 | 1460 | 			outb_p(W83781D_REG_BANK, | 
 | 1461 | 			       client->addr + W83781D_ADDR_REG_OFFSET); | 
 | 1462 | 			outb_p(reg >> 8, | 
 | 1463 | 			       client->addr + W83781D_DATA_REG_OFFSET); | 
 | 1464 | 		} | 
 | 1465 | 		outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); | 
 | 1466 | 		res = inb_p(client->addr + W83781D_DATA_REG_OFFSET); | 
 | 1467 | 		if (word_sized) { | 
 | 1468 | 			outb_p((reg & 0xff) + 1, | 
 | 1469 | 			       client->addr + W83781D_ADDR_REG_OFFSET); | 
 | 1470 | 			res = | 
 | 1471 | 			    (res << 8) + inb_p(client->addr + | 
 | 1472 | 					       W83781D_DATA_REG_OFFSET); | 
 | 1473 | 		} | 
 | 1474 | 		if (reg & 0xff00) { | 
 | 1475 | 			outb_p(W83781D_REG_BANK, | 
 | 1476 | 			       client->addr + W83781D_ADDR_REG_OFFSET); | 
 | 1477 | 			outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); | 
 | 1478 | 		} | 
 | 1479 | 	} else { | 
 | 1480 | 		bank = (reg >> 8) & 0x0f; | 
 | 1481 | 		if (bank > 2) | 
 | 1482 | 			/* switch banks */ | 
 | 1483 | 			i2c_smbus_write_byte_data(client, W83781D_REG_BANK, | 
 | 1484 | 						  bank); | 
 | 1485 | 		if (bank == 0 || bank > 2) { | 
 | 1486 | 			res = i2c_smbus_read_byte_data(client, reg & 0xff); | 
 | 1487 | 		} else { | 
 | 1488 | 			/* switch to subclient */ | 
 | 1489 | 			cl = data->lm75[bank - 1]; | 
 | 1490 | 			/* convert from ISA to LM75 I2C addresses */ | 
 | 1491 | 			switch (reg & 0xff) { | 
 | 1492 | 			case 0x50:	/* TEMP */ | 
 | 1493 | 				res = swab16(i2c_smbus_read_word_data(cl, 0)); | 
 | 1494 | 				break; | 
 | 1495 | 			case 0x52:	/* CONFIG */ | 
 | 1496 | 				res = i2c_smbus_read_byte_data(cl, 1); | 
 | 1497 | 				break; | 
 | 1498 | 			case 0x53:	/* HYST */ | 
 | 1499 | 				res = swab16(i2c_smbus_read_word_data(cl, 2)); | 
 | 1500 | 				break; | 
 | 1501 | 			case 0x55:	/* OVER */ | 
 | 1502 | 			default: | 
 | 1503 | 				res = swab16(i2c_smbus_read_word_data(cl, 3)); | 
 | 1504 | 				break; | 
 | 1505 | 			} | 
 | 1506 | 		} | 
 | 1507 | 		if (bank > 2) | 
 | 1508 | 			i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0); | 
 | 1509 | 	} | 
| Ingo Molnar | 9a61bf6 | 2006-01-18 23:19:26 +0100 | [diff] [blame] | 1510 | 	mutex_unlock(&data->lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1511 | 	return res; | 
 | 1512 | } | 
 | 1513 |  | 
 | 1514 | static int | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1515 | w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1516 | { | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1517 | 	struct i2c_client *client = &data->client; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1518 | 	int word_sized, bank; | 
 | 1519 | 	struct i2c_client *cl; | 
 | 1520 |  | 
| Ingo Molnar | 9a61bf6 | 2006-01-18 23:19:26 +0100 | [diff] [blame] | 1521 | 	mutex_lock(&data->lock); | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1522 | 	if (!client->driver) { /* ISA device */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1523 | 		word_sized = (((reg & 0xff00) == 0x100) | 
 | 1524 | 			      || ((reg & 0xff00) == 0x200)) | 
 | 1525 | 		    && (((reg & 0x00ff) == 0x53) | 
 | 1526 | 			|| ((reg & 0x00ff) == 0x55)); | 
 | 1527 | 		if (reg & 0xff00) { | 
 | 1528 | 			outb_p(W83781D_REG_BANK, | 
 | 1529 | 			       client->addr + W83781D_ADDR_REG_OFFSET); | 
 | 1530 | 			outb_p(reg >> 8, | 
 | 1531 | 			       client->addr + W83781D_DATA_REG_OFFSET); | 
 | 1532 | 		} | 
 | 1533 | 		outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); | 
 | 1534 | 		if (word_sized) { | 
 | 1535 | 			outb_p(value >> 8, | 
 | 1536 | 			       client->addr + W83781D_DATA_REG_OFFSET); | 
 | 1537 | 			outb_p((reg & 0xff) + 1, | 
 | 1538 | 			       client->addr + W83781D_ADDR_REG_OFFSET); | 
 | 1539 | 		} | 
 | 1540 | 		outb_p(value & 0xff, client->addr + W83781D_DATA_REG_OFFSET); | 
 | 1541 | 		if (reg & 0xff00) { | 
 | 1542 | 			outb_p(W83781D_REG_BANK, | 
 | 1543 | 			       client->addr + W83781D_ADDR_REG_OFFSET); | 
 | 1544 | 			outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); | 
 | 1545 | 		} | 
 | 1546 | 	} else { | 
 | 1547 | 		bank = (reg >> 8) & 0x0f; | 
 | 1548 | 		if (bank > 2) | 
 | 1549 | 			/* switch banks */ | 
 | 1550 | 			i2c_smbus_write_byte_data(client, W83781D_REG_BANK, | 
 | 1551 | 						  bank); | 
 | 1552 | 		if (bank == 0 || bank > 2) { | 
 | 1553 | 			i2c_smbus_write_byte_data(client, reg & 0xff, | 
 | 1554 | 						  value & 0xff); | 
 | 1555 | 		} else { | 
 | 1556 | 			/* switch to subclient */ | 
 | 1557 | 			cl = data->lm75[bank - 1]; | 
 | 1558 | 			/* convert from ISA to LM75 I2C addresses */ | 
 | 1559 | 			switch (reg & 0xff) { | 
 | 1560 | 			case 0x52:	/* CONFIG */ | 
 | 1561 | 				i2c_smbus_write_byte_data(cl, 1, value & 0xff); | 
 | 1562 | 				break; | 
 | 1563 | 			case 0x53:	/* HYST */ | 
 | 1564 | 				i2c_smbus_write_word_data(cl, 2, swab16(value)); | 
 | 1565 | 				break; | 
 | 1566 | 			case 0x55:	/* OVER */ | 
 | 1567 | 				i2c_smbus_write_word_data(cl, 3, swab16(value)); | 
 | 1568 | 				break; | 
 | 1569 | 			} | 
 | 1570 | 		} | 
 | 1571 | 		if (bank > 2) | 
 | 1572 | 			i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0); | 
 | 1573 | 	} | 
| Ingo Molnar | 9a61bf6 | 2006-01-18 23:19:26 +0100 | [diff] [blame] | 1574 | 	mutex_unlock(&data->lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1575 | 	return 0; | 
 | 1576 | } | 
 | 1577 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1578 | static void | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1579 | w83781d_init_device(struct device *dev) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1580 | { | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1581 | 	struct w83781d_data *data = dev_get_drvdata(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1582 | 	int i, p; | 
 | 1583 | 	int type = data->type; | 
 | 1584 | 	u8 tmp; | 
 | 1585 |  | 
| Jean Delvare | fabddcd | 2006-02-05 23:26:51 +0100 | [diff] [blame] | 1586 | 	if (reset && type != as99127f) { /* this resets registers we don't have | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1587 | 					   documentation for on the as99127f */ | 
| Jean Delvare | fabddcd | 2006-02-05 23:26:51 +0100 | [diff] [blame] | 1588 | 		/* Resetting the chip has been the default for a long time, | 
 | 1589 | 		   but it causes the BIOS initializations (fan clock dividers, | 
 | 1590 | 		   thermal sensor types...) to be lost, so it is now optional. | 
 | 1591 | 		   It might even go away if nobody reports it as being useful, | 
 | 1592 | 		   as I see very little reason why this would be needed at | 
 | 1593 | 		   all. */ | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1594 | 		dev_info(dev, "If reset=1 solved a problem you were " | 
| Jean Delvare | fabddcd | 2006-02-05 23:26:51 +0100 | [diff] [blame] | 1595 | 			 "having, please report!\n"); | 
 | 1596 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1597 | 		/* save these registers */ | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1598 | 		i = w83781d_read_value(data, W83781D_REG_BEEP_CONFIG); | 
 | 1599 | 		p = w83781d_read_value(data, W83781D_REG_PWMCLK12); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1600 | 		/* Reset all except Watchdog values and last conversion values | 
 | 1601 | 		   This sets fan-divs to 2, among others */ | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1602 | 		w83781d_write_value(data, W83781D_REG_CONFIG, 0x80); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1603 | 		/* Restore the registers and disable power-on abnormal beep. | 
 | 1604 | 		   This saves FAN 1/2/3 input/output values set by BIOS. */ | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1605 | 		w83781d_write_value(data, W83781D_REG_BEEP_CONFIG, i | 0x80); | 
 | 1606 | 		w83781d_write_value(data, W83781D_REG_PWMCLK12, p); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1607 | 		/* Disable master beep-enable (reset turns it on). | 
 | 1608 | 		   Individual beep_mask should be reset to off but for some reason | 
 | 1609 | 		   disabling this bit helps some people not get beeped */ | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1610 | 		w83781d_write_value(data, W83781D_REG_BEEP_INTS2, 0); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1611 | 	} | 
 | 1612 |  | 
| Jean Delvare | fabddcd | 2006-02-05 23:26:51 +0100 | [diff] [blame] | 1613 | 	/* Disable power-on abnormal beep, as advised by the datasheet. | 
 | 1614 | 	   Already done if reset=1. */ | 
 | 1615 | 	if (init && !reset && type != as99127f) { | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1616 | 		i = w83781d_read_value(data, W83781D_REG_BEEP_CONFIG); | 
 | 1617 | 		w83781d_write_value(data, W83781D_REG_BEEP_CONFIG, i | 0x80); | 
| Jean Delvare | fabddcd | 2006-02-05 23:26:51 +0100 | [diff] [blame] | 1618 | 	} | 
 | 1619 |  | 
| Jean Delvare | 303760b | 2005-07-31 21:52:01 +0200 | [diff] [blame] | 1620 | 	data->vrm = vid_which_vrm(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1621 |  | 
 | 1622 | 	if ((type != w83781d) && (type != as99127f)) { | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1623 | 		tmp = w83781d_read_value(data, W83781D_REG_SCFG1); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1624 | 		for (i = 1; i <= 3; i++) { | 
 | 1625 | 			if (!(tmp & BIT_SCFG1[i - 1])) { | 
| Jean Delvare | b26f933 | 2007-08-16 14:30:01 +0200 | [diff] [blame] | 1626 | 				data->sens[i - 1] = 4; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1627 | 			} else { | 
 | 1628 | 				if (w83781d_read_value | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1629 | 				    (data, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1630 | 				     W83781D_REG_SCFG2) & BIT_SCFG2[i - 1]) | 
 | 1631 | 					data->sens[i - 1] = 1; | 
 | 1632 | 				else | 
 | 1633 | 					data->sens[i - 1] = 2; | 
 | 1634 | 			} | 
| Jean Delvare | 7c7a530 | 2005-06-16 19:24:14 +0200 | [diff] [blame] | 1635 | 			if (type == w83783s && i == 2) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1636 | 				break; | 
 | 1637 | 		} | 
 | 1638 | 	} | 
 | 1639 |  | 
 | 1640 | 	if (init && type != as99127f) { | 
 | 1641 | 		/* Enable temp2 */ | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1642 | 		tmp = w83781d_read_value(data, W83781D_REG_TEMP2_CONFIG); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1643 | 		if (tmp & 0x01) { | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1644 | 			dev_warn(dev, "Enabling temp2, readings " | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1645 | 				 "might not make sense\n"); | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1646 | 			w83781d_write_value(data, W83781D_REG_TEMP2_CONFIG, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1647 | 				tmp & 0xfe); | 
 | 1648 | 		} | 
 | 1649 |  | 
 | 1650 | 		/* Enable temp3 */ | 
| Jean Delvare | 7c7a530 | 2005-06-16 19:24:14 +0200 | [diff] [blame] | 1651 | 		if (type != w83783s) { | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1652 | 			tmp = w83781d_read_value(data, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1653 | 				W83781D_REG_TEMP3_CONFIG); | 
 | 1654 | 			if (tmp & 0x01) { | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1655 | 				dev_warn(dev, "Enabling temp3, " | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1656 | 					 "readings might not make sense\n"); | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1657 | 				w83781d_write_value(data, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1658 | 					W83781D_REG_TEMP3_CONFIG, tmp & 0xfe); | 
 | 1659 | 			} | 
 | 1660 | 		} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1661 | 	} | 
 | 1662 |  | 
 | 1663 | 	/* Start monitoring */ | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1664 | 	w83781d_write_value(data, W83781D_REG_CONFIG, | 
 | 1665 | 			    (w83781d_read_value(data, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1666 | 						W83781D_REG_CONFIG) & 0xf7) | 
 | 1667 | 			    | 0x01); | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1668 |  | 
 | 1669 | 	/* A few vars need to be filled upon startup */ | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1670 | 	for (i = 0; i < 3; i++) { | 
 | 1671 | 		data->fan_min[i] = w83781d_read_value(data, | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1672 | 					W83781D_REG_FAN_MIN(i)); | 
 | 1673 | 	} | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1674 |  | 
 | 1675 | 	mutex_init(&data->update_lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1676 | } | 
 | 1677 |  | 
 | 1678 | static struct w83781d_data *w83781d_update_device(struct device *dev) | 
 | 1679 | { | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1680 | 	struct w83781d_data *data = dev_get_drvdata(dev); | 
 | 1681 | 	struct i2c_client *client = &data->client; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1682 | 	int i; | 
 | 1683 |  | 
| Ingo Molnar | 9a61bf6 | 2006-01-18 23:19:26 +0100 | [diff] [blame] | 1684 | 	mutex_lock(&data->update_lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1685 |  | 
 | 1686 | 	if (time_after(jiffies, data->last_updated + HZ + HZ / 2) | 
 | 1687 | 	    || !data->valid) { | 
 | 1688 | 		dev_dbg(dev, "Starting device update\n"); | 
 | 1689 |  | 
 | 1690 | 		for (i = 0; i <= 8; i++) { | 
| Jean Delvare | 7c7a530 | 2005-06-16 19:24:14 +0200 | [diff] [blame] | 1691 | 			if (data->type == w83783s && i == 1) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1692 | 				continue;	/* 783S has no in1 */ | 
 | 1693 | 			data->in[i] = | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1694 | 			    w83781d_read_value(data, W83781D_REG_IN(i)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1695 | 			data->in_min[i] = | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1696 | 			    w83781d_read_value(data, W83781D_REG_IN_MIN(i)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1697 | 			data->in_max[i] = | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1698 | 			    w83781d_read_value(data, W83781D_REG_IN_MAX(i)); | 
| Jean Delvare | 0566336 | 2007-11-30 23:51:24 +0100 | [diff] [blame] | 1699 | 			if ((data->type != w83782d) && (i == 6)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1700 | 				break; | 
 | 1701 | 		} | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1702 | 		for (i = 0; i < 3; i++) { | 
 | 1703 | 			data->fan[i] = | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1704 | 			    w83781d_read_value(data, W83781D_REG_FAN(i)); | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1705 | 			data->fan_min[i] = | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1706 | 			    w83781d_read_value(data, W83781D_REG_FAN_MIN(i)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1707 | 		} | 
 | 1708 | 		if (data->type != w83781d && data->type != as99127f) { | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1709 | 			for (i = 0; i < 4; i++) { | 
 | 1710 | 				data->pwm[i] = | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1711 | 				    w83781d_read_value(data, | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1712 | 						       W83781D_REG_PWM[i]); | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1713 | 				if ((data->type != w83782d || !client->driver) | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1714 | 				    && i == 1) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1715 | 					break; | 
 | 1716 | 			} | 
 | 1717 | 			/* Only PWM2 can be disabled */ | 
| Jean Delvare | 3487533 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1718 | 			data->pwm2_enable = (w83781d_read_value(data, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1719 | 					      W83781D_REG_PWMCLK12) & 0x08) >> 3; | 
 | 1720 | 		} | 
 | 1721 |  | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1722 | 		data->temp = w83781d_read_value(data, W83781D_REG_TEMP(1)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1723 | 		data->temp_max = | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1724 | 		    w83781d_read_value(data, W83781D_REG_TEMP_OVER(1)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1725 | 		data->temp_max_hyst = | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1726 | 		    w83781d_read_value(data, W83781D_REG_TEMP_HYST(1)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1727 | 		data->temp_add[0] = | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1728 | 		    w83781d_read_value(data, W83781D_REG_TEMP(2)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1729 | 		data->temp_max_add[0] = | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1730 | 		    w83781d_read_value(data, W83781D_REG_TEMP_OVER(2)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1731 | 		data->temp_max_hyst_add[0] = | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1732 | 		    w83781d_read_value(data, W83781D_REG_TEMP_HYST(2)); | 
| Jean Delvare | 7c7a530 | 2005-06-16 19:24:14 +0200 | [diff] [blame] | 1733 | 		if (data->type != w83783s) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1734 | 			data->temp_add[1] = | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1735 | 			    w83781d_read_value(data, W83781D_REG_TEMP(3)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1736 | 			data->temp_max_add[1] = | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1737 | 			    w83781d_read_value(data, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1738 | 					       W83781D_REG_TEMP_OVER(3)); | 
 | 1739 | 			data->temp_max_hyst_add[1] = | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1740 | 			    w83781d_read_value(data, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1741 | 					       W83781D_REG_TEMP_HYST(3)); | 
 | 1742 | 		} | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1743 | 		i = w83781d_read_value(data, W83781D_REG_VID_FANDIV); | 
| Jean Delvare | 7c7a530 | 2005-06-16 19:24:14 +0200 | [diff] [blame] | 1744 | 		data->vid = i & 0x0f; | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1745 | 		data->vid |= (w83781d_read_value(data, | 
| Jean Delvare | 7c7a530 | 2005-06-16 19:24:14 +0200 | [diff] [blame] | 1746 | 					W83781D_REG_CHIPID) & 0x01) << 4; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1747 | 		data->fan_div[0] = (i >> 4) & 0x03; | 
 | 1748 | 		data->fan_div[1] = (i >> 6) & 0x03; | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1749 | 		data->fan_div[2] = (w83781d_read_value(data, | 
| Jean Delvare | 7c7a530 | 2005-06-16 19:24:14 +0200 | [diff] [blame] | 1750 | 					W83781D_REG_PIN) >> 6) & 0x03; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1751 | 		if ((data->type != w83781d) && (data->type != as99127f)) { | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1752 | 			i = w83781d_read_value(data, W83781D_REG_VBAT); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1753 | 			data->fan_div[0] |= (i >> 3) & 0x04; | 
 | 1754 | 			data->fan_div[1] |= (i >> 4) & 0x04; | 
| Jean Delvare | 7c7a530 | 2005-06-16 19:24:14 +0200 | [diff] [blame] | 1755 | 			data->fan_div[2] |= (i >> 5) & 0x04; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1756 | 		} | 
| Jean Delvare | 0566336 | 2007-11-30 23:51:24 +0100 | [diff] [blame] | 1757 | 		if (data->type == w83782d) { | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1758 | 			data->alarms = w83781d_read_value(data, | 
| Jean Delvare | c7f5d7e | 2006-02-05 23:13:48 +0100 | [diff] [blame] | 1759 | 						W83782D_REG_ALARM1) | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1760 | 				     | (w83781d_read_value(data, | 
| Jean Delvare | c7f5d7e | 2006-02-05 23:13:48 +0100 | [diff] [blame] | 1761 | 						W83782D_REG_ALARM2) << 8) | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1762 | 				     | (w83781d_read_value(data, | 
| Jean Delvare | c7f5d7e | 2006-02-05 23:13:48 +0100 | [diff] [blame] | 1763 | 						W83782D_REG_ALARM3) << 16); | 
 | 1764 | 		} else if (data->type == w83783s) { | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1765 | 			data->alarms = w83781d_read_value(data, | 
| Jean Delvare | c7f5d7e | 2006-02-05 23:13:48 +0100 | [diff] [blame] | 1766 | 						W83782D_REG_ALARM1) | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1767 | 				     | (w83781d_read_value(data, | 
| Jean Delvare | c7f5d7e | 2006-02-05 23:13:48 +0100 | [diff] [blame] | 1768 | 						W83782D_REG_ALARM2) << 8); | 
 | 1769 | 		} else { | 
 | 1770 | 			/* No real-time status registers, fall back to | 
 | 1771 | 			   interrupt status registers */ | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1772 | 			data->alarms = w83781d_read_value(data, | 
| Jean Delvare | c7f5d7e | 2006-02-05 23:13:48 +0100 | [diff] [blame] | 1773 | 						W83781D_REG_ALARM1) | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1774 | 				     | (w83781d_read_value(data, | 
| Jean Delvare | c7f5d7e | 2006-02-05 23:13:48 +0100 | [diff] [blame] | 1775 | 						W83781D_REG_ALARM2) << 8); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1776 | 		} | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1777 | 		i = w83781d_read_value(data, W83781D_REG_BEEP_INTS2); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1778 | 		data->beep_enable = i >> 7; | 
 | 1779 | 		data->beep_mask = ((i & 0x7f) << 8) + | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1780 | 		    w83781d_read_value(data, W83781D_REG_BEEP_INTS1); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1781 | 		if ((data->type != w83781d) && (data->type != as99127f)) { | 
 | 1782 | 			data->beep_mask |= | 
| Jean Delvare | 31b8dc4 | 2007-05-08 17:22:03 +0200 | [diff] [blame] | 1783 | 			    w83781d_read_value(data, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1784 | 					       W83781D_REG_BEEP_INTS3) << 16; | 
 | 1785 | 		} | 
 | 1786 | 		data->last_updated = jiffies; | 
 | 1787 | 		data->valid = 1; | 
 | 1788 | 	} | 
 | 1789 |  | 
| Ingo Molnar | 9a61bf6 | 2006-01-18 23:19:26 +0100 | [diff] [blame] | 1790 | 	mutex_unlock(&data->update_lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1791 |  | 
 | 1792 | 	return data; | 
 | 1793 | } | 
 | 1794 |  | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1795 | /* return 1 if a supported chip is found, 0 otherwise */ | 
 | 1796 | static int __init | 
 | 1797 | w83781d_isa_found(unsigned short address) | 
 | 1798 | { | 
 | 1799 | 	int val, save, found = 0; | 
 | 1800 |  | 
| Jean Delvare | 2961cb2 | 2008-03-09 13:34:28 +0100 | [diff] [blame] | 1801 | 	/* We have to request the region in two parts because some | 
 | 1802 | 	   boards declare base+4 to base+7 as a PNP device */ | 
 | 1803 | 	if (!request_region(address, 4, "w83781d")) { | 
 | 1804 | 		pr_debug("w83781d: Failed to request low part of region\n"); | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1805 | 		return 0; | 
| Jean Delvare | 2961cb2 | 2008-03-09 13:34:28 +0100 | [diff] [blame] | 1806 | 	} | 
 | 1807 | 	if (!request_region(address + 4, 4, "w83781d")) { | 
 | 1808 | 		pr_debug("w83781d: Failed to request high part of region\n"); | 
 | 1809 | 		release_region(address, 4); | 
 | 1810 | 		return 0; | 
 | 1811 | 	} | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1812 |  | 
 | 1813 | #define REALLY_SLOW_IO | 
 | 1814 | 	/* We need the timeouts for at least some W83781D-like | 
 | 1815 | 	   chips. But only if we read 'undefined' registers. */ | 
 | 1816 | 	val = inb_p(address + 1); | 
 | 1817 | 	if (inb_p(address + 2) != val | 
 | 1818 | 	 || inb_p(address + 3) != val | 
 | 1819 | 	 || inb_p(address + 7) != val) { | 
 | 1820 | 		pr_debug("w83781d: Detection failed at step 1\n"); | 
 | 1821 | 		goto release; | 
 | 1822 | 	} | 
 | 1823 | #undef REALLY_SLOW_IO | 
 | 1824 |  | 
 | 1825 | 	/* We should be able to change the 7 LSB of the address port. The | 
 | 1826 | 	   MSB (busy flag) should be clear initially, set after the write. */ | 
 | 1827 | 	save = inb_p(address + W83781D_ADDR_REG_OFFSET); | 
 | 1828 | 	if (save & 0x80) { | 
 | 1829 | 		pr_debug("w83781d: Detection failed at step 2\n"); | 
 | 1830 | 		goto release; | 
 | 1831 | 	} | 
 | 1832 | 	val = ~save & 0x7f; | 
 | 1833 | 	outb_p(val, address + W83781D_ADDR_REG_OFFSET); | 
 | 1834 | 	if (inb_p(address + W83781D_ADDR_REG_OFFSET) != (val | 0x80)) { | 
 | 1835 | 		outb_p(save, address + W83781D_ADDR_REG_OFFSET); | 
 | 1836 | 		pr_debug("w83781d: Detection failed at step 3\n"); | 
 | 1837 | 		goto release; | 
 | 1838 | 	} | 
 | 1839 |  | 
 | 1840 | 	/* We found a device, now see if it could be a W83781D */ | 
 | 1841 | 	outb_p(W83781D_REG_CONFIG, address + W83781D_ADDR_REG_OFFSET); | 
 | 1842 | 	val = inb_p(address + W83781D_DATA_REG_OFFSET); | 
 | 1843 | 	if (val & 0x80) { | 
 | 1844 | 		pr_debug("w83781d: Detection failed at step 4\n"); | 
 | 1845 | 		goto release; | 
 | 1846 | 	} | 
 | 1847 | 	outb_p(W83781D_REG_BANK, address + W83781D_ADDR_REG_OFFSET); | 
 | 1848 | 	save = inb_p(address + W83781D_DATA_REG_OFFSET); | 
 | 1849 | 	outb_p(W83781D_REG_CHIPMAN, address + W83781D_ADDR_REG_OFFSET); | 
 | 1850 | 	val = inb_p(address + W83781D_DATA_REG_OFFSET); | 
 | 1851 | 	if ((!(save & 0x80) && (val != 0xa3)) | 
 | 1852 | 	 || ((save & 0x80) && (val != 0x5c))) { | 
 | 1853 | 		pr_debug("w83781d: Detection failed at step 5\n"); | 
 | 1854 | 		goto release; | 
 | 1855 | 	} | 
 | 1856 | 	outb_p(W83781D_REG_I2C_ADDR, address + W83781D_ADDR_REG_OFFSET); | 
 | 1857 | 	val = inb_p(address + W83781D_DATA_REG_OFFSET); | 
 | 1858 | 	if (val < 0x03 || val > 0x77) {	/* Not a valid I2C address */ | 
 | 1859 | 		pr_debug("w83781d: Detection failed at step 6\n"); | 
 | 1860 | 		goto release; | 
 | 1861 | 	} | 
 | 1862 |  | 
 | 1863 | 	/* The busy flag should be clear again */ | 
 | 1864 | 	if (inb_p(address + W83781D_ADDR_REG_OFFSET) & 0x80) { | 
 | 1865 | 		pr_debug("w83781d: Detection failed at step 7\n"); | 
 | 1866 | 		goto release; | 
 | 1867 | 	} | 
 | 1868 |  | 
 | 1869 | 	/* Determine the chip type */ | 
 | 1870 | 	outb_p(W83781D_REG_BANK, address + W83781D_ADDR_REG_OFFSET); | 
 | 1871 | 	save = inb_p(address + W83781D_DATA_REG_OFFSET); | 
 | 1872 | 	outb_p(save & 0xf8, address + W83781D_DATA_REG_OFFSET); | 
 | 1873 | 	outb_p(W83781D_REG_WCHIPID, address + W83781D_ADDR_REG_OFFSET); | 
 | 1874 | 	val = inb_p(address + W83781D_DATA_REG_OFFSET); | 
 | 1875 | 	if ((val & 0xfe) == 0x10	/* W83781D */ | 
| Jean Delvare | 0566336 | 2007-11-30 23:51:24 +0100 | [diff] [blame] | 1876 | 	 || val == 0x30)		/* W83782D */ | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1877 | 		found = 1; | 
 | 1878 |  | 
 | 1879 | 	if (found) | 
 | 1880 | 		pr_info("w83781d: Found a %s chip at %#x\n", | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1881 | 			val == 0x30 ? "W83782D" : "W83781D", (int)address); | 
 | 1882 |  | 
 | 1883 |  release: | 
| Jean Delvare | 2961cb2 | 2008-03-09 13:34:28 +0100 | [diff] [blame] | 1884 | 	release_region(address + 4, 4); | 
 | 1885 | 	release_region(address, 4); | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1886 | 	return found; | 
 | 1887 | } | 
 | 1888 |  | 
 | 1889 | static int __init | 
 | 1890 | w83781d_isa_device_add(unsigned short address) | 
 | 1891 | { | 
 | 1892 | 	struct resource res = { | 
 | 1893 | 		.start	= address, | 
| Jean Delvare | 15bde2f | 2007-08-29 10:39:57 +0200 | [diff] [blame] | 1894 | 		.end	= address + W83781D_EXTENT - 1, | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1895 | 		.name	= "w83781d", | 
 | 1896 | 		.flags	= IORESOURCE_IO, | 
 | 1897 | 	}; | 
 | 1898 | 	int err; | 
 | 1899 |  | 
 | 1900 | 	pdev = platform_device_alloc("w83781d", address); | 
 | 1901 | 	if (!pdev) { | 
 | 1902 | 		err = -ENOMEM; | 
 | 1903 | 		printk(KERN_ERR "w83781d: Device allocation failed\n"); | 
 | 1904 | 		goto exit; | 
 | 1905 | 	} | 
 | 1906 |  | 
 | 1907 | 	err = platform_device_add_resources(pdev, &res, 1); | 
 | 1908 | 	if (err) { | 
 | 1909 | 		printk(KERN_ERR "w83781d: Device resource addition failed " | 
 | 1910 | 		       "(%d)\n", err); | 
 | 1911 | 		goto exit_device_put; | 
 | 1912 | 	} | 
 | 1913 |  | 
 | 1914 | 	err = platform_device_add(pdev); | 
 | 1915 | 	if (err) { | 
 | 1916 | 		printk(KERN_ERR "w83781d: Device addition failed (%d)\n", | 
 | 1917 | 		       err); | 
 | 1918 | 		goto exit_device_put; | 
 | 1919 | 	} | 
 | 1920 |  | 
 | 1921 | 	return 0; | 
 | 1922 |  | 
 | 1923 |  exit_device_put: | 
 | 1924 | 	platform_device_put(pdev); | 
 | 1925 |  exit: | 
 | 1926 | 	pdev = NULL; | 
 | 1927 | 	return err; | 
 | 1928 | } | 
 | 1929 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1930 | static int __init | 
 | 1931 | sensors_w83781d_init(void) | 
 | 1932 | { | 
| Jean Delvare | fde0950 | 2005-07-19 23:51:07 +0200 | [diff] [blame] | 1933 | 	int res; | 
 | 1934 |  | 
 | 1935 | 	res = i2c_add_driver(&w83781d_driver); | 
 | 1936 | 	if (res) | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1937 | 		goto exit; | 
| Jean Delvare | fde0950 | 2005-07-19 23:51:07 +0200 | [diff] [blame] | 1938 |  | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1939 | 	if (w83781d_isa_found(isa_address)) { | 
 | 1940 | 		res = platform_driver_register(&w83781d_isa_driver); | 
 | 1941 | 		if (res) | 
 | 1942 | 			goto exit_unreg_i2c_driver; | 
 | 1943 |  | 
 | 1944 | 		/* Sets global pdev as a side effect */ | 
 | 1945 | 		res = w83781d_isa_device_add(isa_address); | 
 | 1946 | 		if (res) | 
 | 1947 | 			goto exit_unreg_isa_driver; | 
 | 1948 | 	} | 
| Jean Delvare | fde0950 | 2005-07-19 23:51:07 +0200 | [diff] [blame] | 1949 |  | 
 | 1950 | 	return 0; | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1951 |  | 
 | 1952 |  exit_unreg_isa_driver: | 
 | 1953 | 	platform_driver_unregister(&w83781d_isa_driver); | 
 | 1954 |  exit_unreg_i2c_driver: | 
 | 1955 | 	i2c_del_driver(&w83781d_driver); | 
 | 1956 |  exit: | 
 | 1957 | 	return res; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1958 | } | 
 | 1959 |  | 
 | 1960 | static void __exit | 
 | 1961 | sensors_w83781d_exit(void) | 
 | 1962 | { | 
| Jean Delvare | 7666c13 | 2007-05-08 17:22:02 +0200 | [diff] [blame] | 1963 | 	if (pdev) { | 
 | 1964 | 		platform_device_unregister(pdev); | 
 | 1965 | 		platform_driver_unregister(&w83781d_isa_driver); | 
 | 1966 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1967 | 	i2c_del_driver(&w83781d_driver); | 
 | 1968 | } | 
 | 1969 |  | 
 | 1970 | MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, " | 
 | 1971 | 	      "Philip Edelbrock <phil@netroedge.com>, " | 
 | 1972 | 	      "and Mark Studebaker <mdsxyz123@yahoo.com>"); | 
 | 1973 | MODULE_DESCRIPTION("W83781D driver"); | 
 | 1974 | MODULE_LICENSE("GPL"); | 
 | 1975 |  | 
 | 1976 | module_init(sensors_w83781d_init); | 
 | 1977 | module_exit(sensors_w83781d_exit); |