diff options
Diffstat (limited to 'drivers/hwmon')
37 files changed, 4177 insertions, 816 deletions
| diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 16db83c83c8b..0b62c3c6b7ce 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -333,7 +333,7 @@ config SENSORS_F71882FG  	    F71858FG  	    F71862FG  	    F71863FG -	    F71869F/E +	    F71869F/E/A  	    F71882FG  	    F71883FG  	    F71889FG/ED/A @@ -623,7 +623,7 @@ config SENSORS_LM90  	  LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, and ADT7461A,  	  Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6657, MAX6658, MAX6659,  	  MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, ON Semiconductor NCT1008, -	  and Winbond/Nuvoton W83L771W/G/AWG/ASG sensor chips. +	  Winbond/Nuvoton W83L771W/G/AWG/ASG and Philips SA56004 sensor chips.  	  This driver can also be built as a module.  If so, the module  	  will be called lm90. @@ -694,14 +694,24 @@ config SENSORS_LTC4261  	  be called ltc4261.  config SENSORS_LM95241 -	tristate "National Semiconductor LM95241 sensor chip" +	tristate "National Semiconductor LM95241 and compatibles"  	depends on I2C  	help -	  If you say yes here you get support for LM95241 sensor chip. +	  If you say yes here you get support for LM95231 and LM95241 sensor +	  chips.  	  This driver can also be built as a module.  If so, the module  	  will be called lm95241. +config SENSORS_LM95245 +	tristate "National Semiconductor LM95245 sensor chip" +	depends on I2C && EXPERIMENTAL +	help +	  If you say yes here you get support for LM95245 sensor chip. + +	  This driver can also be built as a module.  If so, the module +	  will be called lm95245. +  config SENSORS_MAX1111  	tristate "Maxim MAX1111 Multichannel, Serial 8-bit ADC chip"  	depends on SPI_MASTER @@ -736,6 +746,16 @@ config SENSORS_MAX1619  	  This driver can also be built as a module.  If so, the module  	  will be called max1619. +config SENSORS_MAX1668 +	tristate "Maxim MAX1668 and compatibles" +	depends on I2C && EXPERIMENTAL +	help +	  If you say yes here you get support for MAX1668, MAX1989 and +	  MAX1805 chips. + +	  This driver can also be built as a module.  If so, the module +	  will be called max1668. +  config SENSORS_MAX6639  	tristate "Maxim MAX6639 sensor chip"  	depends on I2C && EXPERIMENTAL @@ -767,6 +787,20 @@ config SENSORS_MAX6650  	  This driver can also be built as a module.  If so, the module  	  will be called max6650. +config SENSORS_NTC_THERMISTOR +	tristate "NTC thermistor support" +	depends on EXPERIMENTAL +	help +	  This driver supports NTC thermistors sensor reading and its +	  interpretation. The driver can also monitor the temperature and +	  send notifications about the temperature. + +	  Currently, this driver supports +	  NCP15WB473, NCP18WB473, NCP21WB473, NCP03WB473, and NCP15WL333. + +	  This driver can also be built as a module.  If so, the module +	  will be called ntc-thermistor. +  config SENSORS_PC87360  	tristate "National Semiconductor PC87360 family"  	select HWMON_VID @@ -807,92 +841,7 @@ config SENSORS_PCF8591  	  These devices are hard to detect and rarely found on mainstream  	  hardware.  If unsure, say N. -config PMBUS -	tristate "PMBus support" -	depends on I2C && EXPERIMENTAL -	default n -	help -	  Say yes here if you want to enable PMBus support. - -	  This driver can also be built as a module. If so, the module will -	  be called pmbus_core. - -if PMBUS - -config SENSORS_PMBUS -	tristate "Generic PMBus devices" -	default n -	help -	  If you say yes here you get hardware monitoring support for generic -	  PMBus devices, including but not limited to BMR450, BMR451, BMR453, -	  BMR454, and LTC2978. - -	  This driver can also be built as a module. If so, the module will -	  be called pmbus. - -config SENSORS_ADM1275 -	tristate "Analog Devices ADM1275" -	default n -	help -	  If you say yes here you get hardware monitoring support for Analog -	  Devices ADM1275 Hot-Swap Controller and Digital Power Monitor. - -	  This driver can also be built as a module. If so, the module will -	  be called adm1275. - -config SENSORS_MAX16064 -	tristate "Maxim MAX16064" -	default n -	help -	  If you say yes here you get hardware monitoring support for Maxim -	  MAX16064. - -	  This driver can also be built as a module. If so, the module will -	  be called max16064. - -config SENSORS_MAX34440 -	tristate "Maxim MAX34440/MAX34441" -	default n -	help -	  If you say yes here you get hardware monitoring support for Maxim -	  MAX34440 and MAX34441. - -	  This driver can also be built as a module. If so, the module will -	  be called max34440. - -config SENSORS_MAX8688 -	tristate "Maxim MAX8688" -	default n -	help -	  If you say yes here you get hardware monitoring support for Maxim -	  MAX8688. - -	  This driver can also be built as a module. If so, the module will -	  be called max8688. - -config SENSORS_UCD9000 -	tristate "TI UCD90120, UCD90124, UCD9090, UCD90910" -	default n -	help -	  If you say yes here you get hardware monitoring support for TI -	  UCD90120, UCD90124, UCD9090, UCD90910 Sequencer and System Health -	  Controllers. - -	  This driver can also be built as a module. If so, the module will -	  be called ucd9000. - -config SENSORS_UCD9200 -	tristate "TI UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, UCD9248" -	default n -	help -	  If you say yes here you get hardware monitoring support for TI -	  UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, and UCD9248 -	  Digital PWM System Controllers. - -	  This driver can also be built as a module. If so, the module will -	  be called ucd9200. - -endif # PMBUS +source drivers/hwmon/pmbus/Kconfig  config SENSORS_SHT15  	tristate "Sensiron humidity and temperature sensors. SHT15 and compat." @@ -1041,8 +990,13 @@ config SENSORS_SMSC47B397  	  This driver can also be built as a module.  If so, the module  	  will be called smsc47b397. +config SENSORS_SCH56XX_COMMON +	tristate +	default n +  config SENSORS_SCH5627  	tristate "SMSC SCH5627" +	select SENSORS_SCH56XX_COMMON  	help  	  If you say yes here you get support for the hardware monitoring  	  features of the SMSC SCH5627 Super-I/O chip. @@ -1050,6 +1004,21 @@ config SENSORS_SCH5627  	  This driver can also be built as a module.  If so, the module  	  will be called sch5627. +config SENSORS_SCH5636 +	tristate "SMSC SCH5636" +	select SENSORS_SCH56XX_COMMON +	help +	  SMSC SCH5636 Super I/O chips include an embedded microcontroller for +	  hardware monitoring solutions, allowing motherboard manufacturers to +	  create their own custom hwmon solution based upon the SCH5636. + +	  Currently this driver only supports the Fujitsu Theseus SCH5636 based +	  hwmon solution. Say yes here if you want support for the Fujitsu +	  Theseus' hardware monitoring features. + +	  This driver can also be built as a module.  If so, the module +	  will be called sch5636. +  config SENSORS_ADS1015  	tristate "Texas Instruments ADS1015"  	depends on I2C @@ -1142,6 +1111,7 @@ config SENSORS_TWL4030_MADC  config SENSORS_VIA_CPUTEMP  	tristate "VIA CPU temperature sensor"  	depends on X86 +	select HWMON_VID  	help  	  If you say yes here you get support for the temperature  	  sensor inside your CPU. Supported are all known variants of diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 28061cfa0cdb..3c9ccefea791 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -80,6 +80,7 @@ obj-$(CONFIG_SENSORS_LM90)	+= lm90.o  obj-$(CONFIG_SENSORS_LM92)	+= lm92.o  obj-$(CONFIG_SENSORS_LM93)	+= lm93.o  obj-$(CONFIG_SENSORS_LM95241)	+= lm95241.o +obj-$(CONFIG_SENSORS_LM95245)	+= lm95245.o  obj-$(CONFIG_SENSORS_LTC4151)	+= ltc4151.o  obj-$(CONFIG_SENSORS_LTC4215)	+= ltc4215.o  obj-$(CONFIG_SENSORS_LTC4245)	+= ltc4245.o @@ -87,15 +88,19 @@ obj-$(CONFIG_SENSORS_LTC4261)	+= ltc4261.o  obj-$(CONFIG_SENSORS_MAX1111)	+= max1111.o  obj-$(CONFIG_SENSORS_MAX16065)	+= max16065.o  obj-$(CONFIG_SENSORS_MAX1619)	+= max1619.o +obj-$(CONFIG_SENSORS_MAX1668)	+= max1668.o  obj-$(CONFIG_SENSORS_MAX6639)	+= max6639.o  obj-$(CONFIG_SENSORS_MAX6642)	+= max6642.o  obj-$(CONFIG_SENSORS_MAX6650)	+= max6650.o  obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o +obj-$(CONFIG_SENSORS_NTC_THERMISTOR)	+= ntc_thermistor.o  obj-$(CONFIG_SENSORS_PC87360)	+= pc87360.o  obj-$(CONFIG_SENSORS_PC87427)	+= pc87427.o  obj-$(CONFIG_SENSORS_PCF8591)	+= pcf8591.o  obj-$(CONFIG_SENSORS_S3C)	+= s3c-hwmon.o +obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o  obj-$(CONFIG_SENSORS_SCH5627)	+= sch5627.o +obj-$(CONFIG_SENSORS_SCH5636)	+= sch5636.o  obj-$(CONFIG_SENSORS_SHT15)	+= sht15.o  obj-$(CONFIG_SENSORS_SHT21)	+= sht21.o  obj-$(CONFIG_SENSORS_SIS5595)	+= sis5595.o @@ -119,15 +124,7 @@ obj-$(CONFIG_SENSORS_W83L786NG)	+= w83l786ng.o  obj-$(CONFIG_SENSORS_WM831X)	+= wm831x-hwmon.o  obj-$(CONFIG_SENSORS_WM8350)	+= wm8350-hwmon.o -# PMBus drivers -obj-$(CONFIG_PMBUS)		+= pmbus_core.o -obj-$(CONFIG_SENSORS_PMBUS)	+= pmbus.o -obj-$(CONFIG_SENSORS_ADM1275)	+= adm1275.o -obj-$(CONFIG_SENSORS_MAX16064)	+= max16064.o -obj-$(CONFIG_SENSORS_MAX34440)	+= max34440.o -obj-$(CONFIG_SENSORS_MAX8688)	+= max8688.o -obj-$(CONFIG_SENSORS_UCD9000)	+= ucd9000.o -obj-$(CONFIG_SENSORS_UCD9200)	+= ucd9200.o +obj-$(CONFIG_PMBUS)		+= pmbus/  ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG diff --git a/drivers/hwmon/asus_atk0110.c b/drivers/hwmon/asus_atk0110.c index b5e892017e0c..00e98517f94c 100644 --- a/drivers/hwmon/asus_atk0110.c +++ b/drivers/hwmon/asus_atk0110.c @@ -268,6 +268,7 @@ static struct device_attribute atk_name_attr =  static void atk_init_attribute(struct device_attribute *attr, char *name,  		sysfs_show_func show)  { +	sysfs_attr_init(&attr->attr);  	attr->attr.name = name;  	attr->attr.mode = 0444;  	attr->show = show; @@ -673,6 +674,7 @@ static int atk_debugfs_gitm_get(void *p, u64 *val)  	else  		err = -EIO; +	ACPI_FREE(ret);  	return err;  } @@ -1188,19 +1190,15 @@ static int atk_create_files(struct atk_data *data)  	int err;  	list_for_each_entry(s, &data->sensor_list, list) { -		sysfs_attr_init(&s->input_attr.attr);  		err = device_create_file(data->hwmon_dev, &s->input_attr);  		if (err)  			return err; -		sysfs_attr_init(&s->label_attr.attr);  		err = device_create_file(data->hwmon_dev, &s->label_attr);  		if (err)  			return err; -		sysfs_attr_init(&s->limit1_attr.attr);  		err = device_create_file(data->hwmon_dev, &s->limit1_attr);  		if (err)  			return err; -		sysfs_attr_init(&s->limit2_attr.attr);  		err = device_create_file(data->hwmon_dev, &s->limit2_attr);  		if (err)  			return err; diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index 85e937984ff7..59d83e83da7f 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -44,7 +44,9 @@  #define BASE_SYSFS_ATTR_NO	2	/* Sysfs Base attr no for coretemp */  #define NUM_REAL_CORES		16	/* Number of Real cores per cpu */  #define CORETEMP_NAME_LENGTH	17	/* String Length of attrs */ -#define MAX_ATTRS		5	/* Maximum no of per-core attrs */ +#define MAX_CORE_ATTRS		4	/* Maximum no of basic attrs */ +#define MAX_THRESH_ATTRS	3	/* Maximum no of Threshold attrs */ +#define TOTAL_ATTRS		(MAX_CORE_ATTRS + MAX_THRESH_ATTRS)  #define MAX_CORE_DATA		(NUM_REAL_CORES + BASE_SYSFS_ATTR_NO)  #ifdef CONFIG_SMP @@ -67,6 +69,9 @@   *		This value is passed as "id" field to rdmsr/wrmsr functions.   * @status_reg: One of IA32_THERM_STATUS or IA32_PACKAGE_THERM_STATUS,   *		from where the temperature values should be read. + * @intrpt_reg: One of IA32_THERM_INTERRUPT or IA32_PACKAGE_THERM_INTERRUPT, + *		from where the thresholds are read. + * @attr_size:  Total number of pre-core attrs displayed in the sysfs.   * @is_pkg_data: If this is 1, the temp_data holds pkgtemp data.   *		Otherwise, temp_data holds coretemp data.   * @valid: If this is 1, the current temperature is valid. @@ -74,15 +79,18 @@  struct temp_data {  	int temp;  	int ttarget; +	int tmin;  	int tjmax;  	unsigned long last_updated;  	unsigned int cpu;  	u32 cpu_core_id;  	u32 status_reg; +	u32 intrpt_reg; +	int attr_size;  	bool is_pkg_data;  	bool valid; -	struct sensor_device_attribute sd_attrs[MAX_ATTRS]; -	char attr_name[MAX_ATTRS][CORETEMP_NAME_LENGTH]; +	struct sensor_device_attribute sd_attrs[TOTAL_ATTRS]; +	char attr_name[TOTAL_ATTRS][CORETEMP_NAME_LENGTH];  	struct mutex update_lock;  }; @@ -97,9 +105,7 @@ struct platform_data {  struct pdev_entry {  	struct list_head list;  	struct platform_device *pdev; -	unsigned int cpu;  	u16 phys_proc_id; -	u16 cpu_core_id;  };  static LIST_HEAD(pdev_list); @@ -137,6 +143,19 @@ static ssize_t show_crit_alarm(struct device *dev,  	return sprintf(buf, "%d\n", (eax >> 5) & 1);  } +static ssize_t show_max_alarm(struct device *dev, +				struct device_attribute *devattr, char *buf) +{ +	u32 eax, edx; +	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); +	struct platform_data *pdata = dev_get_drvdata(dev); +	struct temp_data *tdata = pdata->core_data[attr->index]; + +	rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx); + +	return sprintf(buf, "%d\n", !!(eax & THERM_STATUS_THRESHOLD1)); +} +  static ssize_t show_tjmax(struct device *dev,  			struct device_attribute *devattr, char *buf)  { @@ -155,6 +174,83 @@ static ssize_t show_ttarget(struct device *dev,  	return sprintf(buf, "%d\n", pdata->core_data[attr->index]->ttarget);  } +static ssize_t store_ttarget(struct device *dev, +				struct device_attribute *devattr, +				const char *buf, size_t count) +{ +	struct platform_data *pdata = dev_get_drvdata(dev); +	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); +	struct temp_data *tdata = pdata->core_data[attr->index]; +	u32 eax, edx; +	unsigned long val; +	int diff; + +	if (strict_strtoul(buf, 10, &val)) +		return -EINVAL; + +	/* +	 * THERM_MASK_THRESHOLD1 is 7 bits wide. Values are entered in terms +	 * of milli degree celsius. Hence don't accept val > (127 * 1000) +	 */ +	if (val > tdata->tjmax || val > 127000) +		return -EINVAL; + +	diff = (tdata->tjmax - val) / 1000; + +	mutex_lock(&tdata->update_lock); +	rdmsr_on_cpu(tdata->cpu, tdata->intrpt_reg, &eax, &edx); +	eax = (eax & ~THERM_MASK_THRESHOLD1) | +				(diff << THERM_SHIFT_THRESHOLD1); +	wrmsr_on_cpu(tdata->cpu, tdata->intrpt_reg, eax, edx); +	tdata->ttarget = val; +	mutex_unlock(&tdata->update_lock); + +	return count; +} + +static ssize_t show_tmin(struct device *dev, +			struct device_attribute *devattr, char *buf) +{ +	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); +	struct platform_data *pdata = dev_get_drvdata(dev); + +	return sprintf(buf, "%d\n", pdata->core_data[attr->index]->tmin); +} + +static ssize_t store_tmin(struct device *dev, +				struct device_attribute *devattr, +				const char *buf, size_t count) +{ +	struct platform_data *pdata = dev_get_drvdata(dev); +	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); +	struct temp_data *tdata = pdata->core_data[attr->index]; +	u32 eax, edx; +	unsigned long val; +	int diff; + +	if (strict_strtoul(buf, 10, &val)) +		return -EINVAL; + +	/* +	 * THERM_MASK_THRESHOLD0 is 7 bits wide. Values are entered in terms +	 * of milli degree celsius. Hence don't accept val > (127 * 1000) +	 */ +	if (val > tdata->tjmax || val > 127000) +		return -EINVAL; + +	diff = (tdata->tjmax - val) / 1000; + +	mutex_lock(&tdata->update_lock); +	rdmsr_on_cpu(tdata->cpu, tdata->intrpt_reg, &eax, &edx); +	eax = (eax & ~THERM_MASK_THRESHOLD0) | +				(diff << THERM_SHIFT_THRESHOLD0); +	wrmsr_on_cpu(tdata->cpu, tdata->intrpt_reg, eax, edx); +	tdata->tmin = val; +	mutex_unlock(&tdata->update_lock); + +	return count; +} +  static ssize_t show_temp(struct device *dev,  			struct device_attribute *devattr, char *buf)  { @@ -346,23 +442,31 @@ static int create_core_attrs(struct temp_data *tdata, struct device *dev,  				int attr_no)  {  	int err, i; -	static ssize_t (*rd_ptr[MAX_ATTRS]) (struct device *dev, +	static ssize_t (*rd_ptr[TOTAL_ATTRS]) (struct device *dev,  			struct device_attribute *devattr, char *buf) = { -			show_label, show_crit_alarm, show_ttarget, -			show_temp, show_tjmax }; -	static const char *names[MAX_ATTRS] = { +			show_label, show_crit_alarm, show_temp, show_tjmax, +			show_max_alarm, show_ttarget, show_tmin }; +	static ssize_t (*rw_ptr[TOTAL_ATTRS]) (struct device *dev, +			struct device_attribute *devattr, const char *buf, +			size_t count) = { NULL, NULL, NULL, NULL, NULL, +					store_ttarget, store_tmin }; +	static const char *names[TOTAL_ATTRS] = {  					"temp%d_label", "temp%d_crit_alarm", -					"temp%d_max", "temp%d_input", -					"temp%d_crit" }; +					"temp%d_input", "temp%d_crit", +					"temp%d_max_alarm", "temp%d_max", +					"temp%d_max_hyst" }; -	for (i = 0; i < MAX_ATTRS; i++) { +	for (i = 0; i < tdata->attr_size; i++) {  		snprintf(tdata->attr_name[i], CORETEMP_NAME_LENGTH, names[i],  			attr_no);  		sysfs_attr_init(&tdata->sd_attrs[i].dev_attr.attr);  		tdata->sd_attrs[i].dev_attr.attr.name = tdata->attr_name[i];  		tdata->sd_attrs[i].dev_attr.attr.mode = S_IRUGO; +		if (rw_ptr[i]) { +			tdata->sd_attrs[i].dev_attr.attr.mode |= S_IWUSR; +			tdata->sd_attrs[i].dev_attr.store = rw_ptr[i]; +		}  		tdata->sd_attrs[i].dev_attr.show = rd_ptr[i]; -		tdata->sd_attrs[i].dev_attr.store = NULL;  		tdata->sd_attrs[i].index = attr_no;  		err = device_create_file(dev, &tdata->sd_attrs[i].dev_attr);  		if (err) @@ -376,38 +480,6 @@ exit_free:  	return err;  } -static void update_ttarget(__u8 cpu_model, struct temp_data *tdata, -				struct device *dev) -{ -	int err; -	u32 eax, edx; - -	/* -	 * Initialize ttarget value. Eventually this will be -	 * initialized with the value from MSR_IA32_THERM_INTERRUPT -	 * register. If IA32_TEMPERATURE_TARGET is supported, this -	 * value will be over written below. -	 * To Do: Patch to initialize ttarget from MSR_IA32_THERM_INTERRUPT -	 */ -	tdata->ttarget = tdata->tjmax - 20000; - -	/* -	 * Read the still undocumented IA32_TEMPERATURE_TARGET. It exists -	 * on older CPUs but not in this register, -	 * Atoms don't have it either. -	 */ -	if (cpu_model > 0xe && cpu_model != 0x1c) { -		err = rdmsr_safe_on_cpu(tdata->cpu, -				MSR_IA32_TEMPERATURE_TARGET, &eax, &edx); -		if (err) { -			dev_warn(dev, -			"Unable to read IA32_TEMPERATURE_TARGET MSR\n"); -		} else { -			tdata->ttarget = tdata->tjmax - -					((eax >> 8) & 0xff) * 1000; -		} -	} -}  static int __devinit chk_ucode_version(struct platform_device *pdev)  { @@ -466,9 +538,12 @@ static struct temp_data *init_temp_data(unsigned int cpu, int pkg_flag)  	tdata->status_reg = pkg_flag ? MSR_IA32_PACKAGE_THERM_STATUS :  							MSR_IA32_THERM_STATUS; +	tdata->intrpt_reg = pkg_flag ? MSR_IA32_PACKAGE_THERM_INTERRUPT : +						MSR_IA32_THERM_INTERRUPT;  	tdata->is_pkg_data = pkg_flag;  	tdata->cpu = cpu;  	tdata->cpu_core_id = TO_CORE_ID(cpu); +	tdata->attr_size = MAX_CORE_ATTRS;  	mutex_init(&tdata->update_lock);  	return tdata;  } @@ -518,7 +593,17 @@ static int create_core_data(struct platform_data *pdata,  	else  		tdata->tjmax = get_tjmax(c, cpu, &pdev->dev); -	update_ttarget(c->x86_model, tdata, &pdev->dev); +	/* +	 * Test if we can access the intrpt register. If so, increase the +	 * 'size' enough to have ttarget/tmin/max_alarm interfaces. +	 * Initialize ttarget with bits 16:22 of MSR_IA32_THERM_INTERRUPT +	 */ +	err = rdmsr_safe_on_cpu(cpu, tdata->intrpt_reg, &eax, &edx); +	if (!err) { +		tdata->attr_size += MAX_THRESH_ATTRS; +		tdata->ttarget = tdata->tjmax - ((eax >> 16) & 0x7f) * 1000; +	} +  	pdata->core_data[attr_no] = tdata;  	/* Create sysfs interfaces */ @@ -555,7 +640,7 @@ static void coretemp_remove_core(struct platform_data *pdata,  	struct temp_data *tdata = pdata->core_data[indx];  	/* Remove the sysfs attributes */ -	for (i = 0; i < MAX_ATTRS; i++) +	for (i = 0; i < tdata->attr_size; i++)  		device_remove_file(dev, &tdata->sd_attrs[i].dev_attr);  	kfree(pdata->core_data[indx]); @@ -653,9 +738,7 @@ static int __cpuinit coretemp_device_add(unsigned int cpu)  	}  	pdev_entry->pdev = pdev; -	pdev_entry->cpu = cpu;  	pdev_entry->phys_proc_id = TO_PHYS_ID(cpu); -	pdev_entry->cpu_core_id = TO_CORE_ID(cpu);  	list_add_tail(&pdev_entry->list, &pdev_list);  	mutex_unlock(&pdev_list_mutex); diff --git a/drivers/hwmon/emc6w201.c b/drivers/hwmon/emc6w201.c index e0ef32378ac6..0064432f361f 100644 --- a/drivers/hwmon/emc6w201.c +++ b/drivers/hwmon/emc6w201.c @@ -78,8 +78,9 @@ static u16 emc6w201_read16(struct i2c_client *client, u8 reg)  	lsb = i2c_smbus_read_byte_data(client, reg);  	msb = i2c_smbus_read_byte_data(client, reg + 1); -	if (lsb < 0 || msb < 0) { -		dev_err(&client->dev, "16-bit read failed at 0x%02x\n", reg); +	if (unlikely(lsb < 0 || msb < 0)) { +		dev_err(&client->dev, "%d-bit %s failed at 0x%02x\n", +			16, "read", reg);  		return 0xFFFF;	/* Arbitrary value */  	} @@ -95,10 +96,39 @@ static int emc6w201_write16(struct i2c_client *client, u8 reg, u16 val)  	int err;  	err = i2c_smbus_write_byte_data(client, reg, val & 0xff); -	if (!err) +	if (likely(!err))  		err = i2c_smbus_write_byte_data(client, reg + 1, val >> 8); -	if (err < 0) -		dev_err(&client->dev, "16-bit write failed at 0x%02x\n", reg); +	if (unlikely(err < 0)) +		dev_err(&client->dev, "%d-bit %s failed at 0x%02x\n", +			16, "write", reg); + +	return err; +} + +/* Read 8-bit value from register */ +static u8 emc6w201_read8(struct i2c_client *client, u8 reg) +{ +	int val; + +	val = i2c_smbus_read_byte_data(client, reg); +	if (unlikely(val < 0)) { +		dev_err(&client->dev, "%d-bit %s failed at 0x%02x\n", +			8, "read", reg); +		return 0x00;	/* Arbitrary value */ +	} + +	return val; +} + +/* Write 8-bit value to register */ +static int emc6w201_write8(struct i2c_client *client, u8 reg, u8 val) +{ +	int err; + +	err = i2c_smbus_write_byte_data(client, reg, val); +	if (unlikely(err < 0)) +		dev_err(&client->dev, "%d-bit %s failed at 0x%02x\n", +			8, "write", reg);  	return err;  } @@ -114,25 +144,25 @@ static struct emc6w201_data *emc6w201_update_device(struct device *dev)  	if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {  		for (nr = 0; nr < 6; nr++) {  			data->in[input][nr] = -				i2c_smbus_read_byte_data(client, +				emc6w201_read8(client,  						EMC6W201_REG_IN(nr));  			data->in[min][nr] = -				i2c_smbus_read_byte_data(client, +				emc6w201_read8(client,  						EMC6W201_REG_IN_LOW(nr));  			data->in[max][nr] = -				i2c_smbus_read_byte_data(client, +				emc6w201_read8(client,  						EMC6W201_REG_IN_HIGH(nr));  		}  		for (nr = 0; nr < 6; nr++) {  			data->temp[input][nr] = -				i2c_smbus_read_byte_data(client, +				emc6w201_read8(client,  						EMC6W201_REG_TEMP(nr));  			data->temp[min][nr] = -				i2c_smbus_read_byte_data(client, +				emc6w201_read8(client,  						EMC6W201_REG_TEMP_LOW(nr));  			data->temp[max][nr] = -				i2c_smbus_read_byte_data(client, +				emc6w201_read8(client,  						EMC6W201_REG_TEMP_HIGH(nr));  		} @@ -192,7 +222,7 @@ static ssize_t set_in(struct device *dev, struct device_attribute *devattr,  	mutex_lock(&data->update_lock);  	data->in[sf][nr] = SENSORS_LIMIT(val, 0, 255); -	err = i2c_smbus_write_byte_data(client, reg, data->in[sf][nr]); +	err = emc6w201_write8(client, reg, data->in[sf][nr]);  	mutex_unlock(&data->update_lock);  	return err < 0 ? err : count; @@ -229,7 +259,7 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *devattr,  	mutex_lock(&data->update_lock);  	data->temp[sf][nr] = SENSORS_LIMIT(val, -127, 128); -	err = i2c_smbus_write_byte_data(client, reg, data->temp[sf][nr]); +	err = emc6w201_write8(client, reg, data->temp[sf][nr]);  	mutex_unlock(&data->update_lock);  	return err < 0 ? err : count; @@ -444,7 +474,7 @@ static int emc6w201_detect(struct i2c_client *client,  	/* Check configuration */  	config = i2c_smbus_read_byte_data(client, EMC6W201_REG_CONFIG); -	if ((config & 0xF4) != 0x04) +	if (config < 0 || (config & 0xF4) != 0x04)  		return -ENODEV;  	if (!(config & 0x01)) {  		dev_err(&client->dev, "Monitoring not enabled\n"); diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index a4a94a096c90..2d96ed2bf8ed 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -52,6 +52,7 @@  #define SIO_F71858_ID		0x0507  /* Chipset ID */  #define SIO_F71862_ID		0x0601	/* Chipset ID */  #define SIO_F71869_ID		0x0814	/* Chipset ID */ +#define SIO_F71869A_ID		0x1007	/* Chipset ID */  #define SIO_F71882_ID		0x0541	/* Chipset ID */  #define SIO_F71889_ID		0x0723	/* Chipset ID */  #define SIO_F71889E_ID		0x0909	/* Chipset ID */ @@ -108,8 +109,8 @@ static unsigned short force_id;  module_param(force_id, ushort, 0);  MODULE_PARM_DESC(force_id, "Override the detected device ID"); -enum chips { f71808e, f71808a, f71858fg, f71862fg, f71869, f71882fg, f71889fg, -	     f71889ed, f71889a, f8000, f81865f }; +enum chips { f71808e, f71808a, f71858fg, f71862fg, f71869, f71869a, f71882fg, +	     f71889fg, f71889ed, f71889a, f8000, f81865f };  static const char *f71882fg_names[] = {  	"f71808e", @@ -117,6 +118,7 @@ static const char *f71882fg_names[] = {  	"f71858fg",  	"f71862fg",  	"f71869", /* Both f71869f and f71869e, reg. compatible and same id */ +	"f71869a",  	"f71882fg",  	"f71889fg", /* f81801u too, same id */  	"f71889ed", @@ -131,6 +133,7 @@ static const char f71882fg_has_in[][F71882FG_MAX_INS] = {  	[f71858fg]	= { 1, 1, 1, 0, 0, 0, 0, 0, 0 },  	[f71862fg]	= { 1, 1, 1, 1, 1, 1, 1, 1, 1 },  	[f71869]	= { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, +	[f71869a]	= { 1, 1, 1, 1, 1, 1, 1, 1, 1 },  	[f71882fg]	= { 1, 1, 1, 1, 1, 1, 1, 1, 1 },  	[f71889fg]	= { 1, 1, 1, 1, 1, 1, 1, 1, 1 },  	[f71889ed]	= { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, @@ -145,6 +148,7 @@ static const char f71882fg_has_in1_alarm[] = {  	[f71858fg]	= 0,  	[f71862fg]	= 0,  	[f71869]	= 0, +	[f71869a]	= 0,  	[f71882fg]	= 1,  	[f71889fg]	= 1,  	[f71889ed]	= 1, @@ -159,6 +163,7 @@ static const char f71882fg_fan_has_beep[] = {  	[f71858fg]	= 0,  	[f71862fg]	= 1,  	[f71869]	= 1, +	[f71869a]	= 1,  	[f71882fg]	= 1,  	[f71889fg]	= 1,  	[f71889ed]	= 1, @@ -173,6 +178,7 @@ static const char f71882fg_nr_fans[] = {  	[f71858fg]	= 3,  	[f71862fg]	= 3,  	[f71869]	= 3, +	[f71869a]	= 3,  	[f71882fg]	= 4,  	[f71889fg]	= 3,  	[f71889ed]	= 3, @@ -187,6 +193,7 @@ static const char f71882fg_temp_has_beep[] = {  	[f71858fg]	= 0,  	[f71862fg]	= 1,  	[f71869]	= 1, +	[f71869a]	= 1,  	[f71882fg]	= 1,  	[f71889fg]	= 1,  	[f71889ed]	= 1, @@ -201,6 +208,7 @@ static const char f71882fg_nr_temps[] = {  	[f71858fg]	= 3,  	[f71862fg]	= 3,  	[f71869]	= 3, +	[f71869a]	= 3,  	[f71882fg]	= 3,  	[f71889fg]	= 3,  	[f71889ed]	= 3, @@ -2243,6 +2251,7 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)  		case f71808e:  		case f71808a:  		case f71869: +		case f71869a:  			/* These always have signed auto point temps */  			data->auto_point_temp_signed = 1;  			/* Fall through to select correct fan/pwm reg bank! */ @@ -2305,6 +2314,7 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)  		case f71808e:  		case f71808a:  		case f71869: +		case f71869a:  		case f71889fg:  		case f71889ed:  		case f71889a: @@ -2528,6 +2538,9 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address,  	case SIO_F71869_ID:  		sio_data->type = f71869;  		break; +	case SIO_F71869A_ID: +		sio_data->type = f71869a; +		break;  	case SIO_F71882_ID:  		sio_data->type = f71882fg;  		break; @@ -2662,7 +2675,7 @@ static void __exit f71882fg_exit(void)  }  MODULE_DESCRIPTION("F71882FG Hardware Monitoring Driver"); -MODULE_AUTHOR("Hans Edgington, Hans de Goede (hdegoede@redhat.com)"); +MODULE_AUTHOR("Hans Edgington, Hans de Goede <hdegoede@redhat.com>");  MODULE_LICENSE("GPL");  module_init(f71882fg_init); diff --git a/drivers/hwmon/gl520sm.c b/drivers/hwmon/gl520sm.c index ec588026f0a9..131ea8625f08 100644 --- a/drivers/hwmon/gl520sm.c +++ b/drivers/hwmon/gl520sm.c @@ -273,7 +273,7 @@ static SENSOR_DEVICE_ATTR(in4_max, S_IRUGO | S_IWUSR,  #define DIV_FROM_REG(val) (1 << (val))  #define FAN_FROM_REG(val,div) ((val)==0 ? 0 : (480000/((val) << (div)))) -#define FAN_TO_REG(val,div) ((val)<=0?0:SENSORS_LIMIT((480000 + ((val) << ((div)-1))) / ((val) << (div)), 1, 255)); +#define FAN_TO_REG(val,div) ((val)<=0?0:SENSORS_LIMIT((480000 + ((val) << ((div)-1))) / ((val) << (div)), 1, 255))  static ssize_t get_fan_input(struct device *dev, struct device_attribute *attr,  			     char *buf) diff --git a/drivers/hwmon/hwmon-vid.c b/drivers/hwmon/hwmon-vid.c index 2582bfef6ccb..932da8a5aaf4 100644 --- a/drivers/hwmon/hwmon-vid.c +++ b/drivers/hwmon/hwmon-vid.c @@ -140,7 +140,11 @@ int vid_from_reg(int val, u8 vrm)  		return(val & 0x10 ? 975 - (val & 0xF) * 25 :  				    1750 - val * 50);  	case 13: +	case 131:  		val &= 0x3f; +		/* Exception for Eden ULV 500 MHz */ +		if (vrm == 131 && val == 0x3f) +			val++;  		return(1708 - val * 16);  	case 14:		/* Intel Core */  				/* compute in uV, round to mV */ @@ -202,14 +206,48 @@ static struct vrm_model vrm_models[] = {  	{X86_VENDOR_CENTAUR, 0x6, 0x7, ANY, 85},	/* Eden ESP/Ezra */  	{X86_VENDOR_CENTAUR, 0x6, 0x8, 0x7, 85},	/* Ezra T */ -	{X86_VENDOR_CENTAUR, 0x6, 0x9, 0x7, 85},	/* Nemiah */ +	{X86_VENDOR_CENTAUR, 0x6, 0x9, 0x7, 85},	/* Nehemiah */  	{X86_VENDOR_CENTAUR, 0x6, 0x9, ANY, 17},	/* C3-M, Eden-N */  	{X86_VENDOR_CENTAUR, 0x6, 0xA, 0x7, 0},		/* No information */ -	{X86_VENDOR_CENTAUR, 0x6, 0xA, ANY, 13},	/* C7, Esther */ +	{X86_VENDOR_CENTAUR, 0x6, 0xA, ANY, 13},	/* C7-M, C7, Eden (Esther) */ +	{X86_VENDOR_CENTAUR, 0x6, 0xD, ANY, 134},	/* C7-D, C7-M, C7, Eden (Esther) */  	{X86_VENDOR_UNKNOWN, ANY, ANY, ANY, 0}		/* stop here */  }; +/* + * Special case for VIA model D: there are two different possible + * VID tables, so we have to figure out first, which one must be + * used. This resolves temporary drm value 134 to 14 (Intel Core + * 7-bit VID), 13 (Pentium M 6-bit VID) or 131 (Pentium M 6-bit VID + * + quirk for Eden ULV 500 MHz). + * Note: something similar might be needed for model A, I'm not sure. + */ +static u8 get_via_model_d_vrm(void) +{ +	unsigned int vid, brand, dummy; +	static const char *brands[4] = { +		"C7-M", "C7", "Eden", "C7-D" +	}; + +	rdmsr(0x198, dummy, vid); +	vid &= 0xff; + +	rdmsr(0x1154, brand, dummy); +	brand = ((brand >> 4) ^ (brand >> 2)) & 0x03; + +	if (vid > 0x3f) { +		pr_info("Using %d-bit VID table for VIA %s CPU\n", +			7, brands[brand]); +		return 14; +	} else { +		pr_info("Using %d-bit VID table for VIA %s CPU\n", +			6, brands[brand]); +		/* Enable quirk for Eden */ +		return brand == 2 ? 131 : 13; +	} +} +  static u8 find_vrm(u8 eff_family, u8 eff_model, u8 eff_stepping, u8 vendor)  {  	int i = 0; @@ -247,6 +285,8 @@ u8 vid_which_vrm(void)  		eff_model += ((eax & 0x000F0000)>>16)<<4;  	}  	vrm_ret = find_vrm(eff_family, eff_model, eff_stepping, c->x86_vendor); +	if (vrm_ret == 134) +		vrm_ret = get_via_model_d_vrm();  	if (vrm_ret == 0)  		pr_info("Unknown VRM version of your x86 CPU\n");  	return vrm_ret; diff --git a/drivers/hwmon/ibmaem.c b/drivers/hwmon/ibmaem.c index 537409d07ee7..1a409c5bc9bc 100644 --- a/drivers/hwmon/ibmaem.c +++ b/drivers/hwmon/ibmaem.c @@ -947,6 +947,7 @@ static int aem_register_sensors(struct aem_data *data,  	/* Set up read-only sensors */  	while (ro->label) { +		sysfs_attr_init(&sensors->dev_attr.attr);  		sensors->dev_attr.attr.name = ro->label;  		sensors->dev_attr.attr.mode = S_IRUGO;  		sensors->dev_attr.show = ro->show; @@ -963,6 +964,7 @@ static int aem_register_sensors(struct aem_data *data,  	/* Set up read-write sensors */  	while (rw->label) { +		sysfs_attr_init(&sensors->dev_attr.attr);  		sensors->dev_attr.attr.name = rw->label;  		sensors->dev_attr.attr.mode = S_IRUGO | S_IWUSR;  		sensors->dev_attr.show = rw->show; diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c index 06d4eafcf76b..41dbf8161ed7 100644 --- a/drivers/hwmon/ibmpex.c +++ b/drivers/hwmon/ibmpex.c @@ -358,6 +358,7 @@ static int create_sensor(struct ibmpex_bmc_data *data, int type,  	else if (type == POWER_SENSOR)  		sprintf(n, power_sensor_name_templates[func], "power", counter); +	sysfs_attr_init(&data->sensors[sensor].attr[func].dev_attr.attr);  	data->sensors[sensor].attr[func].dev_attr.attr.name = n;  	data->sensors[sensor].attr[func].dev_attr.attr.mode = S_IRUGO;  	data->sensors[sensor].attr[func].dev_attr.show = ibmpex_show_sensor; diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index bb6405b92007..d912649fac50 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -1172,6 +1172,32 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,  	struct it87_data *data = it87_update_device(dev);  	return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1);  } + +static ssize_t clear_intrusion(struct device *dev, struct device_attribute +		*attr, const char *buf, size_t count) +{ +	struct it87_data *data = dev_get_drvdata(dev); +	long val; +	int config; + +	if (strict_strtol(buf, 10, &val) < 0 || val != 0) +		return -EINVAL; + +	mutex_lock(&data->update_lock); +	config = it87_read_value(data, IT87_REG_CONFIG); +	if (config < 0) { +		count = config; +	} else { +		config |= 1 << 5; +		it87_write_value(data, IT87_REG_CONFIG, config); +		/* Invalidate cache to force re-read */ +		data->valid = 0; +	} +	mutex_unlock(&data->update_lock); + +	return count; +} +  static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 8);  static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 9);  static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 10); @@ -1188,6 +1214,8 @@ static SENSOR_DEVICE_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 6);  static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 16);  static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 17);  static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 18); +static SENSOR_DEVICE_ATTR(intrusion0_alarm, S_IRUGO | S_IWUSR, +			  show_alarm, clear_intrusion, 4);  static ssize_t show_beep(struct device *dev, struct device_attribute *attr,  		char *buf) @@ -1350,6 +1378,7 @@ static struct attribute *it87_attributes[] = {  	&sensor_dev_attr_temp3_alarm.dev_attr.attr,  	&dev_attr_alarms.attr, +	&sensor_dev_attr_intrusion0_alarm.dev_attr.attr,  	&dev_attr_name.attr,  	NULL  }; @@ -1538,7 +1567,7 @@ static struct attribute *it87_attributes_label[] = {  };  static const struct attribute_group it87_group_label = { -	.attrs = it87_attributes_vid, +	.attrs = it87_attributes_label,  };  /* SuperIO detection - will change isa_address if a chip is found */ diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index 4cb24eafe318..6df0b4681710 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c @@ -2,7 +2,7 @@      lm78.c - Part of lm_sensors, Linux kernel modules for hardware               monitoring      Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>  -    Copyright (c) 2007        Jean Delvare <khali@linux-fr.org> +    Copyright (c) 2007, 2011  Jean Delvare <khali@linux-fr.org>      This program is free software; you can redistribute it and/or modify      it under the terms of the GNU General Public License as published by @@ -26,23 +26,21 @@  #include <linux/slab.h>  #include <linux/jiffies.h>  #include <linux/i2c.h> -#include <linux/platform_device.h> -#include <linux/ioport.h>  #include <linux/hwmon.h>  #include <linux/hwmon-vid.h>  #include <linux/hwmon-sysfs.h>  #include <linux/err.h>  #include <linux/mutex.h> -#include <linux/io.h> -/* ISA device, if found */ -static struct platform_device *pdev; +#ifdef CONFIG_ISA +#include <linux/platform_device.h> +#include <linux/ioport.h> +#include <linux/io.h> +#endif  /* Addresses to scan */  static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,  						0x2e, 0x2f, I2C_CLIENT_END }; -static unsigned short isa_address = 0x290; -  enum chips { lm78, lm79 };  /* Many LM78 constants specified below */ @@ -143,50 +141,12 @@ struct lm78_data {  }; -static int lm78_i2c_detect(struct i2c_client *client, -			   struct i2c_board_info *info); -static int lm78_i2c_probe(struct i2c_client *client, -			  const struct i2c_device_id *id); -static int lm78_i2c_remove(struct i2c_client *client); - -static int __devinit lm78_isa_probe(struct platform_device *pdev); -static int __devexit lm78_isa_remove(struct platform_device *pdev); -  static int lm78_read_value(struct lm78_data *data, u8 reg);  static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value);  static struct lm78_data *lm78_update_device(struct device *dev);  static void lm78_init_device(struct lm78_data *data); -static const struct i2c_device_id lm78_i2c_id[] = { -	{ "lm78", lm78 }, -	{ "lm79", lm79 }, -	{ } -}; -MODULE_DEVICE_TABLE(i2c, lm78_i2c_id); - -static struct i2c_driver lm78_driver = { -	.class		= I2C_CLASS_HWMON, -	.driver = { -		.name	= "lm78", -	}, -	.probe		= lm78_i2c_probe, -	.remove		= lm78_i2c_remove, -	.id_table	= lm78_i2c_id, -	.detect		= lm78_i2c_detect, -	.address_list	= normal_i2c, -}; - -static struct platform_driver lm78_isa_driver = { -	.driver = { -		.owner	= THIS_MODULE, -		.name	= "lm78", -	}, -	.probe		= lm78_isa_probe, -	.remove		= __devexit_p(lm78_isa_remove), -}; - -  /* 7 Voltages */  static ssize_t show_in(struct device *dev, struct device_attribute *da,  		       char *buf) @@ -514,6 +474,16 @@ static const struct attribute_group lm78_group = {  	.attrs = lm78_attributes,  }; +/* + * ISA related code + */ +#ifdef CONFIG_ISA + +/* ISA device, if found */ +static struct platform_device *pdev; + +static unsigned short isa_address = 0x290; +  /* I2C devices get this name attribute automatically, but for ISA devices     we must create it by ourselves. */  static ssize_t show_name(struct device *dev, struct device_attribute @@ -525,6 +495,11 @@ static ssize_t show_name(struct device *dev, struct device_attribute  }  static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); +static struct lm78_data *lm78_data_if_isa(void) +{ +	return pdev ? platform_get_drvdata(pdev) : NULL; +} +  /* Returns 1 if the I2C chip appears to be an alias of the ISA chip */  static int lm78_alias_detect(struct i2c_client *client, u8 chipid)  { @@ -558,12 +533,24 @@ static int lm78_alias_detect(struct i2c_client *client, u8 chipid)  	return 1;  } +#else /* !CONFIG_ISA */ + +static int lm78_alias_detect(struct i2c_client *client, u8 chipid) +{ +	return 0; +} + +static struct lm78_data *lm78_data_if_isa(void) +{ +	return NULL; +} +#endif /* CONFIG_ISA */  static int lm78_i2c_detect(struct i2c_client *client,  			   struct i2c_board_info *info)  {  	int i; -	struct lm78_data *isa = pdev ? platform_get_drvdata(pdev) : NULL; +	struct lm78_data *isa = lm78_data_if_isa();  	const char *client_name;  	struct i2c_adapter *adapter = client->adapter;  	int address = client->addr; @@ -663,76 +650,24 @@ static int lm78_i2c_remove(struct i2c_client *client)  	return 0;  } -static int __devinit lm78_isa_probe(struct platform_device *pdev) -{ -	int err; -	struct lm78_data *data; -	struct resource *res; - -	/* Reserve the ISA region */ -	res = platform_get_resource(pdev, IORESOURCE_IO, 0); -	if (!request_region(res->start + LM78_ADDR_REG_OFFSET, 2, "lm78")) { -		err = -EBUSY; -		goto exit; -	} - -	if (!(data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL))) { -		err = -ENOMEM; -		goto exit_release_region; -	} -	mutex_init(&data->lock); -	data->isa_addr = res->start; -	platform_set_drvdata(pdev, data); - -	if (lm78_read_value(data, LM78_REG_CHIPID) & 0x80) { -		data->type = lm79; -		data->name = "lm79"; -	} else { -		data->type = lm78; -		data->name = "lm78"; -	} - -	/* Initialize the LM78 chip */ -	lm78_init_device(data); - -	/* Register sysfs hooks */ -	if ((err = sysfs_create_group(&pdev->dev.kobj, &lm78_group)) -	 || (err = device_create_file(&pdev->dev, &dev_attr_name))) -		goto exit_remove_files; - -	data->hwmon_dev = hwmon_device_register(&pdev->dev); -	if (IS_ERR(data->hwmon_dev)) { -		err = PTR_ERR(data->hwmon_dev); -		goto exit_remove_files; -	} - -	return 0; - - exit_remove_files: -	sysfs_remove_group(&pdev->dev.kobj, &lm78_group); -	device_remove_file(&pdev->dev, &dev_attr_name); -	kfree(data); - exit_release_region: -	release_region(res->start + LM78_ADDR_REG_OFFSET, 2); - exit: -	return err; -} - -static int __devexit lm78_isa_remove(struct platform_device *pdev) -{ -	struct lm78_data *data = platform_get_drvdata(pdev); -	struct resource *res; - -	hwmon_device_unregister(data->hwmon_dev); -	sysfs_remove_group(&pdev->dev.kobj, &lm78_group); -	device_remove_file(&pdev->dev, &dev_attr_name); -	kfree(data); - -	res = platform_get_resource(pdev, IORESOURCE_IO, 0); -	release_region(res->start + LM78_ADDR_REG_OFFSET, 2); +static const struct i2c_device_id lm78_i2c_id[] = { +	{ "lm78", lm78 }, +	{ "lm79", lm79 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, lm78_i2c_id); -	return 0; -} +static struct i2c_driver lm78_driver = { +	.class		= I2C_CLASS_HWMON, +	.driver = { +		.name	= "lm78", +	}, +	.probe		= lm78_i2c_probe, +	.remove		= lm78_i2c_remove, +	.id_table	= lm78_i2c_id, +	.detect		= lm78_i2c_detect, +	.address_list	= normal_i2c, +};  /* The SMBus locks itself, but ISA access must be locked explicitly!      We don't want to lock the whole ISA bus, so we lock each client @@ -743,6 +678,7 @@ static int lm78_read_value(struct lm78_data *data, u8 reg)  {  	struct i2c_client *client = data->client; +#ifdef CONFIG_ISA  	if (!client) { /* ISA device */  		int res;  		mutex_lock(&data->lock); @@ -751,6 +687,7 @@ static int lm78_read_value(struct lm78_data *data, u8 reg)  		mutex_unlock(&data->lock);  		return res;  	} else +#endif  		return i2c_smbus_read_byte_data(client, reg);  } @@ -765,6 +702,7 @@ static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value)  {  	struct i2c_client *client = data->client; +#ifdef CONFIG_ISA  	if (!client) { /* ISA device */  		mutex_lock(&data->lock);  		outb_p(reg, data->isa_addr + LM78_ADDR_REG_OFFSET); @@ -772,6 +710,7 @@ static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value)  		mutex_unlock(&data->lock);  		return 0;  	} else +#endif  		return i2c_smbus_write_byte_data(client, reg, value);  } @@ -849,6 +788,88 @@ static struct lm78_data *lm78_update_device(struct device *dev)  	return data;  } +#ifdef CONFIG_ISA +static int __devinit lm78_isa_probe(struct platform_device *pdev) +{ +	int err; +	struct lm78_data *data; +	struct resource *res; + +	/* Reserve the ISA region */ +	res = platform_get_resource(pdev, IORESOURCE_IO, 0); +	if (!request_region(res->start + LM78_ADDR_REG_OFFSET, 2, "lm78")) { +		err = -EBUSY; +		goto exit; +	} + +	data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL); +	if (!data) { +		err = -ENOMEM; +		goto exit_release_region; +	} +	mutex_init(&data->lock); +	data->isa_addr = res->start; +	platform_set_drvdata(pdev, data); + +	if (lm78_read_value(data, LM78_REG_CHIPID) & 0x80) { +		data->type = lm79; +		data->name = "lm79"; +	} else { +		data->type = lm78; +		data->name = "lm78"; +	} + +	/* Initialize the LM78 chip */ +	lm78_init_device(data); + +	/* Register sysfs hooks */ +	if ((err = sysfs_create_group(&pdev->dev.kobj, &lm78_group)) +	 || (err = device_create_file(&pdev->dev, &dev_attr_name))) +		goto exit_remove_files; + +	data->hwmon_dev = hwmon_device_register(&pdev->dev); +	if (IS_ERR(data->hwmon_dev)) { +		err = PTR_ERR(data->hwmon_dev); +		goto exit_remove_files; +	} + +	return 0; + + exit_remove_files: +	sysfs_remove_group(&pdev->dev.kobj, &lm78_group); +	device_remove_file(&pdev->dev, &dev_attr_name); +	kfree(data); + exit_release_region: +	release_region(res->start + LM78_ADDR_REG_OFFSET, 2); + exit: +	return err; +} + +static int __devexit lm78_isa_remove(struct platform_device *pdev) +{ +	struct lm78_data *data = platform_get_drvdata(pdev); +	struct resource *res; + +	hwmon_device_unregister(data->hwmon_dev); +	sysfs_remove_group(&pdev->dev.kobj, &lm78_group); +	device_remove_file(&pdev->dev, &dev_attr_name); +	kfree(data); + +	res = platform_get_resource(pdev, IORESOURCE_IO, 0); +	release_region(res->start + LM78_ADDR_REG_OFFSET, 2); + +	return 0; +} + +static struct platform_driver lm78_isa_driver = { +	.driver = { +		.owner	= THIS_MODULE, +		.name	= "lm78", +	}, +	.probe		= lm78_isa_probe, +	.remove		= __devexit_p(lm78_isa_remove), +}; +  /* return 1 if a supported chip is found, 0 otherwise */  static int __init lm78_isa_found(unsigned short address)  { @@ -969,12 +990,10 @@ static int __init lm78_isa_device_add(unsigned short address)  	return err;  } -static int __init sm_lm78_init(void) +static int __init lm78_isa_register(void)  {  	int res; -	/* We register the ISA device first, so that we can skip the -	 * registration of an I2C interface to the same device. */  	if (lm78_isa_found(isa_address)) {  		res = platform_driver_register(&lm78_isa_driver);  		if (res) @@ -986,32 +1005,62 @@ static int __init sm_lm78_init(void)  			goto exit_unreg_isa_driver;  	} -	res = i2c_add_driver(&lm78_driver); -	if (res) -		goto exit_unreg_isa_device; -  	return 0; - exit_unreg_isa_device: -	platform_device_unregister(pdev);   exit_unreg_isa_driver:  	platform_driver_unregister(&lm78_isa_driver);   exit:  	return res;  } -static void __exit sm_lm78_exit(void) +static void lm78_isa_unregister(void)  {  	if (pdev) {  		platform_device_unregister(pdev);  		platform_driver_unregister(&lm78_isa_driver);  	} -	i2c_del_driver(&lm78_driver);  } +#else /* !CONFIG_ISA */ +static int __init lm78_isa_register(void) +{ +	return 0; +} + +static void lm78_isa_unregister(void) +{ +} +#endif /* CONFIG_ISA */ +static int __init sm_lm78_init(void) +{ +	int res; + +	/* We register the ISA device first, so that we can skip the +	 * registration of an I2C interface to the same device. */ +	res = lm78_isa_register(); +	if (res) +		goto exit; + +	res = i2c_add_driver(&lm78_driver); +	if (res) +		goto exit_unreg_isa_device; + +	return 0; + + exit_unreg_isa_device: +	lm78_isa_unregister(); + exit: +	return res; +} + +static void __exit sm_lm78_exit(void) +{ +	lm78_isa_unregister(); +	i2c_del_driver(&lm78_driver); +} -MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>"); +MODULE_AUTHOR("Frodo Looijaard, Jean Delvare <khali@linux-fr.org>");  MODULE_DESCRIPTION("LM78/LM79 driver");  MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 2f94f9504804..90ddb8774210 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -54,6 +54,9 @@   * and extended mode. They are mostly compatible with LM90 except for a data   * format difference for the temperature value registers.   * + * This driver also supports the SA56004 from Philips. This device is + * pin-compatible with the LM86, the ED/EDP parts are also address-compatible. + *   * Since the LM90 was the first chipset supported by this driver, most   * comments will refer to this chipset, but are actually general and   * concern all supported chipsets, unless mentioned otherwise. @@ -96,13 +99,15 @@   * MAX6659 can have address 0x4c, 0x4d or 0x4e.   * MAX6680 and MAX6681 can have address 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b,   * 0x4c, 0x4d or 0x4e. + * SA56004 can have address 0x48 through 0x4F.   */  static const unsigned short normal_i2c[] = { -	0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END }; +	0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x48, 0x49, 0x4a, 0x4b, 0x4c, +	0x4d, 0x4e, 0x4f, I2C_CLIENT_END };  enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, -	max6646, w83l771, max6696 }; +	max6646, w83l771, max6696, sa56004 };  /*   * The LM90 registers @@ -152,6 +157,10 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680,  #define MAX6659_REG_R_LOCAL_EMERG	0x17  #define MAX6659_REG_W_LOCAL_EMERG	0x17 +/*  SA56004 registers */ + +#define SA56004_REG_R_LOCAL_TEMPL 0x22 +  #define LM90_DEF_CONVRATE_RVAL	6	/* Def conversion rate register value */  #define LM90_MAX_CONVRATE_MS	16000	/* Maximum conversion rate in ms */ @@ -161,7 +170,6 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680,  #define LM90_FLAG_ADT7461_EXT	(1 << 0) /* ADT7461 extended mode	*/  /* Device features */  #define LM90_HAVE_OFFSET	(1 << 1) /* temperature offset register	*/ -#define LM90_HAVE_LOCAL_EXT	(1 << 2) /* extended local temperature	*/  #define LM90_HAVE_REM_LIMIT_EXT	(1 << 3) /* extended remote limit	*/  #define LM90_HAVE_EMERGENCY	(1 << 4) /* 3rd upper (emergency) limit	*/  #define LM90_HAVE_EMERGENCY_ALARM (1 << 5)/* emergency alarm		*/ @@ -192,6 +200,7 @@ static const struct i2c_device_id lm90_id[] = {  	{ "max6696", max6696 },  	{ "nct1008", adt7461 },  	{ "w83l771", w83l771 }, +	{ "sa56004", sa56004 },  	{ }  };  MODULE_DEVICE_TABLE(i2c, lm90_id); @@ -204,6 +213,7 @@ struct lm90_params {  	u16 alert_alarms;	/* Which alarm bits trigger ALERT# */  				/* Upper 8 bits for max6695/96 */  	u8 max_convrate;	/* Maximum conversion rate register value */ +	u8 reg_local_ext;	/* Extended local temp register (optional) */  };  static const struct lm90_params lm90_params[] = { @@ -235,19 +245,20 @@ static const struct lm90_params lm90_params[] = {  		.max_convrate = 9,  	},  	[max6646] = { -		.flags = LM90_HAVE_LOCAL_EXT,  		.alert_alarms = 0x7c,  		.max_convrate = 6, +		.reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL,  	},  	[max6657] = { -		.flags = LM90_HAVE_LOCAL_EXT,  		.alert_alarms = 0x7c,  		.max_convrate = 8, +		.reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL,  	},  	[max6659] = { -		.flags = LM90_HAVE_LOCAL_EXT | LM90_HAVE_EMERGENCY, +		.flags = LM90_HAVE_EMERGENCY,  		.alert_alarms = 0x7c,  		.max_convrate = 8, +		.reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL,  	},  	[max6680] = {  		.flags = LM90_HAVE_OFFSET, @@ -255,16 +266,23 @@ static const struct lm90_params lm90_params[] = {  		.max_convrate = 7,  	},  	[max6696] = { -		.flags = LM90_HAVE_LOCAL_EXT | LM90_HAVE_EMERGENCY +		.flags = LM90_HAVE_EMERGENCY  		  | LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3,  		.alert_alarms = 0x187c,  		.max_convrate = 6, +		.reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL,  	},  	[w83l771] = {  		.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT,  		.alert_alarms = 0x7c,  		.max_convrate = 8,  	}, +	[sa56004] = { +		.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT, +		.alert_alarms = 0x7b, +		.max_convrate = 9, +		.reg_local_ext = SA56004_REG_R_LOCAL_TEMPL, +	},  };  /* @@ -286,6 +304,7 @@ struct lm90_data {  	u16 alert_alarms;	/* Which alarm bits trigger ALERT# */  				/* Upper 8 bits for max6695/96 */  	u8 max_convrate;	/* Maximum conversion rate */ +	u8 reg_local_ext;	/* local extension register offset */  	/* registers values */  	s8 temp8[8];	/* 0: local low limit @@ -452,9 +471,9 @@ static struct lm90_data *lm90_update_device(struct device *dev)  		lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, &data->temp8[3]);  		lm90_read_reg(client, LM90_REG_R_TCRIT_HYST, &data->temp_hyst); -		if (data->flags & LM90_HAVE_LOCAL_EXT) { +		if (data->reg_local_ext) {  			lm90_read16(client, LM90_REG_R_LOCAL_TEMP, -				    MAX6657_REG_R_LOCAL_TEMPL, +				    data->reg_local_ext,  				    &data->temp11[4]);  		} else {  			if (lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP, @@ -1092,7 +1111,7 @@ static int lm90_detect(struct i2c_client *new_client,  	struct i2c_adapter *adapter = new_client->adapter;  	int address = new_client->addr;  	const char *name = NULL; -	int man_id, chip_id, reg_config1, reg_convrate; +	int man_id, chip_id, reg_config1, reg_config2, reg_convrate;  	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))  		return -ENODEV; @@ -1108,15 +1127,16 @@ static int lm90_detect(struct i2c_client *new_client,  						LM90_REG_R_CONVRATE)) < 0)  		return -ENODEV; -	if ((address == 0x4C || address == 0x4D) -	 && man_id == 0x01) { /* National Semiconductor */ -		int reg_config2; - +	if (man_id == 0x01 || man_id == 0x5C || man_id == 0x41) {  		reg_config2 = i2c_smbus_read_byte_data(new_client,  						LM90_REG_R_CONFIG2);  		if (reg_config2 < 0)  			return -ENODEV; +	} else +		reg_config2 = 0;	/* Make compiler happy */ +	if ((address == 0x4C || address == 0x4D) +	 && man_id == 0x01) { /* National Semiconductor */  		if ((reg_config1 & 0x2A) == 0x00  		 && (reg_config2 & 0xF8) == 0x00  		 && reg_convrate <= 0x09) { @@ -1245,13 +1265,6 @@ static int lm90_detect(struct i2c_client *new_client,  	} else  	if (address == 0x4C  	 && man_id == 0x5C) { /* Winbond/Nuvoton */ -		int reg_config2; - -		reg_config2 = i2c_smbus_read_byte_data(new_client, -						LM90_REG_R_CONFIG2); -		if (reg_config2 < 0) -			return -ENODEV; -  		if ((reg_config1 & 0x2A) == 0x00  		 && (reg_config2 & 0xF8) == 0x00) {  			if (chip_id == 0x01 /* W83L771W/G */ @@ -1263,6 +1276,15 @@ static int lm90_detect(struct i2c_client *new_client,  				name = "w83l771";  			}  		} +	} else +	if (address >= 0x48 && address <= 0x4F +	 && man_id == 0xA1) { /*  NXP Semiconductor/Philips */ +		if (chip_id == 0x00 +		 && (reg_config1 & 0x2A) == 0x00 +		 && (reg_config2 & 0xFE) == 0x00 +		 && reg_convrate <= 0x09) { +			name = "sa56004"; +		}  	}  	if (!name) { /* identification failed */ @@ -1368,6 +1390,7 @@ static int lm90_probe(struct i2c_client *new_client,  	/* Set chip capabilities */  	data->flags = lm90_params[data->kind].flags; +	data->reg_local_ext = lm90_params[data->kind].reg_local_ext;  	/* Set maximum conversion rate */  	data->max_convrate = lm90_params[data->kind].max_convrate; diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c index 1a6dfb6df1e7..513901d592a9 100644 --- a/drivers/hwmon/lm95241.c +++ b/drivers/hwmon/lm95241.c @@ -74,8 +74,9 @@ static const unsigned short normal_i2c[] = {  #define TT_OFF 0  #define TT_ON 1  #define TT_MASK 7 -#define MANUFACTURER_ID 0x01 -#define DEFAULT_REVISION 0xA4 +#define NATSEMI_MAN_ID	0x01 +#define LM95231_CHIP_ID	0xA1 +#define LM95241_CHIP_ID	0xA4  static const u8 lm95241_reg_address[] = {  	LM95241_REG_R_LOCAL_TEMPH, @@ -98,11 +99,16 @@ struct lm95241_data {  };  /* Conversions */ -static int TempFromReg(u8 val_h, u8 val_l) +static int temp_from_reg_signed(u8 val_h, u8 val_l)  { -	if (val_h & 0x80) -		return val_h - 0x100; -	return val_h * 1000 + val_l * 1000 / 256; +	s16 val_hl = (val_h << 8) | val_l; +	return val_hl * 1000 / 256; +} + +static int temp_from_reg_unsigned(u8 val_h, u8 val_l) +{ +	u16 val_hl = (val_h << 8) | val_l; +	return val_hl * 1000 / 256;  }  static struct lm95241_data *lm95241_update_device(struct device *dev) @@ -135,10 +141,13 @@ static ssize_t show_input(struct device *dev, struct device_attribute *attr,  			  char *buf)  {  	struct lm95241_data *data = lm95241_update_device(dev); +	int index = to_sensor_dev_attr(attr)->index;  	return snprintf(buf, PAGE_SIZE - 1, "%d\n", -		TempFromReg(data->temp[to_sensor_dev_attr(attr)->index], -			    data->temp[to_sensor_dev_attr(attr)->index + 1])); +			index == 0 || (data->config & (1 << (index / 2))) ? +		temp_from_reg_signed(data->temp[index], data->temp[index + 1]) : +		temp_from_reg_unsigned(data->temp[index], +				       data->temp[index + 1]));  }  static ssize_t show_type(struct device *dev, struct device_attribute *attr, @@ -330,20 +339,25 @@ static int lm95241_detect(struct i2c_client *new_client,  			  struct i2c_board_info *info)  {  	struct i2c_adapter *adapter = new_client->adapter; -	int address = new_client->addr;  	const char *name; +	int mfg_id, chip_id;  	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))  		return -ENODEV; -	if ((i2c_smbus_read_byte_data(new_client, LM95241_REG_R_MAN_ID) -	     == MANUFACTURER_ID) -	    && (i2c_smbus_read_byte_data(new_client, LM95241_REG_R_CHIP_ID) -		>= DEFAULT_REVISION)) { -		name = DEVNAME; -	} else { -		dev_dbg(&adapter->dev, "LM95241 detection failed at 0x%02x\n", -			address); +	mfg_id = i2c_smbus_read_byte_data(new_client, LM95241_REG_R_MAN_ID); +	if (mfg_id != NATSEMI_MAN_ID) +		return -ENODEV; + +	chip_id = i2c_smbus_read_byte_data(new_client, LM95241_REG_R_CHIP_ID); +	switch (chip_id) { +	case LM95231_CHIP_ID: +		name = "lm95231"; +		break; +	case LM95241_CHIP_ID: +		name = "lm95241"; +		break; +	default:  		return -ENODEV;  	} @@ -423,7 +437,8 @@ static int lm95241_remove(struct i2c_client *client)  /* Driver data (common to all clients) */  static const struct i2c_device_id lm95241_id[] = { -	{ DEVNAME, 0 }, +	{ "lm95231", 0 }, +	{ "lm95241", 0 },  	{ }  };  MODULE_DEVICE_TABLE(i2c, lm95241_id); diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c new file mode 100644 index 000000000000..dce9e68241e6 --- /dev/null +++ b/drivers/hwmon/lm95245.c @@ -0,0 +1,543 @@ +/* + * Copyright (C) 2011 Alexander Stein <alexander.stein@systec-electronic.com> + * + * The LM95245 is a sensor chip made by National Semiconductors. + * It reports up to two temperatures (its own plus an external one). + * Complete datasheet can be obtained from National's website at: + *   http://www.national.com/ds.cgi/LM/LM95245.pdf + * + * This driver is based on lm95241.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/sysfs.h> + +#define DEVNAME "lm95245" + +static const unsigned short normal_i2c[] = { +	0x18, 0x19, 0x29, 0x4c, 0x4d, I2C_CLIENT_END }; + +/* LM95245 registers */ +/* general registers */ +#define LM95245_REG_RW_CONFIG1		0x03 +#define LM95245_REG_RW_CONVERS_RATE	0x04 +#define LM95245_REG_W_ONE_SHOT		0x0F + +/* diode configuration */ +#define LM95245_REG_RW_CONFIG2		0xBF +#define LM95245_REG_RW_REMOTE_OFFH	0x11 +#define LM95245_REG_RW_REMOTE_OFFL	0x12 + +/* status registers */ +#define LM95245_REG_R_STATUS1		0x02 +#define LM95245_REG_R_STATUS2		0x33 + +/* limit registers */ +#define LM95245_REG_RW_REMOTE_OS_LIMIT		0x07 +#define LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT	0x20 +#define LM95245_REG_RW_REMOTE_TCRIT_LIMIT	0x19 +#define LM95245_REG_RW_COMMON_HYSTERESIS	0x21 + +/* temperature signed */ +#define LM95245_REG_R_LOCAL_TEMPH_S	0x00 +#define LM95245_REG_R_LOCAL_TEMPL_S	0x30 +#define LM95245_REG_R_REMOTE_TEMPH_S	0x01 +#define LM95245_REG_R_REMOTE_TEMPL_S	0x10 +/* temperature unsigned */ +#define LM95245_REG_R_REMOTE_TEMPH_U	0x31 +#define LM95245_REG_R_REMOTE_TEMPL_U	0x32 + +/* id registers */ +#define LM95245_REG_R_MAN_ID		0xFE +#define LM95245_REG_R_CHIP_ID		0xFF + +/* LM95245 specific bitfields */ +#define CFG_STOP		0x40 +#define CFG_REMOTE_TCRIT_MASK	0x10 +#define CFG_REMOTE_OS_MASK	0x08 +#define CFG_LOCAL_TCRIT_MASK	0x04 +#define CFG_LOCAL_OS_MASK	0x02 + +#define CFG2_OS_A0		0x40 +#define CFG2_DIODE_FAULT_OS	0x20 +#define CFG2_DIODE_FAULT_TCRIT	0x10 +#define CFG2_REMOTE_TT		0x08 +#define CFG2_REMOTE_FILTER_DIS	0x00 +#define CFG2_REMOTE_FILTER_EN	0x06 + +/* conversation rate in ms */ +#define RATE_CR0063	0x00 +#define RATE_CR0364	0x01 +#define RATE_CR1000	0x02 +#define RATE_CR2500	0x03 + +#define STATUS1_DIODE_FAULT	0x04 +#define STATUS1_RTCRIT		0x02 +#define STATUS1_LOC		0x01 + +#define MANUFACTURER_ID		0x01 +#define DEFAULT_REVISION	0xB3 + +static const u8 lm95245_reg_address[] = { +	LM95245_REG_R_LOCAL_TEMPH_S, +	LM95245_REG_R_LOCAL_TEMPL_S, +	LM95245_REG_R_REMOTE_TEMPH_S, +	LM95245_REG_R_REMOTE_TEMPL_S, +	LM95245_REG_R_REMOTE_TEMPH_U, +	LM95245_REG_R_REMOTE_TEMPL_U, +	LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT, +	LM95245_REG_RW_REMOTE_TCRIT_LIMIT, +	LM95245_REG_RW_COMMON_HYSTERESIS, +	LM95245_REG_R_STATUS1, +}; + +/* Client data (each client gets its own) */ +struct lm95245_data { +	struct device *hwmon_dev; +	struct mutex update_lock; +	unsigned long last_updated;	/* in jiffies */ +	unsigned long interval;	/* in msecs */ +	bool valid;		/* zero until following fields are valid */ +	/* registers values */ +	u8 regs[ARRAY_SIZE(lm95245_reg_address)]; +	u8 config1, config2; +}; + +/* Conversions */ +static int temp_from_reg_unsigned(u8 val_h, u8 val_l) +{ +	return val_h * 1000 + val_l * 1000 / 256; +} + +static int temp_from_reg_signed(u8 val_h, u8 val_l) +{ +	if (val_h & 0x80) +		return (val_h - 0x100) * 1000; +	return temp_from_reg_unsigned(val_h, val_l); +} + +static struct lm95245_data *lm95245_update_device(struct device *dev) +{ +	struct i2c_client *client = to_i2c_client(dev); +	struct lm95245_data *data = i2c_get_clientdata(client); + +	mutex_lock(&data->update_lock); + +	if (time_after(jiffies, data->last_updated +		+ msecs_to_jiffies(data->interval)) || !data->valid) { +		int i; + +		dev_dbg(&client->dev, "Updating lm95245 data.\n"); +		for (i = 0; i < ARRAY_SIZE(lm95245_reg_address); i++) +			data->regs[i] +			  = i2c_smbus_read_byte_data(client, +						     lm95245_reg_address[i]); +		data->last_updated = jiffies; +		data->valid = 1; +	} + +	mutex_unlock(&data->update_lock); + +	return data; +} + +static unsigned long lm95245_read_conversion_rate(struct i2c_client *client) +{ +	int rate; +	unsigned long interval; + +	rate = i2c_smbus_read_byte_data(client, LM95245_REG_RW_CONVERS_RATE); + +	switch (rate) { +	case RATE_CR0063: +		interval = 63; +		break; +	case RATE_CR0364: +		interval = 364; +		break; +	case RATE_CR1000: +		interval = 1000; +		break; +	case RATE_CR2500: +	default: +		interval = 2500; +		break; +	} + +	return interval; +} + +static unsigned long lm95245_set_conversion_rate(struct i2c_client *client, +			unsigned long interval) +{ +	int rate; + +	if (interval <= 63) { +		interval = 63; +		rate = RATE_CR0063; +	} else if (interval <= 364) { +		interval = 364; +		rate = RATE_CR0364; +	} else if (interval <= 1000) { +		interval = 1000; +		rate = RATE_CR1000; +	} else { +		interval = 2500; +		rate = RATE_CR2500; +	} + +	i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONVERS_RATE, rate); + +	return interval; +} + +/* Sysfs stuff */ +static ssize_t show_input(struct device *dev, struct device_attribute *attr, +			  char *buf) +{ +	struct lm95245_data *data = lm95245_update_device(dev); +	int temp; +	int index = to_sensor_dev_attr(attr)->index; + +	/* +	 * Index 0 (Local temp) is always signed +	 * Index 2 (Remote temp) has both signed and unsigned data +	 * use signed calculation for remote if signed bit is set +	 */ +	if (index == 0 || data->regs[index] & 0x80) +		temp = temp_from_reg_signed(data->regs[index], +			    data->regs[index + 1]); +	else +		temp = temp_from_reg_unsigned(data->regs[index + 2], +			    data->regs[index + 3]); + +	return snprintf(buf, PAGE_SIZE - 1, "%d\n", temp); +} + +static ssize_t show_limit(struct device *dev, struct device_attribute *attr, +			 char *buf) +{ +	struct lm95245_data *data = lm95245_update_device(dev); +	int index = to_sensor_dev_attr(attr)->index; + +	return snprintf(buf, PAGE_SIZE - 1, "%d\n", +			data->regs[index] * 1000); +} + +static ssize_t set_limit(struct device *dev, struct device_attribute *attr, +			const char *buf, size_t count) +{ +	struct i2c_client *client = to_i2c_client(dev); +	struct lm95245_data *data = i2c_get_clientdata(client); +	int index = to_sensor_dev_attr(attr)->index; +	unsigned long val; + +	if (strict_strtoul(buf, 10, &val) < 0) +		return -EINVAL; + +	val /= 1000; + +	val = SENSORS_LIMIT(val, 0, (index == 6 ? 127 : 255)); + +	mutex_lock(&data->update_lock); + +	data->valid = 0; + +	i2c_smbus_write_byte_data(client, lm95245_reg_address[index], val); + +	mutex_unlock(&data->update_lock); + +	return count; +} + +static ssize_t set_crit_hyst(struct device *dev, struct device_attribute *attr, +			const char *buf, size_t count) +{ +	struct i2c_client *client = to_i2c_client(dev); +	struct lm95245_data *data = i2c_get_clientdata(client); +	unsigned long val; + +	if (strict_strtoul(buf, 10, &val) < 0) +		return -EINVAL; + +	val /= 1000; + +	val = SENSORS_LIMIT(val, 0, 31); + +	mutex_lock(&data->update_lock); + +	data->valid = 0; + +	/* shared crit hysteresis */ +	i2c_smbus_write_byte_data(client, LM95245_REG_RW_COMMON_HYSTERESIS, +		val); + +	mutex_unlock(&data->update_lock); + +	return count; +} + +static ssize_t show_type(struct device *dev, struct device_attribute *attr, +			 char *buf) +{ +	struct i2c_client *client = to_i2c_client(dev); +	struct lm95245_data *data = i2c_get_clientdata(client); + +	return snprintf(buf, PAGE_SIZE - 1, +		data->config2 & CFG2_REMOTE_TT ? "1\n" : "2\n"); +} + +static ssize_t set_type(struct device *dev, struct device_attribute *attr, +			const char *buf, size_t count) +{ +	struct i2c_client *client = to_i2c_client(dev); +	struct lm95245_data *data = i2c_get_clientdata(client); +	unsigned long val; + +	if (strict_strtoul(buf, 10, &val) < 0) +		return -EINVAL; +	if (val != 1 && val != 2) +		return -EINVAL; + +	mutex_lock(&data->update_lock); + +	if (val == 1) +		data->config2 |= CFG2_REMOTE_TT; +	else +		data->config2 &= ~CFG2_REMOTE_TT; + +	data->valid = 0; + +	i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONFIG2, +				  data->config2); + +	mutex_unlock(&data->update_lock); + +	return count; +} + +static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, +			 char *buf) +{ +	struct lm95245_data *data = lm95245_update_device(dev); +	int index = to_sensor_dev_attr(attr)->index; + +	return snprintf(buf, PAGE_SIZE - 1, "%d\n", +			!!(data->regs[9] & index)); +} + +static ssize_t show_interval(struct device *dev, struct device_attribute *attr, +			     char *buf) +{ +	struct lm95245_data *data = lm95245_update_device(dev); + +	return snprintf(buf, PAGE_SIZE - 1, "%lu\n", data->interval); +} + +static ssize_t set_interval(struct device *dev, struct device_attribute *attr, +			    const char *buf, size_t count) +{ +	struct i2c_client *client = to_i2c_client(dev); +	struct lm95245_data *data = i2c_get_clientdata(client); +	unsigned long val; + +	if (strict_strtoul(buf, 10, &val) < 0) +		return -EINVAL; + +	mutex_lock(&data->update_lock); + +	data->interval = lm95245_set_conversion_rate(client, val); + +	mutex_unlock(&data->update_lock); + +	return count; +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_input, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_limit, +		set_limit, 6); +static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_limit, +		set_crit_hyst, 8); +static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, +		STATUS1_LOC); + +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_input, NULL, 2); +static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_limit, +		set_limit, 7); +static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_limit, +		set_crit_hyst, 8); +static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, +		STATUS1_RTCRIT); +static SENSOR_DEVICE_ATTR(temp2_type, S_IWUSR | S_IRUGO, show_type, +		set_type, 0); +static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, +		STATUS1_DIODE_FAULT); + +static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval, +		set_interval); + +static struct attribute *lm95245_attributes[] = { +	&sensor_dev_attr_temp1_input.dev_attr.attr, +	&sensor_dev_attr_temp1_crit.dev_attr.attr, +	&sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, +	&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, +	&sensor_dev_attr_temp2_input.dev_attr.attr, +	&sensor_dev_attr_temp2_crit.dev_attr.attr, +	&sensor_dev_attr_temp2_crit_hyst.dev_attr.attr, +	&sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, +	&sensor_dev_attr_temp2_type.dev_attr.attr, +	&sensor_dev_attr_temp2_fault.dev_attr.attr, +	&dev_attr_update_interval.attr, +	NULL +}; + +static const struct attribute_group lm95245_group = { +	.attrs = lm95245_attributes, +}; + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int lm95245_detect(struct i2c_client *new_client, +			  struct i2c_board_info *info) +{ +	struct i2c_adapter *adapter = new_client->adapter; + +	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) +		return -ENODEV; + +	if (i2c_smbus_read_byte_data(new_client, LM95245_REG_R_MAN_ID) +			!= MANUFACTURER_ID +		|| i2c_smbus_read_byte_data(new_client, LM95245_REG_R_CHIP_ID) +			!= DEFAULT_REVISION) +		return -ENODEV; + +	strlcpy(info->type, DEVNAME, I2C_NAME_SIZE); +	return 0; +} + +static void lm95245_init_client(struct i2c_client *client) +{ +	struct lm95245_data *data = i2c_get_clientdata(client); + +	data->valid = 0; +	data->interval = lm95245_read_conversion_rate(client); + +	data->config1 = i2c_smbus_read_byte_data(client, +		LM95245_REG_RW_CONFIG1); +	data->config2 = i2c_smbus_read_byte_data(client, +		LM95245_REG_RW_CONFIG2); + +	if (data->config1 & CFG_STOP) { +		/* Clear the standby bit */ +		data->config1 &= ~CFG_STOP; +		i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONFIG1, +			data->config1); +	} +} + +static int lm95245_probe(struct i2c_client *new_client, +			 const struct i2c_device_id *id) +{ +	struct lm95245_data *data; +	int err; + +	data = kzalloc(sizeof(struct lm95245_data), GFP_KERNEL); +	if (!data) { +		err = -ENOMEM; +		goto exit; +	} + +	i2c_set_clientdata(new_client, data); +	mutex_init(&data->update_lock); + +	/* Initialize the LM95245 chip */ +	lm95245_init_client(new_client); + +	/* Register sysfs hooks */ +	err = sysfs_create_group(&new_client->dev.kobj, &lm95245_group); +	if (err) +		goto exit_free; + +	data->hwmon_dev = hwmon_device_register(&new_client->dev); +	if (IS_ERR(data->hwmon_dev)) { +		err = PTR_ERR(data->hwmon_dev); +		goto exit_remove_files; +	} + +	return 0; + +exit_remove_files: +	sysfs_remove_group(&new_client->dev.kobj, &lm95245_group); +exit_free: +	kfree(data); +exit: +	return err; +} + +static int lm95245_remove(struct i2c_client *client) +{ +	struct lm95245_data *data = i2c_get_clientdata(client); + +	hwmon_device_unregister(data->hwmon_dev); +	sysfs_remove_group(&client->dev.kobj, &lm95245_group); + +	kfree(data); +	return 0; +} + +/* Driver data (common to all clients) */ +static const struct i2c_device_id lm95245_id[] = { +	{ DEVNAME, 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, lm95245_id); + +static struct i2c_driver lm95245_driver = { +	.class		= I2C_CLASS_HWMON, +	.driver = { +		.name	= DEVNAME, +	}, +	.probe		= lm95245_probe, +	.remove		= lm95245_remove, +	.id_table	= lm95245_id, +	.detect		= lm95245_detect, +	.address_list	= normal_i2c, +}; + +static int __init sensors_lm95245_init(void) +{ +	return i2c_add_driver(&lm95245_driver); +} + +static void __exit sensors_lm95245_exit(void) +{ +	i2c_del_driver(&lm95245_driver); +} + +MODULE_AUTHOR("Alexander Stein <alexander.stein@systec-electronic.com>"); +MODULE_DESCRIPTION("LM95245 sensor driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_lm95245_init); +module_exit(sensors_lm95245_exit); diff --git a/drivers/hwmon/max1111.c b/drivers/hwmon/max1111.c index 12a54aa29776..c97b78ef9116 100644 --- a/drivers/hwmon/max1111.c +++ b/drivers/hwmon/max1111.c @@ -38,8 +38,10 @@ struct max1111_data {  	struct device		*hwmon_dev;  	struct spi_message	msg;  	struct spi_transfer	xfer[2]; -	uint8_t *tx_buf; -	uint8_t *rx_buf; +	uint8_t tx_buf[MAX1111_TX_BUF_SIZE]; +	uint8_t rx_buf[MAX1111_RX_BUF_SIZE]; +	struct mutex		drvdata_lock; +	/* protect msg, xfer and buffers from multiple access */  };  static int max1111_read(struct device *dev, int channel) @@ -48,6 +50,9 @@ static int max1111_read(struct device *dev, int channel)  	uint8_t v1, v2;  	int err; +	/* writing to drvdata struct is not thread safe, wait on mutex */ +	mutex_lock(&data->drvdata_lock); +  	data->tx_buf[0] = (channel << MAX1111_CTRL_SEL_SH) |  		MAX1111_CTRL_PD0 | MAX1111_CTRL_PD1 |  		MAX1111_CTRL_SGL | MAX1111_CTRL_UNI | MAX1111_CTRL_STR; @@ -55,12 +60,15 @@ static int max1111_read(struct device *dev, int channel)  	err = spi_sync(data->spi, &data->msg);  	if (err < 0) {  		dev_err(dev, "spi_sync failed with %d\n", err); +		mutex_unlock(&data->drvdata_lock);  		return err;  	}  	v1 = data->rx_buf[0];  	v2 = data->rx_buf[1]; +	mutex_unlock(&data->drvdata_lock); +  	if ((v1 & 0xc0) || (v2 & 0x3f))  		return -EINVAL; @@ -123,33 +131,23 @@ static const struct attribute_group max1111_attr_group = {  	.attrs	= max1111_attributes,  }; -static int setup_transfer(struct max1111_data *data) +static int __devinit setup_transfer(struct max1111_data *data)  {  	struct spi_message *m;  	struct spi_transfer *x; -	data->tx_buf = kmalloc(MAX1111_TX_BUF_SIZE, GFP_KERNEL); -	if (!data->tx_buf) -		return -ENOMEM; - -	data->rx_buf = kmalloc(MAX1111_RX_BUF_SIZE, GFP_KERNEL); -	if (!data->rx_buf) { -		kfree(data->tx_buf); -		return -ENOMEM; -	} -  	m = &data->msg;  	x = &data->xfer[0];  	spi_message_init(m);  	x->tx_buf = &data->tx_buf[0]; -	x->len = 1; +	x->len = MAX1111_TX_BUF_SIZE;  	spi_message_add_tail(x, m);  	x++;  	x->rx_buf = &data->rx_buf[0]; -	x->len = 2; +	x->len = MAX1111_RX_BUF_SIZE;  	spi_message_add_tail(x, m);  	return 0; @@ -176,13 +174,15 @@ static int __devinit max1111_probe(struct spi_device *spi)  	if (err)  		goto err_free_data; +	mutex_init(&data->drvdata_lock); +  	data->spi = spi;  	spi_set_drvdata(spi, data);  	err = sysfs_create_group(&spi->dev.kobj, &max1111_attr_group);  	if (err) {  		dev_err(&spi->dev, "failed to create attribute group\n"); -		goto err_free_all; +		goto err_free_data;  	}  	data->hwmon_dev = hwmon_device_register(&spi->dev); @@ -199,9 +199,6 @@ static int __devinit max1111_probe(struct spi_device *spi)  err_remove:  	sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group); -err_free_all: -	kfree(data->rx_buf); -	kfree(data->tx_buf);  err_free_data:  	kfree(data);  	return err; @@ -213,8 +210,7 @@ static int __devexit max1111_remove(struct spi_device *spi)  	hwmon_device_unregister(data->hwmon_dev);  	sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group); -	kfree(data->rx_buf); -	kfree(data->tx_buf); +	mutex_destroy(&data->drvdata_lock);  	kfree(data);  	return 0;  } diff --git a/drivers/hwmon/max1668.c b/drivers/hwmon/max1668.c new file mode 100644 index 000000000000..20d1b2ddffb6 --- /dev/null +++ b/drivers/hwmon/max1668.c @@ -0,0 +1,502 @@ +/* +    Copyright (c) 2011 David George <david.george@ska.ac.za> + +    based on adm1021.c +    some credit to Christoph Scheurer, but largely a rewrite + +    This program is free software; you can redistribute it and/or modify +    it under the terms of the GNU General Public License as published by +    the Free Software Foundation; either version 2 of the License, or +    (at your option) any later version. + +    This program is distributed in the hope that it will be useful, +    but WITHOUT ANY WARRANTY; without even the implied warranty of +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +    GNU General Public License for more details. + +    You should have received a copy of the GNU General Public License +    along with this program; if not, write to the Free Software +    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> + +/* Addresses to scan */ +static unsigned short max1668_addr_list[] = { +	0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END }; + +/* max1668 registers */ + +#define MAX1668_REG_TEMP(nr)	(nr) +#define MAX1668_REG_STAT1	0x05 +#define MAX1668_REG_STAT2	0x06 +#define MAX1668_REG_MAN_ID	0xfe +#define MAX1668_REG_DEV_ID	0xff + +/* limits */ + +/* write high limits */ +#define MAX1668_REG_LIMH_WR(nr)	(0x13 + 2 * (nr)) +/* write low limits */ +#define MAX1668_REG_LIML_WR(nr)	(0x14 + 2 * (nr)) +/* read high limits */ +#define MAX1668_REG_LIMH_RD(nr)	(0x08 + 2 * (nr)) +/* read low limits */ +#define MAX1668_REG_LIML_RD(nr)	(0x09 + 2 * (nr)) + +/* manufacturer and device ID Constants */ +#define MAN_ID_MAXIM		0x4d +#define DEV_ID_MAX1668		0x3 +#define DEV_ID_MAX1805		0x5 +#define DEV_ID_MAX1989		0xb + +/* read only mode module parameter */ +static int read_only; +module_param(read_only, bool, 0); +MODULE_PARM_DESC(read_only, "Don't set any values, read only mode"); + +enum chips { max1668, max1805, max1989 }; + +struct max1668_data { +	struct device *hwmon_dev; +	enum chips type; + +	struct mutex update_lock; +	char valid;		/* !=0 if following fields are valid */ +	unsigned long last_updated;	/* In jiffies */ + +	/* 1x local and 4x remote */ +	s8 temp_max[5]; +	s8 temp_min[5]; +	s8 temp[5]; +	u16 alarms; +}; + +static struct max1668_data *max1668_update_device(struct device *dev) +{ +	struct i2c_client *client = to_i2c_client(dev); +	struct max1668_data *data = i2c_get_clientdata(client); +	struct max1668_data *ret = data; +	s32 val; +	int i; + +	mutex_lock(&data->update_lock); + +	if (data->valid && !time_after(jiffies, +			data->last_updated + HZ + HZ / 2)) +		goto abort; + +	for (i = 0; i < 5; i++) { +		val = i2c_smbus_read_byte_data(client, MAX1668_REG_TEMP(i)); +		if (unlikely(val < 0)) { +			ret = ERR_PTR(val); +			goto abort; +		} +		data->temp[i] = (s8) val; + +		val = i2c_smbus_read_byte_data(client, MAX1668_REG_LIMH_RD(i)); +		if (unlikely(val < 0)) { +			ret = ERR_PTR(val); +			goto abort; +		} +		data->temp_max[i] = (s8) val; + +		val = i2c_smbus_read_byte_data(client, MAX1668_REG_LIML_RD(i)); +		if (unlikely(val < 0)) { +			ret = ERR_PTR(val); +			goto abort; +		} +		data->temp_min[i] = (s8) val; +	} + +	val = i2c_smbus_read_byte_data(client, MAX1668_REG_STAT1); +	if (unlikely(val < 0)) { +		ret = ERR_PTR(val); +		goto abort; +	} +	data->alarms = val << 8; + +	val = i2c_smbus_read_byte_data(client, MAX1668_REG_STAT2); +	if (unlikely(val < 0)) { +		ret = ERR_PTR(val); +		goto abort; +	} +	data->alarms |= val; + +	data->last_updated = jiffies; +	data->valid = 1; +abort: +	mutex_unlock(&data->update_lock); + +	return ret; +} + +static ssize_t show_temp(struct device *dev, +			 struct device_attribute *devattr, char *buf) +{ +	int index = to_sensor_dev_attr(devattr)->index; +	struct max1668_data *data = max1668_update_device(dev); + +	if (IS_ERR(data)) +		return PTR_ERR(data); + +	return sprintf(buf, "%d\n", data->temp[index] * 1000); +} + +static ssize_t show_temp_max(struct device *dev, +			     struct device_attribute *devattr, char *buf) +{ +	int index = to_sensor_dev_attr(devattr)->index; +	struct max1668_data *data = max1668_update_device(dev); + +	if (IS_ERR(data)) +		return PTR_ERR(data); + +	return sprintf(buf, "%d\n", data->temp_max[index] * 1000); +} + +static ssize_t show_temp_min(struct device *dev, +			     struct device_attribute *devattr, char *buf) +{ +	int index = to_sensor_dev_attr(devattr)->index; +	struct max1668_data *data = max1668_update_device(dev); + +	if (IS_ERR(data)) +		return PTR_ERR(data); + +	return sprintf(buf, "%d\n", data->temp_min[index] * 1000); +} + +static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, +			  char *buf) +{ +	int index = to_sensor_dev_attr(attr)->index; +	struct max1668_data *data = max1668_update_device(dev); + +	if (IS_ERR(data)) +		return PTR_ERR(data); + +	return sprintf(buf, "%u\n", (data->alarms >> index) & 0x1); +} + +static ssize_t show_fault(struct device *dev, +			  struct device_attribute *devattr, char *buf) +{ +	int index = to_sensor_dev_attr(devattr)->index; +	struct max1668_data *data = max1668_update_device(dev); + +	if (IS_ERR(data)) +		return PTR_ERR(data); + +	return sprintf(buf, "%u\n", +		       (data->alarms & (1 << 12)) && data->temp[index] == 127); +} + +static ssize_t set_temp_max(struct device *dev, +			    struct device_attribute *devattr, +			    const char *buf, size_t count) +{ +	int index = to_sensor_dev_attr(devattr)->index; +	struct i2c_client *client = to_i2c_client(dev); +	struct max1668_data *data = i2c_get_clientdata(client); +	long temp; +	int ret; + +	ret = kstrtol(buf, 10, &temp); +	if (ret < 0) +		return ret; + +	mutex_lock(&data->update_lock); +	data->temp_max[index] = SENSORS_LIMIT(temp/1000, -128, 127); +	if (i2c_smbus_write_byte_data(client, +					MAX1668_REG_LIMH_WR(index), +					data->temp_max[index])) +		count = -EIO; +	mutex_unlock(&data->update_lock); + +	return count; +} + +static ssize_t set_temp_min(struct device *dev, +			    struct device_attribute *devattr, +			    const char *buf, size_t count) +{ +	int index = to_sensor_dev_attr(devattr)->index; +	struct i2c_client *client = to_i2c_client(dev); +	struct max1668_data *data = i2c_get_clientdata(client); +	long temp; +	int ret; + +	ret = kstrtol(buf, 10, &temp); +	if (ret < 0) +		return ret; + +	mutex_lock(&data->update_lock); +	data->temp_min[index] = SENSORS_LIMIT(temp/1000, -128, 127); +	if (i2c_smbus_write_byte_data(client, +					MAX1668_REG_LIML_WR(index), +					data->temp_max[index])) +		count = -EIO; +	mutex_unlock(&data->update_lock); + +	return count; +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_max, +				set_temp_max, 0); +static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO, show_temp_min, +				set_temp_min, 0); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1); +static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO, show_temp_max, +				set_temp_max, 1); +static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO, show_temp_min, +				set_temp_min, 1); +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2); +static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO, show_temp_max, +				set_temp_max, 2); +static SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO, show_temp_min, +				set_temp_min, 2); +static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3); +static SENSOR_DEVICE_ATTR(temp4_max, S_IRUGO, show_temp_max, +				set_temp_max, 3); +static SENSOR_DEVICE_ATTR(temp4_min, S_IRUGO, show_temp_min, +				set_temp_min, 3); +static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp5_max, S_IRUGO, show_temp_max, +				set_temp_max, 4); +static SENSOR_DEVICE_ATTR(temp5_min, S_IRUGO, show_temp_min, +				set_temp_min, 4); + +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 14); +static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 13); +static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 7); +static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 6); +static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, show_alarm, NULL, 5); +static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_alarm, NULL, 4); +static SENSOR_DEVICE_ATTR(temp4_min_alarm, S_IRUGO, show_alarm, NULL, 3); +static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_alarm, NULL, 2); +static SENSOR_DEVICE_ATTR(temp5_min_alarm, S_IRUGO, show_alarm, NULL, 1); +static SENSOR_DEVICE_ATTR(temp5_max_alarm, S_IRUGO, show_alarm, NULL, 0); + +static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_fault, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_fault, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_fault, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_fault, S_IRUGO, show_fault, NULL, 4); + +/* Attributes common to MAX1668, MAX1989 and MAX1805 */ +static struct attribute *max1668_attribute_common[] = { +	&sensor_dev_attr_temp1_max.dev_attr.attr, +	&sensor_dev_attr_temp1_min.dev_attr.attr, +	&sensor_dev_attr_temp1_input.dev_attr.attr, +	&sensor_dev_attr_temp2_max.dev_attr.attr, +	&sensor_dev_attr_temp2_min.dev_attr.attr, +	&sensor_dev_attr_temp2_input.dev_attr.attr, +	&sensor_dev_attr_temp3_max.dev_attr.attr, +	&sensor_dev_attr_temp3_min.dev_attr.attr, +	&sensor_dev_attr_temp3_input.dev_attr.attr, + +	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr, +	&sensor_dev_attr_temp1_min_alarm.dev_attr.attr, +	&sensor_dev_attr_temp2_max_alarm.dev_attr.attr, +	&sensor_dev_attr_temp2_min_alarm.dev_attr.attr, +	&sensor_dev_attr_temp3_max_alarm.dev_attr.attr, +	&sensor_dev_attr_temp3_min_alarm.dev_attr.attr, + +	&sensor_dev_attr_temp2_fault.dev_attr.attr, +	&sensor_dev_attr_temp3_fault.dev_attr.attr, +	NULL +}; + +/* Attributes not present on MAX1805 */ +static struct attribute *max1668_attribute_unique[] = { +	&sensor_dev_attr_temp4_max.dev_attr.attr, +	&sensor_dev_attr_temp4_min.dev_attr.attr, +	&sensor_dev_attr_temp4_input.dev_attr.attr, +	&sensor_dev_attr_temp5_max.dev_attr.attr, +	&sensor_dev_attr_temp5_min.dev_attr.attr, +	&sensor_dev_attr_temp5_input.dev_attr.attr, + +	&sensor_dev_attr_temp4_max_alarm.dev_attr.attr, +	&sensor_dev_attr_temp4_min_alarm.dev_attr.attr, +	&sensor_dev_attr_temp5_max_alarm.dev_attr.attr, +	&sensor_dev_attr_temp5_min_alarm.dev_attr.attr, + +	&sensor_dev_attr_temp4_fault.dev_attr.attr, +	&sensor_dev_attr_temp5_fault.dev_attr.attr, +	NULL +}; + +static mode_t max1668_attribute_mode(struct kobject *kobj, +				     struct attribute *attr, int index) +{ +	int ret = S_IRUGO; +	if (read_only) +		return ret; +	if (attr == &sensor_dev_attr_temp1_max.dev_attr.attr || +	    attr == &sensor_dev_attr_temp2_max.dev_attr.attr || +	    attr == &sensor_dev_attr_temp3_max.dev_attr.attr || +	    attr == &sensor_dev_attr_temp4_max.dev_attr.attr || +	    attr == &sensor_dev_attr_temp5_max.dev_attr.attr || +	    attr == &sensor_dev_attr_temp1_min.dev_attr.attr || +	    attr == &sensor_dev_attr_temp2_min.dev_attr.attr || +	    attr == &sensor_dev_attr_temp3_min.dev_attr.attr || +	    attr == &sensor_dev_attr_temp4_min.dev_attr.attr || +	    attr == &sensor_dev_attr_temp5_min.dev_attr.attr) +		ret |= S_IWUSR; +	return ret; +} + +static const struct attribute_group max1668_group_common = { +	.attrs = max1668_attribute_common, +	.is_visible = max1668_attribute_mode +}; + +static const struct attribute_group max1668_group_unique = { +	.attrs = max1668_attribute_unique, +	.is_visible = max1668_attribute_mode +}; + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int max1668_detect(struct i2c_client *client, +			  struct i2c_board_info *info) +{ +	struct i2c_adapter *adapter = client->adapter; +	const char *type_name; +	int man_id, dev_id; + +	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) +		return -ENODEV; + +	/* Check for unsupported part */ +	man_id = i2c_smbus_read_byte_data(client, MAX1668_REG_MAN_ID); +	if (man_id != MAN_ID_MAXIM) +		return -ENODEV; + +	dev_id = i2c_smbus_read_byte_data(client, MAX1668_REG_DEV_ID); +	if (dev_id < 0) +		return -ENODEV; + +	type_name = NULL; +	if (dev_id == DEV_ID_MAX1668) +		type_name = "max1668"; +	else if (dev_id == DEV_ID_MAX1805) +		type_name = "max1805"; +	else if (dev_id == DEV_ID_MAX1989) +		type_name = "max1989"; + +	if (!type_name) +		return -ENODEV; + +	strlcpy(info->type, type_name, I2C_NAME_SIZE); + +	return 0; +} + +static int max1668_probe(struct i2c_client *client, +			 const struct i2c_device_id *id) +{ +	struct i2c_adapter *adapter = client->adapter; +	struct max1668_data *data; +	int err; + +	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) +		return -ENODEV; + +	data = kzalloc(sizeof(struct max1668_data), GFP_KERNEL); +	if (!data) +		return -ENOMEM; + +	i2c_set_clientdata(client, data); +	data->type = id->driver_data; +	mutex_init(&data->update_lock); + +	/* Register sysfs hooks */ +	err = sysfs_create_group(&client->dev.kobj, &max1668_group_common); +	if (err) +		goto error_free; + +	if (data->type == max1668 || data->type == max1989) { +		err = sysfs_create_group(&client->dev.kobj, +					 &max1668_group_unique); +		if (err) +			goto error_sysrem0; +	} + +	data->hwmon_dev = hwmon_device_register(&client->dev); +	if (IS_ERR(data->hwmon_dev)) { +		err = PTR_ERR(data->hwmon_dev); +		goto error_sysrem1; +	} + +	return 0; + +error_sysrem1: +	if (data->type == max1668 || data->type == max1989) +		sysfs_remove_group(&client->dev.kobj, &max1668_group_unique); +error_sysrem0: +	sysfs_remove_group(&client->dev.kobj, &max1668_group_common); +error_free: +	kfree(data); +	return err; +} + +static int max1668_remove(struct i2c_client *client) +{ +	struct max1668_data *data = i2c_get_clientdata(client); + +	hwmon_device_unregister(data->hwmon_dev); +	if (data->type == max1668 || data->type == max1989) +		sysfs_remove_group(&client->dev.kobj, &max1668_group_unique); + +	sysfs_remove_group(&client->dev.kobj, &max1668_group_common); + +	kfree(data); +	return 0; +} + +static const struct i2c_device_id max1668_id[] = { +	{ "max1668", max1668 }, +	{ "max1805", max1805 }, +	{ "max1989", max1989 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, max1668_id); + +/* This is the driver that will be inserted */ +static struct i2c_driver max1668_driver = { +	.class = I2C_CLASS_HWMON, +	.driver = { +		  .name	= "max1668", +		  }, +	.probe = max1668_probe, +	.remove	= max1668_remove, +	.id_table = max1668_id, +	.detect	= max1668_detect, +	.address_list = max1668_addr_list, +}; + +static int __init sensors_max1668_init(void) +{ +	return i2c_add_driver(&max1668_driver); +} + +static void __exit sensors_max1668_exit(void) +{ +	i2c_del_driver(&max1668_driver); +} + +MODULE_AUTHOR("David George <david.george@ska.ac.za>"); +MODULE_DESCRIPTION("MAX1668 remote temperature sensor driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_max1668_init) +module_exit(sensors_max1668_exit) diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c new file mode 100644 index 000000000000..d7926f4336b5 --- /dev/null +++ b/drivers/hwmon/ntc_thermistor.c @@ -0,0 +1,453 @@ +/* + * ntc_thermistor.c - NTC Thermistors + * + *  Copyright (C) 2010 Samsung Electronics + *  MyungJoo Ham <myungjoo.ham@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + */ + +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/math64.h> +#include <linux/platform_device.h> +#include <linux/err.h> + +#include <linux/platform_data/ntc_thermistor.h> + +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> + +struct ntc_compensation { +	int		temp_C; +	unsigned int	ohm; +}; + +/* + * A compensation table should be sorted by the values of .ohm + * in descending order. + * The following compensation tables are from the specification of Murata NTC + * Thermistors Datasheet + */ +const struct ntc_compensation ncpXXwb473[] = { +	{ .temp_C	= -40, .ohm	= 1747920 }, +	{ .temp_C	= -35, .ohm	= 1245428 }, +	{ .temp_C	= -30, .ohm	= 898485 }, +	{ .temp_C	= -25, .ohm	= 655802 }, +	{ .temp_C	= -20, .ohm	= 483954 }, +	{ .temp_C	= -15, .ohm	= 360850 }, +	{ .temp_C	= -10, .ohm	= 271697 }, +	{ .temp_C	= -5, .ohm	= 206463 }, +	{ .temp_C	= 0, .ohm	= 158214 }, +	{ .temp_C	= 5, .ohm	= 122259 }, +	{ .temp_C	= 10, .ohm	= 95227 }, +	{ .temp_C	= 15, .ohm	= 74730 }, +	{ .temp_C	= 20, .ohm	= 59065 }, +	{ .temp_C	= 25, .ohm	= 47000 }, +	{ .temp_C	= 30, .ohm	= 37643 }, +	{ .temp_C	= 35, .ohm	= 30334 }, +	{ .temp_C	= 40, .ohm	= 24591 }, +	{ .temp_C	= 45, .ohm	= 20048 }, +	{ .temp_C	= 50, .ohm	= 16433 }, +	{ .temp_C	= 55, .ohm	= 13539 }, +	{ .temp_C	= 60, .ohm	= 11209 }, +	{ .temp_C	= 65, .ohm	= 9328 }, +	{ .temp_C	= 70, .ohm	= 7798 }, +	{ .temp_C	= 75, .ohm	= 6544 }, +	{ .temp_C	= 80, .ohm	= 5518 }, +	{ .temp_C	= 85, .ohm	= 4674 }, +	{ .temp_C	= 90, .ohm	= 3972 }, +	{ .temp_C	= 95, .ohm	= 3388 }, +	{ .temp_C	= 100, .ohm	= 2902 }, +	{ .temp_C	= 105, .ohm	= 2494 }, +	{ .temp_C	= 110, .ohm	= 2150 }, +	{ .temp_C	= 115, .ohm	= 1860 }, +	{ .temp_C	= 120, .ohm	= 1615 }, +	{ .temp_C	= 125, .ohm	= 1406 }, +}; +const struct ntc_compensation ncpXXwl333[] = { +	{ .temp_C	= -40, .ohm	= 1610154 }, +	{ .temp_C	= -35, .ohm	= 1130850 }, +	{ .temp_C	= -30, .ohm	= 802609 }, +	{ .temp_C	= -25, .ohm	= 575385 }, +	{ .temp_C	= -20, .ohm	= 416464 }, +	{ .temp_C	= -15, .ohm	= 304219 }, +	{ .temp_C	= -10, .ohm	= 224193 }, +	{ .temp_C	= -5, .ohm	= 166623 }, +	{ .temp_C	= 0, .ohm	= 124850 }, +	{ .temp_C	= 5, .ohm	= 94287 }, +	{ .temp_C	= 10, .ohm	= 71747 }, +	{ .temp_C	= 15, .ohm	= 54996 }, +	{ .temp_C	= 20, .ohm	= 42455 }, +	{ .temp_C	= 25, .ohm	= 33000 }, +	{ .temp_C	= 30, .ohm	= 25822 }, +	{ .temp_C	= 35, .ohm	= 20335 }, +	{ .temp_C	= 40, .ohm	= 16115 }, +	{ .temp_C	= 45, .ohm	= 12849 }, +	{ .temp_C	= 50, .ohm	= 10306 }, +	{ .temp_C	= 55, .ohm	= 8314 }, +	{ .temp_C	= 60, .ohm	= 6746 }, +	{ .temp_C	= 65, .ohm	= 5503 }, +	{ .temp_C	= 70, .ohm	= 4513 }, +	{ .temp_C	= 75, .ohm	= 3721 }, +	{ .temp_C	= 80, .ohm	= 3084 }, +	{ .temp_C	= 85, .ohm	= 2569 }, +	{ .temp_C	= 90, .ohm	= 2151 }, +	{ .temp_C	= 95, .ohm	= 1809 }, +	{ .temp_C	= 100, .ohm	= 1529 }, +	{ .temp_C	= 105, .ohm	= 1299 }, +	{ .temp_C	= 110, .ohm	= 1108 }, +	{ .temp_C	= 115, .ohm	= 949 }, +	{ .temp_C	= 120, .ohm	= 817 }, +	{ .temp_C	= 125, .ohm	= 707 }, +}; + +struct ntc_data { +	struct device *hwmon_dev; +	struct ntc_thermistor_platform_data *pdata; +	const struct ntc_compensation *comp; +	struct device *dev; +	int n_comp; +	char name[PLATFORM_NAME_SIZE]; +}; + +static inline u64 div64_u64_safe(u64 dividend, u64 divisor) +{ +	if (divisor == 0 && dividend == 0) +		return 0; +	if (divisor == 0) +		return UINT_MAX; +	return div64_u64(dividend, divisor); +} + +static unsigned int get_ohm_of_thermistor(struct ntc_data *data, +		unsigned int uV) +{ +	struct ntc_thermistor_platform_data *pdata = data->pdata; +	u64 mV = uV / 1000; +	u64 pmV = pdata->pullup_uV / 1000; +	u64 N, puO, pdO; +	puO = pdata->pullup_ohm; +	pdO = pdata->pulldown_ohm; + +	if (mV == 0) { +		if (pdata->connect == NTC_CONNECTED_POSITIVE) +			return UINT_MAX; +		return 0; +	} +	if (mV >= pmV) +		return (pdata->connect == NTC_CONNECTED_POSITIVE) ? +			0 : UINT_MAX; + +	if (pdata->connect == NTC_CONNECTED_POSITIVE && puO == 0) +		N = div64_u64_safe(pdO * (pmV - mV), mV); +	else if (pdata->connect == NTC_CONNECTED_GROUND && pdO == 0) +		N = div64_u64_safe(puO * mV, pmV - mV); +	else if (pdata->connect == NTC_CONNECTED_POSITIVE) +		N = div64_u64_safe(pdO * puO * (pmV - mV), +				puO * mV - pdO * (pmV - mV)); +	else +		N = div64_u64_safe(pdO * puO * mV, pdO * (pmV - mV) - puO * mV); + +	return (unsigned int) N; +} + +static int lookup_comp(struct ntc_data *data, +		unsigned int ohm, int *i_low, int *i_high) +{ +	int start, end, mid = -1; + +	/* Do a binary search on compensation table */ +	start = 0; +	end = data->n_comp; + +	while (end > start) { +		mid = start + (end - start) / 2; +		if (data->comp[mid].ohm < ohm) +			end = mid; +		else if (data->comp[mid].ohm > ohm) +			start = mid + 1; +		else +			break; +	} + +	if (mid == 0) { +		if (data->comp[mid].ohm > ohm) { +			*i_high = mid; +			*i_low = mid + 1; +			return 0; +		} else { +			*i_low = mid; +			*i_high = -1; +			return -EINVAL; +		} +	} +	if (mid == (data->n_comp - 1)) { +		if (data->comp[mid].ohm <= ohm) { +			*i_low = mid; +			*i_high = mid - 1; +			return 0; +		} else { +			*i_low = -1; +			*i_high = mid; +			return -EINVAL; +		} +	} + +	if (data->comp[mid].ohm <= ohm) { +		*i_low = mid; +		*i_high = mid - 1; +	} +	if (data->comp[mid].ohm > ohm) { +		*i_low = mid + 1; +		*i_high = mid; +	} + +	return 0; +} + +static int get_temp_mC(struct ntc_data *data, unsigned int ohm, int *temp) +{ +	int low, high; +	int ret; + +	ret = lookup_comp(data, ohm, &low, &high); +	if (ret) { +		/* Unable to use linear approximation */ +		if (low != -1) +			*temp = data->comp[low].temp_C * 1000; +		else if (high != -1) +			*temp = data->comp[high].temp_C * 1000; +		else +			return ret; +	} else { +		*temp = data->comp[low].temp_C * 1000 + +			((data->comp[high].temp_C - data->comp[low].temp_C) * +			 1000 * ((int)ohm - (int)data->comp[low].ohm)) / +			((int)data->comp[high].ohm - (int)data->comp[low].ohm); +	} + +	return 0; +} + +static int ntc_thermistor_read(struct ntc_data *data, int *temp) +{ +	int ret; +	int read_ohm, read_uV; +	unsigned int ohm = 0; + +	if (data->pdata->read_ohm) { +		read_ohm = data->pdata->read_ohm(); +		if (read_ohm < 0) +			return read_ohm; +		ohm = (unsigned int)read_ohm; +	} + +	if (data->pdata->read_uV) { +		read_uV = data->pdata->read_uV(); +		if (read_uV < 0) +			return read_uV; +		ohm = get_ohm_of_thermistor(data, (unsigned int)read_uV); +	} + +	ret = get_temp_mC(data, ohm, temp); +	if (ret) { +		dev_dbg(data->dev, "Sensor reading function not available.\n"); +		return ret; +	} + +	return 0; +} + +static ssize_t ntc_show_name(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct ntc_data *data = dev_get_drvdata(dev); + +	return sprintf(buf, "%s\n", data->name); +} + +static ssize_t ntc_show_type(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	return sprintf(buf, "4\n"); +} + +static ssize_t ntc_show_temp(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct ntc_data *data = dev_get_drvdata(dev); +	int temp, ret; + +	ret = ntc_thermistor_read(data, &temp); +	if (ret) +		return ret; +	return sprintf(buf, "%d\n", temp); +} + +static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO, ntc_show_type, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ntc_show_temp, NULL, 0); +static DEVICE_ATTR(name, S_IRUGO, ntc_show_name, NULL); + +static struct attribute *ntc_attributes[] = { +	&dev_attr_name.attr, +	&sensor_dev_attr_temp1_type.dev_attr.attr, +	&sensor_dev_attr_temp1_input.dev_attr.attr, +	NULL, +}; + +static const struct attribute_group ntc_attr_group = { +	.attrs = ntc_attributes, +}; + +static int __devinit ntc_thermistor_probe(struct platform_device *pdev) +{ +	struct ntc_data *data; +	struct ntc_thermistor_platform_data *pdata = pdev->dev.platform_data; +	int ret = 0; + +	if (!pdata) { +		dev_err(&pdev->dev, "No platform init data supplied.\n"); +		return -ENODEV; +	} + +	/* Either one of the two is required. */ +	if (!pdata->read_uV && !pdata->read_ohm) { +		dev_err(&pdev->dev, "Both read_uV and read_ohm missing." +				"Need either one of the two.\n"); +		return -EINVAL; +	} + +	if (pdata->read_uV && pdata->read_ohm) { +		dev_warn(&pdev->dev, "Only one of read_uV and read_ohm " +				"is needed; ignoring read_uV.\n"); +		pdata->read_uV = NULL; +	} + +	if (pdata->read_uV && (pdata->pullup_uV == 0 || +				(pdata->pullup_ohm == 0 && pdata->connect == +				 NTC_CONNECTED_GROUND) || +				(pdata->pulldown_ohm == 0 && pdata->connect == +				 NTC_CONNECTED_POSITIVE) || +				(pdata->connect != NTC_CONNECTED_POSITIVE && +				 pdata->connect != NTC_CONNECTED_GROUND))) { +		dev_err(&pdev->dev, "Required data to use read_uV not " +				"supplied.\n"); +		return -EINVAL; +	} + +	data = kzalloc(sizeof(struct ntc_data), GFP_KERNEL); +	if (!data) +		return -ENOMEM; + +	data->dev = &pdev->dev; +	data->pdata = pdata; +	strncpy(data->name, pdev->id_entry->name, PLATFORM_NAME_SIZE); + +	switch (pdev->id_entry->driver_data) { +	case TYPE_NCPXXWB473: +		data->comp = ncpXXwb473; +		data->n_comp = ARRAY_SIZE(ncpXXwb473); +		break; +	case TYPE_NCPXXWL333: +		data->comp = ncpXXwl333; +		data->n_comp = ARRAY_SIZE(ncpXXwl333); +		break; +	default: +		dev_err(&pdev->dev, "Unknown device type: %lu(%s)\n", +				pdev->id_entry->driver_data, +				pdev->id_entry->name); +		ret = -EINVAL; +		goto err; +	} + +	platform_set_drvdata(pdev, data); + +	ret = sysfs_create_group(&data->dev->kobj, &ntc_attr_group); +	if (ret) { +		dev_err(data->dev, "unable to create sysfs files\n"); +		goto err; +	} + +	data->hwmon_dev = hwmon_device_register(data->dev); +	if (IS_ERR_OR_NULL(data->hwmon_dev)) { +		dev_err(data->dev, "unable to register as hwmon device.\n"); +		ret = -EINVAL; +		goto err_after_sysfs; +	} + +	dev_info(&pdev->dev, "Thermistor %s:%d (type: %s/%lu) successfully probed.\n", +			pdev->name, pdev->id, pdev->id_entry->name, +			pdev->id_entry->driver_data); +	return 0; +err_after_sysfs: +	sysfs_remove_group(&data->dev->kobj, &ntc_attr_group); +err: +	kfree(data); +	return ret; +} + +static int __devexit ntc_thermistor_remove(struct platform_device *pdev) +{ +	struct ntc_data *data = platform_get_drvdata(pdev); + +	hwmon_device_unregister(data->hwmon_dev); +	sysfs_remove_group(&data->dev->kobj, &ntc_attr_group); +	platform_set_drvdata(pdev, NULL); + +	kfree(data); + +	return 0; +} + +static const struct platform_device_id ntc_thermistor_id[] = { +	{ "ncp15wb473", TYPE_NCPXXWB473 }, +	{ "ncp18wb473", TYPE_NCPXXWB473 }, +	{ "ncp21wb473", TYPE_NCPXXWB473 }, +	{ "ncp03wb473", TYPE_NCPXXWB473 }, +	{ "ncp15wl333", TYPE_NCPXXWL333 }, +	{ }, +}; + +static struct platform_driver ntc_thermistor_driver = { +	.driver = { +		.name = "ntc-thermistor", +		.owner = THIS_MODULE, +	}, +	.probe = ntc_thermistor_probe, +	.remove = __devexit_p(ntc_thermistor_remove), +	.id_table = ntc_thermistor_id, +}; + +static int __init ntc_thermistor_init(void) +{ +	return platform_driver_register(&ntc_thermistor_driver); +} + +module_init(ntc_thermistor_init); + +static void __exit ntc_thermistor_cleanup(void) +{ +	platform_driver_unregister(&ntc_thermistor_driver); +} + +module_exit(ntc_thermistor_cleanup); + +MODULE_DESCRIPTION("NTC Thermistor Driver"); +MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ntc-thermistor"); diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig new file mode 100644 index 000000000000..c9237b9dcff2 --- /dev/null +++ b/drivers/hwmon/pmbus/Kconfig @@ -0,0 +1,100 @@ +# +# PMBus chip drivers configuration +# + +menuconfig PMBUS +	tristate "PMBus support" +	depends on I2C && EXPERIMENTAL +	default n +	help +	  Say yes here if you want to enable PMBus support. + +	  This driver can also be built as a module. If so, the module will +	  be called pmbus_core. + +if PMBUS + +config SENSORS_PMBUS +	tristate "Generic PMBus devices" +	default y +	help +	  If you say yes here you get hardware monitoring support for generic +	  PMBus devices, including but not limited to ADP4000, BMR450, BMR451, +	  BMR453, BMR454, LTC2978, NCP4200, and NCP4208. + +	  This driver can also be built as a module. If so, the module will +	  be called pmbus. + +config SENSORS_ADM1275 +	tristate "Analog Devices ADM1275" +	default n +	help +	  If you say yes here you get hardware monitoring support for Analog +	  Devices ADM1275 Hot-Swap Controller and Digital Power Monitor. + +	  This driver can also be built as a module. If so, the module will +	  be called adm1275. + +config SENSORS_LM25066 +	tristate "National Semiconductor LM25066 and compatibles" +	default n +	help +	  If you say yes here you get hardware monitoring support for National +	  Semiconductor LM25066, LM5064, and LM5066. + +	  This driver can also be built as a module. If so, the module will +	  be called lm25066. + +config SENSORS_MAX16064 +	tristate "Maxim MAX16064" +	default n +	help +	  If you say yes here you get hardware monitoring support for Maxim +	  MAX16064. + +	  This driver can also be built as a module. If so, the module will +	  be called max16064. + +config SENSORS_MAX34440 +	tristate "Maxim MAX34440/MAX34441" +	default n +	help +	  If you say yes here you get hardware monitoring support for Maxim +	  MAX34440 and MAX34441. + +	  This driver can also be built as a module. If so, the module will +	  be called max34440. + +config SENSORS_MAX8688 +	tristate "Maxim MAX8688" +	default n +	help +	  If you say yes here you get hardware monitoring support for Maxim +	  MAX8688. + +	  This driver can also be built as a module. If so, the module will +	  be called max8688. + +config SENSORS_UCD9000 +	tristate "TI UCD90120, UCD90124, UCD9090, UCD90910" +	default n +	help +	  If you say yes here you get hardware monitoring support for TI +	  UCD90120, UCD90124, UCD9090, UCD90910 Sequencer and System Health +	  Controllers. + +	  This driver can also be built as a module. If so, the module will +	  be called ucd9000. + +config SENSORS_UCD9200 +	tristate "TI UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, UCD9248" +	default n +	help +	  If you say yes here you get hardware monitoring support for TI +	  UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, and UCD9248 +	  Digital PWM System Controllers. + +	  This driver can also be built as a module. If so, the module will +	  be called ucd9200. + +endif # PMBUS diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile new file mode 100644 index 000000000000..623eedb1ed9a --- /dev/null +++ b/drivers/hwmon/pmbus/Makefile @@ -0,0 +1,13 @@ +# +# Makefile for PMBus chip drivers. +# + +obj-$(CONFIG_PMBUS)		+= pmbus_core.o +obj-$(CONFIG_SENSORS_PMBUS)	+= pmbus.o +obj-$(CONFIG_SENSORS_ADM1275)	+= adm1275.o +obj-$(CONFIG_SENSORS_LM25066)	+= lm25066.o +obj-$(CONFIG_SENSORS_MAX16064)	+= max16064.o +obj-$(CONFIG_SENSORS_MAX34440)	+= max34440.o +obj-$(CONFIG_SENSORS_MAX8688)	+= max8688.o +obj-$(CONFIG_SENSORS_UCD9000)	+= ucd9000.o +obj-$(CONFIG_SENSORS_UCD9200)	+= ucd9200.o diff --git a/drivers/hwmon/adm1275.c b/drivers/hwmon/pmbus/adm1275.c index c2ee2048ab91..c936e2782309 100644 --- a/drivers/hwmon/adm1275.c +++ b/drivers/hwmon/pmbus/adm1275.c @@ -23,15 +23,73 @@  #include <linux/i2c.h>  #include "pmbus.h" +#define ADM1275_PEAK_IOUT		0xd0 +#define ADM1275_PEAK_VIN		0xd1 +#define ADM1275_PEAK_VOUT		0xd2  #define ADM1275_PMON_CONFIG		0xd4  #define ADM1275_VIN_VOUT_SELECT		(1 << 6)  #define ADM1275_VRANGE			(1 << 5) +static int adm1275_read_word_data(struct i2c_client *client, int page, int reg) +{ +	int ret; + +	if (page) +		return -EINVAL; + +	switch (reg) { +	case PMBUS_VIRT_READ_IOUT_MAX: +		ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_IOUT); +		break; +	case PMBUS_VIRT_READ_VOUT_MAX: +		ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_VOUT); +		break; +	case PMBUS_VIRT_READ_VIN_MAX: +		ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_VIN); +		break; +	case PMBUS_VIRT_RESET_IOUT_HISTORY: +	case PMBUS_VIRT_RESET_VOUT_HISTORY: +	case PMBUS_VIRT_RESET_VIN_HISTORY: +		ret = 0; +		break; +	default: +		ret = -ENODATA; +		break; +	} +	return ret; +} + +static int adm1275_write_word_data(struct i2c_client *client, int page, int reg, +				   u16 word) +{ +	int ret; + +	if (page) +		return -EINVAL; + +	switch (reg) { +	case PMBUS_VIRT_RESET_IOUT_HISTORY: +		ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_IOUT, 0); +		break; +	case PMBUS_VIRT_RESET_VOUT_HISTORY: +		ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_VOUT, 0); +		break; +	case PMBUS_VIRT_RESET_VIN_HISTORY: +		ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_VIN, 0); +		break; +	default: +		ret = -ENODATA; +		break; +	} +	return ret; +} +  static int adm1275_probe(struct i2c_client *client,  			 const struct i2c_device_id *id)  {  	int config; +	int ret;  	struct pmbus_driver_info *info;  	if (!i2c_check_functionality(client->adapter, @@ -43,30 +101,35 @@ static int adm1275_probe(struct i2c_client *client,  		return -ENOMEM;  	config = i2c_smbus_read_byte_data(client, ADM1275_PMON_CONFIG); -	if (config < 0) -		return config; +	if (config < 0) { +		ret = config; +		goto err_mem; +	}  	info->pages = 1; -	info->direct[PSC_VOLTAGE_IN] = true; -	info->direct[PSC_VOLTAGE_OUT] = true; -	info->direct[PSC_CURRENT_OUT] = true; -	info->m[PSC_CURRENT_OUT] = 800; +	info->format[PSC_VOLTAGE_IN] = direct; +	info->format[PSC_VOLTAGE_OUT] = direct; +	info->format[PSC_CURRENT_OUT] = direct; +	info->m[PSC_CURRENT_OUT] = 807;  	info->b[PSC_CURRENT_OUT] = 20475;  	info->R[PSC_CURRENT_OUT] = -1;  	info->func[0] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; +	info->read_word_data = adm1275_read_word_data; +	info->write_word_data = adm1275_write_word_data; +  	if (config & ADM1275_VRANGE) { -		info->m[PSC_VOLTAGE_IN] = 19045; +		info->m[PSC_VOLTAGE_IN] = 19199;  		info->b[PSC_VOLTAGE_IN] = 0;  		info->R[PSC_VOLTAGE_IN] = -2; -		info->m[PSC_VOLTAGE_OUT] = 19045; +		info->m[PSC_VOLTAGE_OUT] = 19199;  		info->b[PSC_VOLTAGE_OUT] = 0;  		info->R[PSC_VOLTAGE_OUT] = -2;  	} else { -		info->m[PSC_VOLTAGE_IN] = 6666; +		info->m[PSC_VOLTAGE_IN] = 6720;  		info->b[PSC_VOLTAGE_IN] = 0;  		info->R[PSC_VOLTAGE_IN] = -1; -		info->m[PSC_VOLTAGE_OUT] = 6666; +		info->m[PSC_VOLTAGE_OUT] = 6720;  		info->b[PSC_VOLTAGE_OUT] = 0;  		info->R[PSC_VOLTAGE_OUT] = -1;  	} @@ -76,7 +139,14 @@ static int adm1275_probe(struct i2c_client *client,  	else  		info->func[0] |= PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT; -	return pmbus_do_probe(client, id, info); +	ret = pmbus_do_probe(client, id, info); +	if (ret) +		goto err_mem; +	return 0; + +err_mem: +	kfree(info); +	return ret;  }  static int adm1275_remove(struct i2c_client *client) diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c new file mode 100644 index 000000000000..d4bc114572de --- /dev/null +++ b/drivers/hwmon/pmbus/lm25066.c @@ -0,0 +1,340 @@ +/* + * Hardware monitoring driver for LM25066 / LM5064 / LM5066 + * + * Copyright (c) 2011 Ericsson AB. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include "pmbus.h" + +enum chips { lm25066, lm5064, lm5066 }; + +#define LM25066_READ_VAUX		0xd0 +#define LM25066_MFR_READ_IIN		0xd1 +#define LM25066_MFR_READ_PIN		0xd2 +#define LM25066_MFR_IIN_OC_WARN_LIMIT	0xd3 +#define LM25066_MFR_PIN_OP_WARN_LIMIT	0xd4 +#define LM25066_READ_PIN_PEAK		0xd5 +#define LM25066_CLEAR_PIN_PEAK		0xd6 +#define LM25066_DEVICE_SETUP		0xd9 +#define LM25066_READ_AVG_VIN		0xdc +#define LM25066_READ_AVG_VOUT		0xdd +#define LM25066_READ_AVG_IIN		0xde +#define LM25066_READ_AVG_PIN		0xdf + +#define LM25066_DEV_SETUP_CL		(1 << 4)	/* Current limit */ + +struct lm25066_data { +	int id; +	struct pmbus_driver_info info; +}; + +#define to_lm25066_data(x)  container_of(x, struct lm25066_data, info) + +static int lm25066_read_word_data(struct i2c_client *client, int page, int reg) +{ +	const struct pmbus_driver_info *info = pmbus_get_driver_info(client); +	const struct lm25066_data *data = to_lm25066_data(info); +	int ret; + +	if (page > 1) +		return -EINVAL; + +	/* Map READ_VAUX into READ_VOUT register on page 1 */ +	if (page == 1) { +		switch (reg) { +		case PMBUS_READ_VOUT: +			ret = pmbus_read_word_data(client, 0, +						   LM25066_READ_VAUX); +			if (ret < 0) +				break; +			/* Adjust returned value to match VOUT coefficients */ +			switch (data->id) { +			case lm25066: +				/* VOUT: 4.54 mV VAUX: 283.2 uV LSB */ +				ret = DIV_ROUND_CLOSEST(ret * 2832, 45400); +				break; +			case lm5064: +				/* VOUT: 4.53 mV VAUX: 700 uV LSB */ +				ret = DIV_ROUND_CLOSEST(ret * 70, 453); +				break; +			case lm5066: +				/* VOUT: 2.18 mV VAUX: 725 uV LSB */ +				ret = DIV_ROUND_CLOSEST(ret * 725, 2180); +				break; +			} +			break; +		default: +			/* No other valid registers on page 1 */ +			ret = -EINVAL; +			break; +		} +		goto done; +	} + +	switch (reg) { +	case PMBUS_READ_IIN: +		ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_IIN); +		break; +	case PMBUS_READ_PIN: +		ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_PIN); +		break; +	case PMBUS_IIN_OC_WARN_LIMIT: +		ret = pmbus_read_word_data(client, 0, +					   LM25066_MFR_IIN_OC_WARN_LIMIT); +		break; +	case PMBUS_PIN_OP_WARN_LIMIT: +		ret = pmbus_read_word_data(client, 0, +					   LM25066_MFR_PIN_OP_WARN_LIMIT); +		break; +	case PMBUS_VIRT_READ_VIN_AVG: +		ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_VIN); +		break; +	case PMBUS_VIRT_READ_VOUT_AVG: +		ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_VOUT); +		break; +	case PMBUS_VIRT_READ_IIN_AVG: +		ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_IIN); +		break; +	case PMBUS_VIRT_READ_PIN_AVG: +		ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_PIN); +		break; +	case PMBUS_VIRT_READ_PIN_MAX: +		ret = pmbus_read_word_data(client, 0, LM25066_READ_PIN_PEAK); +		break; +	case PMBUS_VIRT_RESET_PIN_HISTORY: +		ret = 0; +		break; +	default: +		ret = -ENODATA; +		break; +	} +done: +	return ret; +} + +static int lm25066_write_word_data(struct i2c_client *client, int page, int reg, +				   u16 word) +{ +	int ret; + +	if (page > 1) +		return -EINVAL; + +	switch (reg) { +	case PMBUS_IIN_OC_WARN_LIMIT: +		ret = pmbus_write_word_data(client, 0, +					    LM25066_MFR_IIN_OC_WARN_LIMIT, +					    word); +		break; +	case PMBUS_PIN_OP_WARN_LIMIT: +		ret = pmbus_write_word_data(client, 0, +					    LM25066_MFR_PIN_OP_WARN_LIMIT, +					    word); +		break; +	case PMBUS_VIRT_RESET_PIN_HISTORY: +		ret = pmbus_write_byte(client, 0, LM25066_CLEAR_PIN_PEAK); +		break; +	default: +		ret = -ENODATA; +		break; +	} +	return ret; +} + +static int lm25066_probe(struct i2c_client *client, +			  const struct i2c_device_id *id) +{ +	int config; +	int ret; +	struct lm25066_data *data; +	struct pmbus_driver_info *info; + +	if (!i2c_check_functionality(client->adapter, +				     I2C_FUNC_SMBUS_READ_BYTE_DATA)) +		return -ENODEV; + +	data = kzalloc(sizeof(struct lm25066_data), GFP_KERNEL); +	if (!data) +		return -ENOMEM; + +	config = i2c_smbus_read_byte_data(client, LM25066_DEVICE_SETUP); +	if (config < 0) { +		ret = config; +		goto err_mem; +	} + +	data->id = id->driver_data; +	info = &data->info; + +	info->pages = 2; +	info->format[PSC_VOLTAGE_IN] = direct; +	info->format[PSC_VOLTAGE_OUT] = direct; +	info->format[PSC_CURRENT_IN] = direct; +	info->format[PSC_TEMPERATURE] = direct; +	info->format[PSC_POWER] = direct; + +	info->m[PSC_TEMPERATURE] = 16; +	info->b[PSC_TEMPERATURE] = 0; +	info->R[PSC_TEMPERATURE] = 0; + +	info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT +	  | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_PIN | PMBUS_HAVE_IIN +	  | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; +	info->func[1] = PMBUS_HAVE_VOUT; + +	info->read_word_data = lm25066_read_word_data; +	info->write_word_data = lm25066_write_word_data; + +	switch (id->driver_data) { +	case lm25066: +		info->m[PSC_VOLTAGE_IN] = 22070; +		info->b[PSC_VOLTAGE_IN] = 0; +		info->R[PSC_VOLTAGE_IN] = -2; +		info->m[PSC_VOLTAGE_OUT] = 22070; +		info->b[PSC_VOLTAGE_OUT] = 0; +		info->R[PSC_VOLTAGE_OUT] = -2; + +		if (config & LM25066_DEV_SETUP_CL) { +			info->m[PSC_CURRENT_IN] = 6852; +			info->b[PSC_CURRENT_IN] = 0; +			info->R[PSC_CURRENT_IN] = -2; +			info->m[PSC_POWER] = 369; +			info->b[PSC_POWER] = 0; +			info->R[PSC_POWER] = -2; +		} else { +			info->m[PSC_CURRENT_IN] = 13661; +			info->b[PSC_CURRENT_IN] = 0; +			info->R[PSC_CURRENT_IN] = -2; +			info->m[PSC_POWER] = 736; +			info->b[PSC_POWER] = 0; +			info->R[PSC_POWER] = -2; +		} +		break; +	case lm5064: +		info->m[PSC_VOLTAGE_IN] = 22075; +		info->b[PSC_VOLTAGE_IN] = 0; +		info->R[PSC_VOLTAGE_IN] = -2; +		info->m[PSC_VOLTAGE_OUT] = 22075; +		info->b[PSC_VOLTAGE_OUT] = 0; +		info->R[PSC_VOLTAGE_OUT] = -2; + +		if (config & LM25066_DEV_SETUP_CL) { +			info->m[PSC_CURRENT_IN] = 6713; +			info->b[PSC_CURRENT_IN] = 0; +			info->R[PSC_CURRENT_IN] = -2; +			info->m[PSC_POWER] = 3619; +			info->b[PSC_POWER] = 0; +			info->R[PSC_POWER] = -3; +		} else { +			info->m[PSC_CURRENT_IN] = 13426; +			info->b[PSC_CURRENT_IN] = 0; +			info->R[PSC_CURRENT_IN] = -2; +			info->m[PSC_POWER] = 7238; +			info->b[PSC_POWER] = 0; +			info->R[PSC_POWER] = -3; +		} +		break; +	case lm5066: +		info->m[PSC_VOLTAGE_IN] = 4587; +		info->b[PSC_VOLTAGE_IN] = 0; +		info->R[PSC_VOLTAGE_IN] = -2; +		info->m[PSC_VOLTAGE_OUT] = 4587; +		info->b[PSC_VOLTAGE_OUT] = 0; +		info->R[PSC_VOLTAGE_OUT] = -2; + +		if (config & LM25066_DEV_SETUP_CL) { +			info->m[PSC_CURRENT_IN] = 10753; +			info->b[PSC_CURRENT_IN] = 0; +			info->R[PSC_CURRENT_IN] = -2; +			info->m[PSC_POWER] = 1204; +			info->b[PSC_POWER] = 0; +			info->R[PSC_POWER] = -3; +		} else { +			info->m[PSC_CURRENT_IN] = 5405; +			info->b[PSC_CURRENT_IN] = 0; +			info->R[PSC_CURRENT_IN] = -2; +			info->m[PSC_POWER] = 605; +			info->b[PSC_POWER] = 0; +			info->R[PSC_POWER] = -3; +		} +		break; +	default: +		ret = -ENODEV; +		goto err_mem; +	} + +	ret = pmbus_do_probe(client, id, info); +	if (ret) +		goto err_mem; +	return 0; + +err_mem: +	kfree(data); +	return ret; +} + +static int lm25066_remove(struct i2c_client *client) +{ +	const struct pmbus_driver_info *info = pmbus_get_driver_info(client); +	const struct lm25066_data *data = to_lm25066_data(info); +	int ret; + +	ret = pmbus_do_remove(client); +	kfree(data); +	return ret; +} + +static const struct i2c_device_id lm25066_id[] = { +	{"lm25066", lm25066}, +	{"lm5064", lm5064}, +	{"lm5066", lm5066}, +	{ } +}; + +MODULE_DEVICE_TABLE(i2c, lm25066_id); + +/* This is the driver that will be inserted */ +static struct i2c_driver lm25066_driver = { +	.driver = { +		   .name = "lm25066", +		   }, +	.probe = lm25066_probe, +	.remove = lm25066_remove, +	.id_table = lm25066_id, +}; + +static int __init lm25066_init(void) +{ +	return i2c_add_driver(&lm25066_driver); +} + +static void __exit lm25066_exit(void) +{ +	i2c_del_driver(&lm25066_driver); +} + +MODULE_AUTHOR("Guenter Roeck"); +MODULE_DESCRIPTION("PMBus driver for LM25066/LM5064/LM5066"); +MODULE_LICENSE("GPL"); +module_init(lm25066_init); +module_exit(lm25066_exit); diff --git a/drivers/hwmon/max16064.c b/drivers/hwmon/pmbus/max16064.c index 1d6d717060d3..e50b296e8db4 100644 --- a/drivers/hwmon/max16064.c +++ b/drivers/hwmon/pmbus/max16064.c @@ -25,11 +25,60 @@  #include <linux/i2c.h>  #include "pmbus.h" +#define MAX16064_MFR_VOUT_PEAK		0xd4 +#define MAX16064_MFR_TEMPERATURE_PEAK	0xd6 + +static int max16064_read_word_data(struct i2c_client *client, int page, int reg) +{ +	int ret; + +	switch (reg) { +	case PMBUS_VIRT_READ_VOUT_MAX: +		ret = pmbus_read_word_data(client, page, +					   MAX16064_MFR_VOUT_PEAK); +		break; +	case PMBUS_VIRT_READ_TEMP_MAX: +		ret = pmbus_read_word_data(client, page, +					   MAX16064_MFR_TEMPERATURE_PEAK); +		break; +	case PMBUS_VIRT_RESET_VOUT_HISTORY: +	case PMBUS_VIRT_RESET_TEMP_HISTORY: +		ret = 0; +		break; +	default: +		ret = -ENODATA; +		break; +	} +	return ret; +} + +static int max16064_write_word_data(struct i2c_client *client, int page, +				    int reg, u16 word) +{ +	int ret; + +	switch (reg) { +	case PMBUS_VIRT_RESET_VOUT_HISTORY: +		ret = pmbus_write_word_data(client, page, +					    MAX16064_MFR_VOUT_PEAK, 0); +		break; +	case PMBUS_VIRT_RESET_TEMP_HISTORY: +		ret = pmbus_write_word_data(client, page, +					    MAX16064_MFR_TEMPERATURE_PEAK, +					    0xffff); +		break; +	default: +		ret = -ENODATA; +		break; +	} +	return ret; +} +  static struct pmbus_driver_info max16064_info = {  	.pages = 4, -	.direct[PSC_VOLTAGE_IN] = true, -	.direct[PSC_VOLTAGE_OUT] = true, -	.direct[PSC_TEMPERATURE] = true, +	.format[PSC_VOLTAGE_IN] = direct, +	.format[PSC_VOLTAGE_OUT] = direct, +	.format[PSC_TEMPERATURE] = direct,  	.m[PSC_VOLTAGE_IN] = 19995,  	.b[PSC_VOLTAGE_IN] = 0,  	.R[PSC_VOLTAGE_IN] = -1, @@ -44,6 +93,8 @@ static struct pmbus_driver_info max16064_info = {  	.func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,  	.func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,  	.func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, +	.read_word_data = max16064_read_word_data, +	.write_word_data = max16064_write_word_data,  };  static int max16064_probe(struct i2c_client *client, diff --git a/drivers/hwmon/max34440.c b/drivers/hwmon/pmbus/max34440.c index db11e1a175b2..fda621d2e458 100644 --- a/drivers/hwmon/max34440.c +++ b/drivers/hwmon/pmbus/max34440.c @@ -27,11 +27,70 @@  enum chips { max34440, max34441 }; +#define MAX34440_MFR_VOUT_PEAK		0xd4 +#define MAX34440_MFR_IOUT_PEAK		0xd5 +#define MAX34440_MFR_TEMPERATURE_PEAK	0xd6 +  #define MAX34440_STATUS_OC_WARN		(1 << 0)  #define MAX34440_STATUS_OC_FAULT	(1 << 1)  #define MAX34440_STATUS_OT_FAULT	(1 << 5)  #define MAX34440_STATUS_OT_WARN		(1 << 6) +static int max34440_read_word_data(struct i2c_client *client, int page, int reg) +{ +	int ret; + +	switch (reg) { +	case PMBUS_VIRT_READ_VOUT_MAX: +		ret = pmbus_read_word_data(client, page, +					   MAX34440_MFR_VOUT_PEAK); +		break; +	case PMBUS_VIRT_READ_IOUT_MAX: +		ret = pmbus_read_word_data(client, page, +					   MAX34440_MFR_IOUT_PEAK); +		break; +	case PMBUS_VIRT_READ_TEMP_MAX: +		ret = pmbus_read_word_data(client, page, +					   MAX34440_MFR_TEMPERATURE_PEAK); +		break; +	case PMBUS_VIRT_RESET_VOUT_HISTORY: +	case PMBUS_VIRT_RESET_IOUT_HISTORY: +	case PMBUS_VIRT_RESET_TEMP_HISTORY: +		ret = 0; +		break; +	default: +		ret = -ENODATA; +		break; +	} +	return ret; +} + +static int max34440_write_word_data(struct i2c_client *client, int page, +				    int reg, u16 word) +{ +	int ret; + +	switch (reg) { +	case PMBUS_VIRT_RESET_VOUT_HISTORY: +		ret = pmbus_write_word_data(client, page, +					    MAX34440_MFR_VOUT_PEAK, 0); +		break; +	case PMBUS_VIRT_RESET_IOUT_HISTORY: +		ret = pmbus_write_word_data(client, page, +					    MAX34440_MFR_IOUT_PEAK, 0); +		break; +	case PMBUS_VIRT_RESET_TEMP_HISTORY: +		ret = pmbus_write_word_data(client, page, +					    MAX34440_MFR_TEMPERATURE_PEAK, +					    0xffff); +		break; +	default: +		ret = -ENODATA; +		break; +	} +	return ret; +} +  static int max34440_read_byte_data(struct i2c_client *client, int page, int reg)  {  	int ret; @@ -72,10 +131,10 @@ static int max34440_read_byte_data(struct i2c_client *client, int page, int reg)  static struct pmbus_driver_info max34440_info[] = {  	[max34440] = {  		.pages = 14, -		.direct[PSC_VOLTAGE_IN] = true, -		.direct[PSC_VOLTAGE_OUT] = true, -		.direct[PSC_TEMPERATURE] = true, -		.direct[PSC_CURRENT_OUT] = true, +		.format[PSC_VOLTAGE_IN] = direct, +		.format[PSC_VOLTAGE_OUT] = direct, +		.format[PSC_TEMPERATURE] = direct, +		.format[PSC_CURRENT_OUT] = direct,  		.m[PSC_VOLTAGE_IN] = 1,  		.b[PSC_VOLTAGE_IN] = 0,  		.R[PSC_VOLTAGE_IN] = 3,	    /* R = 0 in datasheet reflects mV */ @@ -109,14 +168,16 @@ static struct pmbus_driver_info max34440_info[] = {  		.func[12] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,  		.func[13] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,  		.read_byte_data = max34440_read_byte_data, +		.read_word_data = max34440_read_word_data, +		.write_word_data = max34440_write_word_data,  	},  	[max34441] = {  		.pages = 12, -		.direct[PSC_VOLTAGE_IN] = true, -		.direct[PSC_VOLTAGE_OUT] = true, -		.direct[PSC_TEMPERATURE] = true, -		.direct[PSC_CURRENT_OUT] = true, -		.direct[PSC_FAN] = true, +		.format[PSC_VOLTAGE_IN] = direct, +		.format[PSC_VOLTAGE_OUT] = direct, +		.format[PSC_TEMPERATURE] = direct, +		.format[PSC_CURRENT_OUT] = direct, +		.format[PSC_FAN] = direct,  		.m[PSC_VOLTAGE_IN] = 1,  		.b[PSC_VOLTAGE_IN] = 0,  		.R[PSC_VOLTAGE_IN] = 3, @@ -150,6 +211,8 @@ static struct pmbus_driver_info max34440_info[] = {  		.func[10] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,  		.func[11] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,  		.read_byte_data = max34440_read_byte_data, +		.read_word_data = max34440_read_word_data, +		.write_word_data = max34440_write_word_data,  	},  }; diff --git a/drivers/hwmon/max8688.c b/drivers/hwmon/pmbus/max8688.c index 7fb93f4e9f21..c3e72f1a3cfb 100644 --- a/drivers/hwmon/max8688.c +++ b/drivers/hwmon/pmbus/max8688.c @@ -25,6 +25,9 @@  #include <linux/i2c.h>  #include "pmbus.h" +#define MAX8688_MFR_VOUT_PEAK		0xd4 +#define MAX8688_MFR_IOUT_PEAK		0xd5 +#define MAX8688_MFR_TEMPERATURE_PEAK	0xd6  #define MAX8688_MFG_STATUS		0xd8  #define MAX8688_STATUS_OC_FAULT		(1 << 4) @@ -37,6 +40,62 @@  #define MAX8688_STATUS_OT_FAULT		(1 << 13)  #define MAX8688_STATUS_OT_WARNING	(1 << 14) +static int max8688_read_word_data(struct i2c_client *client, int page, int reg) +{ +	int ret; + +	if (page) +		return -EINVAL; + +	switch (reg) { +	case PMBUS_VIRT_READ_VOUT_MAX: +		ret = pmbus_read_word_data(client, 0, MAX8688_MFR_VOUT_PEAK); +		break; +	case PMBUS_VIRT_READ_IOUT_MAX: +		ret = pmbus_read_word_data(client, 0, MAX8688_MFR_IOUT_PEAK); +		break; +	case PMBUS_VIRT_READ_TEMP_MAX: +		ret = pmbus_read_word_data(client, 0, +					   MAX8688_MFR_TEMPERATURE_PEAK); +		break; +	case PMBUS_VIRT_RESET_VOUT_HISTORY: +	case PMBUS_VIRT_RESET_IOUT_HISTORY: +	case PMBUS_VIRT_RESET_TEMP_HISTORY: +		ret = 0; +		break; +	default: +		ret = -ENODATA; +		break; +	} +	return ret; +} + +static int max8688_write_word_data(struct i2c_client *client, int page, int reg, +				   u16 word) +{ +	int ret; + +	switch (reg) { +	case PMBUS_VIRT_RESET_VOUT_HISTORY: +		ret = pmbus_write_word_data(client, 0, MAX8688_MFR_VOUT_PEAK, +					    0); +		break; +	case PMBUS_VIRT_RESET_IOUT_HISTORY: +		ret = pmbus_write_word_data(client, 0, MAX8688_MFR_IOUT_PEAK, +					    0); +		break; +	case PMBUS_VIRT_RESET_TEMP_HISTORY: +		ret = pmbus_write_word_data(client, 0, +					    MAX8688_MFR_TEMPERATURE_PEAK, +					    0xffff); +		break; +	default: +		ret = -ENODATA; +		break; +	} +	return ret; +} +  static int max8688_read_byte_data(struct i2c_client *client, int page, int reg)  {  	int ret = 0; @@ -91,10 +150,10 @@ static int max8688_read_byte_data(struct i2c_client *client, int page, int reg)  static struct pmbus_driver_info max8688_info = {  	.pages = 1, -	.direct[PSC_VOLTAGE_IN] = true, -	.direct[PSC_VOLTAGE_OUT] = true, -	.direct[PSC_TEMPERATURE] = true, -	.direct[PSC_CURRENT_OUT] = true, +	.format[PSC_VOLTAGE_IN] = direct, +	.format[PSC_VOLTAGE_OUT] = direct, +	.format[PSC_TEMPERATURE] = direct, +	.format[PSC_CURRENT_OUT] = direct,  	.m[PSC_VOLTAGE_IN] = 19995,  	.b[PSC_VOLTAGE_IN] = 0,  	.R[PSC_VOLTAGE_IN] = -1, @@ -111,6 +170,8 @@ static struct pmbus_driver_info max8688_info = {  		| PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT  		| PMBUS_HAVE_STATUS_TEMP,  	.read_byte_data = max8688_read_byte_data, +	.read_word_data = max8688_read_word_data, +	.write_word_data = max8688_write_word_data,  };  static int max8688_probe(struct i2c_client *client, diff --git a/drivers/hwmon/pmbus.c b/drivers/hwmon/pmbus/pmbus.c index 98e2e28899e2..73de9f1f3194 100644 --- a/drivers/hwmon/pmbus.c +++ b/drivers/hwmon/pmbus/pmbus.c @@ -47,22 +47,29 @@ static void pmbus_find_sensor_groups(struct i2c_client *client,  	if (info->func[0]  	    && pmbus_check_byte_register(client, 0, PMBUS_STATUS_INPUT))  		info->func[0] |= PMBUS_HAVE_STATUS_INPUT; -	if (pmbus_check_word_register(client, 0, PMBUS_READ_FAN_SPEED_1)) { +	if (pmbus_check_byte_register(client, 0, PMBUS_FAN_CONFIG_12) && +	    pmbus_check_word_register(client, 0, PMBUS_READ_FAN_SPEED_1)) {  		info->func[0] |= PMBUS_HAVE_FAN12;  		if (pmbus_check_byte_register(client, 0, PMBUS_STATUS_FAN_12))  			info->func[0] |= PMBUS_HAVE_STATUS_FAN12;  	} -	if (pmbus_check_word_register(client, 0, PMBUS_READ_FAN_SPEED_3)) { +	if (pmbus_check_byte_register(client, 0, PMBUS_FAN_CONFIG_34) && +	    pmbus_check_word_register(client, 0, PMBUS_READ_FAN_SPEED_3)) {  		info->func[0] |= PMBUS_HAVE_FAN34;  		if (pmbus_check_byte_register(client, 0, PMBUS_STATUS_FAN_34))  			info->func[0] |= PMBUS_HAVE_STATUS_FAN34;  	} -	if (pmbus_check_word_register(client, 0, PMBUS_READ_TEMPERATURE_1)) { +	if (pmbus_check_word_register(client, 0, PMBUS_READ_TEMPERATURE_1))  		info->func[0] |= PMBUS_HAVE_TEMP; -		if (pmbus_check_byte_register(client, 0, -					      PMBUS_STATUS_TEMPERATURE)) +	if (pmbus_check_word_register(client, 0, PMBUS_READ_TEMPERATURE_2)) +		info->func[0] |= PMBUS_HAVE_TEMP2; +	if (pmbus_check_word_register(client, 0, PMBUS_READ_TEMPERATURE_3)) +		info->func[0] |= PMBUS_HAVE_TEMP3; +	if (info->func[0] & (PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 +			     | PMBUS_HAVE_TEMP3) +	    && pmbus_check_byte_register(client, 0, +					 PMBUS_STATUS_TEMPERATURE))  			info->func[0] |= PMBUS_HAVE_STATUS_TEMP; -	}  	/* Sensors detected on all pages */  	for (page = 0; page < info->pages; page++) { @@ -89,6 +96,8 @@ static void pmbus_find_sensor_groups(struct i2c_client *client,  static int pmbus_identify(struct i2c_client *client,  			  struct pmbus_driver_info *info)  { +	int ret = 0; +  	if (!info->pages) {  		/*  		 * Check if the PAGE command is supported. If it is, @@ -110,6 +119,27 @@ static int pmbus_identify(struct i2c_client *client,  		}  	} +	if (pmbus_check_byte_register(client, 0, PMBUS_VOUT_MODE)) { +		int vout_mode; + +		vout_mode = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE); +		if (vout_mode >= 0 && vout_mode != 0xff) { +			switch (vout_mode >> 5) { +			case 0: +				break; +			case 1: +				info->format[PSC_VOLTAGE_OUT] = vid; +				break; +			case 2: +				info->format[PSC_VOLTAGE_OUT] = direct; +				break; +			default: +				ret = -ENODEV; +				goto abort; +			} +		} +	} +  	/*  	 * We should check if the COEFFICIENTS register is supported.  	 * If it is, and the chip is configured for direct mode, we can read @@ -118,13 +148,18 @@ static int pmbus_identify(struct i2c_client *client,  	 *  	 * To do this, we will need access to a chip which actually supports the  	 * COEFFICIENTS command, since the command is too complex to implement -	 * without testing it. +	 * without testing it. Until then, abort if a chip configured for direct +	 * mode was detected.  	 */ +	if (info->format[PSC_VOLTAGE_OUT] == direct) { +		ret = -ENODEV; +		goto abort; +	}  	/* Try to find sensor groups  */  	pmbus_find_sensor_groups(client, info); - -	return 0; +abort: +	return ret;  }  static int pmbus_probe(struct i2c_client *client, @@ -165,11 +200,14 @@ static int pmbus_remove(struct i2c_client *client)   * Use driver_data to set the number of pages supported by the chip.   */  static const struct i2c_device_id pmbus_id[] = { +	{"adp4000", 1},  	{"bmr450", 1},  	{"bmr451", 1},  	{"bmr453", 1},  	{"bmr454", 1},  	{"ltc2978", 8}, +	{"ncp4200", 1}, +	{"ncp4208", 1},  	{"pmbus", 0},  	{}  }; diff --git a/drivers/hwmon/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index 50647ab7235a..0808d986d75b 100644 --- a/drivers/hwmon/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -126,6 +126,42 @@  #define PMBUS_MFR_SERIAL		0x9E  /* + * Virtual registers. + * Useful to support attributes which are not supported by standard PMBus + * registers but exist as manufacturer specific registers on individual chips. + * Must be mapped to real registers in device specific code. + * + * Semantics: + * Virtual registers are all word size. + * READ registers are read-only; writes are either ignored or return an error. + * RESET registers are read/write. Reading returns zero (used for detection), + * writing any value causes the associated history to be reset. + */ +#define PMBUS_VIRT_BASE			0x100 +#define PMBUS_VIRT_READ_TEMP_MIN	(PMBUS_VIRT_BASE + 0) +#define PMBUS_VIRT_READ_TEMP_MAX	(PMBUS_VIRT_BASE + 1) +#define PMBUS_VIRT_RESET_TEMP_HISTORY	(PMBUS_VIRT_BASE + 2) +#define PMBUS_VIRT_READ_VIN_AVG		(PMBUS_VIRT_BASE + 3) +#define PMBUS_VIRT_READ_VIN_MIN		(PMBUS_VIRT_BASE + 4) +#define PMBUS_VIRT_READ_VIN_MAX		(PMBUS_VIRT_BASE + 5) +#define PMBUS_VIRT_RESET_VIN_HISTORY	(PMBUS_VIRT_BASE + 6) +#define PMBUS_VIRT_READ_IIN_AVG		(PMBUS_VIRT_BASE + 7) +#define PMBUS_VIRT_READ_IIN_MIN		(PMBUS_VIRT_BASE + 8) +#define PMBUS_VIRT_READ_IIN_MAX		(PMBUS_VIRT_BASE + 9) +#define PMBUS_VIRT_RESET_IIN_HISTORY	(PMBUS_VIRT_BASE + 10) +#define PMBUS_VIRT_READ_PIN_AVG		(PMBUS_VIRT_BASE + 11) +#define PMBUS_VIRT_READ_PIN_MAX		(PMBUS_VIRT_BASE + 12) +#define PMBUS_VIRT_RESET_PIN_HISTORY	(PMBUS_VIRT_BASE + 13) +#define PMBUS_VIRT_READ_VOUT_AVG	(PMBUS_VIRT_BASE + 14) +#define PMBUS_VIRT_READ_VOUT_MIN	(PMBUS_VIRT_BASE + 15) +#define PMBUS_VIRT_READ_VOUT_MAX	(PMBUS_VIRT_BASE + 16) +#define PMBUS_VIRT_RESET_VOUT_HISTORY	(PMBUS_VIRT_BASE + 17) +#define PMBUS_VIRT_READ_IOUT_AVG	(PMBUS_VIRT_BASE + 18) +#define PMBUS_VIRT_READ_IOUT_MIN	(PMBUS_VIRT_BASE + 19) +#define PMBUS_VIRT_READ_IOUT_MAX	(PMBUS_VIRT_BASE + 20) +#define PMBUS_VIRT_RESET_IOUT_HISTORY	(PMBUS_VIRT_BASE + 21) + +/*   * CAPABILITY   */  #define PB_CAPABILITY_SMBALERT		(1<<4) @@ -266,11 +302,11 @@ enum pmbus_sensor_classes {  #define PMBUS_HAVE_STATUS_FAN12	(1 << 16)  #define PMBUS_HAVE_STATUS_FAN34	(1 << 17) +enum pmbus_data_format { linear = 0, direct, vid }; +  struct pmbus_driver_info {  	int pages;		/* Total number of pages */ -	bool direct[PSC_NUM_CLASSES]; -				/* true if device uses direct data format -				   for the given sensor class */ +	enum pmbus_data_format format[PSC_NUM_CLASSES];  	/*  	 * Support one set of coefficients for each sensor type  	 * Used for chips providing data in direct mode. @@ -286,6 +322,9 @@ struct pmbus_driver_info {  	 * necessary.  	 */  	int (*read_byte_data)(struct i2c_client *client, int page, int reg); +	int (*read_word_data)(struct i2c_client *client, int page, int reg); +	int (*write_word_data)(struct i2c_client *client, int page, int reg, +			       u16 word);  	/*  	 * The identify function determines supported PMBus functionality.  	 * This function is only necessary if a chip driver supports multiple @@ -299,6 +338,9 @@ struct pmbus_driver_info {  int pmbus_set_page(struct i2c_client *client, u8 page);  int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg); +int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word); +int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg); +int pmbus_write_byte(struct i2c_client *client, int page, u8 value);  void pmbus_clear_faults(struct i2c_client *client);  bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg);  bool pmbus_check_word_register(struct i2c_client *client, int page, int reg); diff --git a/drivers/hwmon/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 354770ed3186..5c1b6cf31701 100644 --- a/drivers/hwmon/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -33,14 +33,18 @@  /*   * Constants needed to determine number of sensors, booleans, and labels.   */ -#define PMBUS_MAX_INPUT_SENSORS		11	/* 6*volt, 3*curr, 2*power */ -#define PMBUS_VOUT_SENSORS_PER_PAGE	5	/* input, min, max, lcrit, -						   crit */ -#define PMBUS_IOUT_SENSORS_PER_PAGE	4	/* input, min, max, crit */ +#define PMBUS_MAX_INPUT_SENSORS		22	/* 10*volt, 7*curr, 5*power */ +#define PMBUS_VOUT_SENSORS_PER_PAGE	9	/* input, min, max, lcrit, +						   crit, lowest, highest, avg, +						   reset */ +#define PMBUS_IOUT_SENSORS_PER_PAGE	8	/* input, min, max, crit, +						   lowest, highest, avg, +						   reset */  #define PMBUS_POUT_SENSORS_PER_PAGE	4	/* input, cap, max, crit */  #define PMBUS_MAX_SENSORS_PER_FAN	1	/* input */ -#define PMBUS_MAX_SENSORS_PER_TEMP	5	/* input, min, max, lcrit, -						   crit */ +#define PMBUS_MAX_SENSORS_PER_TEMP	8	/* input, min, max, lcrit, +						   crit, lowest, highest, +						   reset */  #define PMBUS_MAX_INPUT_BOOLEANS	7	/* v: min_alarm, max_alarm,  						   lcrit_alarm, crit_alarm; @@ -74,11 +78,13 @@  #define PB_STATUS_INPUT_BASE	(PB_STATUS_FAN34_BASE + PMBUS_PAGES)  #define PB_STATUS_TEMP_BASE	(PB_STATUS_INPUT_BASE + 1) +#define PMBUS_NAME_SIZE		24 +  struct pmbus_sensor { -	char name[I2C_NAME_SIZE];	/* sysfs sensor name */ +	char name[PMBUS_NAME_SIZE];	/* sysfs sensor name */  	struct sensor_device_attribute attribute;  	u8 page;		/* page number */ -	u8 reg;			/* register */ +	u16 reg;		/* register */  	enum pmbus_sensor_classes class;	/* sensor class */  	bool update;		/* runtime sensor update needed */  	int data;		/* Sensor data. @@ -86,14 +92,14 @@ struct pmbus_sensor {  };  struct pmbus_boolean { -	char name[I2C_NAME_SIZE];	/* sysfs boolean name */ +	char name[PMBUS_NAME_SIZE];	/* sysfs boolean name */  	struct sensor_device_attribute attribute;  };  struct pmbus_label { -	char name[I2C_NAME_SIZE];	/* sysfs label name */ +	char name[PMBUS_NAME_SIZE];	/* sysfs label name */  	struct sensor_device_attribute attribute; -	char label[I2C_NAME_SIZE];	/* label */ +	char label[PMBUS_NAME_SIZE];	/* label */  };  struct pmbus_data { @@ -162,19 +168,21 @@ int pmbus_set_page(struct i2c_client *client, u8 page)  }  EXPORT_SYMBOL_GPL(pmbus_set_page); -static int pmbus_write_byte(struct i2c_client *client, u8 page, u8 value) +int pmbus_write_byte(struct i2c_client *client, int page, u8 value)  {  	int rv; -	rv = pmbus_set_page(client, page); -	if (rv < 0) -		return rv; +	if (page >= 0) { +		rv = pmbus_set_page(client, page); +		if (rv < 0) +			return rv; +	}  	return i2c_smbus_write_byte(client, value);  } +EXPORT_SYMBOL_GPL(pmbus_write_byte); -static int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, -				 u16 word) +int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word)  {  	int rv; @@ -184,6 +192,28 @@ static int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg,  	return i2c_smbus_write_word_data(client, reg, word);  } +EXPORT_SYMBOL_GPL(pmbus_write_word_data); + +/* + * _pmbus_write_word_data() is similar to pmbus_write_word_data(), but checks if + * a device specific mapping function exists and calls it if necessary. + */ +static int _pmbus_write_word_data(struct i2c_client *client, int page, int reg, +				  u16 word) +{ +	struct pmbus_data *data = i2c_get_clientdata(client); +	const struct pmbus_driver_info *info = data->info; +	int status; + +	if (info->write_word_data) { +		status = info->write_word_data(client, page, reg, word); +		if (status != -ENODATA) +			return status; +	} +	if (reg >= PMBUS_VIRT_BASE) +		return -EINVAL; +	return pmbus_write_word_data(client, page, reg, word); +}  int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg)  { @@ -197,16 +227,57 @@ int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg)  }  EXPORT_SYMBOL_GPL(pmbus_read_word_data); -static int pmbus_read_byte_data(struct i2c_client *client, u8 page, u8 reg) +/* + * _pmbus_read_word_data() is similar to pmbus_read_word_data(), but checks if + * a device specific mapping function exists and calls it if necessary. + */ +static int _pmbus_read_word_data(struct i2c_client *client, int page, int reg) +{ +	struct pmbus_data *data = i2c_get_clientdata(client); +	const struct pmbus_driver_info *info = data->info; +	int status; + +	if (info->read_word_data) { +		status = info->read_word_data(client, page, reg); +		if (status != -ENODATA) +			return status; +	} +	if (reg >= PMBUS_VIRT_BASE) +		return -EINVAL; +	return pmbus_read_word_data(client, page, reg); +} + +int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg)  {  	int rv; -	rv = pmbus_set_page(client, page); -	if (rv < 0) -		return rv; +	if (page >= 0) { +		rv = pmbus_set_page(client, page); +		if (rv < 0) +			return rv; +	}  	return i2c_smbus_read_byte_data(client, reg);  } +EXPORT_SYMBOL_GPL(pmbus_read_byte_data); + +/* + * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if + * a device specific mapping function exists and calls it if necessary. + */ +static int _pmbus_read_byte_data(struct i2c_client *client, int page, int reg) +{ +	struct pmbus_data *data = i2c_get_clientdata(client); +	const struct pmbus_driver_info *info = data->info; +	int status; + +	if (info->read_byte_data) { +		status = info->read_byte_data(client, page, reg); +		if (status != -ENODATA) +			return status; +	} +	return pmbus_read_byte_data(client, page, reg); +}  static void pmbus_clear_fault_page(struct i2c_client *client, int page)  { @@ -223,13 +294,13 @@ void pmbus_clear_faults(struct i2c_client *client)  }  EXPORT_SYMBOL_GPL(pmbus_clear_faults); -static int pmbus_check_status_cml(struct i2c_client *client, int page) +static int pmbus_check_status_cml(struct i2c_client *client)  {  	int status, status2; -	status = pmbus_read_byte_data(client, page, PMBUS_STATUS_BYTE); +	status = pmbus_read_byte_data(client, -1, PMBUS_STATUS_BYTE);  	if (status < 0 || (status & PB_STATUS_CML)) { -		status2 = pmbus_read_byte_data(client, page, PMBUS_STATUS_CML); +		status2 = pmbus_read_byte_data(client, -1, PMBUS_STATUS_CML);  		if (status2 < 0 || (status2 & PB_CML_FAULT_INVALID_COMMAND))  			return -EINVAL;  	} @@ -241,10 +312,10 @@ bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg)  	int rv;  	struct pmbus_data *data = i2c_get_clientdata(client); -	rv = pmbus_read_byte_data(client, page, reg); +	rv = _pmbus_read_byte_data(client, page, reg);  	if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK)) -		rv = pmbus_check_status_cml(client, page); -	pmbus_clear_fault_page(client, page); +		rv = pmbus_check_status_cml(client); +	pmbus_clear_fault_page(client, -1);  	return rv >= 0;  }  EXPORT_SYMBOL_GPL(pmbus_check_byte_register); @@ -254,10 +325,10 @@ bool pmbus_check_word_register(struct i2c_client *client, int page, int reg)  	int rv;  	struct pmbus_data *data = i2c_get_clientdata(client); -	rv = pmbus_read_word_data(client, page, reg); +	rv = _pmbus_read_word_data(client, page, reg);  	if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK)) -		rv = pmbus_check_status_cml(client, page); -	pmbus_clear_fault_page(client, page); +		rv = pmbus_check_status_cml(client); +	pmbus_clear_fault_page(client, -1);  	return rv >= 0;  }  EXPORT_SYMBOL_GPL(pmbus_check_word_register); @@ -270,24 +341,6 @@ const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client)  }  EXPORT_SYMBOL_GPL(pmbus_get_driver_info); -/* - * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if - * a device specific mapping funcion exists and calls it if necessary. - */ -static int _pmbus_read_byte_data(struct i2c_client *client, int page, int reg) -{ -	struct pmbus_data *data = i2c_get_clientdata(client); -	const struct pmbus_driver_info *info = data->info; -	int status; - -	if (info->read_byte_data) { -		status = info->read_byte_data(client, page, reg); -		if (status != -ENODATA) -			return status; -	} -	return pmbus_read_byte_data(client, page, reg); -} -  static struct pmbus_data *pmbus_update_device(struct device *dev)  {  	struct i2c_client *client = to_i2c_client(dev); @@ -347,8 +400,9 @@ static struct pmbus_data *pmbus_update_device(struct device *dev)  			if (!data->valid || sensor->update)  				sensor->data -				    = pmbus_read_word_data(client, sensor->page, -							   sensor->reg); +				    = _pmbus_read_word_data(client, +							    sensor->page, +							    sensor->reg);  		}  		pmbus_clear_faults(client);  		data->last_updated = jiffies; @@ -362,8 +416,8 @@ static struct pmbus_data *pmbus_update_device(struct device *dev)   * Convert linear sensor values to milli- or micro-units   * depending on sensor type.   */ -static int pmbus_reg2data_linear(struct pmbus_data *data, -				 struct pmbus_sensor *sensor) +static long pmbus_reg2data_linear(struct pmbus_data *data, +				  struct pmbus_sensor *sensor)  {  	s16 exponent;  	s32 mantissa; @@ -397,15 +451,15 @@ static int pmbus_reg2data_linear(struct pmbus_data *data,  	else  		val >>= -exponent; -	return (int)val; +	return val;  }  /*   * Convert direct sensor values to milli- or micro-units   * depending on sensor type.   */ -static int pmbus_reg2data_direct(struct pmbus_data *data, -				 struct pmbus_sensor *sensor) +static long pmbus_reg2data_direct(struct pmbus_data *data, +				  struct pmbus_sensor *sensor)  {  	long val = (s16) sensor->data;  	long m, b, R; @@ -440,18 +494,40 @@ static int pmbus_reg2data_direct(struct pmbus_data *data,  		R++;  	} -	return (int)((val - b) / m); +	return (val - b) / m; +} + +/* + * Convert VID sensor values to milli- or micro-units + * depending on sensor type. + * We currently only support VR11. + */ +static long pmbus_reg2data_vid(struct pmbus_data *data, +			       struct pmbus_sensor *sensor) +{ +	long val = sensor->data; + +	if (val < 0x02 || val > 0xb2) +		return 0; +	return DIV_ROUND_CLOSEST(160000 - (val - 2) * 625, 100);  } -static int pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor) +static long pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor)  { -	int val; +	long val; -	if (data->info->direct[sensor->class]) +	switch (data->info->format[sensor->class]) { +	case direct:  		val = pmbus_reg2data_direct(data, sensor); -	else +		break; +	case vid: +		val = pmbus_reg2data_vid(data, sensor); +		break; +	case linear: +	default:  		val = pmbus_reg2data_linear(data, sensor); - +		break; +	}  	return val;  } @@ -561,16 +637,31 @@ static u16 pmbus_data2reg_direct(struct pmbus_data *data,  	return val;  } +static u16 pmbus_data2reg_vid(struct pmbus_data *data, +			      enum pmbus_sensor_classes class, long val) +{ +	val = SENSORS_LIMIT(val, 500, 1600); + +	return 2 + DIV_ROUND_CLOSEST((1600 - val) * 100, 625); +} +  static u16 pmbus_data2reg(struct pmbus_data *data,  			  enum pmbus_sensor_classes class, long val)  {  	u16 regval; -	if (data->info->direct[class]) +	switch (data->info->format[class]) { +	case direct:  		regval = pmbus_data2reg_direct(data, class, val); -	else +		break; +	case vid: +		regval = pmbus_data2reg_vid(data, class, val); +		break; +	case linear: +	default:  		regval = pmbus_data2reg_linear(data, class, val); - +		break; +	}  	return regval;  } @@ -619,7 +710,7 @@ static int pmbus_get_boolean(struct pmbus_data *data, int index, int *val)  	if (!s1 && !s2)  		*val = !!regval;  	else { -		int v1, v2; +		long v1, v2;  		struct pmbus_sensor *sensor1, *sensor2;  		sensor1 = &data->sensors[s1]; @@ -661,7 +752,7 @@ static ssize_t pmbus_show_sensor(struct device *dev,  	if (sensor->data < 0)  		return sensor->data; -	return snprintf(buf, PAGE_SIZE, "%d\n", pmbus_reg2data(data, sensor)); +	return snprintf(buf, PAGE_SIZE, "%ld\n", pmbus_reg2data(data, sensor));  }  static ssize_t pmbus_set_sensor(struct device *dev, @@ -682,7 +773,7 @@ static ssize_t pmbus_set_sensor(struct device *dev,  	mutex_lock(&data->update_lock);  	regval = pmbus_data2reg(data, sensor->class, val); -	ret = pmbus_write_word_data(client, sensor->page, sensor->reg, regval); +	ret = _pmbus_write_word_data(client, sensor->page, sensor->reg, regval);  	if (ret < 0)  		rv = ret;  	else @@ -867,7 +958,8 @@ static void pmbus_find_max_attr(struct i2c_client *client,   * and its associated alarm attribute.   */  struct pmbus_limit_attr { -	u8 reg;			/* Limit register */ +	u16 reg;		/* Limit register */ +	bool update;		/* True if register needs updates */  	const char *attr;	/* Attribute name */  	const char *alarm;	/* Alarm attribute name */  	u32 sbit;		/* Alarm attribute status bit */ @@ -912,9 +1004,10 @@ static bool pmbus_add_limit_attrs(struct i2c_client *client,  		if (pmbus_check_word_register(client, page, l->reg)) {  			cindex = data->num_sensors;  			pmbus_add_sensor(data, name, l->attr, index, page, -					 l->reg, attr->class, attr->update, +					 l->reg, attr->class, +					 attr->update || l->update,  					 false); -			if (info->func[page] & attr->sfunc) { +			if (l->sbit && (info->func[page] & attr->sfunc)) {  				if (attr->compare) {  					pmbus_add_boolean_cmp(data, name,  						l->alarm, index, @@ -953,9 +1046,11 @@ static void pmbus_add_sensor_attrs_one(struct i2c_client *client,  						   index, page, cbase, attr);  		/*  		 * Add generic alarm attribute only if there are no individual -		 * alarm attributes, and if there is a global alarm bit. +		 * alarm attributes, if there is a global alarm bit, and if +		 * the generic status register for this page is accessible.  		 */ -		if (!have_alarm && attr->gbit) +		if (!have_alarm && attr->gbit && +		    pmbus_check_byte_register(client, page, PMBUS_STATUS_BYTE))  			pmbus_add_boolean_reg(data, name, "alarm", index,  					      PB_STATUS_BASE + page,  					      attr->gbit); @@ -1008,6 +1103,21 @@ static const struct pmbus_limit_attr vin_limit_attrs[] = {  		.attr = "crit",  		.alarm = "crit_alarm",  		.sbit = PB_VOLTAGE_OV_FAULT, +	}, { +		.reg = PMBUS_VIRT_READ_VIN_AVG, +		.update = true, +		.attr = "average", +	}, { +		.reg = PMBUS_VIRT_READ_VIN_MIN, +		.update = true, +		.attr = "lowest", +	}, { +		.reg = PMBUS_VIRT_READ_VIN_MAX, +		.update = true, +		.attr = "highest", +	}, { +		.reg = PMBUS_VIRT_RESET_VIN_HISTORY, +		.attr = "reset_history",  	},  }; @@ -1032,6 +1142,21 @@ static const struct pmbus_limit_attr vout_limit_attrs[] = {  		.attr = "crit",  		.alarm = "crit_alarm",  		.sbit = PB_VOLTAGE_OV_FAULT, +	}, { +		.reg = PMBUS_VIRT_READ_VOUT_AVG, +		.update = true, +		.attr = "average", +	}, { +		.reg = PMBUS_VIRT_READ_VOUT_MIN, +		.update = true, +		.attr = "lowest", +	}, { +		.reg = PMBUS_VIRT_READ_VOUT_MAX, +		.update = true, +		.attr = "highest", +	}, { +		.reg = PMBUS_VIRT_RESET_VOUT_HISTORY, +		.attr = "reset_history",  	}  }; @@ -1078,6 +1203,21 @@ static const struct pmbus_limit_attr iin_limit_attrs[] = {  		.attr = "crit",  		.alarm = "crit_alarm",  		.sbit = PB_IIN_OC_FAULT, +	}, { +		.reg = PMBUS_VIRT_READ_IIN_AVG, +		.update = true, +		.attr = "average", +	}, { +		.reg = PMBUS_VIRT_READ_IIN_MIN, +		.update = true, +		.attr = "lowest", +	}, { +		.reg = PMBUS_VIRT_READ_IIN_MAX, +		.update = true, +		.attr = "highest", +	}, { +		.reg = PMBUS_VIRT_RESET_IIN_HISTORY, +		.attr = "reset_history",  	}  }; @@ -1097,6 +1237,21 @@ static const struct pmbus_limit_attr iout_limit_attrs[] = {  		.attr = "crit",  		.alarm = "crit_alarm",  		.sbit = PB_IOUT_OC_FAULT, +	}, { +		.reg = PMBUS_VIRT_READ_IOUT_AVG, +		.update = true, +		.attr = "average", +	}, { +		.reg = PMBUS_VIRT_READ_IOUT_MIN, +		.update = true, +		.attr = "lowest", +	}, { +		.reg = PMBUS_VIRT_READ_IOUT_MAX, +		.update = true, +		.attr = "highest", +	}, { +		.reg = PMBUS_VIRT_RESET_IOUT_HISTORY, +		.attr = "reset_history",  	}  }; @@ -1132,6 +1287,17 @@ static const struct pmbus_limit_attr pin_limit_attrs[] = {  		.attr = "max",  		.alarm = "alarm",  		.sbit = PB_PIN_OP_WARNING, +	}, { +		.reg = PMBUS_VIRT_READ_PIN_AVG, +		.update = true, +		.attr = "average", +	}, { +		.reg = PMBUS_VIRT_READ_PIN_MAX, +		.update = true, +		.attr = "input_highest", +	}, { +		.reg = PMBUS_VIRT_RESET_PIN_HISTORY, +		.attr = "reset_history",  	}  }; @@ -1200,6 +1366,39 @@ static const struct pmbus_limit_attr temp_limit_attrs[] = {  		.attr = "crit",  		.alarm = "crit_alarm",  		.sbit = PB_TEMP_OT_FAULT, +	}, { +		.reg = PMBUS_VIRT_READ_TEMP_MIN, +		.attr = "lowest", +	}, { +		.reg = PMBUS_VIRT_READ_TEMP_MAX, +		.attr = "highest", +	}, { +		.reg = PMBUS_VIRT_RESET_TEMP_HISTORY, +		.attr = "reset_history", +	} +}; + +static const struct pmbus_limit_attr temp_limit_attrs23[] = { +	{ +		.reg = PMBUS_UT_WARN_LIMIT, +		.attr = "min", +		.alarm = "min_alarm", +		.sbit = PB_TEMP_UT_WARNING, +	}, { +		.reg = PMBUS_UT_FAULT_LIMIT, +		.attr = "lcrit", +		.alarm = "lcrit_alarm", +		.sbit = PB_TEMP_UT_FAULT, +	}, { +		.reg = PMBUS_OT_WARN_LIMIT, +		.attr = "max", +		.alarm = "max_alarm", +		.sbit = PB_TEMP_OT_WARNING, +	}, { +		.reg = PMBUS_OT_FAULT_LIMIT, +		.attr = "crit", +		.alarm = "crit_alarm", +		.sbit = PB_TEMP_OT_FAULT,  	}  }; @@ -1226,8 +1425,8 @@ static const struct pmbus_sensor_attr temp_attributes[] = {  		.sfunc = PMBUS_HAVE_STATUS_TEMP,  		.sbase = PB_STATUS_TEMP_BASE,  		.gbit = PB_STATUS_TEMPERATURE, -		.limit = temp_limit_attrs, -		.nlimit = ARRAY_SIZE(temp_limit_attrs), +		.limit = temp_limit_attrs23, +		.nlimit = ARRAY_SIZE(temp_limit_attrs23),  	}, {  		.reg = PMBUS_READ_TEMPERATURE_3,  		.class = PSC_TEMPERATURE, @@ -1238,8 +1437,8 @@ static const struct pmbus_sensor_attr temp_attributes[] = {  		.sfunc = PMBUS_HAVE_STATUS_TEMP,  		.sbase = PB_STATUS_TEMP_BASE,  		.gbit = PB_STATUS_TEMPERATURE, -		.limit = temp_limit_attrs, -		.nlimit = ARRAY_SIZE(temp_limit_attrs), +		.limit = temp_limit_attrs23, +		.nlimit = ARRAY_SIZE(temp_limit_attrs23),  	}  }; @@ -1380,7 +1579,7 @@ static int pmbus_identify_common(struct i2c_client *client,  		 */  		switch (vout_mode >> 5) {  		case 0:	/* linear mode      */ -			if (data->info->direct[PSC_VOLTAGE_OUT]) +			if (data->info->format[PSC_VOLTAGE_OUT] != linear)  				return -ENODEV;  			exponent = vout_mode & 0x1f; @@ -1389,8 +1588,12 @@ static int pmbus_identify_common(struct i2c_client *client,  				exponent |= ~0x1f;  			data->exponent = exponent;  			break; +		case 1: /* VID mode         */ +			if (data->info->format[PSC_VOLTAGE_OUT] != vid) +				return -ENODEV; +			break;  		case 2:	/* direct mode      */ -			if (!data->info->direct[PSC_VOLTAGE_OUT]) +			if (data->info->format[PSC_VOLTAGE_OUT] != direct)  				return -ENODEV;  			break;  		default: @@ -1430,14 +1633,9 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id,  	i2c_set_clientdata(client, data);  	mutex_init(&data->update_lock); -	/* -	 * Bail out if status register or PMBus revision register -	 * does not exist. -	 */ -	if (i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE) < 0 -	    || i2c_smbus_read_byte_data(client, PMBUS_REVISION) < 0) { -		dev_err(&client->dev, -			"Status or revision register not found\n"); +	/* Bail out if PMBus status register does not exist. */ +	if (i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE) < 0) { +		dev_err(&client->dev, "PMBus status register not found\n");  		ret = -ENODEV;  		goto out_data;  	} @@ -1462,18 +1660,6 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id,  		ret = -EINVAL;  		goto out_data;  	} -	/* -	 * Bail out if more than one page was configured, but we can not -	 * select the highest page. This is an indication that the wrong -	 * chip type was selected. Better bail out now than keep -	 * returning errors later on. -	 */ -	if (info->pages > 1 && pmbus_set_page(client, info->pages - 1) < 0) { -		dev_err(&client->dev, "Failed to select page %d\n", -			info->pages - 1); -		ret = -EINVAL; -		goto out_data; -	}  	ret = pmbus_identify_common(client, data);  	if (ret < 0) { diff --git a/drivers/hwmon/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c index ace1c7319734..ace1c7319734 100644 --- a/drivers/hwmon/ucd9000.c +++ b/drivers/hwmon/pmbus/ucd9000.c diff --git a/drivers/hwmon/ucd9200.c b/drivers/hwmon/pmbus/ucd9200.c index ffcc1cf3609d..ffcc1cf3609d 100644 --- a/drivers/hwmon/ucd9200.c +++ b/drivers/hwmon/pmbus/ucd9200.c diff --git a/drivers/hwmon/s3c-hwmon.c b/drivers/hwmon/s3c-hwmon.c index 92b42db43bcf..b39f52e2752a 100644 --- a/drivers/hwmon/s3c-hwmon.c +++ b/drivers/hwmon/s3c-hwmon.c @@ -232,6 +232,7 @@ static int s3c_hwmon_create_attr(struct device *dev,  	attr = &attrs->in;  	attr->index = channel; +	sysfs_attr_init(&attr->dev_attr.attr);  	attr->dev_attr.attr.name  = attrs->in_name;  	attr->dev_attr.attr.mode  = S_IRUGO;  	attr->dev_attr.show = s3c_hwmon_ch_show; @@ -249,6 +250,7 @@ static int s3c_hwmon_create_attr(struct device *dev,  		attr = &attrs->label;  		attr->index = channel; +		sysfs_attr_init(&attr->dev_attr.attr);  		attr->dev_attr.attr.name  = attrs->label_name;  		attr->dev_attr.attr.mode  = S_IRUGO;  		attr->dev_attr.show = s3c_hwmon_label_show; diff --git a/drivers/hwmon/sch5627.c b/drivers/hwmon/sch5627.c index 020c87273ea1..e3b5c6039c25 100644 --- a/drivers/hwmon/sch5627.c +++ b/drivers/hwmon/sch5627.c @@ -28,33 +28,15 @@  #include <linux/hwmon-sysfs.h>  #include <linux/err.h>  #include <linux/mutex.h> -#include <linux/io.h> -#include <linux/acpi.h> -#include <linux/delay.h> +#include "sch56xx-common.h"  #define DRVNAME "sch5627"  #define DEVNAME DRVNAME /* We only support one model */ -#define SIO_SCH5627_EM_LD	0x0C	/* Embedded Microcontroller LD */ -#define SIO_UNLOCK_KEY		0x55	/* Key to enable Super-I/O */ -#define SIO_LOCK_KEY		0xAA	/* Key to disable Super-I/O */ - -#define SIO_REG_LDSEL		0x07	/* Logical device select */ -#define SIO_REG_DEVID		0x20	/* Device ID */ -#define SIO_REG_ENABLE		0x30	/* Logical device enable */ -#define SIO_REG_ADDR		0x66	/* Logical device address (2 bytes) */ - -#define SIO_SCH5627_ID		0xC6	/* Chipset ID */ - -#define REGION_LENGTH		9 -  #define SCH5627_HWMON_ID		0xa5  #define SCH5627_COMPANY_ID		0x5c  #define SCH5627_PRIMARY_ID		0xa0 -#define SCH5627_CMD_READ		0x02 -#define SCH5627_CMD_WRITE		0x03 -  #define SCH5627_REG_BUILD_CODE		0x39  #define SCH5627_REG_BUILD_ID		0x3a  #define SCH5627_REG_HWMON_ID		0x3c @@ -111,182 +93,6 @@ struct sch5627_data {  	u16 in[SCH5627_NO_IN];  }; -static struct platform_device *sch5627_pdev; - -/* Super I/O functions */ -static inline int superio_inb(int base, int reg) -{ -	outb(reg, base); -	return inb(base + 1); -} - -static inline int superio_enter(int base) -{ -	/* Don't step on other drivers' I/O space by accident */ -	if (!request_muxed_region(base, 2, DRVNAME)) { -		pr_err("I/O address 0x%04x already in use\n", base); -		return -EBUSY; -	} - -	outb(SIO_UNLOCK_KEY, base); - -	return 0; -} - -static inline void superio_select(int base, int ld) -{ -	outb(SIO_REG_LDSEL, base); -	outb(ld, base + 1); -} - -static inline void superio_exit(int base) -{ -	outb(SIO_LOCK_KEY, base); -	release_region(base, 2); -} - -static int sch5627_send_cmd(struct sch5627_data *data, u8 cmd, u16 reg, u8 v) -{ -	u8 val; -	int i; -	/* -	 * According to SMSC for the commands we use the maximum time for -	 * the EM to respond is 15 ms, but testing shows in practice it -	 * responds within 15-32 reads, so we first busy poll, and if -	 * that fails sleep a bit and try again until we are way past -	 * the 15 ms maximum response time. -	 */ -	const int max_busy_polls = 64; -	const int max_lazy_polls = 32; - -	/* (Optional) Write-Clear the EC to Host Mailbox Register */ -	val = inb(data->addr + 1); -	outb(val, data->addr + 1); - -	/* Set Mailbox Address Pointer to first location in Region 1 */ -	outb(0x00, data->addr + 2); -	outb(0x80, data->addr + 3); - -	/* Write Request Packet Header */ -	outb(cmd, data->addr + 4); /* VREG Access Type read:0x02 write:0x03 */ -	outb(0x01, data->addr + 5); /* # of Entries: 1 Byte (8-bit) */ -	outb(0x04, data->addr + 2); /* Mailbox AP to first data entry loc. */ - -	/* Write Value field */ -	if (cmd == SCH5627_CMD_WRITE) -		outb(v, data->addr + 4); - -	/* Write Address field */ -	outb(reg & 0xff, data->addr + 6); -	outb(reg >> 8, data->addr + 7); - -	/* Execute the Random Access Command */ -	outb(0x01, data->addr); /* Write 01h to the Host-to-EC register */ - -	/* EM Interface Polling "Algorithm" */ -	for (i = 0; i < max_busy_polls + max_lazy_polls; i++) { -		if (i >= max_busy_polls) -			msleep(1); -		/* Read Interrupt source Register */ -		val = inb(data->addr + 8); -		/* Write Clear the interrupt source bits */ -		if (val) -			outb(val, data->addr + 8); -		/* Command Completed ? */ -		if (val & 0x01) -			break; -	} -	if (i == max_busy_polls + max_lazy_polls) { -		pr_err("Max retries exceeded reading virtual " -		       "register 0x%04hx (%d)\n", reg, 1); -		return -EIO; -	} - -	/* -	 * According to SMSC we may need to retry this, but sofar I've always -	 * seen this succeed in 1 try. -	 */ -	for (i = 0; i < max_busy_polls; i++) { -		/* Read EC-to-Host Register */ -		val = inb(data->addr + 1); -		/* Command Completed ? */ -		if (val == 0x01) -			break; - -		if (i == 0) -			pr_warn("EC reports: 0x%02x reading virtual register " -				"0x%04hx\n", (unsigned int)val, reg); -	} -	if (i == max_busy_polls) { -		pr_err("Max retries exceeded reading virtual " -		       "register 0x%04hx (%d)\n", reg, 2); -		return -EIO; -	} - -	/* -	 * According to the SMSC app note we should now do: -	 * -	 * Set Mailbox Address Pointer to first location in Region 1 * -	 * outb(0x00, data->addr + 2); -	 * outb(0x80, data->addr + 3); -	 * -	 * But if we do that things don't work, so let's not. -	 */ - -	/* Read Value field */ -	if (cmd == SCH5627_CMD_READ) -		return inb(data->addr + 4); - -	return 0; -} - -static int sch5627_read_virtual_reg(struct sch5627_data *data, u16 reg) -{ -	return sch5627_send_cmd(data, SCH5627_CMD_READ, reg, 0); -} - -static int sch5627_write_virtual_reg(struct sch5627_data *data, -				     u16 reg, u8 val) -{ -	return sch5627_send_cmd(data, SCH5627_CMD_WRITE, reg, val); -} - -static int sch5627_read_virtual_reg16(struct sch5627_data *data, u16 reg) -{ -	int lsb, msb; - -	/* Read LSB first, this will cause the matching MSB to be latched */ -	lsb = sch5627_read_virtual_reg(data, reg); -	if (lsb < 0) -		return lsb; - -	msb = sch5627_read_virtual_reg(data, reg + 1); -	if (msb < 0) -		return msb; - -	return lsb | (msb << 8); -} - -static int sch5627_read_virtual_reg12(struct sch5627_data *data, u16 msb_reg, -				      u16 lsn_reg, int high_nibble) -{ -	int msb, lsn; - -	/* Read MSB first, this will cause the matching LSN to be latched */ -	msb = sch5627_read_virtual_reg(data, msb_reg); -	if (msb < 0) -		return msb; - -	lsn = sch5627_read_virtual_reg(data, lsn_reg); -	if (lsn < 0) -		return lsn; - -	if (high_nibble) -		return (msb << 4) | (lsn >> 4); -	else -		return (msb << 4) | (lsn & 0x0f); -} -  static struct sch5627_data *sch5627_update_device(struct device *dev)  {  	struct sch5627_data *data = dev_get_drvdata(dev); @@ -297,7 +103,7 @@ static struct sch5627_data *sch5627_update_device(struct device *dev)  	/* Trigger a Vbat voltage measurement every 5 minutes */  	if (time_after(jiffies, data->last_battery + 300 * HZ)) { -		sch5627_write_virtual_reg(data, SCH5627_REG_CTRL, +		sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL,  					  data->control | 0x10);  		data->last_battery = jiffies;  	} @@ -305,7 +111,7 @@ static struct sch5627_data *sch5627_update_device(struct device *dev)  	/* Cache the values for 1 second */  	if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {  		for (i = 0; i < SCH5627_NO_TEMPS; i++) { -			val = sch5627_read_virtual_reg12(data, +			val = sch56xx_read_virtual_reg12(data->addr,  				SCH5627_REG_TEMP_MSB[i],  				SCH5627_REG_TEMP_LSN[i],  				SCH5627_REG_TEMP_HIGH_NIBBLE[i]); @@ -317,7 +123,7 @@ static struct sch5627_data *sch5627_update_device(struct device *dev)  		}  		for (i = 0; i < SCH5627_NO_FANS; i++) { -			val = sch5627_read_virtual_reg16(data, +			val = sch56xx_read_virtual_reg16(data->addr,  							 SCH5627_REG_FAN[i]);  			if (unlikely(val < 0)) {  				ret = ERR_PTR(val); @@ -327,7 +133,7 @@ static struct sch5627_data *sch5627_update_device(struct device *dev)  		}  		for (i = 0; i < SCH5627_NO_IN; i++) { -			val = sch5627_read_virtual_reg12(data, +			val = sch56xx_read_virtual_reg12(data->addr,  				SCH5627_REG_IN_MSB[i],  				SCH5627_REG_IN_LSN[i],  				SCH5627_REG_IN_HIGH_NIBBLE[i]); @@ -355,18 +161,21 @@ static int __devinit sch5627_read_limits(struct sch5627_data *data)  		 * Note what SMSC calls ABS, is what lm_sensors calls max  		 * (aka high), and HIGH is what lm_sensors calls crit.  		 */ -		val = sch5627_read_virtual_reg(data, SCH5627_REG_TEMP_ABS[i]); +		val = sch56xx_read_virtual_reg(data->addr, +					       SCH5627_REG_TEMP_ABS[i]);  		if (val < 0)  			return val;  		data->temp_max[i] = val; -		val = sch5627_read_virtual_reg(data, SCH5627_REG_TEMP_HIGH[i]); +		val = sch56xx_read_virtual_reg(data->addr, +					       SCH5627_REG_TEMP_HIGH[i]);  		if (val < 0)  			return val;  		data->temp_crit[i] = val;  	}  	for (i = 0; i < SCH5627_NO_FANS; i++) { -		val = sch5627_read_virtual_reg16(data, SCH5627_REG_FAN_MIN[i]); +		val = sch56xx_read_virtual_reg16(data->addr, +						 SCH5627_REG_FAN_MIN[i]);  		if (val < 0)  			return val;  		data->fan_min[i] = val; @@ -667,7 +476,7 @@ static int __devinit sch5627_probe(struct platform_device *pdev)  	mutex_init(&data->update_lock);  	platform_set_drvdata(pdev, data); -	val = sch5627_read_virtual_reg(data, SCH5627_REG_HWMON_ID); +	val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_HWMON_ID);  	if (val < 0) {  		err = val;  		goto error; @@ -679,7 +488,7 @@ static int __devinit sch5627_probe(struct platform_device *pdev)  		goto error;  	} -	val = sch5627_read_virtual_reg(data, SCH5627_REG_COMPANY_ID); +	val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_COMPANY_ID);  	if (val < 0) {  		err = val;  		goto error; @@ -691,7 +500,7 @@ static int __devinit sch5627_probe(struct platform_device *pdev)  		goto error;  	} -	val = sch5627_read_virtual_reg(data, SCH5627_REG_PRIMARY_ID); +	val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_PRIMARY_ID);  	if (val < 0) {  		err = val;  		goto error; @@ -703,25 +512,28 @@ static int __devinit sch5627_probe(struct platform_device *pdev)  		goto error;  	} -	build_code = sch5627_read_virtual_reg(data, SCH5627_REG_BUILD_CODE); +	build_code = sch56xx_read_virtual_reg(data->addr, +					      SCH5627_REG_BUILD_CODE);  	if (build_code < 0) {  		err = build_code;  		goto error;  	} -	build_id = sch5627_read_virtual_reg16(data, SCH5627_REG_BUILD_ID); +	build_id = sch56xx_read_virtual_reg16(data->addr, +					      SCH5627_REG_BUILD_ID);  	if (build_id < 0) {  		err = build_id;  		goto error;  	} -	hwmon_rev = sch5627_read_virtual_reg(data, SCH5627_REG_HWMON_REV); +	hwmon_rev = sch56xx_read_virtual_reg(data->addr, +					     SCH5627_REG_HWMON_REV);  	if (hwmon_rev < 0) {  		err = hwmon_rev;  		goto error;  	} -	val = sch5627_read_virtual_reg(data, SCH5627_REG_CTRL); +	val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_CTRL);  	if (val < 0) {  		err = val;  		goto error; @@ -734,7 +546,7 @@ static int __devinit sch5627_probe(struct platform_device *pdev)  	}  	/* Trigger a Vbat voltage measurement, so that we get a valid reading  	   the first time we read Vbat */ -	sch5627_write_virtual_reg(data, SCH5627_REG_CTRL, +	sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL,  				  data->control | 0x10);  	data->last_battery = jiffies; @@ -746,6 +558,7 @@ static int __devinit sch5627_probe(struct platform_device *pdev)  	if (err)  		goto error; +	pr_info("found %s chip at %#hx\n", DEVNAME, data->addr);  	pr_info("firmware build: code 0x%02X, id 0x%04X, hwmon: rev 0x%02X\n",  		build_code, build_id, hwmon_rev); @@ -768,85 +581,6 @@ error:  	return err;  } -static int __init sch5627_find(int sioaddr, unsigned short *address) -{ -	u8 devid; -	int err = superio_enter(sioaddr); -	if (err) -		return err; - -	devid = superio_inb(sioaddr, SIO_REG_DEVID); -	if (devid != SIO_SCH5627_ID) { -		pr_debug("Unsupported device id: 0x%02x\n", -			 (unsigned int)devid); -		err = -ENODEV; -		goto exit; -	} - -	superio_select(sioaddr, SIO_SCH5627_EM_LD); - -	if (!(superio_inb(sioaddr, SIO_REG_ENABLE) & 0x01)) { -		pr_warn("Device not activated\n"); -		err = -ENODEV; -		goto exit; -	} - -	/* -	 * Warning the order of the low / high byte is the other way around -	 * as on most other superio devices!! -	 */ -	*address = superio_inb(sioaddr, SIO_REG_ADDR) | -		   superio_inb(sioaddr, SIO_REG_ADDR + 1) << 8; -	if (*address == 0) { -		pr_warn("Base address not set\n"); -		err = -ENODEV; -		goto exit; -	} - -	pr_info("Found %s chip at %#hx\n", DEVNAME, *address); -exit: -	superio_exit(sioaddr); -	return err; -} - -static int __init sch5627_device_add(unsigned short address) -{ -	struct resource res = { -		.start	= address, -		.end	= address + REGION_LENGTH - 1, -		.flags	= IORESOURCE_IO, -	}; -	int err; - -	sch5627_pdev = platform_device_alloc(DRVNAME, address); -	if (!sch5627_pdev) -		return -ENOMEM; - -	res.name = sch5627_pdev->name; -	err = acpi_check_resource_conflict(&res); -	if (err) -		goto exit_device_put; - -	err = platform_device_add_resources(sch5627_pdev, &res, 1); -	if (err) { -		pr_err("Device resource addition failed\n"); -		goto exit_device_put; -	} - -	err = platform_device_add(sch5627_pdev); -	if (err) { -		pr_err("Device addition failed\n"); -		goto exit_device_put; -	} - -	return 0; - -exit_device_put: -	platform_device_put(sch5627_pdev); - -	return err; -} -  static struct platform_driver sch5627_driver = {  	.driver = {  		.owner	= THIS_MODULE, @@ -858,36 +592,16 @@ static struct platform_driver sch5627_driver = {  static int __init sch5627_init(void)  { -	int err = -ENODEV; -	unsigned short address; - -	if (sch5627_find(0x4e, &address) && sch5627_find(0x2e, &address)) -		goto exit; - -	err = platform_driver_register(&sch5627_driver); -	if (err) -		goto exit; - -	err = sch5627_device_add(address); -	if (err) -		goto exit_driver; - -	return 0; - -exit_driver: -	platform_driver_unregister(&sch5627_driver); -exit: -	return err; +	return platform_driver_register(&sch5627_driver);  }  static void __exit sch5627_exit(void)  { -	platform_device_unregister(sch5627_pdev);  	platform_driver_unregister(&sch5627_driver);  }  MODULE_DESCRIPTION("SMSC SCH5627 Hardware Monitoring Driver"); -MODULE_AUTHOR("Hans de Goede (hdegoede@redhat.com)"); +MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");  MODULE_LICENSE("GPL");  module_init(sch5627_init); diff --git a/drivers/hwmon/sch5636.c b/drivers/hwmon/sch5636.c new file mode 100644 index 000000000000..244407aa79fc --- /dev/null +++ b/drivers/hwmon/sch5636.c @@ -0,0 +1,539 @@ +/*************************************************************************** + *   Copyright (C) 2011 Hans de Goede <hdegoede@redhat.com>                * + *                                                                         * + *   This program is free software; you can redistribute it and/or modify  * + *   it under the terms of the GNU General Public License as published by  * + *   the Free Software Foundation; either version 2 of the License, or     * + *   (at your option) any later version.                                   * + *                                                                         * + *   This program is distributed in the hope that it will be useful,       * + *   but WITHOUT ANY WARRANTY; without even the implied warranty of        * + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         * + *   GNU General Public License for more details.                          * + *                                                                         * + *   You should have received a copy of the GNU General Public License     * + *   along with this program; if not, write to the                         * + *   Free Software Foundation, Inc.,                                       * + *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             * + ***************************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/platform_device.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include "sch56xx-common.h" + +#define DRVNAME "sch5636" +#define DEVNAME "theseus" /* We only support one model for now */ + +#define SCH5636_REG_FUJITSU_ID		0x780 +#define SCH5636_REG_FUJITSU_REV		0x783 + +#define SCH5636_NO_INS			5 +#define SCH5636_NO_TEMPS		16 +#define SCH5636_NO_FANS			8 + +static const u16 SCH5636_REG_IN_VAL[SCH5636_NO_INS] = { +	0x22, 0x23, 0x24, 0x25, 0x189 }; +static const u16 SCH5636_REG_IN_FACTORS[SCH5636_NO_INS] = { +	4400, 1500, 4000, 4400, 16000 }; +static const char * const SCH5636_IN_LABELS[SCH5636_NO_INS] = { +	"3.3V", "VREF", "VBAT", "3.3AUX", "12V" }; + +static const u16 SCH5636_REG_TEMP_VAL[SCH5636_NO_TEMPS] = { +	0x2B, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x180, 0x181, +	0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C }; +#define SCH5636_REG_TEMP_CTRL(i)	(0x790 + (i)) +#define SCH5636_TEMP_WORKING		0x01 +#define SCH5636_TEMP_ALARM		0x02 +#define SCH5636_TEMP_DEACTIVATED	0x80 + +static const u16 SCH5636_REG_FAN_VAL[SCH5636_NO_FANS] = { +	0x2C, 0x2E, 0x30, 0x32, 0x62, 0x64, 0x66, 0x68 }; +#define SCH5636_REG_FAN_CTRL(i)		(0x880 + (i)) +/* FAULT in datasheet, but acts as an alarm */ +#define SCH5636_FAN_ALARM		0x04 +#define SCH5636_FAN_NOT_PRESENT		0x08 +#define SCH5636_FAN_DEACTIVATED		0x80 + + +struct sch5636_data { +	unsigned short addr; +	struct device *hwmon_dev; + +	struct mutex update_lock; +	char valid;			/* !=0 if following fields are valid */ +	unsigned long last_updated;	/* In jiffies */ +	u8 in[SCH5636_NO_INS]; +	u8 temp_val[SCH5636_NO_TEMPS]; +	u8 temp_ctrl[SCH5636_NO_TEMPS]; +	u16 fan_val[SCH5636_NO_FANS]; +	u8 fan_ctrl[SCH5636_NO_FANS]; +}; + +static struct sch5636_data *sch5636_update_device(struct device *dev) +{ +	struct sch5636_data *data = dev_get_drvdata(dev); +	struct sch5636_data *ret = data; +	int i, val; + +	mutex_lock(&data->update_lock); + +	/* Cache the values for 1 second */ +	if (data->valid && !time_after(jiffies, data->last_updated + HZ)) +		goto abort; + +	for (i = 0; i < SCH5636_NO_INS; i++) { +		val = sch56xx_read_virtual_reg(data->addr, +					       SCH5636_REG_IN_VAL[i]); +		if (unlikely(val < 0)) { +			ret = ERR_PTR(val); +			goto abort; +		} +		data->in[i] = val; +	} + +	for (i = 0; i < SCH5636_NO_TEMPS; i++) { +		if (data->temp_ctrl[i] & SCH5636_TEMP_DEACTIVATED) +			continue; + +		val = sch56xx_read_virtual_reg(data->addr, +					       SCH5636_REG_TEMP_VAL[i]); +		if (unlikely(val < 0)) { +			ret = ERR_PTR(val); +			goto abort; +		} +		data->temp_val[i] = val; + +		val = sch56xx_read_virtual_reg(data->addr, +					       SCH5636_REG_TEMP_CTRL(i)); +		if (unlikely(val < 0)) { +			ret = ERR_PTR(val); +			goto abort; +		} +		data->temp_ctrl[i] = val; +		/* Alarms need to be explicitly write-cleared */ +		if (val & SCH5636_TEMP_ALARM) { +			sch56xx_write_virtual_reg(data->addr, +						SCH5636_REG_TEMP_CTRL(i), val); +		} +	} + +	for (i = 0; i < SCH5636_NO_FANS; i++) { +		if (data->fan_ctrl[i] & SCH5636_FAN_DEACTIVATED) +			continue; + +		val = sch56xx_read_virtual_reg16(data->addr, +						 SCH5636_REG_FAN_VAL[i]); +		if (unlikely(val < 0)) { +			ret = ERR_PTR(val); +			goto abort; +		} +		data->fan_val[i] = val; + +		val = sch56xx_read_virtual_reg(data->addr, +					       SCH5636_REG_FAN_CTRL(i)); +		if (unlikely(val < 0)) { +			ret = ERR_PTR(val); +			goto abort; +		} +		data->fan_ctrl[i] = val; +		/* Alarms need to be explicitly write-cleared */ +		if (val & SCH5636_FAN_ALARM) { +			sch56xx_write_virtual_reg(data->addr, +						SCH5636_REG_FAN_CTRL(i), val); +		} +	} + +	data->last_updated = jiffies; +	data->valid = 1; +abort: +	mutex_unlock(&data->update_lock); +	return ret; +} + +static int reg_to_rpm(u16 reg) +{ +	if (reg == 0) +		return -EIO; +	if (reg == 0xffff) +		return 0; + +	return 5400540 / reg; +} + +static ssize_t show_name(struct device *dev, struct device_attribute *devattr, +	char *buf) +{ +	return snprintf(buf, PAGE_SIZE, "%s\n", DEVNAME); +} + +static ssize_t show_in_value(struct device *dev, struct device_attribute +	*devattr, char *buf) +{ +	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); +	struct sch5636_data *data = sch5636_update_device(dev); +	int val; + +	if (IS_ERR(data)) +		return PTR_ERR(data); + +	val = DIV_ROUND_CLOSEST( +		data->in[attr->index] * SCH5636_REG_IN_FACTORS[attr->index], +		255); +	return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_in_label(struct device *dev, struct device_attribute +	*devattr, char *buf) +{ +	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + +	return snprintf(buf, PAGE_SIZE, "%s\n", +			SCH5636_IN_LABELS[attr->index]); +} + +static ssize_t show_temp_value(struct device *dev, struct device_attribute +	*devattr, char *buf) +{ +	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); +	struct sch5636_data *data = sch5636_update_device(dev); +	int val; + +	if (IS_ERR(data)) +		return PTR_ERR(data); + +	val = (data->temp_val[attr->index] - 64) * 1000; +	return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_temp_fault(struct device *dev, struct device_attribute +	*devattr, char *buf) +{ +	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); +	struct sch5636_data *data = sch5636_update_device(dev); +	int val; + +	if (IS_ERR(data)) +		return PTR_ERR(data); + +	val = (data->temp_ctrl[attr->index] & SCH5636_TEMP_WORKING) ? 0 : 1; +	return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_temp_alarm(struct device *dev, struct device_attribute +	*devattr, char *buf) +{ +	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); +	struct sch5636_data *data = sch5636_update_device(dev); +	int val; + +	if (IS_ERR(data)) +		return PTR_ERR(data); + +	val = (data->temp_ctrl[attr->index] & SCH5636_TEMP_ALARM) ? 1 : 0; +	return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_fan_value(struct device *dev, struct device_attribute +	*devattr, char *buf) +{ +	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); +	struct sch5636_data *data = sch5636_update_device(dev); +	int val; + +	if (IS_ERR(data)) +		return PTR_ERR(data); + +	val = reg_to_rpm(data->fan_val[attr->index]); +	if (val < 0) +		return val; + +	return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_fan_fault(struct device *dev, struct device_attribute +	*devattr, char *buf) +{ +	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); +	struct sch5636_data *data = sch5636_update_device(dev); +	int val; + +	if (IS_ERR(data)) +		return PTR_ERR(data); + +	val = (data->fan_ctrl[attr->index] & SCH5636_FAN_NOT_PRESENT) ? 1 : 0; +	return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_fan_alarm(struct device *dev, struct device_attribute +	*devattr, char *buf) +{ +	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); +	struct sch5636_data *data = sch5636_update_device(dev); +	int val; + +	if (IS_ERR(data)) +		return PTR_ERR(data); + +	val = (data->fan_ctrl[attr->index] & SCH5636_FAN_ALARM) ? 1 : 0; +	return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static struct sensor_device_attribute sch5636_attr[] = { +	SENSOR_ATTR(name, 0444, show_name, NULL, 0), +	SENSOR_ATTR(in0_input, 0444, show_in_value, NULL, 0), +	SENSOR_ATTR(in0_label, 0444, show_in_label, NULL, 0), +	SENSOR_ATTR(in1_input, 0444, show_in_value, NULL, 1), +	SENSOR_ATTR(in1_label, 0444, show_in_label, NULL, 1), +	SENSOR_ATTR(in2_input, 0444, show_in_value, NULL, 2), +	SENSOR_ATTR(in2_label, 0444, show_in_label, NULL, 2), +	SENSOR_ATTR(in3_input, 0444, show_in_value, NULL, 3), +	SENSOR_ATTR(in3_label, 0444, show_in_label, NULL, 3), +	SENSOR_ATTR(in4_input, 0444, show_in_value, NULL, 4), +	SENSOR_ATTR(in4_label, 0444, show_in_label, NULL, 4), +}; + +static struct sensor_device_attribute sch5636_temp_attr[] = { +	SENSOR_ATTR(temp1_input, 0444, show_temp_value, NULL, 0), +	SENSOR_ATTR(temp1_fault, 0444, show_temp_fault, NULL, 0), +	SENSOR_ATTR(temp1_alarm, 0444, show_temp_alarm, NULL, 0), +	SENSOR_ATTR(temp2_input, 0444, show_temp_value, NULL, 1), +	SENSOR_ATTR(temp2_fault, 0444, show_temp_fault, NULL, 1), +	SENSOR_ATTR(temp2_alarm, 0444, show_temp_alarm, NULL, 1), +	SENSOR_ATTR(temp3_input, 0444, show_temp_value, NULL, 2), +	SENSOR_ATTR(temp3_fault, 0444, show_temp_fault, NULL, 2), +	SENSOR_ATTR(temp3_alarm, 0444, show_temp_alarm, NULL, 2), +	SENSOR_ATTR(temp4_input, 0444, show_temp_value, NULL, 3), +	SENSOR_ATTR(temp4_fault, 0444, show_temp_fault, NULL, 3), +	SENSOR_ATTR(temp4_alarm, 0444, show_temp_alarm, NULL, 3), +	SENSOR_ATTR(temp5_input, 0444, show_temp_value, NULL, 4), +	SENSOR_ATTR(temp5_fault, 0444, show_temp_fault, NULL, 4), +	SENSOR_ATTR(temp5_alarm, 0444, show_temp_alarm, NULL, 4), +	SENSOR_ATTR(temp6_input, 0444, show_temp_value, NULL, 5), +	SENSOR_ATTR(temp6_fault, 0444, show_temp_fault, NULL, 5), +	SENSOR_ATTR(temp6_alarm, 0444, show_temp_alarm, NULL, 5), +	SENSOR_ATTR(temp7_input, 0444, show_temp_value, NULL, 6), +	SENSOR_ATTR(temp7_fault, 0444, show_temp_fault, NULL, 6), +	SENSOR_ATTR(temp7_alarm, 0444, show_temp_alarm, NULL, 6), +	SENSOR_ATTR(temp8_input, 0444, show_temp_value, NULL, 7), +	SENSOR_ATTR(temp8_fault, 0444, show_temp_fault, NULL, 7), +	SENSOR_ATTR(temp8_alarm, 0444, show_temp_alarm, NULL, 7), +	SENSOR_ATTR(temp9_input, 0444, show_temp_value, NULL, 8), +	SENSOR_ATTR(temp9_fault, 0444, show_temp_fault, NULL, 8), +	SENSOR_ATTR(temp9_alarm, 0444, show_temp_alarm, NULL, 8), +	SENSOR_ATTR(temp10_input, 0444, show_temp_value, NULL, 9), +	SENSOR_ATTR(temp10_fault, 0444, show_temp_fault, NULL, 9), +	SENSOR_ATTR(temp10_alarm, 0444, show_temp_alarm, NULL, 9), +	SENSOR_ATTR(temp11_input, 0444, show_temp_value, NULL, 10), +	SENSOR_ATTR(temp11_fault, 0444, show_temp_fault, NULL, 10), +	SENSOR_ATTR(temp11_alarm, 0444, show_temp_alarm, NULL, 10), +	SENSOR_ATTR(temp12_input, 0444, show_temp_value, NULL, 11), +	SENSOR_ATTR(temp12_fault, 0444, show_temp_fault, NULL, 11), +	SENSOR_ATTR(temp12_alarm, 0444, show_temp_alarm, NULL, 11), +	SENSOR_ATTR(temp13_input, 0444, show_temp_value, NULL, 12), +	SENSOR_ATTR(temp13_fault, 0444, show_temp_fault, NULL, 12), +	SENSOR_ATTR(temp13_alarm, 0444, show_temp_alarm, NULL, 12), +	SENSOR_ATTR(temp14_input, 0444, show_temp_value, NULL, 13), +	SENSOR_ATTR(temp14_fault, 0444, show_temp_fault, NULL, 13), +	SENSOR_ATTR(temp14_alarm, 0444, show_temp_alarm, NULL, 13), +	SENSOR_ATTR(temp15_input, 0444, show_temp_value, NULL, 14), +	SENSOR_ATTR(temp15_fault, 0444, show_temp_fault, NULL, 14), +	SENSOR_ATTR(temp15_alarm, 0444, show_temp_alarm, NULL, 14), +	SENSOR_ATTR(temp16_input, 0444, show_temp_value, NULL, 15), +	SENSOR_ATTR(temp16_fault, 0444, show_temp_fault, NULL, 15), +	SENSOR_ATTR(temp16_alarm, 0444, show_temp_alarm, NULL, 15), +}; + +static struct sensor_device_attribute sch5636_fan_attr[] = { +	SENSOR_ATTR(fan1_input, 0444, show_fan_value, NULL, 0), +	SENSOR_ATTR(fan1_fault, 0444, show_fan_fault, NULL, 0), +	SENSOR_ATTR(fan1_alarm, 0444, show_fan_alarm, NULL, 0), +	SENSOR_ATTR(fan2_input, 0444, show_fan_value, NULL, 1), +	SENSOR_ATTR(fan2_fault, 0444, show_fan_fault, NULL, 1), +	SENSOR_ATTR(fan2_alarm, 0444, show_fan_alarm, NULL, 1), +	SENSOR_ATTR(fan3_input, 0444, show_fan_value, NULL, 2), +	SENSOR_ATTR(fan3_fault, 0444, show_fan_fault, NULL, 2), +	SENSOR_ATTR(fan3_alarm, 0444, show_fan_alarm, NULL, 2), +	SENSOR_ATTR(fan4_input, 0444, show_fan_value, NULL, 3), +	SENSOR_ATTR(fan4_fault, 0444, show_fan_fault, NULL, 3), +	SENSOR_ATTR(fan4_alarm, 0444, show_fan_alarm, NULL, 3), +	SENSOR_ATTR(fan5_input, 0444, show_fan_value, NULL, 4), +	SENSOR_ATTR(fan5_fault, 0444, show_fan_fault, NULL, 4), +	SENSOR_ATTR(fan5_alarm, 0444, show_fan_alarm, NULL, 4), +	SENSOR_ATTR(fan6_input, 0444, show_fan_value, NULL, 5), +	SENSOR_ATTR(fan6_fault, 0444, show_fan_fault, NULL, 5), +	SENSOR_ATTR(fan6_alarm, 0444, show_fan_alarm, NULL, 5), +	SENSOR_ATTR(fan7_input, 0444, show_fan_value, NULL, 6), +	SENSOR_ATTR(fan7_fault, 0444, show_fan_fault, NULL, 6), +	SENSOR_ATTR(fan7_alarm, 0444, show_fan_alarm, NULL, 6), +	SENSOR_ATTR(fan8_input, 0444, show_fan_value, NULL, 7), +	SENSOR_ATTR(fan8_fault, 0444, show_fan_fault, NULL, 7), +	SENSOR_ATTR(fan8_alarm, 0444, show_fan_alarm, NULL, 7), +}; + +static int sch5636_remove(struct platform_device *pdev) +{ +	struct sch5636_data *data = platform_get_drvdata(pdev); +	int i; + +	if (data->hwmon_dev) +		hwmon_device_unregister(data->hwmon_dev); + +	for (i = 0; i < ARRAY_SIZE(sch5636_attr); i++) +		device_remove_file(&pdev->dev, &sch5636_attr[i].dev_attr); + +	for (i = 0; i < SCH5636_NO_TEMPS * 3; i++) +		device_remove_file(&pdev->dev, +				   &sch5636_temp_attr[i].dev_attr); + +	for (i = 0; i < SCH5636_NO_FANS * 3; i++) +		device_remove_file(&pdev->dev, +				   &sch5636_fan_attr[i].dev_attr); + +	platform_set_drvdata(pdev, NULL); +	kfree(data); + +	return 0; +} + +static int __devinit sch5636_probe(struct platform_device *pdev) +{ +	struct sch5636_data *data; +	int i, err, val, revision[2]; +	char id[4]; + +	data = kzalloc(sizeof(struct sch5636_data), GFP_KERNEL); +	if (!data) +		return -ENOMEM; + +	data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start; +	mutex_init(&data->update_lock); +	platform_set_drvdata(pdev, data); + +	for (i = 0; i < 3; i++) { +		val = sch56xx_read_virtual_reg(data->addr, +					       SCH5636_REG_FUJITSU_ID + i); +		if (val < 0) { +			pr_err("Could not read Fujitsu id byte at %#x\n", +				SCH5636_REG_FUJITSU_ID + i); +			err = val; +			goto error; +		} +		id[i] = val; +	} +	id[i] = '\0'; + +	if (strcmp(id, "THS")) { +		pr_err("Unknown Fujitsu id: %02x%02x%02x\n", +		       id[0], id[1], id[2]); +		err = -ENODEV; +		goto error; +	} + +	for (i = 0; i < 2; i++) { +		val = sch56xx_read_virtual_reg(data->addr, +					       SCH5636_REG_FUJITSU_REV + i); +		if (val < 0) { +			err = val; +			goto error; +		} +		revision[i] = val; +	} +	pr_info("Found %s chip at %#hx, revison: %d.%02d\n", DEVNAME, +		data->addr, revision[0], revision[1]); + +	/* Read all temp + fan ctrl registers to determine which are active */ +	for (i = 0; i < SCH5636_NO_TEMPS; i++) { +		val = sch56xx_read_virtual_reg(data->addr, +					       SCH5636_REG_TEMP_CTRL(i)); +		if (unlikely(val < 0)) { +			err = val; +			goto error; +		} +		data->temp_ctrl[i] = val; +	} + +	for (i = 0; i < SCH5636_NO_FANS; i++) { +		val = sch56xx_read_virtual_reg(data->addr, +					       SCH5636_REG_FAN_CTRL(i)); +		if (unlikely(val < 0)) { +			err = val; +			goto error; +		} +		data->fan_ctrl[i] = val; +	} + +	for (i = 0; i < ARRAY_SIZE(sch5636_attr); i++) { +		err = device_create_file(&pdev->dev, +					 &sch5636_attr[i].dev_attr); +		if (err) +			goto error; +	} + +	for (i = 0; i < (SCH5636_NO_TEMPS * 3); i++) { +		if (data->temp_ctrl[i/3] & SCH5636_TEMP_DEACTIVATED) +			continue; + +		err = device_create_file(&pdev->dev, +					&sch5636_temp_attr[i].dev_attr); +		if (err) +			goto error; +	} + +	for (i = 0; i < (SCH5636_NO_FANS * 3); i++) { +		if (data->fan_ctrl[i/3] & SCH5636_FAN_DEACTIVATED) +			continue; + +		err = device_create_file(&pdev->dev, +					&sch5636_fan_attr[i].dev_attr); +		if (err) +			goto error; +	} + +	data->hwmon_dev = hwmon_device_register(&pdev->dev); +	if (IS_ERR(data->hwmon_dev)) { +		err = PTR_ERR(data->hwmon_dev); +		data->hwmon_dev = NULL; +		goto error; +	} + +	return 0; + +error: +	sch5636_remove(pdev); +	return err; +} + +static struct platform_driver sch5636_driver = { +	.driver = { +		.owner	= THIS_MODULE, +		.name	= DRVNAME, +	}, +	.probe		= sch5636_probe, +	.remove		= sch5636_remove, +}; + +static int __init sch5636_init(void) +{ +	return platform_driver_register(&sch5636_driver); +} + +static void __exit sch5636_exit(void) +{ +	platform_driver_unregister(&sch5636_driver); +} + +MODULE_DESCRIPTION("SMSC SCH5636 Hardware Monitoring Driver"); +MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); +MODULE_LICENSE("GPL"); + +module_init(sch5636_init); +module_exit(sch5636_exit); diff --git a/drivers/hwmon/sch56xx-common.c b/drivers/hwmon/sch56xx-common.c new file mode 100644 index 000000000000..fac32ee0b10e --- /dev/null +++ b/drivers/hwmon/sch56xx-common.c @@ -0,0 +1,340 @@ +/*************************************************************************** + *   Copyright (C) 2010-2011 Hans de Goede <hdegoede@redhat.com>           * + *                                                                         * + *   This program is free software; you can redistribute it and/or modify  * + *   it under the terms of the GNU General Public License as published by  * + *   the Free Software Foundation; either version 2 of the License, or     * + *   (at your option) any later version.                                   * + *                                                                         * + *   This program is distributed in the hope that it will be useful,       * + *   but WITHOUT ANY WARRANTY; without even the implied warranty of        * + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         * + *   GNU General Public License for more details.                          * + *                                                                         * + *   You should have received a copy of the GNU General Public License     * + *   along with this program; if not, write to the                         * + *   Free Software Foundation, Inc.,                                       * + *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             * + ***************************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/acpi.h> +#include <linux/delay.h> +#include "sch56xx-common.h" + +#define SIO_SCH56XX_LD_EM	0x0C	/* Embedded uController Logical Dev */ +#define SIO_UNLOCK_KEY		0x55	/* Key to enable Super-I/O */ +#define SIO_LOCK_KEY		0xAA	/* Key to disable Super-I/O */ + +#define SIO_REG_LDSEL		0x07	/* Logical device select */ +#define SIO_REG_DEVID		0x20	/* Device ID */ +#define SIO_REG_ENABLE		0x30	/* Logical device enable */ +#define SIO_REG_ADDR		0x66	/* Logical device address (2 bytes) */ + +#define SIO_SCH5627_ID		0xC6	/* Chipset ID */ +#define SIO_SCH5636_ID		0xC7	/* Chipset ID */ + +#define REGION_LENGTH		9 + +#define SCH56XX_CMD_READ	0x02 +#define SCH56XX_CMD_WRITE	0x03 + +static struct platform_device *sch56xx_pdev; + +/* Super I/O functions */ +static inline int superio_inb(int base, int reg) +{ +	outb(reg, base); +	return inb(base + 1); +} + +static inline int superio_enter(int base) +{ +	/* Don't step on other drivers' I/O space by accident */ +	if (!request_muxed_region(base, 2, "sch56xx")) { +		pr_err("I/O address 0x%04x already in use\n", base); +		return -EBUSY; +	} + +	outb(SIO_UNLOCK_KEY, base); + +	return 0; +} + +static inline void superio_select(int base, int ld) +{ +	outb(SIO_REG_LDSEL, base); +	outb(ld, base + 1); +} + +static inline void superio_exit(int base) +{ +	outb(SIO_LOCK_KEY, base); +	release_region(base, 2); +} + +static int sch56xx_send_cmd(u16 addr, u8 cmd, u16 reg, u8 v) +{ +	u8 val; +	int i; +	/* +	 * According to SMSC for the commands we use the maximum time for +	 * the EM to respond is 15 ms, but testing shows in practice it +	 * responds within 15-32 reads, so we first busy poll, and if +	 * that fails sleep a bit and try again until we are way past +	 * the 15 ms maximum response time. +	 */ +	const int max_busy_polls = 64; +	const int max_lazy_polls = 32; + +	/* (Optional) Write-Clear the EC to Host Mailbox Register */ +	val = inb(addr + 1); +	outb(val, addr + 1); + +	/* Set Mailbox Address Pointer to first location in Region 1 */ +	outb(0x00, addr + 2); +	outb(0x80, addr + 3); + +	/* Write Request Packet Header */ +	outb(cmd, addr + 4); /* VREG Access Type read:0x02 write:0x03 */ +	outb(0x01, addr + 5); /* # of Entries: 1 Byte (8-bit) */ +	outb(0x04, addr + 2); /* Mailbox AP to first data entry loc. */ + +	/* Write Value field */ +	if (cmd == SCH56XX_CMD_WRITE) +		outb(v, addr + 4); + +	/* Write Address field */ +	outb(reg & 0xff, addr + 6); +	outb(reg >> 8, addr + 7); + +	/* Execute the Random Access Command */ +	outb(0x01, addr); /* Write 01h to the Host-to-EC register */ + +	/* EM Interface Polling "Algorithm" */ +	for (i = 0; i < max_busy_polls + max_lazy_polls; i++) { +		if (i >= max_busy_polls) +			msleep(1); +		/* Read Interrupt source Register */ +		val = inb(addr + 8); +		/* Write Clear the interrupt source bits */ +		if (val) +			outb(val, addr + 8); +		/* Command Completed ? */ +		if (val & 0x01) +			break; +	} +	if (i == max_busy_polls + max_lazy_polls) { +		pr_err("Max retries exceeded reading virtual " +		       "register 0x%04hx (%d)\n", reg, 1); +		return -EIO; +	} + +	/* +	 * According to SMSC we may need to retry this, but sofar I've always +	 * seen this succeed in 1 try. +	 */ +	for (i = 0; i < max_busy_polls; i++) { +		/* Read EC-to-Host Register */ +		val = inb(addr + 1); +		/* Command Completed ? */ +		if (val == 0x01) +			break; + +		if (i == 0) +			pr_warn("EC reports: 0x%02x reading virtual register " +				"0x%04hx\n", (unsigned int)val, reg); +	} +	if (i == max_busy_polls) { +		pr_err("Max retries exceeded reading virtual " +		       "register 0x%04hx (%d)\n", reg, 2); +		return -EIO; +	} + +	/* +	 * According to the SMSC app note we should now do: +	 * +	 * Set Mailbox Address Pointer to first location in Region 1 * +	 * outb(0x00, addr + 2); +	 * outb(0x80, addr + 3); +	 * +	 * But if we do that things don't work, so let's not. +	 */ + +	/* Read Value field */ +	if (cmd == SCH56XX_CMD_READ) +		return inb(addr + 4); + +	return 0; +} + +int sch56xx_read_virtual_reg(u16 addr, u16 reg) +{ +	return sch56xx_send_cmd(addr, SCH56XX_CMD_READ, reg, 0); +} +EXPORT_SYMBOL(sch56xx_read_virtual_reg); + +int sch56xx_write_virtual_reg(u16 addr, u16 reg, u8 val) +{ +	return sch56xx_send_cmd(addr, SCH56XX_CMD_WRITE, reg, val); +} +EXPORT_SYMBOL(sch56xx_write_virtual_reg); + +int sch56xx_read_virtual_reg16(u16 addr, u16 reg) +{ +	int lsb, msb; + +	/* Read LSB first, this will cause the matching MSB to be latched */ +	lsb = sch56xx_read_virtual_reg(addr, reg); +	if (lsb < 0) +		return lsb; + +	msb = sch56xx_read_virtual_reg(addr, reg + 1); +	if (msb < 0) +		return msb; + +	return lsb | (msb << 8); +} +EXPORT_SYMBOL(sch56xx_read_virtual_reg16); + +int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg, +			       int high_nibble) +{ +	int msb, lsn; + +	/* Read MSB first, this will cause the matching LSN to be latched */ +	msb = sch56xx_read_virtual_reg(addr, msb_reg); +	if (msb < 0) +		return msb; + +	lsn = sch56xx_read_virtual_reg(addr, lsn_reg); +	if (lsn < 0) +		return lsn; + +	if (high_nibble) +		return (msb << 4) | (lsn >> 4); +	else +		return (msb << 4) | (lsn & 0x0f); +} +EXPORT_SYMBOL(sch56xx_read_virtual_reg12); + +static int __init sch56xx_find(int sioaddr, unsigned short *address, +			       const char **name) +{ +	u8 devid; +	int err; + +	err = superio_enter(sioaddr); +	if (err) +		return err; + +	devid = superio_inb(sioaddr, SIO_REG_DEVID); +	switch (devid) { +	case SIO_SCH5627_ID: +		*name = "sch5627"; +		break; +	case SIO_SCH5636_ID: +		*name = "sch5636"; +		break; +	default: +		pr_debug("Unsupported device id: 0x%02x\n", +			 (unsigned int)devid); +		err = -ENODEV; +		goto exit; +	} + +	superio_select(sioaddr, SIO_SCH56XX_LD_EM); + +	if (!(superio_inb(sioaddr, SIO_REG_ENABLE) & 0x01)) { +		pr_warn("Device not activated\n"); +		err = -ENODEV; +		goto exit; +	} + +	/* +	 * Warning the order of the low / high byte is the other way around +	 * as on most other superio devices!! +	 */ +	*address = superio_inb(sioaddr, SIO_REG_ADDR) | +		   superio_inb(sioaddr, SIO_REG_ADDR + 1) << 8; +	if (*address == 0) { +		pr_warn("Base address not set\n"); +		err = -ENODEV; +		goto exit; +	} + +exit: +	superio_exit(sioaddr); +	return err; +} + +static int __init sch56xx_device_add(unsigned short address, const char *name) +{ +	struct resource res = { +		.start	= address, +		.end	= address + REGION_LENGTH - 1, +		.flags	= IORESOURCE_IO, +	}; +	int err; + +	sch56xx_pdev = platform_device_alloc(name, address); +	if (!sch56xx_pdev) +		return -ENOMEM; + +	res.name = sch56xx_pdev->name; +	err = acpi_check_resource_conflict(&res); +	if (err) +		goto exit_device_put; + +	err = platform_device_add_resources(sch56xx_pdev, &res, 1); +	if (err) { +		pr_err("Device resource addition failed\n"); +		goto exit_device_put; +	} + +	err = platform_device_add(sch56xx_pdev); +	if (err) { +		pr_err("Device addition failed\n"); +		goto exit_device_put; +	} + +	return 0; + +exit_device_put: +	platform_device_put(sch56xx_pdev); + +	return err; +} + +static int __init sch56xx_init(void) +{ +	int err; +	unsigned short address; +	const char *name; + +	err = sch56xx_find(0x4e, &address, &name); +	if (err) +		err = sch56xx_find(0x2e, &address, &name); +	if (err) +		return err; + +	return sch56xx_device_add(address, name); +} + +static void __exit sch56xx_exit(void) +{ +	platform_device_unregister(sch56xx_pdev); +} + +MODULE_DESCRIPTION("SMSC SCH56xx Hardware Monitoring Common Code"); +MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); +MODULE_LICENSE("GPL"); + +module_init(sch56xx_init); +module_exit(sch56xx_exit); diff --git a/drivers/hwmon/sch56xx-common.h b/drivers/hwmon/sch56xx-common.h new file mode 100644 index 000000000000..d5eaf3b9ebf5 --- /dev/null +++ b/drivers/hwmon/sch56xx-common.h @@ -0,0 +1,24 @@ +/*************************************************************************** + *   Copyright (C) 2010-2011 Hans de Goede <hdegoede@redhat.com>           * + *                                                                         * + *   This program is free software; you can redistribute it and/or modify  * + *   it under the terms of the GNU General Public License as published by  * + *   the Free Software Foundation; either version 2 of the License, or     * + *   (at your option) any later version.                                   * + *                                                                         * + *   This program is distributed in the hope that it will be useful,       * + *   but WITHOUT ANY WARRANTY; without even the implied warranty of        * + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         * + *   GNU General Public License for more details.                          * + *                                                                         * + *   You should have received a copy of the GNU General Public License     * + *   along with this program; if not, write to the                         * + *   Free Software Foundation, Inc.,                                       * + *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             * + ***************************************************************************/ + +int sch56xx_read_virtual_reg(u16 addr, u16 reg); +int sch56xx_write_virtual_reg(u16 addr, u16 reg, u8 val); +int sch56xx_read_virtual_reg16(u16 addr, u16 reg); +int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg, +			       int high_nibble); diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c index cf4330b352ef..fe4104c6b764 100644 --- a/drivers/hwmon/sht15.c +++ b/drivers/hwmon/sht15.c @@ -32,7 +32,7 @@  #include <linux/sht15.h>  #include <linux/regulator/consumer.h>  #include <linux/slab.h> -#include <asm/atomic.h> +#include <linux/atomic.h>  /* Commands */  #define SHT15_MEASURE_TEMP		0x03 @@ -671,7 +671,7 @@ static ssize_t sht15_show_status(struct device *dev,   * @buf:	sysfs buffer to read the new heater state from.   * @count:	length of the data.   * - * Will be called on read access to heater_enable sysfs attribute. + * Will be called on write access to heater_enable sysfs attribute.   * Returns number of bytes actually decoded, negative errno on error.   */  static ssize_t sht15_store_heater(struct device *dev, diff --git a/drivers/hwmon/via-cputemp.c b/drivers/hwmon/via-cputemp.c index 0d18de424c66..8eac67d769fa 100644 --- a/drivers/hwmon/via-cputemp.c +++ b/drivers/hwmon/via-cputemp.c @@ -27,6 +27,7 @@  #include <linux/init.h>  #include <linux/slab.h>  #include <linux/hwmon.h> +#include <linux/hwmon-vid.h>  #include <linux/sysfs.h>  #include <linux/hwmon-sysfs.h>  #include <linux/err.h> @@ -48,8 +49,10 @@ enum { SHOW_TEMP, SHOW_LABEL, SHOW_NAME };  struct via_cputemp_data {  	struct device *hwmon_dev;  	const char *name; +	u8 vrm;  	u32 id; -	u32 msr; +	u32 msr_temp; +	u32 msr_vid;  };  /* @@ -77,13 +80,27 @@ static ssize_t show_temp(struct device *dev,  	u32 eax, edx;  	int err; -	err = rdmsr_safe_on_cpu(data->id, data->msr, &eax, &edx); +	err = rdmsr_safe_on_cpu(data->id, data->msr_temp, &eax, &edx);  	if (err)  		return -EAGAIN;  	return sprintf(buf, "%lu\n", ((unsigned long)eax & 0xffffff) * 1000);  } +static ssize_t show_cpu_vid(struct device *dev, +			    struct device_attribute *devattr, char *buf) +{ +	struct via_cputemp_data *data = dev_get_drvdata(dev); +	u32 eax, edx; +	int err; + +	err = rdmsr_safe_on_cpu(data->id, data->msr_vid, &eax, &edx); +	if (err) +		return -EAGAIN; + +	return sprintf(buf, "%d\n", vid_from_reg(~edx & 0x7f, data->vrm)); +} +  static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL,  			  SHOW_TEMP);  static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_name, NULL, SHOW_LABEL); @@ -100,6 +117,9 @@ static const struct attribute_group via_cputemp_group = {  	.attrs = via_cputemp_attributes,  }; +/* Optional attributes */ +static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_cpu_vid, NULL); +  static int __devinit via_cputemp_probe(struct platform_device *pdev)  {  	struct via_cputemp_data *data; @@ -122,11 +142,12 @@ static int __devinit via_cputemp_probe(struct platform_device *pdev)  		/* C7 A */  	case 0xD:  		/* C7 D */ -		data->msr = 0x1169; +		data->msr_temp = 0x1169; +		data->msr_vid = 0x198;  		break;  	case 0xF:  		/* Nano */ -		data->msr = 0x1423; +		data->msr_temp = 0x1423;  		break;  	default:  		err = -ENODEV; @@ -134,7 +155,7 @@ static int __devinit via_cputemp_probe(struct platform_device *pdev)  	}  	/* test if we can access the TEMPERATURE MSR */ -	err = rdmsr_safe_on_cpu(data->id, data->msr, &eax, &edx); +	err = rdmsr_safe_on_cpu(data->id, data->msr_temp, &eax, &edx);  	if (err) {  		dev_err(&pdev->dev,  			"Unable to access TEMPERATURE MSR, giving up\n"); @@ -147,6 +168,15 @@ static int __devinit via_cputemp_probe(struct platform_device *pdev)  	if (err)  		goto exit_free; +	if (data->msr_vid) +		data->vrm = vid_which_vrm(); + +	if (data->vrm) { +		err = device_create_file(&pdev->dev, &dev_attr_cpu0_vid); +		if (err) +			goto exit_remove; +	} +  	data->hwmon_dev = hwmon_device_register(&pdev->dev);  	if (IS_ERR(data->hwmon_dev)) {  		err = PTR_ERR(data->hwmon_dev); @@ -158,6 +188,8 @@ static int __devinit via_cputemp_probe(struct platform_device *pdev)  	return 0;  exit_remove: +	if (data->vrm) +		device_remove_file(&pdev->dev, &dev_attr_cpu0_vid);  	sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group);  exit_free:  	platform_set_drvdata(pdev, NULL); @@ -171,6 +203,8 @@ static int __devexit via_cputemp_remove(struct platform_device *pdev)  	struct via_cputemp_data *data = platform_get_drvdata(pdev);  	hwmon_device_unregister(data->hwmon_dev); +	if (data->vrm) +		device_remove_file(&pdev->dev, &dev_attr_cpu0_vid);  	sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group);  	platform_set_drvdata(pdev, NULL);  	kfree(data); | 
