aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hwmon
diff options
context:
space:
mode:
authorAndreas Herrmann <andreas.herrmann3@amd.com>2011-05-25 14:43:31 -0400
committerJean Delvare <khali@endymion.delvare>2011-05-25 14:43:31 -0400
commit512d1027a6c58def3c2a410e8be65b7e730aad3b (patch)
treea83a92937cb27a135f56610b299dbcac5fbcaa2f /drivers/hwmon
parent9e5813111265ad3c37a4370f0ee7e634dc07a7d6 (diff)
hwmon: Add driver for AMD family 15h processor power information
This CPU family provides NB register values to gather following TDP information * ProcessorPwrWatts: Specifies in Watts the maximum amount of power the processor can support. * CurrPwrWatts: Specifies in Watts the current amount of power being consumed by the processor. This driver provides * power1_crit (ProcessorPwrWatts) * power1_input (CurrPwrWatts) Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com> Signed-off-by: Jean Delvare <khali@linux-fr.org>
Diffstat (limited to 'drivers/hwmon')
-rw-r--r--drivers/hwmon/Kconfig10
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/fam15h_power.c229
3 files changed, 240 insertions, 0 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 511f1f4802f1..567dfae94bbc 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -248,6 +248,16 @@ config SENSORS_K10TEMP
248 This driver can also be built as a module. If so, the module 248 This driver can also be built as a module. If so, the module
249 will be called k10temp. 249 will be called k10temp.
250 250
251config SENSORS_FAM15H_POWER
252 tristate "AMD Family 15h processor power"
253 depends on X86 && PCI
254 help
255 If you say yes here you get support for processor power
256 information of your AMD family 15h CPU.
257
258 This driver can also be built as a module. If so, the module
259 will be called fam15h_power.
260
251config SENSORS_ASB100 261config SENSORS_ASB100
252 tristate "Asus ASB100 Bach" 262 tristate "Asus ASB100 Bach"
253 depends on X86 && I2C && EXPERIMENTAL 263 depends on X86 && I2C && EXPERIMENTAL
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 28e8d52f6379..8524db270bf0 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_SENSORS_EMC2103) += emc2103.o
48obj-$(CONFIG_SENSORS_F71805F) += f71805f.o 48obj-$(CONFIG_SENSORS_F71805F) += f71805f.o
49obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o 49obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o
50obj-$(CONFIG_SENSORS_F75375S) += f75375s.o 50obj-$(CONFIG_SENSORS_F75375S) += f75375s.o
51obj-$(CONFIG_SENSORS_FAM15H_POWER) += fam15h_power.o
51obj-$(CONFIG_SENSORS_FSCHMD) += fschmd.o 52obj-$(CONFIG_SENSORS_FSCHMD) += fschmd.o
52obj-$(CONFIG_SENSORS_G760A) += g760a.o 53obj-$(CONFIG_SENSORS_G760A) += g760a.o
53obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o 54obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c
new file mode 100644
index 000000000000..523f8fb9e7d9
--- /dev/null
+++ b/drivers/hwmon/fam15h_power.c
@@ -0,0 +1,229 @@
1/*
2 * fam15h_power.c - AMD Family 15h processor power monitoring
3 *
4 * Copyright (c) 2011 Advanced Micro Devices, Inc.
5 * Author: Andreas Herrmann <andreas.herrmann3@amd.com>
6 *
7 *
8 * This driver is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This driver is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 * See the GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this driver; if not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include <linux/err.h>
22#include <linux/hwmon.h>
23#include <linux/hwmon-sysfs.h>
24#include <linux/init.h>
25#include <linux/module.h>
26#include <linux/pci.h>
27#include <linux/bitops.h>
28#include <asm/processor.h>
29
30MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor");
31MODULE_AUTHOR("Andreas Herrmann <andreas.herrmann3@amd.com>");
32MODULE_LICENSE("GPL");
33
34/* D18F3 */
35#define REG_NORTHBRIDGE_CAP 0xe8
36
37/* D18F4 */
38#define REG_PROCESSOR_TDP 0x1b8
39
40/* D18F5 */
41#define REG_TDP_RUNNING_AVERAGE 0xe0
42#define REG_TDP_LIMIT3 0xe8
43
44struct fam15h_power_data {
45 struct device *hwmon_dev;
46 unsigned int tdp_to_watts;
47 unsigned int base_tdp;
48 unsigned int processor_pwr_watts;
49};
50
51static ssize_t show_power(struct device *dev,
52 struct device_attribute *attr, char *buf)
53{
54 u32 val, tdp_limit, running_avg_range;
55 s32 running_avg_capture;
56 u64 curr_pwr_watts;
57 struct pci_dev *f4 = to_pci_dev(dev);
58 struct fam15h_power_data *data = dev_get_drvdata(dev);
59
60 pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
61 REG_TDP_RUNNING_AVERAGE, &val);
62 running_avg_capture = (val >> 4) & 0x3fffff;
63 running_avg_capture = sign_extend32(running_avg_capture, 22);
64 running_avg_range = val & 0xf;
65
66 pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
67 REG_TDP_LIMIT3, &val);
68
69 tdp_limit = val >> 16;
70 curr_pwr_watts = tdp_limit + data->base_tdp -
71 (s32)(running_avg_capture >> (running_avg_range + 1));
72 curr_pwr_watts *= data->tdp_to_watts;
73
74 /*
75 * Convert to microWatt
76 *
77 * power is in Watt provided as fixed point integer with
78 * scaling factor 1/(2^16). For conversion we use
79 * (10^6)/(2^16) = 15625/(2^10)
80 */
81 curr_pwr_watts = (curr_pwr_watts * 15625) >> 10;
82 return sprintf(buf, "%u\n", (unsigned int) curr_pwr_watts);
83}
84static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL);
85
86static ssize_t show_power_crit(struct device *dev,
87 struct device_attribute *attr, char *buf)
88{
89 struct fam15h_power_data *data = dev_get_drvdata(dev);
90
91 return sprintf(buf, "%u\n", data->processor_pwr_watts);
92}
93static DEVICE_ATTR(power1_crit, S_IRUGO, show_power_crit, NULL);
94
95static ssize_t show_name(struct device *dev,
96 struct device_attribute *attr, char *buf)
97{
98 return sprintf(buf, "fam15h_power\n");
99}
100static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
101
102static struct attribute *fam15h_power_attrs[] = {
103 &dev_attr_power1_input.attr,
104 &dev_attr_power1_crit.attr,
105 &dev_attr_name.attr,
106 NULL
107};
108
109static const struct attribute_group fam15h_power_attr_group = {
110 .attrs = fam15h_power_attrs,
111};
112
113static bool __devinit fam15h_power_is_internal_node0(struct pci_dev *f4)
114{
115 u32 val;
116
117 pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3),
118 REG_NORTHBRIDGE_CAP, &val);
119 if ((val & BIT(29)) && ((val >> 30) & 3))
120 return false;
121
122 return true;
123}
124
125static void __devinit fam15h_power_init_data(struct pci_dev *f4,
126 struct fam15h_power_data *data)
127{
128 u32 val;
129 u64 tmp;
130
131 pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
132 data->base_tdp = val >> 16;
133 tmp = val & 0xffff;
134
135 pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
136 REG_TDP_LIMIT3, &val);
137
138 data->tdp_to_watts = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
139 tmp *= data->tdp_to_watts;
140
141 /* result not allowed to be >= 256W */
142 if ((tmp >> 16) >= 256)
143 dev_warn(&f4->dev, "Bogus value for ProcessorPwrWatts "
144 "(processor_pwr_watts>=%u)\n",
145 (unsigned int) (tmp >> 16));
146
147 /* convert to microWatt */
148 data->processor_pwr_watts = (tmp * 15625) >> 10;
149}
150
151static int __devinit fam15h_power_probe(struct pci_dev *pdev,
152 const struct pci_device_id *id)
153{
154 struct fam15h_power_data *data;
155 struct device *dev;
156 int err;
157
158 if (!fam15h_power_is_internal_node0(pdev)) {
159 err = -ENODEV;
160 goto exit;
161 }
162
163 data = kzalloc(sizeof(struct fam15h_power_data), GFP_KERNEL);
164 if (!data) {
165 err = -ENOMEM;
166 goto exit;
167 }
168 fam15h_power_init_data(pdev, data);
169 dev = &pdev->dev;
170
171 dev_set_drvdata(dev, data);
172 err = sysfs_create_group(&dev->kobj, &fam15h_power_attr_group);
173 if (err)
174 goto exit_free_data;
175
176 data->hwmon_dev = hwmon_device_register(dev);
177 if (IS_ERR(data->hwmon_dev)) {
178 err = PTR_ERR(data->hwmon_dev);
179 goto exit_remove_group;
180 }
181
182 return 0;
183
184exit_remove_group:
185 sysfs_remove_group(&dev->kobj, &fam15h_power_attr_group);
186exit_free_data:
187 kfree(data);
188exit:
189 return err;
190}
191
192static void __devexit fam15h_power_remove(struct pci_dev *pdev)
193{
194 struct device *dev;
195 struct fam15h_power_data *data;
196
197 dev = &pdev->dev;
198 data = dev_get_drvdata(dev);
199 hwmon_device_unregister(data->hwmon_dev);
200 sysfs_remove_group(&dev->kobj, &fam15h_power_attr_group);
201 dev_set_drvdata(dev, NULL);
202 kfree(data);
203}
204
205static DEFINE_PCI_DEVICE_TABLE(fam15h_power_id_table) = {
206 { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) },
207 {}
208};
209MODULE_DEVICE_TABLE(pci, fam15h_power_id_table);
210
211static struct pci_driver fam15h_power_driver = {
212 .name = "fam15h_power",
213 .id_table = fam15h_power_id_table,
214 .probe = fam15h_power_probe,
215 .remove = __devexit_p(fam15h_power_remove),
216};
217
218static int __init fam15h_power_init(void)
219{
220 return pci_register_driver(&fam15h_power_driver);
221}
222
223static void __exit fam15h_power_exit(void)
224{
225 pci_unregister_driver(&fam15h_power_driver);
226}
227
228module_init(fam15h_power_init)
229module_exit(fam15h_power_exit)