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