Merge branch 'drm-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/airlied...
[linux-2.6-block.git] / drivers / hwmon / k10temp.c
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
28 MODULE_DESCRIPTION("AMD Family 10h/11h CPU core temperature monitor");
29 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
30 MODULE_LICENSE("GPL");
31
32 static bool force;
33 module_param(force, bool, 0444);
34 MODULE_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
44 static 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
54 static 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
60 static 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
76 static ssize_t show_name(struct device *dev,
77                          struct device_attribute *attr, char *buf)
78 {
79         return sprintf(buf, "k10temp\n");
80 }
81
82 static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
83 static DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_max, NULL);
84 static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp_crit, NULL, 0);
85 static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, show_temp_crit, NULL, 1);
86 static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
87
88 static 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
97 static 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
147 exit_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);
155 exit:
156         return err;
157 }
158
159 static 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
172 static 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 };
177 MODULE_DEVICE_TABLE(pci, k10temp_id_table);
178
179 static 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
186 static int __init k10temp_init(void)
187 {
188         return pci_register_driver(&k10temp_driver);
189 }
190
191 static void __exit k10temp_exit(void)
192 {
193         pci_unregister_driver(&k10temp_driver);
194 }
195
196 module_init(k10temp_init)
197 module_exit(k10temp_exit)