aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2009-12-17 19:48:08 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2009-12-17 19:48:08 -0500
commite82b1dae2a8730c89e2a30c5c28562ef066f39d9 (patch)
tree4811b7fd81c45d6d9b35bbd0519f8bc232dde616
parentfc6f0700d5cd54b5f8b30c4f0d93b06a6ba04b81 (diff)
parentc46c0e9188685c0276b4c0adf9fb7e903937e35b (diff)
Merge branch 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelvare/staging
* 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelvare/staging: hwmon: (w83627hf) Fix for "No such device" hwmon: (sht15) Off-by-one error in array index + incorrect constants hwmon: Add driver for VIA CPU core temperature hwmon: (smsc47m1) Enable device if needed hwmon: (smsc47m1) Fail module loading on error hwmon: (smsc47m1) Only request I/O ports we really use hwmon: New driver for AMD Family 10h/11h CPUs
-rw-r--r--Documentation/hwmon/k10temp60
-rw-r--r--drivers/hwmon/Kconfig20
-rw-r--r--drivers/hwmon/Makefile2
-rw-r--r--drivers/hwmon/k10temp.c197
-rw-r--r--drivers/hwmon/sht15.c6
-rw-r--r--drivers/hwmon/smsc47m1.c153
-rw-r--r--drivers/hwmon/via-cputemp.c356
-rw-r--r--drivers/hwmon/w83627hf.c2
8 files changed, 766 insertions, 30 deletions
diff --git a/Documentation/hwmon/k10temp b/Documentation/hwmon/k10temp
new file mode 100644
index 000000000000..a7a18d453a51
--- /dev/null
+++ b/Documentation/hwmon/k10temp
@@ -0,0 +1,60 @@
1Kernel driver k10temp
2=====================
3
4Supported chips:
5* AMD Family 10h processors:
6 Socket F: Quad-Core/Six-Core/Embedded Opteron
7 Socket AM2+: Opteron, Phenom (II) X3/X4
8 Socket AM3: Quad-Core Opteron, Athlon/Phenom II X2/X3/X4, Sempron II
9 Socket S1G3: Athlon II, Sempron, Turion II
10* AMD Family 11h processors:
11 Socket S1G2: Athlon (X2), Sempron (X2), Turion X2 (Ultra)
12
13 Prefix: 'k10temp'
14 Addresses scanned: PCI space
15 Datasheets:
16 BIOS and Kernel Developer's Guide (BKDG) For AMD Family 10h Processors:
17 http://support.amd.com/us/Processor_TechDocs/31116.pdf
18 BIOS and Kernel Developer's Guide (BKDG) for AMD Family 11h Processors:
19 http://support.amd.com/us/Processor_TechDocs/41256.pdf
20 Revision Guide for AMD Family 10h Processors:
21 http://support.amd.com/us/Processor_TechDocs/41322.pdf
22 Revision Guide for AMD Family 11h Processors:
23 http://support.amd.com/us/Processor_TechDocs/41788.pdf
24 AMD Family 11h Processor Power and Thermal Data Sheet for Notebooks:
25 http://support.amd.com/us/Processor_TechDocs/43373.pdf
26 AMD Family 10h Server and Workstation Processor Power and Thermal Data Sheet:
27 http://support.amd.com/us/Processor_TechDocs/43374.pdf
28 AMD Family 10h Desktop Processor Power and Thermal Data Sheet:
29 http://support.amd.com/us/Processor_TechDocs/43375.pdf
30
31Author: Clemens Ladisch <clemens@ladisch.de>
32
33Description
34-----------
35
36This driver permits reading of the internal temperature sensor of AMD
37Family 10h and 11h processors.
38
39All these processors have a sensor, but on older revisions of Family 10h
40processors, the sensor may return inconsistent values (erratum 319). The
41driver will refuse to load on these revisions unless you specify the
42"force=1" module parameter.
43
44There is one temperature measurement value, available as temp1_input in
45sysfs. It is measured in degrees Celsius with a resolution of 1/8th degree.
46Please note that it is defined as a relative value; to quote the AMD manual:
47
48 Tctl is the processor temperature control value, used by the platform to
49 control cooling systems. Tctl is a non-physical temperature on an
50 arbitrary scale measured in degrees. It does _not_ represent an actual
51 physical temperature like die or case temperature. Instead, it specifies
52 the processor temperature relative to the point at which the system must
53 supply the maximum cooling for the processor's specified maximum case
54 temperature and maximum thermal power dissipation.
55
56The maximum value for Tctl is available in the file temp1_max.
57
58If the BIOS has enabled hardware temperature control, the threshold at
59which the processor will throttle itself to avoid damage is available in
60temp1_crit and temp1_crit_hyst.
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index bf28945c610d..46c3c566307e 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -228,6 +228,18 @@ config SENSORS_K8TEMP
228 This driver can also be built as a module. If so, the module 228 This driver can also be built as a module. If so, the module
229 will be called k8temp. 229 will be called k8temp.
230 230
231config SENSORS_K10TEMP
232 tristate "AMD Phenom/Sempron/Turion/Opteron temperature sensor"
233 depends on X86 && PCI
234 help
235 If you say yes here you get support for the temperature
236 sensor(s) inside your CPU. Supported are later revisions of
237 the AMD Family 10h and all revisions of the AMD Family 11h
238 microarchitectures.
239
240 This driver can also be built as a module. If so, the module
241 will be called k10temp.
242
231config SENSORS_AMS 243config SENSORS_AMS
232 tristate "Apple Motion Sensor driver" 244 tristate "Apple Motion Sensor driver"
233 depends on PPC_PMAC && !PPC64 && INPUT && ((ADB_PMU && I2C = y) || (ADB_PMU && !I2C) || I2C) && EXPERIMENTAL 245 depends on PPC_PMAC && !PPC64 && INPUT && ((ADB_PMU && I2C = y) || (ADB_PMU && !I2C) || I2C) && EXPERIMENTAL
@@ -810,6 +822,14 @@ config SENSORS_TMP421
810 This driver can also be built as a module. If so, the module 822 This driver can also be built as a module. If so, the module
811 will be called tmp421. 823 will be called tmp421.
812 824
825config SENSORS_VIA_CPUTEMP
826 tristate "VIA CPU temperature sensor"
827 depends on X86
828 help
829 If you say yes here you get support for the temperature
830 sensor inside your CPU. Supported are all known variants of
831 the VIA C7 and Nano.
832
813config SENSORS_VIA686A 833config SENSORS_VIA686A
814 tristate "VIA686A" 834 tristate "VIA686A"
815 depends on PCI 835 depends on PCI
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 4131e253f96a..450c8e894277 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -53,6 +53,7 @@ obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o
53obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o 53obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o
54obj-$(CONFIG_SENSORS_IT87) += it87.o 54obj-$(CONFIG_SENSORS_IT87) += it87.o
55obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o 55obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o
56obj-$(CONFIG_SENSORS_K10TEMP) += k10temp.o
56obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o 57obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o
57obj-$(CONFIG_SENSORS_LIS3_SPI) += lis3lv02d.o lis3lv02d_spi.o 58obj-$(CONFIG_SENSORS_LIS3_SPI) += lis3lv02d.o lis3lv02d_spi.o
58obj-$(CONFIG_SENSORS_LIS3_I2C) += lis3lv02d.o lis3lv02d_i2c.o 59obj-$(CONFIG_SENSORS_LIS3_I2C) += lis3lv02d.o lis3lv02d_i2c.o
@@ -88,6 +89,7 @@ obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
88obj-$(CONFIG_SENSORS_THMC50) += thmc50.o 89obj-$(CONFIG_SENSORS_THMC50) += thmc50.o
89obj-$(CONFIG_SENSORS_TMP401) += tmp401.o 90obj-$(CONFIG_SENSORS_TMP401) += tmp401.o
90obj-$(CONFIG_SENSORS_TMP421) += tmp421.o 91obj-$(CONFIG_SENSORS_TMP421) += tmp421.o
92obj-$(CONFIG_SENSORS_VIA_CPUTEMP)+= via-cputemp.o
91obj-$(CONFIG_SENSORS_VIA686A) += via686a.o 93obj-$(CONFIG_SENSORS_VIA686A) += via686a.o
92obj-$(CONFIG_SENSORS_VT1211) += vt1211.o 94obj-$(CONFIG_SENSORS_VT1211) += vt1211.o
93obj-$(CONFIG_SENSORS_VT8231) += vt8231.o 95obj-$(CONFIG_SENSORS_VT8231) += vt8231.o
diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c
new file mode 100644
index 000000000000..d8a26d16d948
--- /dev/null
+++ b/drivers/hwmon/k10temp.c
@@ -0,0 +1,197 @@
1/*
2 * k10temp.c - AMD Family 10h/11h processor hardware monitoring
3 *
4 * Copyright (c) 2009 Clemens Ladisch <clemens@ladisch.de>
5 *
6 *
7 * This driver is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This driver is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this driver; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <linux/err.h>
21#include <linux/hwmon.h>
22#include <linux/hwmon-sysfs.h>
23#include <linux/init.h>
24#include <linux/module.h>
25#include <linux/pci.h>
26#include <asm/processor.h>
27
28MODULE_DESCRIPTION("AMD Family 10h/11h CPU core temperature monitor");
29MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
30MODULE_LICENSE("GPL");
31
32static bool force;
33module_param(force, bool, 0444);
34MODULE_PARM_DESC(force, "force loading on processors with erratum 319");
35
36#define REG_HARDWARE_THERMAL_CONTROL 0x64
37#define HTC_ENABLE 0x00000001
38
39#define REG_REPORTED_TEMPERATURE 0xa4
40
41#define REG_NORTHBRIDGE_CAPABILITIES 0xe8
42#define NB_CAP_HTC 0x00000400
43
44static ssize_t show_temp(struct device *dev,
45 struct device_attribute *attr, char *buf)
46{
47 u32 regval;
48
49 pci_read_config_dword(to_pci_dev(dev),
50 REG_REPORTED_TEMPERATURE, &regval);
51 return sprintf(buf, "%u\n", (regval >> 21) * 125);
52}
53
54static ssize_t show_temp_max(struct device *dev,
55 struct device_attribute *attr, char *buf)
56{
57 return sprintf(buf, "%d\n", 70 * 1000);
58}
59
60static ssize_t show_temp_crit(struct device *dev,
61 struct device_attribute *devattr, char *buf)
62{
63 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
64 int show_hyst = attr->index;
65 u32 regval;
66 int value;
67
68 pci_read_config_dword(to_pci_dev(dev),
69 REG_HARDWARE_THERMAL_CONTROL, &regval);
70 value = ((regval >> 16) & 0x7f) * 500 + 52000;
71 if (show_hyst)
72 value -= ((regval >> 24) & 0xf) * 500;
73 return sprintf(buf, "%d\n", value);
74}
75
76static ssize_t show_name(struct device *dev,
77 struct device_attribute *attr, char *buf)
78{
79 return sprintf(buf, "k10temp\n");
80}
81
82static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
83static DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_max, NULL);
84static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp_crit, NULL, 0);
85static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, show_temp_crit, NULL, 1);
86static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
87
88static bool __devinit has_erratum_319(void)
89{
90 /*
91 * Erratum 319: The thermal sensor of older Family 10h processors
92 * (B steppings) may be unreliable.
93 */
94 return boot_cpu_data.x86 == 0x10 && boot_cpu_data.x86_model <= 2;
95}
96
97static int __devinit k10temp_probe(struct pci_dev *pdev,
98 const struct pci_device_id *id)
99{
100 struct device *hwmon_dev;
101 u32 reg_caps, reg_htc;
102 int err;
103
104 if (has_erratum_319() && !force) {
105 dev_err(&pdev->dev,
106 "unreliable CPU thermal sensor; monitoring disabled\n");
107 err = -ENODEV;
108 goto exit;
109 }
110
111 err = device_create_file(&pdev->dev, &dev_attr_temp1_input);
112 if (err)
113 goto exit;
114 err = device_create_file(&pdev->dev, &dev_attr_temp1_max);
115 if (err)
116 goto exit_remove;
117
118 pci_read_config_dword(pdev, REG_NORTHBRIDGE_CAPABILITIES, &reg_caps);
119 pci_read_config_dword(pdev, REG_HARDWARE_THERMAL_CONTROL, &reg_htc);
120 if ((reg_caps & NB_CAP_HTC) && (reg_htc & HTC_ENABLE)) {
121 err = device_create_file(&pdev->dev,
122 &sensor_dev_attr_temp1_crit.dev_attr);
123 if (err)
124 goto exit_remove;
125 err = device_create_file(&pdev->dev,
126 &sensor_dev_attr_temp1_crit_hyst.dev_attr);
127 if (err)
128 goto exit_remove;
129 }
130
131 err = device_create_file(&pdev->dev, &dev_attr_name);
132 if (err)
133 goto exit_remove;
134
135 hwmon_dev = hwmon_device_register(&pdev->dev);
136 if (IS_ERR(hwmon_dev)) {
137 err = PTR_ERR(hwmon_dev);
138 goto exit_remove;
139 }
140 dev_set_drvdata(&pdev->dev, hwmon_dev);
141
142 if (has_erratum_319() && force)
143 dev_warn(&pdev->dev,
144 "unreliable CPU thermal sensor; check erratum 319\n");
145 return 0;
146
147exit_remove:
148 device_remove_file(&pdev->dev, &dev_attr_name);
149 device_remove_file(&pdev->dev, &dev_attr_temp1_input);
150 device_remove_file(&pdev->dev, &dev_attr_temp1_max);
151 device_remove_file(&pdev->dev,
152 &sensor_dev_attr_temp1_crit.dev_attr);
153 device_remove_file(&pdev->dev,
154 &sensor_dev_attr_temp1_crit_hyst.dev_attr);
155exit:
156 return err;
157}
158
159static void __devexit k10temp_remove(struct pci_dev *pdev)
160{
161 hwmon_device_unregister(dev_get_drvdata(&pdev->dev));
162 device_remove_file(&pdev->dev, &dev_attr_name);
163 device_remove_file(&pdev->dev, &dev_attr_temp1_input);
164 device_remove_file(&pdev->dev, &dev_attr_temp1_max);
165 device_remove_file(&pdev->dev,
166 &sensor_dev_attr_temp1_crit.dev_attr);
167 device_remove_file(&pdev->dev,
168 &sensor_dev_attr_temp1_crit_hyst.dev_attr);
169 dev_set_drvdata(&pdev->dev, NULL);
170}
171
172static struct pci_device_id k10temp_id_table[] = {
173 { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_10H_NB_MISC) },
174 { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_11H_NB_MISC) },
175 {}
176};
177MODULE_DEVICE_TABLE(pci, k10temp_id_table);
178
179static struct pci_driver k10temp_driver = {
180 .name = "k10temp",
181 .id_table = k10temp_id_table,
182 .probe = k10temp_probe,
183 .remove = __devexit_p(k10temp_remove),
184};
185
186static int __init k10temp_init(void)
187{
188 return pci_register_driver(&k10temp_driver);
189}
190
191static void __exit k10temp_exit(void)
192{
193 pci_unregister_driver(&k10temp_driver);
194}
195
196module_init(k10temp_init)
197module_exit(k10temp_exit)
diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c
index ebe38b680ee3..864a371f6eb9 100644
--- a/drivers/hwmon/sht15.c
+++ b/drivers/hwmon/sht15.c
@@ -305,7 +305,7 @@ static inline int sht15_calc_temp(struct sht15_data *data)
305 int d1 = 0; 305 int d1 = 0;
306 int i; 306 int i;
307 307
308 for (i = 1; i < ARRAY_SIZE(temppoints) - 1; i++) 308 for (i = 1; i < ARRAY_SIZE(temppoints); i++)
309 /* Find pointer to interpolate */ 309 /* Find pointer to interpolate */
310 if (data->supply_uV > temppoints[i - 1].vdd) { 310 if (data->supply_uV > temppoints[i - 1].vdd) {
311 d1 = (data->supply_uV/1000 - temppoints[i - 1].vdd) 311 d1 = (data->supply_uV/1000 - temppoints[i - 1].vdd)
@@ -332,12 +332,12 @@ static inline int sht15_calc_humid(struct sht15_data *data)
332 332
333 const int c1 = -4; 333 const int c1 = -4;
334 const int c2 = 40500; /* x 10 ^ -6 */ 334 const int c2 = 40500; /* x 10 ^ -6 */
335 const int c3 = 2800; /* x10 ^ -9 */ 335 const int c3 = -2800; /* x10 ^ -9 */
336 336
337 RHlinear = c1*1000 337 RHlinear = c1*1000
338 + c2 * data->val_humid/1000 338 + c2 * data->val_humid/1000
339 + (data->val_humid * data->val_humid * c3)/1000000; 339 + (data->val_humid * data->val_humid * c3)/1000000;
340 return (temp - 25000) * (10000 + 800 * data->val_humid) 340 return (temp - 25000) * (10000 + 80 * data->val_humid)
341 / 1000000 + RHlinear; 341 / 1000000 + RHlinear;
342} 342}
343 343
diff --git a/drivers/hwmon/smsc47m1.c b/drivers/hwmon/smsc47m1.c
index 8ad50fdba00d..9ca97818bd4b 100644
--- a/drivers/hwmon/smsc47m1.c
+++ b/drivers/hwmon/smsc47m1.c
@@ -136,11 +136,11 @@ struct smsc47m1_data {
136 136
137struct smsc47m1_sio_data { 137struct smsc47m1_sio_data {
138 enum chips type; 138 enum chips type;
139 u8 activate; /* Remember initial device state */
139}; 140};
140 141
141 142
142static int smsc47m1_probe(struct platform_device *pdev); 143static int __exit smsc47m1_remove(struct platform_device *pdev);
143static int __devexit smsc47m1_remove(struct platform_device *pdev);
144static struct smsc47m1_data *smsc47m1_update_device(struct device *dev, 144static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
145 int init); 145 int init);
146 146
@@ -160,8 +160,7 @@ static struct platform_driver smsc47m1_driver = {
160 .owner = THIS_MODULE, 160 .owner = THIS_MODULE,
161 .name = DRVNAME, 161 .name = DRVNAME,
162 }, 162 },
163 .probe = smsc47m1_probe, 163 .remove = __exit_p(smsc47m1_remove),
164 .remove = __devexit_p(smsc47m1_remove),
165}; 164};
166 165
167static ssize_t get_fan(struct device *dev, struct device_attribute 166static ssize_t get_fan(struct device *dev, struct device_attribute
@@ -470,24 +469,126 @@ static int __init smsc47m1_find(unsigned short *addr,
470 superio_select(); 469 superio_select();
471 *addr = (superio_inb(SUPERIO_REG_BASE) << 8) 470 *addr = (superio_inb(SUPERIO_REG_BASE) << 8)
472 | superio_inb(SUPERIO_REG_BASE + 1); 471 | superio_inb(SUPERIO_REG_BASE + 1);
473 val = superio_inb(SUPERIO_REG_ACT); 472 if (*addr == 0) {
474 if (*addr == 0 || (val & 0x01) == 0) { 473 pr_info(DRVNAME ": Device address not set, will not use\n");
475 pr_info(DRVNAME ": Device is disabled, will not use\n");
476 superio_exit(); 474 superio_exit();
477 return -ENODEV; 475 return -ENODEV;
478 } 476 }
479 477
478 /* Enable only if address is set (needed at least on the
479 * Compaq Presario S4000NX) */
480 sio_data->activate = superio_inb(SUPERIO_REG_ACT);
481 if ((sio_data->activate & 0x01) == 0) {
482 pr_info(DRVNAME ": Enabling device\n");
483 superio_outb(SUPERIO_REG_ACT, sio_data->activate | 0x01);
484 }
485
480 superio_exit(); 486 superio_exit();
481 return 0; 487 return 0;
482} 488}
483 489
484static int __devinit smsc47m1_probe(struct platform_device *pdev) 490/* Restore device to its initial state */
491static void __init smsc47m1_restore(const struct smsc47m1_sio_data *sio_data)
492{
493 if ((sio_data->activate & 0x01) == 0) {
494 superio_enter();
495 superio_select();
496
497 pr_info(DRVNAME ": Disabling device\n");
498 superio_outb(SUPERIO_REG_ACT, sio_data->activate);
499
500 superio_exit();
501 }
502}
503
504#define CHECK 1
505#define REQUEST 2
506#define RELEASE 3
507
508/*
509 * This function can be used to:
510 * - test for resource conflicts with ACPI
511 * - request the resources
512 * - release the resources
513 * We only allocate the I/O ports we really need, to minimize the risk of
514 * conflicts with ACPI or with other drivers.
515 */
516static int smsc47m1_handle_resources(unsigned short address, enum chips type,
517 int action, struct device *dev)
518{
519 static const u8 ports_m1[] = {
520 /* register, region length */
521 0x04, 1,
522 0x33, 4,
523 0x56, 7,
524 };
525
526 static const u8 ports_m2[] = {
527 /* register, region length */
528 0x04, 1,
529 0x09, 1,
530 0x2c, 2,
531 0x35, 4,
532 0x56, 7,
533 0x69, 4,
534 };
535
536 int i, ports_size, err;
537 const u8 *ports;
538
539 switch (type) {
540 case smsc47m1:
541 default:
542 ports = ports_m1;
543 ports_size = ARRAY_SIZE(ports_m1);
544 break;
545 case smsc47m2:
546 ports = ports_m2;
547 ports_size = ARRAY_SIZE(ports_m2);
548 break;
549 }
550
551 for (i = 0; i + 1 < ports_size; i += 2) {
552 unsigned short start = address + ports[i];
553 unsigned short len = ports[i + 1];
554
555 switch (action) {
556 case CHECK:
557 /* Only check for conflicts */
558 err = acpi_check_region(start, len, DRVNAME);
559 if (err)
560 return err;
561 break;
562 case REQUEST:
563 /* Request the resources */
564 if (!request_region(start, len, DRVNAME)) {
565 dev_err(dev, "Region 0x%hx-0x%hx already in "
566 "use!\n", start, start + len);
567
568 /* Undo all requests */
569 for (i -= 2; i >= 0; i -= 2)
570 release_region(address + ports[i],
571 ports[i + 1]);
572 return -EBUSY;
573 }
574 break;
575 case RELEASE:
576 /* Release the resources */
577 release_region(start, len);
578 break;
579 }
580 }
581
582 return 0;
583}
584
585static int __init smsc47m1_probe(struct platform_device *pdev)
485{ 586{
486 struct device *dev = &pdev->dev; 587 struct device *dev = &pdev->dev;
487 struct smsc47m1_sio_data *sio_data = dev->platform_data; 588 struct smsc47m1_sio_data *sio_data = dev->platform_data;
488 struct smsc47m1_data *data; 589 struct smsc47m1_data *data;
489 struct resource *res; 590 struct resource *res;
490 int err = 0; 591 int err;
491 int fan1, fan2, fan3, pwm1, pwm2, pwm3; 592 int fan1, fan2, fan3, pwm1, pwm2, pwm3;
492 593
493 static const char *names[] = { 594 static const char *names[] = {
@@ -496,12 +597,10 @@ static int __devinit smsc47m1_probe(struct platform_device *pdev)
496 }; 597 };
497 598
498 res = platform_get_resource(pdev, IORESOURCE_IO, 0); 599 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
499 if (!request_region(res->start, SMSC_EXTENT, DRVNAME)) { 600 err = smsc47m1_handle_resources(res->start, sio_data->type,
500 dev_err(dev, "Region 0x%lx-0x%lx already in use!\n", 601 REQUEST, dev);
501 (unsigned long)res->start, 602 if (err < 0)
502 (unsigned long)res->end); 603 return err;
503 return -EBUSY;
504 }
505 604
506 if (!(data = kzalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) { 605 if (!(data = kzalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) {
507 err = -ENOMEM; 606 err = -ENOMEM;
@@ -637,11 +736,11 @@ error_free:
637 platform_set_drvdata(pdev, NULL); 736 platform_set_drvdata(pdev, NULL);
638 kfree(data); 737 kfree(data);
639error_release: 738error_release:
640 release_region(res->start, SMSC_EXTENT); 739 smsc47m1_handle_resources(res->start, sio_data->type, RELEASE, dev);
641 return err; 740 return err;
642} 741}
643 742
644static int __devexit smsc47m1_remove(struct platform_device *pdev) 743static int __exit smsc47m1_remove(struct platform_device *pdev)
645{ 744{
646 struct smsc47m1_data *data = platform_get_drvdata(pdev); 745 struct smsc47m1_data *data = platform_get_drvdata(pdev);
647 struct resource *res; 746 struct resource *res;
@@ -650,7 +749,7 @@ static int __devexit smsc47m1_remove(struct platform_device *pdev)
650 sysfs_remove_group(&pdev->dev.kobj, &smsc47m1_group); 749 sysfs_remove_group(&pdev->dev.kobj, &smsc47m1_group);
651 750
652 res = platform_get_resource(pdev, IORESOURCE_IO, 0); 751 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
653 release_region(res->start, SMSC_EXTENT); 752 smsc47m1_handle_resources(res->start, data->type, RELEASE, &pdev->dev);
654 platform_set_drvdata(pdev, NULL); 753 platform_set_drvdata(pdev, NULL);
655 kfree(data); 754 kfree(data);
656 755
@@ -717,7 +816,7 @@ static int __init smsc47m1_device_add(unsigned short address,
717 }; 816 };
718 int err; 817 int err;
719 818
720 err = acpi_check_resource_conflict(&res); 819 err = smsc47m1_handle_resources(address, sio_data->type, CHECK, NULL);
721 if (err) 820 if (err)
722 goto exit; 821 goto exit;
723 822
@@ -766,27 +865,29 @@ static int __init sm_smsc47m1_init(void)
766 if (smsc47m1_find(&address, &sio_data)) 865 if (smsc47m1_find(&address, &sio_data))
767 return -ENODEV; 866 return -ENODEV;
768 867
769 err = platform_driver_register(&smsc47m1_driver); 868 /* Sets global pdev as a side effect */
869 err = smsc47m1_device_add(address, &sio_data);
770 if (err) 870 if (err)
771 goto exit; 871 goto exit;
772 872
773 /* Sets global pdev as a side effect */ 873 err = platform_driver_probe(&smsc47m1_driver, smsc47m1_probe);
774 err = smsc47m1_device_add(address, &sio_data);
775 if (err) 874 if (err)
776 goto exit_driver; 875 goto exit_device;
777 876
778 return 0; 877 return 0;
779 878
780exit_driver: 879exit_device:
781 platform_driver_unregister(&smsc47m1_driver); 880 platform_device_unregister(pdev);
881 smsc47m1_restore(&sio_data);
782exit: 882exit:
783 return err; 883 return err;
784} 884}
785 885
786static void __exit sm_smsc47m1_exit(void) 886static void __exit sm_smsc47m1_exit(void)
787{ 887{
788 platform_device_unregister(pdev);
789 platform_driver_unregister(&smsc47m1_driver); 888 platform_driver_unregister(&smsc47m1_driver);
889 smsc47m1_restore(pdev->dev.platform_data);
890 platform_device_unregister(pdev);
790} 891}
791 892
792MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>"); 893MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
diff --git a/drivers/hwmon/via-cputemp.c b/drivers/hwmon/via-cputemp.c
new file mode 100644
index 000000000000..7442cf754856
--- /dev/null
+++ b/drivers/hwmon/via-cputemp.c
@@ -0,0 +1,356 @@
1/*
2 * via-cputemp.c - Driver for VIA CPU core temperature monitoring
3 * Copyright (C) 2009 VIA Technologies, Inc.
4 *
5 * based on existing coretemp.c, which is
6 *
7 * Copyright (C) 2007 Rudolf Marek <r.marek@assembler.cz>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 2 of the License.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 * 02110-1301 USA.
22 */
23
24#include <linux/module.h>
25#include <linux/delay.h>
26#include <linux/init.h>
27#include <linux/slab.h>
28#include <linux/jiffies.h>
29#include <linux/hwmon.h>
30#include <linux/sysfs.h>
31#include <linux/hwmon-sysfs.h>
32#include <linux/err.h>
33#include <linux/mutex.h>
34#include <linux/list.h>
35#include <linux/platform_device.h>
36#include <linux/cpu.h>
37#include <asm/msr.h>
38#include <asm/processor.h>
39
40#define DRVNAME "via_cputemp"
41
42enum { SHOW_TEMP, SHOW_LABEL, SHOW_NAME } SHOW;
43
44/*
45 * Functions declaration
46 */
47
48struct via_cputemp_data {
49 struct device *hwmon_dev;
50 const char *name;
51 u32 id;
52 u32 msr;
53};
54
55/*
56 * Sysfs stuff
57 */
58
59static ssize_t show_name(struct device *dev, struct device_attribute
60 *devattr, char *buf)
61{
62 int ret;
63 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
64 struct via_cputemp_data *data = dev_get_drvdata(dev);
65
66 if (attr->index == SHOW_NAME)
67 ret = sprintf(buf, "%s\n", data->name);
68 else /* show label */
69 ret = sprintf(buf, "Core %d\n", data->id);
70 return ret;
71}
72
73static ssize_t show_temp(struct device *dev,
74 struct device_attribute *devattr, char *buf)
75{
76 struct via_cputemp_data *data = dev_get_drvdata(dev);
77 u32 eax, edx;
78 int err;
79
80 err = rdmsr_safe_on_cpu(data->id, data->msr, &eax, &edx);
81 if (err)
82 return -EAGAIN;
83
84 return sprintf(buf, "%lu\n", ((unsigned long)eax & 0xffffff) * 1000);
85}
86
87static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL,
88 SHOW_TEMP);
89static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_name, NULL, SHOW_LABEL);
90static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, SHOW_NAME);
91
92static struct attribute *via_cputemp_attributes[] = {
93 &sensor_dev_attr_name.dev_attr.attr,
94 &sensor_dev_attr_temp1_label.dev_attr.attr,
95 &sensor_dev_attr_temp1_input.dev_attr.attr,
96 NULL
97};
98
99static const struct attribute_group via_cputemp_group = {
100 .attrs = via_cputemp_attributes,
101};
102
103static int __devinit via_cputemp_probe(struct platform_device *pdev)
104{
105 struct via_cputemp_data *data;
106 struct cpuinfo_x86 *c = &cpu_data(pdev->id);
107 int err;
108 u32 eax, edx;
109
110 data = kzalloc(sizeof(struct via_cputemp_data), GFP_KERNEL);
111 if (!data) {
112 err = -ENOMEM;
113 dev_err(&pdev->dev, "Out of memory\n");
114 goto exit;
115 }
116
117 data->id = pdev->id;
118 data->name = "via_cputemp";
119
120 switch (c->x86_model) {
121 case 0xA:
122 /* C7 A */
123 case 0xD:
124 /* C7 D */
125 data->msr = 0x1169;
126 break;
127 case 0xF:
128 /* Nano */
129 data->msr = 0x1423;
130 break;
131 default:
132 err = -ENODEV;
133 goto exit_free;
134 }
135
136 /* test if we can access the TEMPERATURE MSR */
137 err = rdmsr_safe_on_cpu(data->id, data->msr, &eax, &edx);
138 if (err) {
139 dev_err(&pdev->dev,
140 "Unable to access TEMPERATURE MSR, giving up\n");
141 goto exit_free;
142 }
143
144 platform_set_drvdata(pdev, data);
145
146 err = sysfs_create_group(&pdev->dev.kobj, &via_cputemp_group);
147 if (err)
148 goto exit_free;
149
150 data->hwmon_dev = hwmon_device_register(&pdev->dev);
151 if (IS_ERR(data->hwmon_dev)) {
152 err = PTR_ERR(data->hwmon_dev);
153 dev_err(&pdev->dev, "Class registration failed (%d)\n",
154 err);
155 goto exit_remove;
156 }
157
158 return 0;
159
160exit_remove:
161 sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group);
162exit_free:
163 platform_set_drvdata(pdev, NULL);
164 kfree(data);
165exit:
166 return err;
167}
168
169static int __devexit via_cputemp_remove(struct platform_device *pdev)
170{
171 struct via_cputemp_data *data = platform_get_drvdata(pdev);
172
173 hwmon_device_unregister(data->hwmon_dev);
174 sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group);
175 platform_set_drvdata(pdev, NULL);
176 kfree(data);
177 return 0;
178}
179
180static struct platform_driver via_cputemp_driver = {
181 .driver = {
182 .owner = THIS_MODULE,
183 .name = DRVNAME,
184 },
185 .probe = via_cputemp_probe,
186 .remove = __devexit_p(via_cputemp_remove),
187};
188
189struct pdev_entry {
190 struct list_head list;
191 struct platform_device *pdev;
192 unsigned int cpu;
193};
194
195static LIST_HEAD(pdev_list);
196static DEFINE_MUTEX(pdev_list_mutex);
197
198static int __cpuinit via_cputemp_device_add(unsigned int cpu)
199{
200 int err;
201 struct platform_device *pdev;
202 struct pdev_entry *pdev_entry;
203
204 pdev = platform_device_alloc(DRVNAME, cpu);
205 if (!pdev) {
206 err = -ENOMEM;
207 printk(KERN_ERR DRVNAME ": Device allocation failed\n");
208 goto exit;
209 }
210
211 pdev_entry = kzalloc(sizeof(struct pdev_entry), GFP_KERNEL);
212 if (!pdev_entry) {
213 err = -ENOMEM;
214 goto exit_device_put;
215 }
216
217 err = platform_device_add(pdev);
218 if (err) {
219 printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n",
220 err);
221 goto exit_device_free;
222 }
223
224 pdev_entry->pdev = pdev;
225 pdev_entry->cpu = cpu;
226 mutex_lock(&pdev_list_mutex);
227 list_add_tail(&pdev_entry->list, &pdev_list);
228 mutex_unlock(&pdev_list_mutex);
229
230 return 0;
231
232exit_device_free:
233 kfree(pdev_entry);
234exit_device_put:
235 platform_device_put(pdev);
236exit:
237 return err;
238}
239
240#ifdef CONFIG_HOTPLUG_CPU
241static void via_cputemp_device_remove(unsigned int cpu)
242{
243 struct pdev_entry *p, *n;
244 mutex_lock(&pdev_list_mutex);
245 list_for_each_entry_safe(p, n, &pdev_list, list) {
246 if (p->cpu == cpu) {
247 platform_device_unregister(p->pdev);
248 list_del(&p->list);
249 kfree(p);
250 }
251 }
252 mutex_unlock(&pdev_list_mutex);
253}
254
255static int __cpuinit via_cputemp_cpu_callback(struct notifier_block *nfb,
256 unsigned long action, void *hcpu)
257{
258 unsigned int cpu = (unsigned long) hcpu;
259
260 switch (action) {
261 case CPU_ONLINE:
262 case CPU_DOWN_FAILED:
263 via_cputemp_device_add(cpu);
264 break;
265 case CPU_DOWN_PREPARE:
266 via_cputemp_device_remove(cpu);
267 break;
268 }
269 return NOTIFY_OK;
270}
271
272static struct notifier_block via_cputemp_cpu_notifier __refdata = {
273 .notifier_call = via_cputemp_cpu_callback,
274};
275#endif /* !CONFIG_HOTPLUG_CPU */
276
277static int __init via_cputemp_init(void)
278{
279 int i, err;
280 struct pdev_entry *p, *n;
281
282 if (cpu_data(0).x86_vendor != X86_VENDOR_CENTAUR) {
283 printk(KERN_DEBUG DRVNAME ": Not a VIA CPU\n");
284 err = -ENODEV;
285 goto exit;
286 }
287
288 err = platform_driver_register(&via_cputemp_driver);
289 if (err)
290 goto exit;
291
292 for_each_online_cpu(i) {
293 struct cpuinfo_x86 *c = &cpu_data(i);
294
295 if (c->x86 != 6)
296 continue;
297
298 if (c->x86_model < 0x0a)
299 continue;
300
301 if (c->x86_model > 0x0f) {
302 printk(KERN_WARNING DRVNAME ": Unknown CPU "
303 "model 0x%x\n", c->x86_model);
304 continue;
305 }
306
307 err = via_cputemp_device_add(i);
308 if (err)
309 goto exit_devices_unreg;
310 }
311 if (list_empty(&pdev_list)) {
312 err = -ENODEV;
313 goto exit_driver_unreg;
314 }
315
316#ifdef CONFIG_HOTPLUG_CPU
317 register_hotcpu_notifier(&via_cputemp_cpu_notifier);
318#endif
319 return 0;
320
321exit_devices_unreg:
322 mutex_lock(&pdev_list_mutex);
323 list_for_each_entry_safe(p, n, &pdev_list, list) {
324 platform_device_unregister(p->pdev);
325 list_del(&p->list);
326 kfree(p);
327 }
328 mutex_unlock(&pdev_list_mutex);
329exit_driver_unreg:
330 platform_driver_unregister(&via_cputemp_driver);
331exit:
332 return err;
333}
334
335static void __exit via_cputemp_exit(void)
336{
337 struct pdev_entry *p, *n;
338#ifdef CONFIG_HOTPLUG_CPU
339 unregister_hotcpu_notifier(&via_cputemp_cpu_notifier);
340#endif
341 mutex_lock(&pdev_list_mutex);
342 list_for_each_entry_safe(p, n, &pdev_list, list) {
343 platform_device_unregister(p->pdev);
344 list_del(&p->list);
345 kfree(p);
346 }
347 mutex_unlock(&pdev_list_mutex);
348 platform_driver_unregister(&via_cputemp_driver);
349}
350
351MODULE_AUTHOR("Harald Welte <HaraldWelte@viatech.com>");
352MODULE_DESCRIPTION("VIA CPU temperature monitor");
353MODULE_LICENSE("GPL");
354
355module_init(via_cputemp_init)
356module_exit(via_cputemp_exit)
diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c
index b257c7223733..38e280523071 100644
--- a/drivers/hwmon/w83627hf.c
+++ b/drivers/hwmon/w83627hf.c
@@ -1135,6 +1135,7 @@ static int __init w83627hf_find(int sioaddr, unsigned short *addr,
1135 "W83687THF", 1135 "W83687THF",
1136 }; 1136 };
1137 1137
1138 sio_data->sioaddr = sioaddr;
1138 superio_enter(sio_data); 1139 superio_enter(sio_data);
1139 val = force_id ? force_id : superio_inb(sio_data, DEVID); 1140 val = force_id ? force_id : superio_inb(sio_data, DEVID);
1140 switch (val) { 1141 switch (val) {
@@ -1177,7 +1178,6 @@ static int __init w83627hf_find(int sioaddr, unsigned short *addr,
1177 } 1178 }
1178 1179
1179 err = 0; 1180 err = 0;
1180 sio_data->sioaddr = sioaddr;
1181 pr_info(DRVNAME ": Found %s chip at %#x\n", 1181 pr_info(DRVNAME ": Found %s chip at %#x\n",
1182 names[sio_data->type], *addr); 1182 names[sio_data->type], *addr);
1183 1183