All of lore.kernel.org
 help / color / mirror / Atom feed
* [lm-sensors] [PATCH] hwmon: Add driver for AMD family 15h processor
@ 2011-04-04 16:07 Andreas Herrmann
  2011-04-05 14:45   ` [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h Andreas Herrmann
  0 siblings, 1 reply; 43+ messages in thread
From: Andreas Herrmann @ 2011-04-04 16:07 UTC (permalink / raw)
  To: lm-sensors

From: Andreas Herrmann <andreas.herrmann3@amd.com>

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_cap (ProcessorPwrWatts)
* power1_input (CurrPwrWatts)

Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com>
---
 drivers/hwmon/Kconfig      |   10 +++
 drivers/hwmon/Makefile     |    1 +
 drivers/hwmon/f15h_power.c |  190 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 201 insertions(+), 0 deletions(-)
 create mode 100644 drivers/hwmon/f15h_power.c

When using lm_sensors 3.x, the sensors command shows

  # sensors

  f15h_power-pci-00c4
  Adapter: PCI adapter
  power1:      94.41 W

  f15h_power-pci-00cc
  Adapter: PCI adapter
  power1:      94.41 W

(This example was with lm_sensors 3.1.2.)

Comments welcome. If there are no objections please apply the patch.


Thanks,

Andreas


PS: Patch is against v2.6.38-4148-g054cfaa

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 8e84b31..0268623 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -249,6 +249,16 @@ config SENSORS_K10TEMP
 	  This driver can also be built as a module.  If so, the module
 	  will be called k10temp.
 
+config SENSORS_F15H_POWER
+	tristate "AMD Family 15h processor power"
+	depends on X86 && PCI
+	help
+	  If you say yes here you get support for processor power
+	  information of your AMD family 15h CPU.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called f15hpower.
+
 config SENSORS_ASB100
 	tristate "Asus ASB100 Bach"
 	depends on X86 && I2C && EXPERIMENTAL
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index dde02d9..e554b88 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -62,6 +62,7 @@ obj-$(CONFIG_SENSORS_JC42)	+= jc42.o
 obj-$(CONFIG_SENSORS_JZ4740)	+= jz4740-hwmon.o
 obj-$(CONFIG_SENSORS_K8TEMP)	+= k8temp.o
 obj-$(CONFIG_SENSORS_K10TEMP)	+= k10temp.o
+obj-$(CONFIG_SENSORS_F15H_POWER) += f15h_power.o
 obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o
 obj-$(CONFIG_SENSORS_LIS3_SPI)	+= lis3lv02d.o lis3lv02d_spi.o
 obj-$(CONFIG_SENSORS_LIS3_I2C)	+= lis3lv02d.o lis3lv02d_i2c.o
diff --git a/drivers/hwmon/f15h_power.c b/drivers/hwmon/f15h_power.c
new file mode 100644
index 0000000..732db28
--- /dev/null
+++ b/drivers/hwmon/f15h_power.c
@@ -0,0 +1,190 @@
+/*
+ * f15h_power.c - AMD Family 15h processor power monitoring
+ *
+ * Copyright (c) 2011 Advanced Micro Devices, Inc.
+ * Author: Andreas Herrmann <andreas.herrmann3@amd.com>
+ *
+ *
+ * This driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This driver 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 driver; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <asm/processor.h>
+
+MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor");
+MODULE_AUTHOR("Andreas Herrmann <andreas.herrmann3@amd.com>");
+MODULE_LICENSE("GPL");
+
+/* D18F48 */
+#define REG_PROCESSOR_TDP		0x1b8
+
+/* D18F5 */
+#define REG_TDP_RUNNING_AVERAGE		0xe0
+#define REG_TDP_LIMIT3			0xe8
+
+static ssize_t show_power(struct device *dev,
+			  struct device_attribute *attr, char *buf)
+{
+	u32 val, btdp, tdpl, tdp2w, arange;
+	s32 acap;
+	u64 ctdp;
+	struct pci_dev *f4 = to_pci_dev(dev);
+	struct pci_dev *f5;
+
+	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
+	btdp = (val >> 16) & 0xffff;
+
+	f5 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5));
+	if (!f5) {
+		dev_err(dev, "no function 5 available on this slot\n");
+		return 0;
+	}
+
+	pci_read_config_dword(f5, REG_TDP_RUNNING_AVERAGE, &val);
+	acap = (val >> 4) & 0x3fffff;
+	acap = sign_extend32(acap, 22);
+	arange = val & 0xf;
+
+	pci_read_config_dword(f5, REG_TDP_LIMIT3, &val);
+	pci_dev_put(f5);
+
+	tdpl = (val >> 16) & 0x1fff;
+	tdp2w = ((val & 0x3ff) << 10) | ((val >> 10) & 0x3f);
+	tdp2w = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
+	ctdp = tdpl - (s32)(acap >> (arange + 1)) + btdp;
+	ctdp *= tdp2w;
+
+	/*
+	 * Convert to microWatt
+	 *
+	 * power is in Watt provided as fixed point integer with
+	 * scaling factor 1/(2^16).  For conversion we use
+	 * (10^6)/(2^16) = 15625/(2^10)
+	 */
+	ctdp = (ctdp * 15625) >> 10;
+	return sprintf(buf, "%d\n", (u32) ctdp);
+}
+static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL);
+
+static ssize_t show_power_cap(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	u32 val, tdp2w;
+	u64 ptdp;
+	struct pci_dev *f4 = to_pci_dev(dev);
+	struct pci_dev *f5;
+
+	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
+	ptdp = val & 0xffff;
+
+	f5 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5));
+	if (!f5) {
+		dev_err(dev, "no function 5 available on this slot\n");
+		return 0;
+	}
+
+	pci_read_config_dword(f5, REG_TDP_LIMIT3, &val);
+	pci_dev_put(f5);
+
+	tdp2w = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
+	ptdp *= tdp2w;
+
+	/* result not allowed to be >= 256W */
+	WARN_ON(ptdp>>16 >= 256);
+
+	/* convert to microWatt */
+	ptdp = (ptdp * 15625) >> 10;
+	return sprintf(buf, "%d\n", (u32) ptdp);
+}
+static DEVICE_ATTR(power1_cap, S_IRUGO, show_power_cap, NULL);
+
+static ssize_t show_name(struct device *dev,
+			 struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "f15h_power\n");
+}
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static int __devinit f15h_power_probe(struct pci_dev *pdev,
+				      const struct pci_device_id *id)
+{
+	struct device *hwmon_dev;
+	int err;
+
+	err = device_create_file(&pdev->dev, &dev_attr_power1_input);
+	if (err)
+		goto exit;
+	err = device_create_file(&pdev->dev, &dev_attr_power1_cap);
+	if (err)
+		goto exit_remove;
+
+	err = device_create_file(&pdev->dev, &dev_attr_name);
+	if (err)
+		goto exit_remove;
+
+	hwmon_dev = hwmon_device_register(&pdev->dev);
+	if (IS_ERR(hwmon_dev)) {
+		err = PTR_ERR(hwmon_dev);
+		goto exit_remove;
+	}
+	dev_set_drvdata(&pdev->dev, hwmon_dev);
+
+	return 0;
+
+exit_remove:
+	device_remove_file(&pdev->dev, &dev_attr_name);
+	device_remove_file(&pdev->dev, &dev_attr_power1_input);
+	device_remove_file(&pdev->dev, &dev_attr_power1_cap);
+exit:
+	return err;
+}
+
+static void __devexit f15h_power_remove(struct pci_dev *pdev)
+{
+	hwmon_device_unregister(dev_get_drvdata(&pdev->dev));
+	device_remove_file(&pdev->dev, &dev_attr_name);
+	device_remove_file(&pdev->dev, &dev_attr_power1_input);
+	device_remove_file(&pdev->dev, &dev_attr_power1_cap);
+	dev_set_drvdata(&pdev->dev, NULL);
+}
+
+static const struct pci_device_id f15h_power_id_table[] = {
+	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_LINK) },
+	{}
+};
+MODULE_DEVICE_TABLE(pci, f15h_power_id_table);
+
+static struct pci_driver f15h_power_driver = {
+	.name = "f15h_power",
+	.id_table = f15h_power_id_table,
+	.probe = f15h_power_probe,
+	.remove = __devexit_p(f15h_power_remove),
+};
+
+static int __init f15h_power_init(void)
+{
+	return pci_register_driver(&f15h_power_driver);
+}
+
+static void __exit f15h_power_exit(void)
+{
+	pci_unregister_driver(&f15h_power_driver);
+}
+
+module_init(f15h_power_init)
+module_exit(f15h_power_exit)
-- 
1.7.3.1


_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH v2] hwmon: Add driver for AMD family 15h processor power information
  2011-04-04 16:07 [lm-sensors] [PATCH] hwmon: Add driver for AMD family 15h processor Andreas Herrmann
@ 2011-04-05 14:45   ` Andreas Herrmann
  0 siblings, 0 replies; 43+ messages in thread
From: Andreas Herrmann @ 2011-04-05 14:45 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck; +Cc: Thomas Renninger, lm-sensors, linux-kernel

From: Andreas Herrmann <andreas.herrmann3@amd.com>

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_cap (ProcessorPwrWatts)
* power1_input (CurrPwrWatts)

Changes from v1::
 - removed deprecated code line,
 - fixed comment,
 - report power only once per socket (Power information is provided
   for the entire package. On multi-node processors it should only be
   reported on internal node 0.)

Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com>
---
 drivers/hwmon/Kconfig      |   10 ++
 drivers/hwmon/Makefile     |    1 +
 drivers/hwmon/f15h_power.c |  216 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 227 insertions(+), 0 deletions(-)
 create mode 100644 drivers/hwmon/f15h_power.c

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 8e84b31..0268623 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -249,6 +249,16 @@ config SENSORS_K10TEMP
 	  This driver can also be built as a module.  If so, the module
 	  will be called k10temp.
 
+config SENSORS_F15H_POWER
+	tristate "AMD Family 15h processor power"
+	depends on X86 && PCI
+	help
+	  If you say yes here you get support for processor power
+	  information of your AMD family 15h CPU.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called f15hpower.
+
 config SENSORS_ASB100
 	tristate "Asus ASB100 Bach"
 	depends on X86 && I2C && EXPERIMENTAL
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index dde02d9..e554b88 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -62,6 +62,7 @@ obj-$(CONFIG_SENSORS_JC42)	+= jc42.o
 obj-$(CONFIG_SENSORS_JZ4740)	+= jz4740-hwmon.o
 obj-$(CONFIG_SENSORS_K8TEMP)	+= k8temp.o
 obj-$(CONFIG_SENSORS_K10TEMP)	+= k10temp.o
+obj-$(CONFIG_SENSORS_F15H_POWER) += f15h_power.o
 obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o
 obj-$(CONFIG_SENSORS_LIS3_SPI)	+= lis3lv02d.o lis3lv02d_spi.o
 obj-$(CONFIG_SENSORS_LIS3_I2C)	+= lis3lv02d.o lis3lv02d_i2c.o
diff --git a/drivers/hwmon/f15h_power.c b/drivers/hwmon/f15h_power.c
new file mode 100644
index 0000000..40e3cbd
--- /dev/null
+++ b/drivers/hwmon/f15h_power.c
@@ -0,0 +1,216 @@
+/*
+ * f15h_power.c - AMD Family 15h processor power monitoring
+ *
+ * Copyright (c) 2011 Advanced Micro Devices, Inc.
+ * Author: Andreas Herrmann <andreas.herrmann3@amd.com>
+ *
+ *
+ * This driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This driver 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 driver; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/bitops.h>
+#include <asm/processor.h>
+
+MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor");
+MODULE_AUTHOR("Andreas Herrmann <andreas.herrmann3@amd.com>");
+MODULE_LICENSE("GPL");
+
+/* D18F3 */
+#define REG_NORTHBRIDGE_CAP		0xe8
+
+/* D18F4 */
+#define REG_PROCESSOR_TDP		0x1b8
+
+/* D18F5 */
+#define REG_TDP_RUNNING_AVERAGE		0xe0
+#define REG_TDP_LIMIT3			0xe8
+
+static ssize_t show_power(struct device *dev,
+			  struct device_attribute *attr, char *buf)
+{
+	u32 val, btdp, tdpl, tdp2w, arange;
+	s32 acap;
+	u64 ctdp;
+	struct pci_dev *f4 = to_pci_dev(dev);
+	struct pci_dev *f5;
+
+	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
+	btdp = (val >> 16) & 0xffff;
+
+	f5 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5));
+	if (!f5) {
+		dev_err(dev, "no function 5 available on this slot\n");
+		return 0;
+	}
+
+	pci_read_config_dword(f5, REG_TDP_RUNNING_AVERAGE, &val);
+	acap = (val >> 4) & 0x3fffff;
+	acap = sign_extend32(acap, 22);
+	arange = val & 0xf;
+
+	pci_read_config_dword(f5, REG_TDP_LIMIT3, &val);
+	pci_dev_put(f5);
+
+	tdpl = (val >> 16) & 0x1fff;
+	tdp2w = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
+	ctdp = tdpl - (s32)(acap >> (arange + 1)) + btdp;
+	ctdp *= tdp2w;
+
+	/*
+	 * Convert to microWatt
+	 *
+	 * power is in Watt provided as fixed point integer with
+	 * scaling factor 1/(2^16).  For conversion we use
+	 * (10^6)/(2^16) = 15625/(2^10)
+	 */
+	ctdp = (ctdp * 15625) >> 10;
+	return sprintf(buf, "%d\n", (u32) ctdp);
+}
+static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL);
+
+static ssize_t show_power_cap(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	u32 val, tdp2w;
+	u64 ptdp;
+	struct pci_dev *f4 = to_pci_dev(dev);
+	struct pci_dev *f5;
+
+	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
+	ptdp = val & 0xffff;
+
+	f5 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5));
+	if (!f5) {
+		dev_err(dev, "no function 5 available on this slot\n");
+		return 0;
+	}
+
+	pci_read_config_dword(f5, REG_TDP_LIMIT3, &val);
+	pci_dev_put(f5);
+
+	tdp2w = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
+	ptdp *= tdp2w;
+
+	/* result not allowed to be >= 256W */
+	WARN_ON(ptdp>>16 >= 256);
+
+	/* convert to microWatt */
+	ptdp = (ptdp * 15625) >> 10;
+	return sprintf(buf, "%d\n", (u32) ptdp);
+}
+static DEVICE_ATTR(power1_cap, S_IRUGO, show_power_cap, NULL);
+
+static ssize_t show_name(struct device *dev,
+			 struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "f15h_power\n");
+}
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static int __devinit f15h_power_is_internal_node0(struct pci_dev *f4)
+{
+	u32 val;
+	struct pci_dev *f3;
+
+	f3 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3));
+	if (!f3) {
+		dev_err(&f4->dev, "no function 3 available on this slot\n");
+		return 0;
+	}
+
+	pci_read_config_dword(f3, REG_NORTHBRIDGE_CAP, &val);
+	pci_dev_put(f3);
+
+	if ((val & BIT(29)) && ((val >> 30) & 3))
+		return 0;
+
+	return 1;
+}
+
+static int __devinit f15h_power_probe(struct pci_dev *pdev,
+				      const struct pci_device_id *id)
+{
+	struct device *hwmon_dev;
+	int err = -ENODEV;
+
+	if (!f15h_power_is_internal_node0(pdev))
+		goto exit;
+
+	err = device_create_file(&pdev->dev, &dev_attr_power1_input);
+	if (err)
+		goto exit;
+	err = device_create_file(&pdev->dev, &dev_attr_power1_cap);
+	if (err)
+		goto exit_remove;
+
+	err = device_create_file(&pdev->dev, &dev_attr_name);
+	if (err)
+		goto exit_remove;
+
+	hwmon_dev = hwmon_device_register(&pdev->dev);
+	if (IS_ERR(hwmon_dev)) {
+		err = PTR_ERR(hwmon_dev);
+		goto exit_remove;
+	}
+	dev_set_drvdata(&pdev->dev, hwmon_dev);
+
+	return 0;
+
+exit_remove:
+	device_remove_file(&pdev->dev, &dev_attr_name);
+	device_remove_file(&pdev->dev, &dev_attr_power1_input);
+	device_remove_file(&pdev->dev, &dev_attr_power1_cap);
+exit:
+	return err;
+}
+
+static void __devexit f15h_power_remove(struct pci_dev *pdev)
+{
+	hwmon_device_unregister(dev_get_drvdata(&pdev->dev));
+	device_remove_file(&pdev->dev, &dev_attr_name);
+	device_remove_file(&pdev->dev, &dev_attr_power1_input);
+	device_remove_file(&pdev->dev, &dev_attr_power1_cap);
+	dev_set_drvdata(&pdev->dev, NULL);
+}
+
+static const struct pci_device_id f15h_power_id_table[] = {
+	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_LINK) },
+	{}
+};
+MODULE_DEVICE_TABLE(pci, f15h_power_id_table);
+
+static struct pci_driver f15h_power_driver = {
+	.name = "f15h_power",
+	.id_table = f15h_power_id_table,
+	.probe = f15h_power_probe,
+	.remove = __devexit_p(f15h_power_remove),
+};
+
+static int __init f15h_power_init(void)
+{
+	return pci_register_driver(&f15h_power_driver);
+}
+
+static void __exit f15h_power_exit(void)
+{
+	pci_unregister_driver(&f15h_power_driver);
+}
+
+module_init(f15h_power_init)
+module_exit(f15h_power_exit)
-- 
1.7.3.1


^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h
@ 2011-04-05 14:45   ` Andreas Herrmann
  0 siblings, 0 replies; 43+ messages in thread
From: Andreas Herrmann @ 2011-04-05 14:45 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck; +Cc: Thomas Renninger, lm-sensors, linux-kernel

From: Andreas Herrmann <andreas.herrmann3@amd.com>

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_cap (ProcessorPwrWatts)
* power1_input (CurrPwrWatts)

Changes from v1::
 - removed deprecated code line,
 - fixed comment,
 - report power only once per socket (Power information is provided
   for the entire package. On multi-node processors it should only be
   reported on internal node 0.)

Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com>
---
 drivers/hwmon/Kconfig      |   10 ++
 drivers/hwmon/Makefile     |    1 +
 drivers/hwmon/f15h_power.c |  216 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 227 insertions(+), 0 deletions(-)
 create mode 100644 drivers/hwmon/f15h_power.c

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 8e84b31..0268623 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -249,6 +249,16 @@ config SENSORS_K10TEMP
 	  This driver can also be built as a module.  If so, the module
 	  will be called k10temp.
 
+config SENSORS_F15H_POWER
+	tristate "AMD Family 15h processor power"
+	depends on X86 && PCI
+	help
+	  If you say yes here you get support for processor power
+	  information of your AMD family 15h CPU.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called f15hpower.
+
 config SENSORS_ASB100
 	tristate "Asus ASB100 Bach"
 	depends on X86 && I2C && EXPERIMENTAL
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index dde02d9..e554b88 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -62,6 +62,7 @@ obj-$(CONFIG_SENSORS_JC42)	+= jc42.o
 obj-$(CONFIG_SENSORS_JZ4740)	+= jz4740-hwmon.o
 obj-$(CONFIG_SENSORS_K8TEMP)	+= k8temp.o
 obj-$(CONFIG_SENSORS_K10TEMP)	+= k10temp.o
+obj-$(CONFIG_SENSORS_F15H_POWER) += f15h_power.o
 obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o
 obj-$(CONFIG_SENSORS_LIS3_SPI)	+= lis3lv02d.o lis3lv02d_spi.o
 obj-$(CONFIG_SENSORS_LIS3_I2C)	+= lis3lv02d.o lis3lv02d_i2c.o
diff --git a/drivers/hwmon/f15h_power.c b/drivers/hwmon/f15h_power.c
new file mode 100644
index 0000000..40e3cbd
--- /dev/null
+++ b/drivers/hwmon/f15h_power.c
@@ -0,0 +1,216 @@
+/*
+ * f15h_power.c - AMD Family 15h processor power monitoring
+ *
+ * Copyright (c) 2011 Advanced Micro Devices, Inc.
+ * Author: Andreas Herrmann <andreas.herrmann3@amd.com>
+ *
+ *
+ * This driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This driver 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 driver; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/bitops.h>
+#include <asm/processor.h>
+
+MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor");
+MODULE_AUTHOR("Andreas Herrmann <andreas.herrmann3@amd.com>");
+MODULE_LICENSE("GPL");
+
+/* D18F3 */
+#define REG_NORTHBRIDGE_CAP		0xe8
+
+/* D18F4 */
+#define REG_PROCESSOR_TDP		0x1b8
+
+/* D18F5 */
+#define REG_TDP_RUNNING_AVERAGE		0xe0
+#define REG_TDP_LIMIT3			0xe8
+
+static ssize_t show_power(struct device *dev,
+			  struct device_attribute *attr, char *buf)
+{
+	u32 val, btdp, tdpl, tdp2w, arange;
+	s32 acap;
+	u64 ctdp;
+	struct pci_dev *f4 = to_pci_dev(dev);
+	struct pci_dev *f5;
+
+	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
+	btdp = (val >> 16) & 0xffff;
+
+	f5 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5));
+	if (!f5) {
+		dev_err(dev, "no function 5 available on this slot\n");
+		return 0;
+	}
+
+	pci_read_config_dword(f5, REG_TDP_RUNNING_AVERAGE, &val);
+	acap = (val >> 4) & 0x3fffff;
+	acap = sign_extend32(acap, 22);
+	arange = val & 0xf;
+
+	pci_read_config_dword(f5, REG_TDP_LIMIT3, &val);
+	pci_dev_put(f5);
+
+	tdpl = (val >> 16) & 0x1fff;
+	tdp2w = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
+	ctdp = tdpl - (s32)(acap >> (arange + 1)) + btdp;
+	ctdp *= tdp2w;
+
+	/*
+	 * Convert to microWatt
+	 *
+	 * power is in Watt provided as fixed point integer with
+	 * scaling factor 1/(2^16).  For conversion we use
+	 * (10^6)/(2^16) = 15625/(2^10)
+	 */
+	ctdp = (ctdp * 15625) >> 10;
+	return sprintf(buf, "%d\n", (u32) ctdp);
+}
+static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL);
+
+static ssize_t show_power_cap(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	u32 val, tdp2w;
+	u64 ptdp;
+	struct pci_dev *f4 = to_pci_dev(dev);
+	struct pci_dev *f5;
+
+	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
+	ptdp = val & 0xffff;
+
+	f5 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5));
+	if (!f5) {
+		dev_err(dev, "no function 5 available on this slot\n");
+		return 0;
+	}
+
+	pci_read_config_dword(f5, REG_TDP_LIMIT3, &val);
+	pci_dev_put(f5);
+
+	tdp2w = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
+	ptdp *= tdp2w;
+
+	/* result not allowed to be >= 256W */
+	WARN_ON(ptdp>>16 >= 256);
+
+	/* convert to microWatt */
+	ptdp = (ptdp * 15625) >> 10;
+	return sprintf(buf, "%d\n", (u32) ptdp);
+}
+static DEVICE_ATTR(power1_cap, S_IRUGO, show_power_cap, NULL);
+
+static ssize_t show_name(struct device *dev,
+			 struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "f15h_power\n");
+}
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static int __devinit f15h_power_is_internal_node0(struct pci_dev *f4)
+{
+	u32 val;
+	struct pci_dev *f3;
+
+	f3 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3));
+	if (!f3) {
+		dev_err(&f4->dev, "no function 3 available on this slot\n");
+		return 0;
+	}
+
+	pci_read_config_dword(f3, REG_NORTHBRIDGE_CAP, &val);
+	pci_dev_put(f3);
+
+	if ((val & BIT(29)) && ((val >> 30) & 3))
+		return 0;
+
+	return 1;
+}
+
+static int __devinit f15h_power_probe(struct pci_dev *pdev,
+				      const struct pci_device_id *id)
+{
+	struct device *hwmon_dev;
+	int err = -ENODEV;
+
+	if (!f15h_power_is_internal_node0(pdev))
+		goto exit;
+
+	err = device_create_file(&pdev->dev, &dev_attr_power1_input);
+	if (err)
+		goto exit;
+	err = device_create_file(&pdev->dev, &dev_attr_power1_cap);
+	if (err)
+		goto exit_remove;
+
+	err = device_create_file(&pdev->dev, &dev_attr_name);
+	if (err)
+		goto exit_remove;
+
+	hwmon_dev = hwmon_device_register(&pdev->dev);
+	if (IS_ERR(hwmon_dev)) {
+		err = PTR_ERR(hwmon_dev);
+		goto exit_remove;
+	}
+	dev_set_drvdata(&pdev->dev, hwmon_dev);
+
+	return 0;
+
+exit_remove:
+	device_remove_file(&pdev->dev, &dev_attr_name);
+	device_remove_file(&pdev->dev, &dev_attr_power1_input);
+	device_remove_file(&pdev->dev, &dev_attr_power1_cap);
+exit:
+	return err;
+}
+
+static void __devexit f15h_power_remove(struct pci_dev *pdev)
+{
+	hwmon_device_unregister(dev_get_drvdata(&pdev->dev));
+	device_remove_file(&pdev->dev, &dev_attr_name);
+	device_remove_file(&pdev->dev, &dev_attr_power1_input);
+	device_remove_file(&pdev->dev, &dev_attr_power1_cap);
+	dev_set_drvdata(&pdev->dev, NULL);
+}
+
+static const struct pci_device_id f15h_power_id_table[] = {
+	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_LINK) },
+	{}
+};
+MODULE_DEVICE_TABLE(pci, f15h_power_id_table);
+
+static struct pci_driver f15h_power_driver = {
+	.name = "f15h_power",
+	.id_table = f15h_power_id_table,
+	.probe = f15h_power_probe,
+	.remove = __devexit_p(f15h_power_remove),
+};
+
+static int __init f15h_power_init(void)
+{
+	return pci_register_driver(&f15h_power_driver);
+}
+
+static void __exit f15h_power_exit(void)
+{
+	pci_unregister_driver(&f15h_power_driver);
+}
+
+module_init(f15h_power_init)
+module_exit(f15h_power_exit)
-- 
1.7.3.1


_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* Re: [PATCH v2] hwmon: Add driver for AMD family 15h processor power information
  2011-04-05 14:45   ` [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h Andreas Herrmann
@ 2011-04-05 20:12     ` Guenter Roeck
  -1 siblings, 0 replies; 43+ messages in thread
From: Guenter Roeck @ 2011-04-05 20:12 UTC (permalink / raw)
  To: Andreas Herrmann; +Cc: Jean Delvare, Thomas Renninger, lm-sensors, linux-kernel

On Tue, Apr 05, 2011 at 10:45:36AM -0400, Andreas Herrmann wrote:
> From: Andreas Herrmann <andreas.herrmann3@amd.com>
> 
> 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_cap (ProcessorPwrWatts)

Hi Andreas,

What does the CPU do if ProcessorPwrWatts is reached ? Does it start to limit 
power consumption, ie does it enforce the limit ?

If not you might want to use power1_max instead.

> * power1_input (CurrPwrWatts)
> 
> Changes from v1::
>  - removed deprecated code line,
>  - fixed comment,
>  - report power only once per socket (Power information is provided
>    for the entire package. On multi-node processors it should only be
>    reported on internal node 0.)
> 
> Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com>
> ---
>  drivers/hwmon/Kconfig      |   10 ++
>  drivers/hwmon/Makefile     |    1 +
>  drivers/hwmon/f15h_power.c |  216 ++++++++++++++++++++++++++++++++++++++++++++

Please also add Documentation/hwmon/f15h_power.

>  3 files changed, 227 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/hwmon/f15h_power.c
> 
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 8e84b31..0268623 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -249,6 +249,16 @@ config SENSORS_K10TEMP
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called k10temp.
>  
> +config SENSORS_F15H_POWER
> +	tristate "AMD Family 15h processor power"
> +	depends on X86 && PCI
> +	help
> +	  If you say yes here you get support for processor power
> +	  information of your AMD family 15h CPU.
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called f15hpower.

	f15h_power

> +
>  config SENSORS_ASB100
>  	tristate "Asus ASB100 Bach"
>  	depends on X86 && I2C && EXPERIMENTAL
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index dde02d9..e554b88 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -62,6 +62,7 @@ obj-$(CONFIG_SENSORS_JC42)	+= jc42.o
>  obj-$(CONFIG_SENSORS_JZ4740)	+= jz4740-hwmon.o
>  obj-$(CONFIG_SENSORS_K8TEMP)	+= k8temp.o
>  obj-$(CONFIG_SENSORS_K10TEMP)	+= k10temp.o
> +obj-$(CONFIG_SENSORS_F15H_POWER) += f15h_power.o
>  obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o
>  obj-$(CONFIG_SENSORS_LIS3_SPI)	+= lis3lv02d.o lis3lv02d_spi.o
>  obj-$(CONFIG_SENSORS_LIS3_I2C)	+= lis3lv02d.o lis3lv02d_i2c.o
> diff --git a/drivers/hwmon/f15h_power.c b/drivers/hwmon/f15h_power.c
> new file mode 100644
> index 0000000..40e3cbd
> --- /dev/null
> +++ b/drivers/hwmon/f15h_power.c
> @@ -0,0 +1,216 @@
> +/*
> + * f15h_power.c - AMD Family 15h processor power monitoring
> + *
> + * Copyright (c) 2011 Advanced Micro Devices, Inc.
> + * Author: Andreas Herrmann <andreas.herrmann3@amd.com>
> + *
> + *
> + * This driver is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This driver 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 driver; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/err.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <linux/bitops.h>
> +#include <asm/processor.h>
> +
> +MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor");
> +MODULE_AUTHOR("Andreas Herrmann <andreas.herrmann3@amd.com>");
> +MODULE_LICENSE("GPL");
> +
> +/* D18F3 */
> +#define REG_NORTHBRIDGE_CAP		0xe8
> +
> +/* D18F4 */
> +#define REG_PROCESSOR_TDP		0x1b8
> +
> +/* D18F5 */
> +#define REG_TDP_RUNNING_AVERAGE		0xe0
> +#define REG_TDP_LIMIT3			0xe8
> +
> +static ssize_t show_power(struct device *dev,
> +			  struct device_attribute *attr, char *buf)
> +{
> +	u32 val, btdp, tdpl, tdp2w, arange;
> +	s32 acap;
> +	u64 ctdp;
> +	struct pci_dev *f4 = to_pci_dev(dev);
> +	struct pci_dev *f5;
> +
> +	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
> +	btdp = (val >> 16) & 0xffff;
> +
> +	f5 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5));
> +	if (!f5) {
> +		dev_err(dev, "no function 5 available on this slot\n");
> +		return 0;
> +	}

Unless this is a temporary error condition, you should check if this function
exists in the probe function, and not create a sysfs entry if it doesn't.

If it is a temporary error which needs a runtime check, you should return 
a negative value to indicate that there was an error.

> +
> +	pci_read_config_dword(f5, REG_TDP_RUNNING_AVERAGE, &val);
> +	acap = (val >> 4) & 0x3fffff;
> +	acap = sign_extend32(acap, 22);
> +	arange = val & 0xf;
> +
> +	pci_read_config_dword(f5, REG_TDP_LIMIT3, &val);
> +	pci_dev_put(f5);
> +
> +	tdpl = (val >> 16) & 0x1fff;
> +	tdp2w = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
> +	ctdp = tdpl - (s32)(acap >> (arange + 1)) + btdp;
> +	ctdp *= tdp2w;
> +
> +	/*
> +	 * Convert to microWatt
> +	 *
> +	 * power is in Watt provided as fixed point integer with
> +	 * scaling factor 1/(2^16).  For conversion we use
> +	 * (10^6)/(2^16) = 15625/(2^10)
> +	 */
> +	ctdp = (ctdp * 15625) >> 10;
> +	return sprintf(buf, "%d\n", (u32) ctdp);

	%u

> +}
> +static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL);
> +
> +static ssize_t show_power_cap(struct device *dev,
> +			     struct device_attribute *attr, char *buf)
> +{
> +	u32 val, tdp2w;
> +	u64 ptdp;
> +	struct pci_dev *f4 = to_pci_dev(dev);
> +	struct pci_dev *f5;
> +
> +	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
> +	ptdp = val & 0xffff;
> +
> +	f5 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5));
> +	if (!f5) {
> +		dev_err(dev, "no function 5 available on this slot\n");
> +		return 0;
> +	}
> +
Same as above - should be checked in probe function.

If it is a temporary error which needs a runtime check, you should return 
a negative value to indicate that there was an error.

> +	pci_read_config_dword(f5, REG_TDP_LIMIT3, &val);
> +	pci_dev_put(f5);
> +
> +	tdp2w = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
> +	ptdp *= tdp2w;
> +
> +	/* result not allowed to be >= 256W */
> +	WARN_ON(ptdp>>16 >= 256);
> +
Does this really ask for such drastic measures, or would dev_warn() be sufficient ?

> +	/* convert to microWatt */
> +	ptdp = (ptdp * 15625) >> 10;
> +	return sprintf(buf, "%d\n", (u32) ptdp);

	%u

> +}
> +static DEVICE_ATTR(power1_cap, S_IRUGO, show_power_cap, NULL);
> +
> +static ssize_t show_name(struct device *dev,
> +			 struct device_attribute *attr, char *buf)
> +{
> +	return sprintf(buf, "f15h_power\n");
> +}
> +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
> +
> +static int __devinit f15h_power_is_internal_node0(struct pci_dev *f4)
> +{
> +	u32 val;
> +	struct pci_dev *f3;
> +
> +	f3 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3));
> +	if (!f3) {
> +		dev_err(&f4->dev, "no function 3 available on this slot\n");
> +		return 0;

It is a common practice to return a negative value on errors. Why not here ?
Also, is this really an error which asks for an error message, or just a CPU
or system which does not support the attribute ? In the latter case, you should 
not display an error message.

> +	}
> +
> +	pci_read_config_dword(f3, REG_NORTHBRIDGE_CAP, &val);
> +	pci_dev_put(f3);
> +
> +	if ((val & BIT(29)) && ((val >> 30) & 3))
> +		return 0;
> +
> +	return 1;
> +}
> +
> +static int __devinit f15h_power_probe(struct pci_dev *pdev,
> +				      const struct pci_device_id *id)
> +{
> +	struct device *hwmon_dev;
> +	int err = -ENODEV;
> +
> +	if (!f15h_power_is_internal_node0(pdev))
> +		goto exit;
> +
> +	err = device_create_file(&pdev->dev, &dev_attr_power1_input);
> +	if (err)
> +		goto exit;
> +	err = device_create_file(&pdev->dev, &dev_attr_power1_cap);
> +	if (err)
> +		goto exit_remove;
> +
> +	err = device_create_file(&pdev->dev, &dev_attr_name);
> +	if (err)
> +		goto exit_remove;
> +
> +	hwmon_dev = hwmon_device_register(&pdev->dev);
> +	if (IS_ERR(hwmon_dev)) {
> +		err = PTR_ERR(hwmon_dev);
> +		goto exit_remove;
> +	}
> +	dev_set_drvdata(&pdev->dev, hwmon_dev);
> +
> +	return 0;
> +
> +exit_remove:
> +	device_remove_file(&pdev->dev, &dev_attr_name);
> +	device_remove_file(&pdev->dev, &dev_attr_power1_input);
> +	device_remove_file(&pdev->dev, &dev_attr_power1_cap);
> +exit:
> +	return err;
> +}
> +
> +static void __devexit f15h_power_remove(struct pci_dev *pdev)
> +{
> +	hwmon_device_unregister(dev_get_drvdata(&pdev->dev));
> +	device_remove_file(&pdev->dev, &dev_attr_name);
> +	device_remove_file(&pdev->dev, &dev_attr_power1_input);
> +	device_remove_file(&pdev->dev, &dev_attr_power1_cap);
> +	dev_set_drvdata(&pdev->dev, NULL);
> +}
> +
> +static const struct pci_device_id f15h_power_id_table[] = {

checkpatch says:

WARNING: Use DEFINE_PCI_DEVICE_TABLE for struct pci_device_id
#326: FILE: drivers/hwmon/f15h_power.c:192:

Please fix.

> +	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_LINK) },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(pci, f15h_power_id_table);
> +
> +static struct pci_driver f15h_power_driver = {
> +	.name = "f15h_power",
> +	.id_table = f15h_power_id_table,
> +	.probe = f15h_power_probe,
> +	.remove = __devexit_p(f15h_power_remove),
> +};
> +
> +static int __init f15h_power_init(void)
> +{
> +	return pci_register_driver(&f15h_power_driver);
> +}
> +
> +static void __exit f15h_power_exit(void)
> +{
> +	pci_unregister_driver(&f15h_power_driver);
> +}
> +
> +module_init(f15h_power_init)
> +module_exit(f15h_power_exit)
> -- 
> 1.7.3.1
> 

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h
@ 2011-04-05 20:12     ` Guenter Roeck
  0 siblings, 0 replies; 43+ messages in thread
From: Guenter Roeck @ 2011-04-05 20:12 UTC (permalink / raw)
  To: Andreas Herrmann; +Cc: Jean Delvare, Thomas Renninger, lm-sensors, linux-kernel

On Tue, Apr 05, 2011 at 10:45:36AM -0400, Andreas Herrmann wrote:
> From: Andreas Herrmann <andreas.herrmann3@amd.com>
> 
> 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_cap (ProcessorPwrWatts)

Hi Andreas,

What does the CPU do if ProcessorPwrWatts is reached ? Does it start to limit 
power consumption, ie does it enforce the limit ?

If not you might want to use power1_max instead.

> * power1_input (CurrPwrWatts)
> 
> Changes from v1::
>  - removed deprecated code line,
>  - fixed comment,
>  - report power only once per socket (Power information is provided
>    for the entire package. On multi-node processors it should only be
>    reported on internal node 0.)
> 
> Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com>
> ---
>  drivers/hwmon/Kconfig      |   10 ++
>  drivers/hwmon/Makefile     |    1 +
>  drivers/hwmon/f15h_power.c |  216 ++++++++++++++++++++++++++++++++++++++++++++

Please also add Documentation/hwmon/f15h_power.

>  3 files changed, 227 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/hwmon/f15h_power.c
> 
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 8e84b31..0268623 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -249,6 +249,16 @@ config SENSORS_K10TEMP
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called k10temp.
>  
> +config SENSORS_F15H_POWER
> +	tristate "AMD Family 15h processor power"
> +	depends on X86 && PCI
> +	help
> +	  If you say yes here you get support for processor power
> +	  information of your AMD family 15h CPU.
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called f15hpower.

	f15h_power

> +
>  config SENSORS_ASB100
>  	tristate "Asus ASB100 Bach"
>  	depends on X86 && I2C && EXPERIMENTAL
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index dde02d9..e554b88 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -62,6 +62,7 @@ obj-$(CONFIG_SENSORS_JC42)	+= jc42.o
>  obj-$(CONFIG_SENSORS_JZ4740)	+= jz4740-hwmon.o
>  obj-$(CONFIG_SENSORS_K8TEMP)	+= k8temp.o
>  obj-$(CONFIG_SENSORS_K10TEMP)	+= k10temp.o
> +obj-$(CONFIG_SENSORS_F15H_POWER) += f15h_power.o
>  obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o
>  obj-$(CONFIG_SENSORS_LIS3_SPI)	+= lis3lv02d.o lis3lv02d_spi.o
>  obj-$(CONFIG_SENSORS_LIS3_I2C)	+= lis3lv02d.o lis3lv02d_i2c.o
> diff --git a/drivers/hwmon/f15h_power.c b/drivers/hwmon/f15h_power.c
> new file mode 100644
> index 0000000..40e3cbd
> --- /dev/null
> +++ b/drivers/hwmon/f15h_power.c
> @@ -0,0 +1,216 @@
> +/*
> + * f15h_power.c - AMD Family 15h processor power monitoring
> + *
> + * Copyright (c) 2011 Advanced Micro Devices, Inc.
> + * Author: Andreas Herrmann <andreas.herrmann3@amd.com>
> + *
> + *
> + * This driver is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This driver 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 driver; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/err.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <linux/bitops.h>
> +#include <asm/processor.h>
> +
> +MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor");
> +MODULE_AUTHOR("Andreas Herrmann <andreas.herrmann3@amd.com>");
> +MODULE_LICENSE("GPL");
> +
> +/* D18F3 */
> +#define REG_NORTHBRIDGE_CAP		0xe8
> +
> +/* D18F4 */
> +#define REG_PROCESSOR_TDP		0x1b8
> +
> +/* D18F5 */
> +#define REG_TDP_RUNNING_AVERAGE		0xe0
> +#define REG_TDP_LIMIT3			0xe8
> +
> +static ssize_t show_power(struct device *dev,
> +			  struct device_attribute *attr, char *buf)
> +{
> +	u32 val, btdp, tdpl, tdp2w, arange;
> +	s32 acap;
> +	u64 ctdp;
> +	struct pci_dev *f4 = to_pci_dev(dev);
> +	struct pci_dev *f5;
> +
> +	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
> +	btdp = (val >> 16) & 0xffff;
> +
> +	f5 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5));
> +	if (!f5) {
> +		dev_err(dev, "no function 5 available on this slot\n");
> +		return 0;
> +	}

Unless this is a temporary error condition, you should check if this function
exists in the probe function, and not create a sysfs entry if it doesn't.

If it is a temporary error which needs a runtime check, you should return 
a negative value to indicate that there was an error.

> +
> +	pci_read_config_dword(f5, REG_TDP_RUNNING_AVERAGE, &val);
> +	acap = (val >> 4) & 0x3fffff;
> +	acap = sign_extend32(acap, 22);
> +	arange = val & 0xf;
> +
> +	pci_read_config_dword(f5, REG_TDP_LIMIT3, &val);
> +	pci_dev_put(f5);
> +
> +	tdpl = (val >> 16) & 0x1fff;
> +	tdp2w = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
> +	ctdp = tdpl - (s32)(acap >> (arange + 1)) + btdp;
> +	ctdp *= tdp2w;
> +
> +	/*
> +	 * Convert to microWatt
> +	 *
> +	 * power is in Watt provided as fixed point integer with
> +	 * scaling factor 1/(2^16).  For conversion we use
> +	 * (10^6)/(2^16) = 15625/(2^10)
> +	 */
> +	ctdp = (ctdp * 15625) >> 10;
> +	return sprintf(buf, "%d\n", (u32) ctdp);

	%u

> +}
> +static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL);
> +
> +static ssize_t show_power_cap(struct device *dev,
> +			     struct device_attribute *attr, char *buf)
> +{
> +	u32 val, tdp2w;
> +	u64 ptdp;
> +	struct pci_dev *f4 = to_pci_dev(dev);
> +	struct pci_dev *f5;
> +
> +	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
> +	ptdp = val & 0xffff;
> +
> +	f5 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5));
> +	if (!f5) {
> +		dev_err(dev, "no function 5 available on this slot\n");
> +		return 0;
> +	}
> +
Same as above - should be checked in probe function.

If it is a temporary error which needs a runtime check, you should return 
a negative value to indicate that there was an error.

> +	pci_read_config_dword(f5, REG_TDP_LIMIT3, &val);
> +	pci_dev_put(f5);
> +
> +	tdp2w = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
> +	ptdp *= tdp2w;
> +
> +	/* result not allowed to be >= 256W */
> +	WARN_ON(ptdp>>16 >= 256);
> +
Does this really ask for such drastic measures, or would dev_warn() be sufficient ?

> +	/* convert to microWatt */
> +	ptdp = (ptdp * 15625) >> 10;
> +	return sprintf(buf, "%d\n", (u32) ptdp);

	%u

> +}
> +static DEVICE_ATTR(power1_cap, S_IRUGO, show_power_cap, NULL);
> +
> +static ssize_t show_name(struct device *dev,
> +			 struct device_attribute *attr, char *buf)
> +{
> +	return sprintf(buf, "f15h_power\n");
> +}
> +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
> +
> +static int __devinit f15h_power_is_internal_node0(struct pci_dev *f4)
> +{
> +	u32 val;
> +	struct pci_dev *f3;
> +
> +	f3 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3));
> +	if (!f3) {
> +		dev_err(&f4->dev, "no function 3 available on this slot\n");
> +		return 0;

It is a common practice to return a negative value on errors. Why not here ?
Also, is this really an error which asks for an error message, or just a CPU
or system which does not support the attribute ? In the latter case, you should 
not display an error message.

> +	}
> +
> +	pci_read_config_dword(f3, REG_NORTHBRIDGE_CAP, &val);
> +	pci_dev_put(f3);
> +
> +	if ((val & BIT(29)) && ((val >> 30) & 3))
> +		return 0;
> +
> +	return 1;
> +}
> +
> +static int __devinit f15h_power_probe(struct pci_dev *pdev,
> +				      const struct pci_device_id *id)
> +{
> +	struct device *hwmon_dev;
> +	int err = -ENODEV;
> +
> +	if (!f15h_power_is_internal_node0(pdev))
> +		goto exit;
> +
> +	err = device_create_file(&pdev->dev, &dev_attr_power1_input);
> +	if (err)
> +		goto exit;
> +	err = device_create_file(&pdev->dev, &dev_attr_power1_cap);
> +	if (err)
> +		goto exit_remove;
> +
> +	err = device_create_file(&pdev->dev, &dev_attr_name);
> +	if (err)
> +		goto exit_remove;
> +
> +	hwmon_dev = hwmon_device_register(&pdev->dev);
> +	if (IS_ERR(hwmon_dev)) {
> +		err = PTR_ERR(hwmon_dev);
> +		goto exit_remove;
> +	}
> +	dev_set_drvdata(&pdev->dev, hwmon_dev);
> +
> +	return 0;
> +
> +exit_remove:
> +	device_remove_file(&pdev->dev, &dev_attr_name);
> +	device_remove_file(&pdev->dev, &dev_attr_power1_input);
> +	device_remove_file(&pdev->dev, &dev_attr_power1_cap);
> +exit:
> +	return err;
> +}
> +
> +static void __devexit f15h_power_remove(struct pci_dev *pdev)
> +{
> +	hwmon_device_unregister(dev_get_drvdata(&pdev->dev));
> +	device_remove_file(&pdev->dev, &dev_attr_name);
> +	device_remove_file(&pdev->dev, &dev_attr_power1_input);
> +	device_remove_file(&pdev->dev, &dev_attr_power1_cap);
> +	dev_set_drvdata(&pdev->dev, NULL);
> +}
> +
> +static const struct pci_device_id f15h_power_id_table[] = {

checkpatch says:

WARNING: Use DEFINE_PCI_DEVICE_TABLE for struct pci_device_id
#326: FILE: drivers/hwmon/f15h_power.c:192:

Please fix.

> +	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_LINK) },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(pci, f15h_power_id_table);
> +
> +static struct pci_driver f15h_power_driver = {
> +	.name = "f15h_power",
> +	.id_table = f15h_power_id_table,
> +	.probe = f15h_power_probe,
> +	.remove = __devexit_p(f15h_power_remove),
> +};
> +
> +static int __init f15h_power_init(void)
> +{
> +	return pci_register_driver(&f15h_power_driver);
> +}
> +
> +static void __exit f15h_power_exit(void)
> +{
> +	pci_unregister_driver(&f15h_power_driver);
> +}
> +
> +module_init(f15h_power_init)
> +module_exit(f15h_power_exit)
> -- 
> 1.7.3.1
> 

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v2] hwmon: Add driver for AMD family 15h processor power information
  2011-04-05 20:12     ` [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h Guenter Roeck
@ 2011-04-06  7:14       ` Clemens Ladisch
  -1 siblings, 0 replies; 43+ messages in thread
From: Clemens Ladisch @ 2011-04-06  7:14 UTC (permalink / raw)
  To: Guenter Roeck, Andreas Herrmann
  Cc: Jean Delvare, Thomas Renninger, lm-sensors, linux-kernel

Guenter Roeck wrote:
> On Tue, Apr 05, 2011 at 10:45:36AM -0400, Andreas Herrmann wrote:
> > +static int __devinit f15h_power_is_internal_node0(struct pci_dev *f4)
> > +{
> > +	u32 val;
> > +	struct pci_dev *f3;
> > +
> > +	f3 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3));
> > +	if (!f3) {
> > +		dev_err(&f4->dev, "no function 3 available on this slot\n");
> > +		return 0;
> 
> It is a common practice to return a negative value on errors. Why not here ?

Apparently, this function returns a boolean value.  Using "bool"/"true"/
"false" would have made this more obvious.

> Also, is this really an error which asks for an error message, or just a CPU
> or system which does not support the attribute ?

AFAICT all F15h CPUs are _known_ to have all these PCI functions; the
error should never occur in practice.

What I do in the k10temp driver in this situation is to trust the CPU to
be there, omitting the pci_get_slot/pci_dev_put and just replacing this:

> > +	pci_read_config_dword(f3, REG_NORTHBRIDGE_CAP, &val);
> > +	pci_dev_put(f3);

with the equivalent of:

  pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3),
                            REG_NORTHBRIDGE_CAP, &val);


BTW: The family 15h CPUs have the same temperature sensor registers
(D18F3xA4 and D18F3x64) as the earlier families, haven't they?


Regards,
Clemens

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h
@ 2011-04-06  7:14       ` Clemens Ladisch
  0 siblings, 0 replies; 43+ messages in thread
From: Clemens Ladisch @ 2011-04-06  7:14 UTC (permalink / raw)
  To: Guenter Roeck, Andreas Herrmann
  Cc: Jean Delvare, Thomas Renninger, lm-sensors, linux-kernel

Guenter Roeck wrote:
> On Tue, Apr 05, 2011 at 10:45:36AM -0400, Andreas Herrmann wrote:
> > +static int __devinit f15h_power_is_internal_node0(struct pci_dev *f4)
> > +{
> > +	u32 val;
> > +	struct pci_dev *f3;
> > +
> > +	f3 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3));
> > +	if (!f3) {
> > +		dev_err(&f4->dev, "no function 3 available on this slot\n");
> > +		return 0;
> 
> It is a common practice to return a negative value on errors. Why not here ?

Apparently, this function returns a boolean value.  Using "bool"/"true"/
"false" would have made this more obvious.

> Also, is this really an error which asks for an error message, or just a CPU
> or system which does not support the attribute ?

AFAICT all F15h CPUs are _known_ to have all these PCI functions; the
error should never occur in practice.

What I do in the k10temp driver in this situation is to trust the CPU to
be there, omitting the pci_get_slot/pci_dev_put and just replacing this:

> > +	pci_read_config_dword(f3, REG_NORTHBRIDGE_CAP, &val);
> > +	pci_dev_put(f3);

with the equivalent of:

  pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3),
                            REG_NORTHBRIDGE_CAP, &val);


BTW: The family 15h CPUs have the same temperature sensor registers
(D18F3xA4 and D18F3x64) as the earlier families, haven't they?


Regards,
Clemens

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v2] hwmon: Add driver for AMD family 15h processor power information
  2011-04-05 20:12     ` [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h Guenter Roeck
@ 2011-04-06  9:31       ` Andreas Herrmann
  -1 siblings, 0 replies; 43+ messages in thread
From: Andreas Herrmann @ 2011-04-06  9:31 UTC (permalink / raw)
  To: Guenter Roeck; +Cc: Jean Delvare, Thomas Renninger, lm-sensors, linux-kernel

On Tue, Apr 05, 2011 at 01:12:38PM -0700, Guenter Roeck wrote:
> On Tue, Apr 05, 2011 at 10:45:36AM -0400, Andreas Herrmann wrote:
> > From: Andreas Herrmann <andreas.herrmann3@amd.com>
> > 
> > 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_cap (ProcessorPwrWatts)
> 
> Hi Andreas,
> 
> What does the CPU do if ProcessorPwrWatts is reached ? Does it start to limit 
> power consumption, ie does it enforce the limit ?

No.

> If not you might want to use power1_max instead.

Ok, so I picked the wrong attribute. Will correct this.

> > * power1_input (CurrPwrWatts)
> > 
> > Changes from v1::
> >  - removed deprecated code line,
> >  - fixed comment,
> >  - report power only once per socket (Power information is provided
> >    for the entire package. On multi-node processors it should only be
> >    reported on internal node 0.)
> > 
> > Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com>
> > ---
> >  drivers/hwmon/Kconfig      |   10 ++
> >  drivers/hwmon/Makefile     |    1 +
> >  drivers/hwmon/f15h_power.c |  216 ++++++++++++++++++++++++++++++++++++++++++++
> 
> Please also add Documentation/hwmon/f15h_power.

Ok.

> >  3 files changed, 227 insertions(+), 0 deletions(-)
> >  create mode 100644 drivers/hwmon/f15h_power.c
> > 
> > diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> > index 8e84b31..0268623 100644
> > --- a/drivers/hwmon/Kconfig
> > +++ b/drivers/hwmon/Kconfig
> > @@ -249,6 +249,16 @@ config SENSORS_K10TEMP
> >  	  This driver can also be built as a module.  If so, the module
> >  	  will be called k10temp.
> >  
> > +config SENSORS_F15H_POWER
> > +	tristate "AMD Family 15h processor power"
> > +	depends on X86 && PCI
> > +	help
> > +	  If you say yes here you get support for processor power
> > +	  information of your AMD family 15h CPU.
> > +
> > +	  This driver can also be built as a module.  If so, the module
> > +	  will be called f15hpower.
> 
> 	f15h_power

Good catch.

> > +
> >  config SENSORS_ASB100
> >  	tristate "Asus ASB100 Bach"
> >  	depends on X86 && I2C && EXPERIMENTAL
> > diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> > index dde02d9..e554b88 100644
> > --- a/drivers/hwmon/Makefile
> > +++ b/drivers/hwmon/Makefile
> > @@ -62,6 +62,7 @@ obj-$(CONFIG_SENSORS_JC42)	+= jc42.o
> >  obj-$(CONFIG_SENSORS_JZ4740)	+= jz4740-hwmon.o
> >  obj-$(CONFIG_SENSORS_K8TEMP)	+= k8temp.o
> >  obj-$(CONFIG_SENSORS_K10TEMP)	+= k10temp.o
> > +obj-$(CONFIG_SENSORS_F15H_POWER) += f15h_power.o
> >  obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o
> >  obj-$(CONFIG_SENSORS_LIS3_SPI)	+= lis3lv02d.o lis3lv02d_spi.o
> >  obj-$(CONFIG_SENSORS_LIS3_I2C)	+= lis3lv02d.o lis3lv02d_i2c.o
> > diff --git a/drivers/hwmon/f15h_power.c b/drivers/hwmon/f15h_power.c
> > new file mode 100644
> > index 0000000..40e3cbd
> > --- /dev/null
> > +++ b/drivers/hwmon/f15h_power.c
> > @@ -0,0 +1,216 @@
> > +/*
> > + * f15h_power.c - AMD Family 15h processor power monitoring
> > + *
> > + * Copyright (c) 2011 Advanced Micro Devices, Inc.
> > + * Author: Andreas Herrmann <andreas.herrmann3@amd.com>
> > + *
> > + *
> > + * This driver is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU General Public License; either
> > + * version 2 of the License, or (at your option) any later version.
> > + *
> > + * This driver 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 driver; if not, see <http://www.gnu.org/licenses/>.
> > + */
> > +
> > +#include <linux/err.h>
> > +#include <linux/hwmon.h>
> > +#include <linux/hwmon-sysfs.h>
> > +#include <linux/init.h>
> > +#include <linux/module.h>
> > +#include <linux/pci.h>
> > +#include <linux/bitops.h>
> > +#include <asm/processor.h>
> > +
> > +MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor");
> > +MODULE_AUTHOR("Andreas Herrmann <andreas.herrmann3@amd.com>");
> > +MODULE_LICENSE("GPL");
> > +
> > +/* D18F3 */
> > +#define REG_NORTHBRIDGE_CAP		0xe8
> > +
> > +/* D18F4 */
> > +#define REG_PROCESSOR_TDP		0x1b8
> > +
> > +/* D18F5 */
> > +#define REG_TDP_RUNNING_AVERAGE		0xe0
> > +#define REG_TDP_LIMIT3			0xe8
> > +
> > +static ssize_t show_power(struct device *dev,
> > +			  struct device_attribute *attr, char *buf)
> > +{
> > +	u32 val, btdp, tdpl, tdp2w, arange;
> > +	s32 acap;
> > +	u64 ctdp;
> > +	struct pci_dev *f4 = to_pci_dev(dev);
> > +	struct pci_dev *f5;
> > +
> > +	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
> > +	btdp = (val >> 16) & 0xffff;
> > +
> > +	f5 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5));
> > +	if (!f5) {
> > +		dev_err(dev, "no function 5 available on this slot\n");
> > +		return 0;
> > +	}
> 
> Unless this is a temporary error condition, you should check if this function
> exists in the probe function, and not create a sysfs entry if it doesn't.
> 
> If it is a temporary error which needs a runtime check, you should return 
> a negative value to indicate that there was an error.

Probably I'll follow Clemens' advice to ignore the theoretic case that
the device doesn't exist. (AFAIK such a scenario is only imaginable
under virtualization if only some PCI devices are passed into a
guest.)

> > +	pci_read_config_dword(f5, REG_TDP_RUNNING_AVERAGE, &val);
> > +	acap = (val >> 4) & 0x3fffff;
> > +	acap = sign_extend32(acap, 22);
> > +	arange = val & 0xf;
> > +
> > +	pci_read_config_dword(f5, REG_TDP_LIMIT3, &val);
> > +	pci_dev_put(f5);
> > +
> > +	tdpl = (val >> 16) & 0x1fff;
> > +	tdp2w = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
> > +	ctdp = tdpl - (s32)(acap >> (arange + 1)) + btdp;
> > +	ctdp *= tdp2w;
> > +
> > +	/*
> > +	 * Convert to microWatt
> > +	 *
> > +	 * power is in Watt provided as fixed point integer with
> > +	 * scaling factor 1/(2^16).  For conversion we use
> > +	 * (10^6)/(2^16) = 15625/(2^10)
> > +	 */
> > +	ctdp = (ctdp * 15625) >> 10;
> > +	return sprintf(buf, "%d\n", (u32) ctdp);
> 
> 	%u

Yep.

> > +}
> > +static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL);
> > +
> > +static ssize_t show_power_cap(struct device *dev,
> > +			     struct device_attribute *attr, char *buf)
> > +{
> > +	u32 val, tdp2w;
> > +	u64 ptdp;
> > +	struct pci_dev *f4 = to_pci_dev(dev);
> > +	struct pci_dev *f5;
> > +
> > +	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
> > +	ptdp = val & 0xffff;
> > +
> > +	f5 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5));
> > +	if (!f5) {
> > +		dev_err(dev, "no function 5 available on this slot\n");
> > +		return 0;
> > +	}
> > +
> Same as above - should be checked in probe function.
> 
> If it is a temporary error which needs a runtime check, you should return 
> a negative value to indicate that there was an error.
> 
> > +	pci_read_config_dword(f5, REG_TDP_LIMIT3, &val);
> > +	pci_dev_put(f5);
> > +
> > +	tdp2w = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
> > +	ptdp *= tdp2w;
> > +
> > +	/* result not allowed to be >= 256W */
> > +	WARN_ON(ptdp>>16 >= 256);
> > +
> Does this really ask for such drastic measures, or would dev_warn() be sufficient ?

dev_warn() should be sufficient.

> > +	/* convert to microWatt */
> > +	ptdp = (ptdp * 15625) >> 10;
> > +	return sprintf(buf, "%d\n", (u32) ptdp);
> 
> 	%u



> > +}
> > +static DEVICE_ATTR(power1_cap, S_IRUGO, show_power_cap, NULL);
> > +
> > +static ssize_t show_name(struct device *dev,
> > +			 struct device_attribute *attr, char *buf)
> > +{
> > +	return sprintf(buf, "f15h_power\n");
> > +}
> > +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
> > +
> > +static int __devinit f15h_power_is_internal_node0(struct pci_dev *f4)
> > +{
> > +	u32 val;
> > +	struct pci_dev *f3;
> > +
> > +	f3 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3));
> > +	if (!f3) {
> > +		dev_err(&f4->dev, "no function 3 available on this slot\n");
> > +		return 0;
> 
> It is a common practice to return a negative value on errors. Why not here ?
> Also, is this really an error which asks for an error message, or just a CPU
> or system which does not support the attribute ? In the latter case, you should 
> not display an error message.

This paranoid check will be removed.

> > +	}
> > +
> > +	pci_read_config_dword(f3, REG_NORTHBRIDGE_CAP, &val);
> > +	pci_dev_put(f3);
> > +
> > +	if ((val & BIT(29)) && ((val >> 30) & 3))
> > +		return 0;
> > +
> > +	return 1;
> > +}
> > +
> > +static int __devinit f15h_power_probe(struct pci_dev *pdev,
> > +				      const struct pci_device_id *id)
> > +{
> > +	struct device *hwmon_dev;
> > +	int err = -ENODEV;
> > +
> > +	if (!f15h_power_is_internal_node0(pdev))
> > +		goto exit;
> > +
> > +	err = device_create_file(&pdev->dev, &dev_attr_power1_input);
> > +	if (err)
> > +		goto exit;
> > +	err = device_create_file(&pdev->dev, &dev_attr_power1_cap);
> > +	if (err)
> > +		goto exit_remove;
> > +
> > +	err = device_create_file(&pdev->dev, &dev_attr_name);
> > +	if (err)
> > +		goto exit_remove;
> > +
> > +	hwmon_dev = hwmon_device_register(&pdev->dev);
> > +	if (IS_ERR(hwmon_dev)) {
> > +		err = PTR_ERR(hwmon_dev);
> > +		goto exit_remove;
> > +	}
> > +	dev_set_drvdata(&pdev->dev, hwmon_dev);
> > +
> > +	return 0;
> > +
> > +exit_remove:
> > +	device_remove_file(&pdev->dev, &dev_attr_name);
> > +	device_remove_file(&pdev->dev, &dev_attr_power1_input);
> > +	device_remove_file(&pdev->dev, &dev_attr_power1_cap);
> > +exit:
> > +	return err;
> > +}
> > +
> > +static void __devexit f15h_power_remove(struct pci_dev *pdev)
> > +{
> > +	hwmon_device_unregister(dev_get_drvdata(&pdev->dev));
> > +	device_remove_file(&pdev->dev, &dev_attr_name);
> > +	device_remove_file(&pdev->dev, &dev_attr_power1_input);
> > +	device_remove_file(&pdev->dev, &dev_attr_power1_cap);
> > +	dev_set_drvdata(&pdev->dev, NULL);
> > +}
> > +
> > +static const struct pci_device_id f15h_power_id_table[] = {
> 
> checkpatch says:
> 
> WARNING: Use DEFINE_PCI_DEVICE_TABLE for struct pci_device_id
> #326: FILE: drivers/hwmon/f15h_power.c:192:
> 
> Please fix.

Ok.

> > +	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_LINK) },
> > +	{}
> > +};
> > +MODULE_DEVICE_TABLE(pci, f15h_power_id_table);
> > +
> > +static struct pci_driver f15h_power_driver = {
> > +	.name = "f15h_power",
> > +	.id_table = f15h_power_id_table,
> > +	.probe = f15h_power_probe,
> > +	.remove = __devexit_p(f15h_power_remove),
> > +};
> > +
> > +static int __init f15h_power_init(void)
> > +{
> > +	return pci_register_driver(&f15h_power_driver);
> > +}
> > +
> > +static void __exit f15h_power_exit(void)
> > +{
> > +	pci_unregister_driver(&f15h_power_driver);
> > +}
> > +
> > +module_init(f15h_power_init)
> > +module_exit(f15h_power_exit)


Thanks for the review.


Andreas

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h
@ 2011-04-06  9:31       ` Andreas Herrmann
  0 siblings, 0 replies; 43+ messages in thread
From: Andreas Herrmann @ 2011-04-06  9:31 UTC (permalink / raw)
  To: Guenter Roeck; +Cc: Jean Delvare, Thomas Renninger, lm-sensors, linux-kernel

On Tue, Apr 05, 2011 at 01:12:38PM -0700, Guenter Roeck wrote:
> On Tue, Apr 05, 2011 at 10:45:36AM -0400, Andreas Herrmann wrote:
> > From: Andreas Herrmann <andreas.herrmann3@amd.com>
> > 
> > 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_cap (ProcessorPwrWatts)
> 
> Hi Andreas,
> 
> What does the CPU do if ProcessorPwrWatts is reached ? Does it start to limit 
> power consumption, ie does it enforce the limit ?

No.

> If not you might want to use power1_max instead.

Ok, so I picked the wrong attribute. Will correct this.

> > * power1_input (CurrPwrWatts)
> > 
> > Changes from v1::
> >  - removed deprecated code line,
> >  - fixed comment,
> >  - report power only once per socket (Power information is provided
> >    for the entire package. On multi-node processors it should only be
> >    reported on internal node 0.)
> > 
> > Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com>
> > ---
> >  drivers/hwmon/Kconfig      |   10 ++
> >  drivers/hwmon/Makefile     |    1 +
> >  drivers/hwmon/f15h_power.c |  216 ++++++++++++++++++++++++++++++++++++++++++++
> 
> Please also add Documentation/hwmon/f15h_power.

Ok.

> >  3 files changed, 227 insertions(+), 0 deletions(-)
> >  create mode 100644 drivers/hwmon/f15h_power.c
> > 
> > diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> > index 8e84b31..0268623 100644
> > --- a/drivers/hwmon/Kconfig
> > +++ b/drivers/hwmon/Kconfig
> > @@ -249,6 +249,16 @@ config SENSORS_K10TEMP
> >  	  This driver can also be built as a module.  If so, the module
> >  	  will be called k10temp.
> >  
> > +config SENSORS_F15H_POWER
> > +	tristate "AMD Family 15h processor power"
> > +	depends on X86 && PCI
> > +	help
> > +	  If you say yes here you get support for processor power
> > +	  information of your AMD family 15h CPU.
> > +
> > +	  This driver can also be built as a module.  If so, the module
> > +	  will be called f15hpower.
> 
> 	f15h_power

Good catch.

> > +
> >  config SENSORS_ASB100
> >  	tristate "Asus ASB100 Bach"
> >  	depends on X86 && I2C && EXPERIMENTAL
> > diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> > index dde02d9..e554b88 100644
> > --- a/drivers/hwmon/Makefile
> > +++ b/drivers/hwmon/Makefile
> > @@ -62,6 +62,7 @@ obj-$(CONFIG_SENSORS_JC42)	+= jc42.o
> >  obj-$(CONFIG_SENSORS_JZ4740)	+= jz4740-hwmon.o
> >  obj-$(CONFIG_SENSORS_K8TEMP)	+= k8temp.o
> >  obj-$(CONFIG_SENSORS_K10TEMP)	+= k10temp.o
> > +obj-$(CONFIG_SENSORS_F15H_POWER) += f15h_power.o
> >  obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o
> >  obj-$(CONFIG_SENSORS_LIS3_SPI)	+= lis3lv02d.o lis3lv02d_spi.o
> >  obj-$(CONFIG_SENSORS_LIS3_I2C)	+= lis3lv02d.o lis3lv02d_i2c.o
> > diff --git a/drivers/hwmon/f15h_power.c b/drivers/hwmon/f15h_power.c
> > new file mode 100644
> > index 0000000..40e3cbd
> > --- /dev/null
> > +++ b/drivers/hwmon/f15h_power.c
> > @@ -0,0 +1,216 @@
> > +/*
> > + * f15h_power.c - AMD Family 15h processor power monitoring
> > + *
> > + * Copyright (c) 2011 Advanced Micro Devices, Inc.
> > + * Author: Andreas Herrmann <andreas.herrmann3@amd.com>
> > + *
> > + *
> > + * This driver is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU General Public License; either
> > + * version 2 of the License, or (at your option) any later version.
> > + *
> > + * This driver 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 driver; if not, see <http://www.gnu.org/licenses/>.
> > + */
> > +
> > +#include <linux/err.h>
> > +#include <linux/hwmon.h>
> > +#include <linux/hwmon-sysfs.h>
> > +#include <linux/init.h>
> > +#include <linux/module.h>
> > +#include <linux/pci.h>
> > +#include <linux/bitops.h>
> > +#include <asm/processor.h>
> > +
> > +MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor");
> > +MODULE_AUTHOR("Andreas Herrmann <andreas.herrmann3@amd.com>");
> > +MODULE_LICENSE("GPL");
> > +
> > +/* D18F3 */
> > +#define REG_NORTHBRIDGE_CAP		0xe8
> > +
> > +/* D18F4 */
> > +#define REG_PROCESSOR_TDP		0x1b8
> > +
> > +/* D18F5 */
> > +#define REG_TDP_RUNNING_AVERAGE		0xe0
> > +#define REG_TDP_LIMIT3			0xe8
> > +
> > +static ssize_t show_power(struct device *dev,
> > +			  struct device_attribute *attr, char *buf)
> > +{
> > +	u32 val, btdp, tdpl, tdp2w, arange;
> > +	s32 acap;
> > +	u64 ctdp;
> > +	struct pci_dev *f4 = to_pci_dev(dev);
> > +	struct pci_dev *f5;
> > +
> > +	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
> > +	btdp = (val >> 16) & 0xffff;
> > +
> > +	f5 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5));
> > +	if (!f5) {
> > +		dev_err(dev, "no function 5 available on this slot\n");
> > +		return 0;
> > +	}
> 
> Unless this is a temporary error condition, you should check if this function
> exists in the probe function, and not create a sysfs entry if it doesn't.
> 
> If it is a temporary error which needs a runtime check, you should return 
> a negative value to indicate that there was an error.

Probably I'll follow Clemens' advice to ignore the theoretic case that
the device doesn't exist. (AFAIK such a scenario is only imaginable
under virtualization if only some PCI devices are passed into a
guest.)

> > +	pci_read_config_dword(f5, REG_TDP_RUNNING_AVERAGE, &val);
> > +	acap = (val >> 4) & 0x3fffff;
> > +	acap = sign_extend32(acap, 22);
> > +	arange = val & 0xf;
> > +
> > +	pci_read_config_dword(f5, REG_TDP_LIMIT3, &val);
> > +	pci_dev_put(f5);
> > +
> > +	tdpl = (val >> 16) & 0x1fff;
> > +	tdp2w = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
> > +	ctdp = tdpl - (s32)(acap >> (arange + 1)) + btdp;
> > +	ctdp *= tdp2w;
> > +
> > +	/*
> > +	 * Convert to microWatt
> > +	 *
> > +	 * power is in Watt provided as fixed point integer with
> > +	 * scaling factor 1/(2^16).  For conversion we use
> > +	 * (10^6)/(2^16) = 15625/(2^10)
> > +	 */
> > +	ctdp = (ctdp * 15625) >> 10;
> > +	return sprintf(buf, "%d\n", (u32) ctdp);
> 
> 	%u

Yep.

> > +}
> > +static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL);
> > +
> > +static ssize_t show_power_cap(struct device *dev,
> > +			     struct device_attribute *attr, char *buf)
> > +{
> > +	u32 val, tdp2w;
> > +	u64 ptdp;
> > +	struct pci_dev *f4 = to_pci_dev(dev);
> > +	struct pci_dev *f5;
> > +
> > +	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
> > +	ptdp = val & 0xffff;
> > +
> > +	f5 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5));
> > +	if (!f5) {
> > +		dev_err(dev, "no function 5 available on this slot\n");
> > +		return 0;
> > +	}
> > +
> Same as above - should be checked in probe function.
> 
> If it is a temporary error which needs a runtime check, you should return 
> a negative value to indicate that there was an error.
> 
> > +	pci_read_config_dword(f5, REG_TDP_LIMIT3, &val);
> > +	pci_dev_put(f5);
> > +
> > +	tdp2w = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
> > +	ptdp *= tdp2w;
> > +
> > +	/* result not allowed to be >= 256W */
> > +	WARN_ON(ptdp>>16 >= 256);
> > +
> Does this really ask for such drastic measures, or would dev_warn() be sufficient ?

dev_warn() should be sufficient.

> > +	/* convert to microWatt */
> > +	ptdp = (ptdp * 15625) >> 10;
> > +	return sprintf(buf, "%d\n", (u32) ptdp);
> 
> 	%u



> > +}
> > +static DEVICE_ATTR(power1_cap, S_IRUGO, show_power_cap, NULL);
> > +
> > +static ssize_t show_name(struct device *dev,
> > +			 struct device_attribute *attr, char *buf)
> > +{
> > +	return sprintf(buf, "f15h_power\n");
> > +}
> > +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
> > +
> > +static int __devinit f15h_power_is_internal_node0(struct pci_dev *f4)
> > +{
> > +	u32 val;
> > +	struct pci_dev *f3;
> > +
> > +	f3 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3));
> > +	if (!f3) {
> > +		dev_err(&f4->dev, "no function 3 available on this slot\n");
> > +		return 0;
> 
> It is a common practice to return a negative value on errors. Why not here ?
> Also, is this really an error which asks for an error message, or just a CPU
> or system which does not support the attribute ? In the latter case, you should 
> not display an error message.

This paranoid check will be removed.

> > +	}
> > +
> > +	pci_read_config_dword(f3, REG_NORTHBRIDGE_CAP, &val);
> > +	pci_dev_put(f3);
> > +
> > +	if ((val & BIT(29)) && ((val >> 30) & 3))
> > +		return 0;
> > +
> > +	return 1;
> > +}
> > +
> > +static int __devinit f15h_power_probe(struct pci_dev *pdev,
> > +				      const struct pci_device_id *id)
> > +{
> > +	struct device *hwmon_dev;
> > +	int err = -ENODEV;
> > +
> > +	if (!f15h_power_is_internal_node0(pdev))
> > +		goto exit;
> > +
> > +	err = device_create_file(&pdev->dev, &dev_attr_power1_input);
> > +	if (err)
> > +		goto exit;
> > +	err = device_create_file(&pdev->dev, &dev_attr_power1_cap);
> > +	if (err)
> > +		goto exit_remove;
> > +
> > +	err = device_create_file(&pdev->dev, &dev_attr_name);
> > +	if (err)
> > +		goto exit_remove;
> > +
> > +	hwmon_dev = hwmon_device_register(&pdev->dev);
> > +	if (IS_ERR(hwmon_dev)) {
> > +		err = PTR_ERR(hwmon_dev);
> > +		goto exit_remove;
> > +	}
> > +	dev_set_drvdata(&pdev->dev, hwmon_dev);
> > +
> > +	return 0;
> > +
> > +exit_remove:
> > +	device_remove_file(&pdev->dev, &dev_attr_name);
> > +	device_remove_file(&pdev->dev, &dev_attr_power1_input);
> > +	device_remove_file(&pdev->dev, &dev_attr_power1_cap);
> > +exit:
> > +	return err;
> > +}
> > +
> > +static void __devexit f15h_power_remove(struct pci_dev *pdev)
> > +{
> > +	hwmon_device_unregister(dev_get_drvdata(&pdev->dev));
> > +	device_remove_file(&pdev->dev, &dev_attr_name);
> > +	device_remove_file(&pdev->dev, &dev_attr_power1_input);
> > +	device_remove_file(&pdev->dev, &dev_attr_power1_cap);
> > +	dev_set_drvdata(&pdev->dev, NULL);
> > +}
> > +
> > +static const struct pci_device_id f15h_power_id_table[] = {
> 
> checkpatch says:
> 
> WARNING: Use DEFINE_PCI_DEVICE_TABLE for struct pci_device_id
> #326: FILE: drivers/hwmon/f15h_power.c:192:
> 
> Please fix.

Ok.

> > +	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_LINK) },
> > +	{}
> > +};
> > +MODULE_DEVICE_TABLE(pci, f15h_power_id_table);
> > +
> > +static struct pci_driver f15h_power_driver = {
> > +	.name = "f15h_power",
> > +	.id_table = f15h_power_id_table,
> > +	.probe = f15h_power_probe,
> > +	.remove = __devexit_p(f15h_power_remove),
> > +};
> > +
> > +static int __init f15h_power_init(void)
> > +{
> > +	return pci_register_driver(&f15h_power_driver);
> > +}
> > +
> > +static void __exit f15h_power_exit(void)
> > +{
> > +	pci_unregister_driver(&f15h_power_driver);
> > +}
> > +
> > +module_init(f15h_power_init)
> > +module_exit(f15h_power_exit)


Thanks for the review.


Andreas

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v2] hwmon: Add driver for AMD family 15h processor power information
  2011-04-06  7:14       ` [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h Clemens Ladisch
@ 2011-04-06  9:38         ` Andreas Herrmann
  -1 siblings, 0 replies; 43+ messages in thread
From: Andreas Herrmann @ 2011-04-06  9:38 UTC (permalink / raw)
  To: Clemens Ladisch
  Cc: Guenter Roeck, Jean Delvare, Thomas Renninger, lm-sensors, linux-kernel

On Wed, Apr 06, 2011 at 09:14:07AM +0200, Clemens Ladisch wrote:
> Guenter Roeck wrote:
> > On Tue, Apr 05, 2011 at 10:45:36AM -0400, Andreas Herrmann wrote:
> > > +static int __devinit f15h_power_is_internal_node0(struct pci_dev *f4)
> > > +{
> > > +	u32 val;
> > > +	struct pci_dev *f3;
> > > +
> > > +	f3 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3));
> > > +	if (!f3) {
> > > +		dev_err(&f4->dev, "no function 3 available on this slot\n");
> > > +		return 0;
> > 
> > It is a common practice to return a negative value on errors. Why not here ?
> 
> Apparently, this function returns a boolean value.  Using "bool"/"true"/
> "false" would have made this more obvious.

Agreed, using bool as return type would have been the better choice.
I'll adapt it.

> > Also, is this really an error which asks for an error message, or just a CPU
> > or system which does not support the attribute ?
> 
> AFAICT all F15h CPUs are _known_ to have all these PCI functions; the
> error should never occur in practice.

Except under virtualization. But common practice is to not pass any
such devices into a guest. So either all CPU northbridge functions are
there or no function at all. I'll adapt the code to your suggestion.

> What I do in the k10temp driver in this situation is to trust the CPU to
> be there, omitting the pci_get_slot/pci_dev_put and just replacing this:
> 
> > > +	pci_read_config_dword(f3, REG_NORTHBRIDGE_CAP, &val);
> > > +	pci_dev_put(f3);
> 
> with the equivalent of:
> 
>   pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3),
>                             REG_NORTHBRIDGE_CAP, &val);
> 
> 
> BTW: The family 15h CPUs have the same temperature sensor registers
> (D18F3xA4 and D18F3x64) as the earlier families, haven't they?

Yes and I think Andre has just sent a patch to add support for 15h to
k10temp.


Thanks,

Andreas



^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h
@ 2011-04-06  9:38         ` Andreas Herrmann
  0 siblings, 0 replies; 43+ messages in thread
From: Andreas Herrmann @ 2011-04-06  9:38 UTC (permalink / raw)
  To: Clemens Ladisch
  Cc: Guenter Roeck, Jean Delvare, Thomas Renninger, lm-sensors, linux-kernel

On Wed, Apr 06, 2011 at 09:14:07AM +0200, Clemens Ladisch wrote:
> Guenter Roeck wrote:
> > On Tue, Apr 05, 2011 at 10:45:36AM -0400, Andreas Herrmann wrote:
> > > +static int __devinit f15h_power_is_internal_node0(struct pci_dev *f4)
> > > +{
> > > +	u32 val;
> > > +	struct pci_dev *f3;
> > > +
> > > +	f3 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3));
> > > +	if (!f3) {
> > > +		dev_err(&f4->dev, "no function 3 available on this slot\n");
> > > +		return 0;
> > 
> > It is a common practice to return a negative value on errors. Why not here ?
> 
> Apparently, this function returns a boolean value.  Using "bool"/"true"/
> "false" would have made this more obvious.

Agreed, using bool as return type would have been the better choice.
I'll adapt it.

> > Also, is this really an error which asks for an error message, or just a CPU
> > or system which does not support the attribute ?
> 
> AFAICT all F15h CPUs are _known_ to have all these PCI functions; the
> error should never occur in practice.

Except under virtualization. But common practice is to not pass any
such devices into a guest. So either all CPU northbridge functions are
there or no function at all. I'll adapt the code to your suggestion.

> What I do in the k10temp driver in this situation is to trust the CPU to
> be there, omitting the pci_get_slot/pci_dev_put and just replacing this:
> 
> > > +	pci_read_config_dword(f3, REG_NORTHBRIDGE_CAP, &val);
> > > +	pci_dev_put(f3);
> 
> with the equivalent of:
> 
>   pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3),
>                             REG_NORTHBRIDGE_CAP, &val);
> 
> 
> BTW: The family 15h CPUs have the same temperature sensor registers
> (D18F3xA4 and D18F3x64) as the earlier families, haven't they?

Yes and I think Andre has just sent a patch to add support for 15h to
k10temp.


Thanks,

Andreas



_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH v3] hwmon: Add driver for AMD family 15h processor power information
  2011-04-05 14:45   ` [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h Andreas Herrmann
@ 2011-04-06 13:54     ` Andreas Herrmann
  -1 siblings, 0 replies; 43+ messages in thread
From: Andreas Herrmann @ 2011-04-06 13:54 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck
  Cc: Thomas Renninger, lm-sensors, linux-kernel, Clemens Ladisch

From: Andreas Herrmann <andreas.herrmann3@amd.com>

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_max (ProcessorPwrWatts)
* power1_input (CurrPwrWatts)

Changes from v2::
 - fix format strings
 - removed paranoid checks for existense of functions 3 and 5
 - changed return type of function f15h_power_is_internal_node0
 - provide power1_max instead of power1_cap
 - use dev_warn instead of WARN_ON
 - rebased against 2.6.39-rc2
 - added Documentation/hwmon/f15h_power

Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com>
---
 Documentation/hwmon/f15h_power |   37 ++++++++
 drivers/hwmon/Kconfig          |   10 ++
 drivers/hwmon/Makefile         |    1 +
 drivers/hwmon/f15h_power.c     |  197 ++++++++++++++++++++++++++++++++++++++++
 drivers/hwmon/k10temp.c        |    2 +-
 5 files changed, 246 insertions(+), 1 deletions(-)
 create mode 100644 Documentation/hwmon/f15h_power
 create mode 100644 drivers/hwmon/f15h_power.c

diff --git a/Documentation/hwmon/f15h_power b/Documentation/hwmon/f15h_power
new file mode 100644
index 0000000..5e990d3
--- /dev/null
+++ b/Documentation/hwmon/f15h_power
@@ -0,0 +1,37 @@
+Kernel driver f15h_power
+========================
+
+Supported chips:
+* AMD Family 15h Processors
+
+  Prefix: 'f15h_power'
+  Addresses scanned: PCI space
+  Datasheets:
+  BIOS and Kernel Developer's Guide (BKDG) For AMD Family 15h Processors
+    (not yet published)
+
+Author: Andreas Herrmann <andreas.herrmann3@amd.com>
+
+Description
+-----------
+
+This driver permits reading of registers providing power information
+of AMD Family 15h processors.
+
+For AMD Family 15h processors the following power values can be
+calculated using different processor northbridge function registers
+
+* BasePwrWatts: Specifies in watts the maximum amount of power
+  consumed by the processor for NB and logic external to the core.
+* 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 ProcessorPwrWatts and CurrPwrWatts:
+* power1_max (ProcessorPwrWatts)
+* power1_input (CurrPwrWatts)
+
+On multi-node processors the calculated value is for the entire
+package and not for a single node. Thus the driver creates sysfs
+attributes only for internal node0 of a multi-node processor.
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 060ef63..36ae085 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -249,6 +249,16 @@ config SENSORS_K10TEMP
 	  This driver can also be built as a module.  If so, the module
 	  will be called k10temp.
 
+config SENSORS_F15H_POWER
+	tristate "AMD Family 15h processor power"
+	depends on X86 && PCI
+	help
+	  If you say yes here you get support for processor power
+	  information of your AMD family 15h CPU.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called f15h_power.
+
 config SENSORS_ASB100
 	tristate "Asus ASB100 Bach"
 	depends on X86 && I2C && EXPERIMENTAL
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 967d0ea..a8d03d5 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_SENSORS_JC42)	+= jc42.o
 obj-$(CONFIG_SENSORS_JZ4740)	+= jz4740-hwmon.o
 obj-$(CONFIG_SENSORS_K8TEMP)	+= k8temp.o
 obj-$(CONFIG_SENSORS_K10TEMP)	+= k10temp.o
+obj-$(CONFIG_SENSORS_F15H_POWER) += f15h_power.o
 obj-$(CONFIG_SENSORS_LINEAGE)	+= lineage-pem.o
 obj-$(CONFIG_SENSORS_LM63)	+= lm63.o
 obj-$(CONFIG_SENSORS_LM70)	+= lm70.o
diff --git a/drivers/hwmon/f15h_power.c b/drivers/hwmon/f15h_power.c
new file mode 100644
index 0000000..648c21d
--- /dev/null
+++ b/drivers/hwmon/f15h_power.c
@@ -0,0 +1,197 @@
+/*
+ * f15h_power.c - AMD Family 15h processor power monitoring
+ *
+ * Copyright (c) 2011 Advanced Micro Devices, Inc.
+ * Author: Andreas Herrmann <andreas.herrmann3@amd.com>
+ *
+ *
+ * This driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This driver 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 driver; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/bitops.h>
+#include <asm/processor.h>
+
+MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor");
+MODULE_AUTHOR("Andreas Herrmann <andreas.herrmann3@amd.com>");
+MODULE_LICENSE("GPL");
+
+/* D18F3 */
+#define REG_NORTHBRIDGE_CAP		0xe8
+
+/* D18F4 */
+#define REG_PROCESSOR_TDP		0x1b8
+
+/* D18F5 */
+#define REG_TDP_RUNNING_AVERAGE		0xe0
+#define REG_TDP_LIMIT3			0xe8
+
+static ssize_t show_power(struct device *dev,
+			  struct device_attribute *attr, char *buf)
+{
+	u32 val, btdp, tdpl, tdp2w, arange;
+	s32 acap;
+	u64 ctdp;
+	struct pci_dev *f4 = to_pci_dev(dev);
+
+	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
+	btdp = (val >> 16) & 0xffff;
+
+	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
+				  REG_TDP_RUNNING_AVERAGE, &val);
+	acap = (val >> 4) & 0x3fffff;
+	acap = sign_extend32(acap, 22);
+	arange = val & 0xf;
+
+	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
+				  REG_TDP_LIMIT3, &val);
+
+	tdpl = (val >> 16) & 0x1fff;
+	tdp2w = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
+	ctdp = tdpl - (s32)(acap >> (arange + 1)) + btdp;
+	ctdp *= tdp2w;
+
+	/*
+	 * Convert to microWatt
+	 *
+	 * power is in Watt provided as fixed point integer with
+	 * scaling factor 1/(2^16).  For conversion we use
+	 * (10^6)/(2^16) = 15625/(2^10)
+	 */
+	ctdp = (ctdp * 15625) >> 10;
+	return sprintf(buf, "%u\n", (u32) ctdp);
+}
+static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL);
+
+static ssize_t show_power_max(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	u32 val, tdp2w;
+	u64 ptdp;
+	struct pci_dev *f4 = to_pci_dev(dev);
+
+	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
+	ptdp = val & 0xffff;
+
+	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
+				  REG_TDP_LIMIT3, &val);
+
+	tdp2w = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
+	ptdp *= tdp2w;
+
+	/* result not allowed to be >= 256W */
+	if ((ptdp>>16) >= 256)
+		dev_warn(dev, "Bogus value for ProcessorPwrWatts (ptdp>=%u)\n",
+			 (u32) (ptdp >> 16));
+
+	/* convert to microWatt */
+	ptdp = (ptdp * 15625) >> 10;
+	return sprintf(buf, "%u\n", (u32) ptdp);
+}
+static DEVICE_ATTR(power1_max, S_IRUGO, show_power_max, NULL);
+
+static ssize_t show_name(struct device *dev,
+			 struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "f15h_power\n");
+}
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static bool __devinit f15h_power_is_internal_node0(struct pci_dev *f4)
+{
+	u32 val;
+
+	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3),
+				  REG_NORTHBRIDGE_CAP, &val);
+	if ((val & BIT(29)) && ((val >> 30) & 3))
+		return false;
+
+	return true;
+}
+
+static int __devinit f15h_power_probe(struct pci_dev *pdev,
+				      const struct pci_device_id *id)
+{
+	struct device *hwmon_dev;
+	int err = -ENODEV;
+
+	if (!f15h_power_is_internal_node0(pdev))
+		goto exit;
+
+	err = device_create_file(&pdev->dev, &dev_attr_power1_input);
+	if (err)
+		goto exit;
+	err = device_create_file(&pdev->dev, &dev_attr_power1_max);
+	if (err)
+		goto exit_remove;
+
+	err = device_create_file(&pdev->dev, &dev_attr_name);
+	if (err)
+		goto exit_remove;
+
+	hwmon_dev = hwmon_device_register(&pdev->dev);
+	if (IS_ERR(hwmon_dev)) {
+		err = PTR_ERR(hwmon_dev);
+		goto exit_remove;
+	}
+	dev_set_drvdata(&pdev->dev, hwmon_dev);
+
+	return 0;
+
+exit_remove:
+	device_remove_file(&pdev->dev, &dev_attr_name);
+	device_remove_file(&pdev->dev, &dev_attr_power1_input);
+	device_remove_file(&pdev->dev, &dev_attr_power1_max);
+exit:
+	return err;
+}
+
+static void __devexit f15h_power_remove(struct pci_dev *pdev)
+{
+	hwmon_device_unregister(dev_get_drvdata(&pdev->dev));
+	device_remove_file(&pdev->dev, &dev_attr_name);
+	device_remove_file(&pdev->dev, &dev_attr_power1_input);
+	device_remove_file(&pdev->dev, &dev_attr_power1_max);
+	dev_set_drvdata(&pdev->dev, NULL);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(f15h_power_id_table) = {
+	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) },
+	{}
+};
+MODULE_DEVICE_TABLE(pci, f15h_power_id_table);
+
+static struct pci_driver f15h_power_driver = {
+	.name = "f15h_power",
+	.id_table = f15h_power_id_table,
+	.probe = f15h_power_probe,
+	.remove = __devexit_p(f15h_power_remove),
+};
+
+static int __init f15h_power_init(void)
+{
+	return pci_register_driver(&f15h_power_driver);
+}
+
+static void __exit f15h_power_exit(void)
+{
+	pci_unregister_driver(&f15h_power_driver);
+}
+
+module_init(f15h_power_init)
+module_exit(f15h_power_exit)
diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c
index 82bf65a..2cbc1ab 100644
--- a/drivers/hwmon/k10temp.c
+++ b/drivers/hwmon/k10temp.c
@@ -205,7 +205,7 @@ static void __devexit k10temp_remove(struct pci_dev *pdev)
 	dev_set_drvdata(&pdev->dev, NULL);
 }
 
-static const struct pci_device_id k10temp_id_table[] = {
+static DEFINE_PCI_DEVICE_TABLE(k10temp_id_table) = {
 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_10H_NB_MISC) },
 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_11H_NB_MISC) },
 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) },
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [lm-sensors] [PATCH v3] hwmon: Add driver for AMD family 15h
@ 2011-04-06 13:54     ` Andreas Herrmann
  0 siblings, 0 replies; 43+ messages in thread
From: Andreas Herrmann @ 2011-04-06 13:54 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck
  Cc: Thomas Renninger, lm-sensors, linux-kernel, Clemens Ladisch

From: Andreas Herrmann <andreas.herrmann3@amd.com>

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_max (ProcessorPwrWatts)
* power1_input (CurrPwrWatts)

Changes from v2::
 - fix format strings
 - removed paranoid checks for existense of functions 3 and 5
 - changed return type of function f15h_power_is_internal_node0
 - provide power1_max instead of power1_cap
 - use dev_warn instead of WARN_ON
 - rebased against 2.6.39-rc2
 - added Documentation/hwmon/f15h_power

Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com>
---
 Documentation/hwmon/f15h_power |   37 ++++++++
 drivers/hwmon/Kconfig          |   10 ++
 drivers/hwmon/Makefile         |    1 +
 drivers/hwmon/f15h_power.c     |  197 ++++++++++++++++++++++++++++++++++++++++
 drivers/hwmon/k10temp.c        |    2 +-
 5 files changed, 246 insertions(+), 1 deletions(-)
 create mode 100644 Documentation/hwmon/f15h_power
 create mode 100644 drivers/hwmon/f15h_power.c

diff --git a/Documentation/hwmon/f15h_power b/Documentation/hwmon/f15h_power
new file mode 100644
index 0000000..5e990d3
--- /dev/null
+++ b/Documentation/hwmon/f15h_power
@@ -0,0 +1,37 @@
+Kernel driver f15h_power
+============
+
+Supported chips:
+* AMD Family 15h Processors
+
+  Prefix: 'f15h_power'
+  Addresses scanned: PCI space
+  Datasheets:
+  BIOS and Kernel Developer's Guide (BKDG) For AMD Family 15h Processors
+    (not yet published)
+
+Author: Andreas Herrmann <andreas.herrmann3@amd.com>
+
+Description
+-----------
+
+This driver permits reading of registers providing power information
+of AMD Family 15h processors.
+
+For AMD Family 15h processors the following power values can be
+calculated using different processor northbridge function registers
+
+* BasePwrWatts: Specifies in watts the maximum amount of power
+  consumed by the processor for NB and logic external to the core.
+* 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 ProcessorPwrWatts and CurrPwrWatts:
+* power1_max (ProcessorPwrWatts)
+* power1_input (CurrPwrWatts)
+
+On multi-node processors the calculated value is for the entire
+package and not for a single node. Thus the driver creates sysfs
+attributes only for internal node0 of a multi-node processor.
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 060ef63..36ae085 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -249,6 +249,16 @@ config SENSORS_K10TEMP
 	  This driver can also be built as a module.  If so, the module
 	  will be called k10temp.
 
+config SENSORS_F15H_POWER
+	tristate "AMD Family 15h processor power"
+	depends on X86 && PCI
+	help
+	  If you say yes here you get support for processor power
+	  information of your AMD family 15h CPU.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called f15h_power.
+
 config SENSORS_ASB100
 	tristate "Asus ASB100 Bach"
 	depends on X86 && I2C && EXPERIMENTAL
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 967d0ea..a8d03d5 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_SENSORS_JC42)	+= jc42.o
 obj-$(CONFIG_SENSORS_JZ4740)	+= jz4740-hwmon.o
 obj-$(CONFIG_SENSORS_K8TEMP)	+= k8temp.o
 obj-$(CONFIG_SENSORS_K10TEMP)	+= k10temp.o
+obj-$(CONFIG_SENSORS_F15H_POWER) += f15h_power.o
 obj-$(CONFIG_SENSORS_LINEAGE)	+= lineage-pem.o
 obj-$(CONFIG_SENSORS_LM63)	+= lm63.o
 obj-$(CONFIG_SENSORS_LM70)	+= lm70.o
diff --git a/drivers/hwmon/f15h_power.c b/drivers/hwmon/f15h_power.c
new file mode 100644
index 0000000..648c21d
--- /dev/null
+++ b/drivers/hwmon/f15h_power.c
@@ -0,0 +1,197 @@
+/*
+ * f15h_power.c - AMD Family 15h processor power monitoring
+ *
+ * Copyright (c) 2011 Advanced Micro Devices, Inc.
+ * Author: Andreas Herrmann <andreas.herrmann3@amd.com>
+ *
+ *
+ * This driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This driver 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 driver; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/bitops.h>
+#include <asm/processor.h>
+
+MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor");
+MODULE_AUTHOR("Andreas Herrmann <andreas.herrmann3@amd.com>");
+MODULE_LICENSE("GPL");
+
+/* D18F3 */
+#define REG_NORTHBRIDGE_CAP		0xe8
+
+/* D18F4 */
+#define REG_PROCESSOR_TDP		0x1b8
+
+/* D18F5 */
+#define REG_TDP_RUNNING_AVERAGE		0xe0
+#define REG_TDP_LIMIT3			0xe8
+
+static ssize_t show_power(struct device *dev,
+			  struct device_attribute *attr, char *buf)
+{
+	u32 val, btdp, tdpl, tdp2w, arange;
+	s32 acap;
+	u64 ctdp;
+	struct pci_dev *f4 = to_pci_dev(dev);
+
+	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
+	btdp = (val >> 16) & 0xffff;
+
+	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
+				  REG_TDP_RUNNING_AVERAGE, &val);
+	acap = (val >> 4) & 0x3fffff;
+	acap = sign_extend32(acap, 22);
+	arange = val & 0xf;
+
+	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
+				  REG_TDP_LIMIT3, &val);
+
+	tdpl = (val >> 16) & 0x1fff;
+	tdp2w = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
+	ctdp = tdpl - (s32)(acap >> (arange + 1)) + btdp;
+	ctdp *= tdp2w;
+
+	/*
+	 * Convert to microWatt
+	 *
+	 * power is in Watt provided as fixed point integer with
+	 * scaling factor 1/(2^16).  For conversion we use
+	 * (10^6)/(2^16) = 15625/(2^10)
+	 */
+	ctdp = (ctdp * 15625) >> 10;
+	return sprintf(buf, "%u\n", (u32) ctdp);
+}
+static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL);
+
+static ssize_t show_power_max(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	u32 val, tdp2w;
+	u64 ptdp;
+	struct pci_dev *f4 = to_pci_dev(dev);
+
+	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
+	ptdp = val & 0xffff;
+
+	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
+				  REG_TDP_LIMIT3, &val);
+
+	tdp2w = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
+	ptdp *= tdp2w;
+
+	/* result not allowed to be >= 256W */
+	if ((ptdp>>16) >= 256)
+		dev_warn(dev, "Bogus value for ProcessorPwrWatts (ptdp>=%u)\n",
+			 (u32) (ptdp >> 16));
+
+	/* convert to microWatt */
+	ptdp = (ptdp * 15625) >> 10;
+	return sprintf(buf, "%u\n", (u32) ptdp);
+}
+static DEVICE_ATTR(power1_max, S_IRUGO, show_power_max, NULL);
+
+static ssize_t show_name(struct device *dev,
+			 struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "f15h_power\n");
+}
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static bool __devinit f15h_power_is_internal_node0(struct pci_dev *f4)
+{
+	u32 val;
+
+	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3),
+				  REG_NORTHBRIDGE_CAP, &val);
+	if ((val & BIT(29)) && ((val >> 30) & 3))
+		return false;
+
+	return true;
+}
+
+static int __devinit f15h_power_probe(struct pci_dev *pdev,
+				      const struct pci_device_id *id)
+{
+	struct device *hwmon_dev;
+	int err = -ENODEV;
+
+	if (!f15h_power_is_internal_node0(pdev))
+		goto exit;
+
+	err = device_create_file(&pdev->dev, &dev_attr_power1_input);
+	if (err)
+		goto exit;
+	err = device_create_file(&pdev->dev, &dev_attr_power1_max);
+	if (err)
+		goto exit_remove;
+
+	err = device_create_file(&pdev->dev, &dev_attr_name);
+	if (err)
+		goto exit_remove;
+
+	hwmon_dev = hwmon_device_register(&pdev->dev);
+	if (IS_ERR(hwmon_dev)) {
+		err = PTR_ERR(hwmon_dev);
+		goto exit_remove;
+	}
+	dev_set_drvdata(&pdev->dev, hwmon_dev);
+
+	return 0;
+
+exit_remove:
+	device_remove_file(&pdev->dev, &dev_attr_name);
+	device_remove_file(&pdev->dev, &dev_attr_power1_input);
+	device_remove_file(&pdev->dev, &dev_attr_power1_max);
+exit:
+	return err;
+}
+
+static void __devexit f15h_power_remove(struct pci_dev *pdev)
+{
+	hwmon_device_unregister(dev_get_drvdata(&pdev->dev));
+	device_remove_file(&pdev->dev, &dev_attr_name);
+	device_remove_file(&pdev->dev, &dev_attr_power1_input);
+	device_remove_file(&pdev->dev, &dev_attr_power1_max);
+	dev_set_drvdata(&pdev->dev, NULL);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(f15h_power_id_table) = {
+	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) },
+	{}
+};
+MODULE_DEVICE_TABLE(pci, f15h_power_id_table);
+
+static struct pci_driver f15h_power_driver = {
+	.name = "f15h_power",
+	.id_table = f15h_power_id_table,
+	.probe = f15h_power_probe,
+	.remove = __devexit_p(f15h_power_remove),
+};
+
+static int __init f15h_power_init(void)
+{
+	return pci_register_driver(&f15h_power_driver);
+}
+
+static void __exit f15h_power_exit(void)
+{
+	pci_unregister_driver(&f15h_power_driver);
+}
+
+module_init(f15h_power_init)
+module_exit(f15h_power_exit)
diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c
index 82bf65a..2cbc1ab 100644
--- a/drivers/hwmon/k10temp.c
+++ b/drivers/hwmon/k10temp.c
@@ -205,7 +205,7 @@ static void __devexit k10temp_remove(struct pci_dev *pdev)
 	dev_set_drvdata(&pdev->dev, NULL);
 }
 
-static const struct pci_device_id k10temp_id_table[] = {
+static DEFINE_PCI_DEVICE_TABLE(k10temp_id_table) = {
 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_10H_NB_MISC) },
 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_11H_NB_MISC) },
 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) },
-- 
1.7.4.1


_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* Re: [PATCH v2] hwmon: Add driver for AMD family 15h processor power information
  2011-04-05 14:45   ` [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h Andreas Herrmann
@ 2011-04-06 14:14     ` Jean Delvare
  -1 siblings, 0 replies; 43+ messages in thread
From: Jean Delvare @ 2011-04-06 14:14 UTC (permalink / raw)
  To: Andreas Herrmann
  Cc: Guenter Roeck, Thomas Renninger, lm-sensors, linux-kernel

On Tue, 5 Apr 2011 16:45:36 +0200, Andreas Herrmann wrote:
> From: Andreas Herrmann <andreas.herrmann3@amd.com>
> 
> 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_cap (ProcessorPwrWatts)
> * power1_input (CurrPwrWatts)

Just a few random comments as I don't have the time for a complete
review:

> 
> Changes from v1::
>  - removed deprecated code line,
>  - fixed comment,
>  - report power only once per socket (Power information is provided
>    for the entire package. On multi-node processors it should only be
>    reported on internal node 0.)
> 
> Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com>
> ---
>  drivers/hwmon/Kconfig      |   10 ++
>  drivers/hwmon/Makefile     |    1 +
>  drivers/hwmon/f15h_power.c |  216 ++++++++++++++++++++++++++++++++++++++++++++

I would prefer fam15h to f15h. The latter is a little too cryptic for
me.

>  3 files changed, 227 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/hwmon/f15h_power.c
> 
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 8e84b31..0268623 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -249,6 +249,16 @@ config SENSORS_K10TEMP
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called k10temp.
>  
> +config SENSORS_F15H_POWER
> +	tristate "AMD Family 15h processor power"
> +	depends on X86 && PCI
> +	help
> +	  If you say yes here you get support for processor power
> +	  information of your AMD family 15h CPU.
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called f15hpower.
> +
>  config SENSORS_ASB100
>  	tristate "Asus ASB100 Bach"
>  	depends on X86 && I2C && EXPERIMENTAL
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index dde02d9..e554b88 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -62,6 +62,7 @@ obj-$(CONFIG_SENSORS_JC42)	+= jc42.o
>  obj-$(CONFIG_SENSORS_JZ4740)	+= jz4740-hwmon.o
>  obj-$(CONFIG_SENSORS_K8TEMP)	+= k8temp.o
>  obj-$(CONFIG_SENSORS_K10TEMP)	+= k10temp.o
> +obj-$(CONFIG_SENSORS_F15H_POWER) += f15h_power.o

Alphabetic order please.

>  obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o
>  obj-$(CONFIG_SENSORS_LIS3_SPI)	+= lis3lv02d.o lis3lv02d_spi.o
>  obj-$(CONFIG_SENSORS_LIS3_I2C)	+= lis3lv02d.o lis3lv02d_i2c.o
> diff --git a/drivers/hwmon/f15h_power.c b/drivers/hwmon/f15h_power.c
> new file mode 100644
> index 0000000..40e3cbd
> --- /dev/null
> +++ b/drivers/hwmon/f15h_power.c
> @@ -0,0 +1,216 @@
> +/*
> + * f15h_power.c - AMD Family 15h processor power monitoring
> + *
> + * Copyright (c) 2011 Advanced Micro Devices, Inc.
> + * Author: Andreas Herrmann <andreas.herrmann3@amd.com>
> + *
> + *
> + * This driver is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This driver 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 driver; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/err.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <linux/bitops.h>
> +#include <asm/processor.h>
> +
> +MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor");
> +MODULE_AUTHOR("Andreas Herrmann <andreas.herrmann3@amd.com>");
> +MODULE_LICENSE("GPL");
> +
> +/* D18F3 */
> +#define REG_NORTHBRIDGE_CAP		0xe8
> +
> +/* D18F4 */
> +#define REG_PROCESSOR_TDP		0x1b8
> +
> +/* D18F5 */
> +#define REG_TDP_RUNNING_AVERAGE		0xe0
> +#define REG_TDP_LIMIT3			0xe8
> +
> +static ssize_t show_power(struct device *dev,
> +			  struct device_attribute *attr, char *buf)
> +{
> +	u32 val, btdp, tdpl, tdp2w, arange;
> +	s32 acap;
> +	u64 ctdp;

These variable names aren't easy to understand.

> +	struct pci_dev *f4 = to_pci_dev(dev);
> +	struct pci_dev *f5;
> +
> +	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
> +	btdp = (val >> 16) & 0xffff;

Useless masking.

> +
> +	f5 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5));
> +	if (!f5) {
> +		dev_err(dev, "no function 5 available on this slot\n");
> +		return 0;
> +	}
> +
> +	pci_read_config_dword(f5, REG_TDP_RUNNING_AVERAGE, &val);

Does this value change over time?

> +	acap = (val >> 4) & 0x3fffff;
> +	acap = sign_extend32(acap, 22);
> +	arange = val & 0xf;
> +
> +	pci_read_config_dword(f5, REG_TDP_LIMIT3, &val);

Same question here. If these values don't change over time, they should
be stored in a per-device structure to avoid repeated PCI configuration
space access.

> +	pci_dev_put(f5);
> +
> +	tdpl = (val >> 16) & 0x1fff;
> +	tdp2w = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
> +	ctdp = tdpl - (s32)(acap >> (arange + 1)) + btdp;
> +	ctdp *= tdp2w;
> +
> +	/*
> +	 * Convert to microWatt
> +	 *
> +	 * power is in Watt provided as fixed point integer with
> +	 * scaling factor 1/(2^16).  For conversion we use
> +	 * (10^6)/(2^16) = 15625/(2^10)
> +	 */
> +	ctdp = (ctdp * 15625) >> 10;
> +	return sprintf(buf, "%d\n", (u32) ctdp);

%d and u32 aren't compatible. %lu and long unsigned int would be OK.

> +}
> +static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL);
> +
> +static ssize_t show_power_cap(struct device *dev,
> +			     struct device_attribute *attr, char *buf)
> +{
> +	u32 val, tdp2w;
> +	u64 ptdp;
> +	struct pci_dev *f4 = to_pci_dev(dev);
> +	struct pci_dev *f5;
> +
> +	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
> +	ptdp = val & 0xffff;
> +
> +	f5 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5));
> +	if (!f5) {
> +		dev_err(dev, "no function 5 available on this slot\n");
> +		return 0;
> +	}
> +
> +	pci_read_config_dword(f5, REG_TDP_LIMIT3, &val);

Same question as above, can this value change over time?

> +	pci_dev_put(f5);
> +
> +	tdp2w = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
> +	ptdp *= tdp2w;
> +
> +	/* result not allowed to be >= 256W */
> +	WARN_ON(ptdp>>16 >= 256);
> +
> +	/* convert to microWatt */
> +	ptdp = (ptdp * 15625) >> 10;
> +	return sprintf(buf, "%d\n", (u32) ptdp);

%d and u32 aren't compatible. %lu and long unsigned int would be OK.

> +}
> +static DEVICE_ATTR(power1_cap, S_IRUGO, show_power_cap, NULL);
> +
> +static ssize_t show_name(struct device *dev,
> +			 struct device_attribute *attr, char *buf)
> +{
> +	return sprintf(buf, "f15h_power\n");
> +}
> +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
> +
> +static int __devinit f15h_power_is_internal_node0(struct pci_dev *f4)
> +{
> +	u32 val;
> +	struct pci_dev *f3;
> +
> +	f3 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3));
> +	if (!f3) {
> +		dev_err(&f4->dev, "no function 3 available on this slot\n");

Please use %d and pass the number as a parameter, and do the same for
the 2 similar messages above. This will let the compiler reuse the
string.

> +		return 0;
> +	}
> +
> +	pci_read_config_dword(f3, REG_NORTHBRIDGE_CAP, &val);
> +	pci_dev_put(f3);
> +
> +	if ((val & BIT(29)) && ((val >> 30) & 3))
> +		return 0;
> +
> +	return 1;
> +}
> +
> +static int __devinit f15h_power_probe(struct pci_dev *pdev,
> +				      const struct pci_device_id *id)
> +{
> +	struct device *hwmon_dev;
> +	int err = -ENODEV;

You should move this initialization to the only case which needs it.
Otherwise you're slowing down the success path with no good reason.

> +
> +	if (!f15h_power_is_internal_node0(pdev))
> +		goto exit;
> +
> +	err = device_create_file(&pdev->dev, &dev_attr_power1_input);
> +	if (err)
> +		goto exit;
> +	err = device_create_file(&pdev->dev, &dev_attr_power1_cap);
> +	if (err)
> +		goto exit_remove;
> +
> +	err = device_create_file(&pdev->dev, &dev_attr_name);
> +	if (err)
> +		goto exit_remove;

Please consider defining and using an attribute group, it makes the
code more simple.

> +
> +	hwmon_dev = hwmon_device_register(&pdev->dev);
> +	if (IS_ERR(hwmon_dev)) {
> +		err = PTR_ERR(hwmon_dev);
> +		goto exit_remove;
> +	}
> +	dev_set_drvdata(&pdev->dev, hwmon_dev);

pci_set_drvdata()

> +
> +	return 0;
> +
> +exit_remove:
> +	device_remove_file(&pdev->dev, &dev_attr_name);
> +	device_remove_file(&pdev->dev, &dev_attr_power1_input);
> +	device_remove_file(&pdev->dev, &dev_attr_power1_cap);
> +exit:
> +	return err;
> +}
> +
> +static void __devexit f15h_power_remove(struct pci_dev *pdev)
> +{
> +	hwmon_device_unregister(dev_get_drvdata(&pdev->dev));
> +	device_remove_file(&pdev->dev, &dev_attr_name);
> +	device_remove_file(&pdev->dev, &dev_attr_power1_input);
> +	device_remove_file(&pdev->dev, &dev_attr_power1_cap);
> +	dev_set_drvdata(&pdev->dev, NULL);

pci_set_drvdata()

> +}
> +
> +static const struct pci_device_id f15h_power_id_table[] = {
> +	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_LINK) },

Not defined anywhere, causing the driver to fail building:

drivers/hwmon/f15h_power.c:193:1: error: 'PCI_DEVICE_ID_AMD_15H_NB_LINK' undeclared here (not in a function)
make[2]: *** [drivers/hwmon/f15h_power.o] Error 1
make[1]: *** [drivers/hwmon] Error 2
make[1]: *** Waiting for unfinished jobs....
make: *** [drivers] Error 2

> +	{}
> +};
> +MODULE_DEVICE_TABLE(pci, f15h_power_id_table);
> +
> +static struct pci_driver f15h_power_driver = {
> +	.name = "f15h_power",
> +	.id_table = f15h_power_id_table,
> +	.probe = f15h_power_probe,
> +	.remove = __devexit_p(f15h_power_remove),
> +};
> +
> +static int __init f15h_power_init(void)
> +{
> +	return pci_register_driver(&f15h_power_driver);
> +}
> +
> +static void __exit f15h_power_exit(void)
> +{
> +	pci_unregister_driver(&f15h_power_driver);
> +}
> +
> +module_init(f15h_power_init)
> +module_exit(f15h_power_exit)


-- 
Jean Delvare

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h
@ 2011-04-06 14:14     ` Jean Delvare
  0 siblings, 0 replies; 43+ messages in thread
From: Jean Delvare @ 2011-04-06 14:14 UTC (permalink / raw)
  To: Andreas Herrmann
  Cc: Guenter Roeck, Thomas Renninger, lm-sensors, linux-kernel

On Tue, 5 Apr 2011 16:45:36 +0200, Andreas Herrmann wrote:
> From: Andreas Herrmann <andreas.herrmann3@amd.com>
> 
> 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_cap (ProcessorPwrWatts)
> * power1_input (CurrPwrWatts)

Just a few random comments as I don't have the time for a complete
review:

> 
> Changes from v1::
>  - removed deprecated code line,
>  - fixed comment,
>  - report power only once per socket (Power information is provided
>    for the entire package. On multi-node processors it should only be
>    reported on internal node 0.)
> 
> Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com>
> ---
>  drivers/hwmon/Kconfig      |   10 ++
>  drivers/hwmon/Makefile     |    1 +
>  drivers/hwmon/f15h_power.c |  216 ++++++++++++++++++++++++++++++++++++++++++++

I would prefer fam15h to f15h. The latter is a little too cryptic for
me.

>  3 files changed, 227 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/hwmon/f15h_power.c
> 
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 8e84b31..0268623 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -249,6 +249,16 @@ config SENSORS_K10TEMP
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called k10temp.
>  
> +config SENSORS_F15H_POWER
> +	tristate "AMD Family 15h processor power"
> +	depends on X86 && PCI
> +	help
> +	  If you say yes here you get support for processor power
> +	  information of your AMD family 15h CPU.
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called f15hpower.
> +
>  config SENSORS_ASB100
>  	tristate "Asus ASB100 Bach"
>  	depends on X86 && I2C && EXPERIMENTAL
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index dde02d9..e554b88 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -62,6 +62,7 @@ obj-$(CONFIG_SENSORS_JC42)	+= jc42.o
>  obj-$(CONFIG_SENSORS_JZ4740)	+= jz4740-hwmon.o
>  obj-$(CONFIG_SENSORS_K8TEMP)	+= k8temp.o
>  obj-$(CONFIG_SENSORS_K10TEMP)	+= k10temp.o
> +obj-$(CONFIG_SENSORS_F15H_POWER) += f15h_power.o

Alphabetic order please.

>  obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o
>  obj-$(CONFIG_SENSORS_LIS3_SPI)	+= lis3lv02d.o lis3lv02d_spi.o
>  obj-$(CONFIG_SENSORS_LIS3_I2C)	+= lis3lv02d.o lis3lv02d_i2c.o
> diff --git a/drivers/hwmon/f15h_power.c b/drivers/hwmon/f15h_power.c
> new file mode 100644
> index 0000000..40e3cbd
> --- /dev/null
> +++ b/drivers/hwmon/f15h_power.c
> @@ -0,0 +1,216 @@
> +/*
> + * f15h_power.c - AMD Family 15h processor power monitoring
> + *
> + * Copyright (c) 2011 Advanced Micro Devices, Inc.
> + * Author: Andreas Herrmann <andreas.herrmann3@amd.com>
> + *
> + *
> + * This driver is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This driver 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 driver; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/err.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <linux/bitops.h>
> +#include <asm/processor.h>
> +
> +MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor");
> +MODULE_AUTHOR("Andreas Herrmann <andreas.herrmann3@amd.com>");
> +MODULE_LICENSE("GPL");
> +
> +/* D18F3 */
> +#define REG_NORTHBRIDGE_CAP		0xe8
> +
> +/* D18F4 */
> +#define REG_PROCESSOR_TDP		0x1b8
> +
> +/* D18F5 */
> +#define REG_TDP_RUNNING_AVERAGE		0xe0
> +#define REG_TDP_LIMIT3			0xe8
> +
> +static ssize_t show_power(struct device *dev,
> +			  struct device_attribute *attr, char *buf)
> +{
> +	u32 val, btdp, tdpl, tdp2w, arange;
> +	s32 acap;
> +	u64 ctdp;

These variable names aren't easy to understand.

> +	struct pci_dev *f4 = to_pci_dev(dev);
> +	struct pci_dev *f5;
> +
> +	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
> +	btdp = (val >> 16) & 0xffff;

Useless masking.

> +
> +	f5 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5));
> +	if (!f5) {
> +		dev_err(dev, "no function 5 available on this slot\n");
> +		return 0;
> +	}
> +
> +	pci_read_config_dword(f5, REG_TDP_RUNNING_AVERAGE, &val);

Does this value change over time?

> +	acap = (val >> 4) & 0x3fffff;
> +	acap = sign_extend32(acap, 22);
> +	arange = val & 0xf;
> +
> +	pci_read_config_dword(f5, REG_TDP_LIMIT3, &val);

Same question here. If these values don't change over time, they should
be stored in a per-device structure to avoid repeated PCI configuration
space access.

> +	pci_dev_put(f5);
> +
> +	tdpl = (val >> 16) & 0x1fff;
> +	tdp2w = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
> +	ctdp = tdpl - (s32)(acap >> (arange + 1)) + btdp;
> +	ctdp *= tdp2w;
> +
> +	/*
> +	 * Convert to microWatt
> +	 *
> +	 * power is in Watt provided as fixed point integer with
> +	 * scaling factor 1/(2^16).  For conversion we use
> +	 * (10^6)/(2^16) = 15625/(2^10)
> +	 */
> +	ctdp = (ctdp * 15625) >> 10;
> +	return sprintf(buf, "%d\n", (u32) ctdp);

%d and u32 aren't compatible. %lu and long unsigned int would be OK.

> +}
> +static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL);
> +
> +static ssize_t show_power_cap(struct device *dev,
> +			     struct device_attribute *attr, char *buf)
> +{
> +	u32 val, tdp2w;
> +	u64 ptdp;
> +	struct pci_dev *f4 = to_pci_dev(dev);
> +	struct pci_dev *f5;
> +
> +	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
> +	ptdp = val & 0xffff;
> +
> +	f5 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5));
> +	if (!f5) {
> +		dev_err(dev, "no function 5 available on this slot\n");
> +		return 0;
> +	}
> +
> +	pci_read_config_dword(f5, REG_TDP_LIMIT3, &val);

Same question as above, can this value change over time?

> +	pci_dev_put(f5);
> +
> +	tdp2w = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
> +	ptdp *= tdp2w;
> +
> +	/* result not allowed to be >= 256W */
> +	WARN_ON(ptdp>>16 >= 256);
> +
> +	/* convert to microWatt */
> +	ptdp = (ptdp * 15625) >> 10;
> +	return sprintf(buf, "%d\n", (u32) ptdp);

%d and u32 aren't compatible. %lu and long unsigned int would be OK.

> +}
> +static DEVICE_ATTR(power1_cap, S_IRUGO, show_power_cap, NULL);
> +
> +static ssize_t show_name(struct device *dev,
> +			 struct device_attribute *attr, char *buf)
> +{
> +	return sprintf(buf, "f15h_power\n");
> +}
> +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
> +
> +static int __devinit f15h_power_is_internal_node0(struct pci_dev *f4)
> +{
> +	u32 val;
> +	struct pci_dev *f3;
> +
> +	f3 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3));
> +	if (!f3) {
> +		dev_err(&f4->dev, "no function 3 available on this slot\n");

Please use %d and pass the number as a parameter, and do the same for
the 2 similar messages above. This will let the compiler reuse the
string.

> +		return 0;
> +	}
> +
> +	pci_read_config_dword(f3, REG_NORTHBRIDGE_CAP, &val);
> +	pci_dev_put(f3);
> +
> +	if ((val & BIT(29)) && ((val >> 30) & 3))
> +		return 0;
> +
> +	return 1;
> +}
> +
> +static int __devinit f15h_power_probe(struct pci_dev *pdev,
> +				      const struct pci_device_id *id)
> +{
> +	struct device *hwmon_dev;
> +	int err = -ENODEV;

You should move this initialization to the only case which needs it.
Otherwise you're slowing down the success path with no good reason.

> +
> +	if (!f15h_power_is_internal_node0(pdev))
> +		goto exit;
> +
> +	err = device_create_file(&pdev->dev, &dev_attr_power1_input);
> +	if (err)
> +		goto exit;
> +	err = device_create_file(&pdev->dev, &dev_attr_power1_cap);
> +	if (err)
> +		goto exit_remove;
> +
> +	err = device_create_file(&pdev->dev, &dev_attr_name);
> +	if (err)
> +		goto exit_remove;

Please consider defining and using an attribute group, it makes the
code more simple.

> +
> +	hwmon_dev = hwmon_device_register(&pdev->dev);
> +	if (IS_ERR(hwmon_dev)) {
> +		err = PTR_ERR(hwmon_dev);
> +		goto exit_remove;
> +	}
> +	dev_set_drvdata(&pdev->dev, hwmon_dev);

pci_set_drvdata()

> +
> +	return 0;
> +
> +exit_remove:
> +	device_remove_file(&pdev->dev, &dev_attr_name);
> +	device_remove_file(&pdev->dev, &dev_attr_power1_input);
> +	device_remove_file(&pdev->dev, &dev_attr_power1_cap);
> +exit:
> +	return err;
> +}
> +
> +static void __devexit f15h_power_remove(struct pci_dev *pdev)
> +{
> +	hwmon_device_unregister(dev_get_drvdata(&pdev->dev));
> +	device_remove_file(&pdev->dev, &dev_attr_name);
> +	device_remove_file(&pdev->dev, &dev_attr_power1_input);
> +	device_remove_file(&pdev->dev, &dev_attr_power1_cap);
> +	dev_set_drvdata(&pdev->dev, NULL);

pci_set_drvdata()

> +}
> +
> +static const struct pci_device_id f15h_power_id_table[] = {
> +	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_LINK) },

Not defined anywhere, causing the driver to fail building:

drivers/hwmon/f15h_power.c:193:1: error: 'PCI_DEVICE_ID_AMD_15H_NB_LINK' undeclared here (not in a function)
make[2]: *** [drivers/hwmon/f15h_power.o] Error 1
make[1]: *** [drivers/hwmon] Error 2
make[1]: *** Waiting for unfinished jobs....
make: *** [drivers] Error 2

> +	{}
> +};
> +MODULE_DEVICE_TABLE(pci, f15h_power_id_table);
> +
> +static struct pci_driver f15h_power_driver = {
> +	.name = "f15h_power",
> +	.id_table = f15h_power_id_table,
> +	.probe = f15h_power_probe,
> +	.remove = __devexit_p(f15h_power_remove),
> +};
> +
> +static int __init f15h_power_init(void)
> +{
> +	return pci_register_driver(&f15h_power_driver);
> +}
> +
> +static void __exit f15h_power_exit(void)
> +{
> +	pci_unregister_driver(&f15h_power_driver);
> +}
> +
> +module_init(f15h_power_init)
> +module_exit(f15h_power_exit)


-- 
Jean Delvare

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v2] hwmon: Add driver for AMD family 15h processor power information
  2011-04-06 14:14     ` [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h Jean Delvare
@ 2011-04-06 15:19       ` Andreas Herrmann
  -1 siblings, 0 replies; 43+ messages in thread
From: Andreas Herrmann @ 2011-04-06 15:19 UTC (permalink / raw)
  To: Jean Delvare; +Cc: Guenter Roeck, Thomas Renninger, lm-sensors, linux-kernel

On Wed, Apr 06, 2011 at 04:14:01PM +0200, Jean Delvare wrote:
> On Tue, 5 Apr 2011 16:45:36 +0200, Andreas Herrmann wrote:
> > From: Andreas Herrmann <andreas.herrmann3@amd.com>
> > 
> > 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_cap (ProcessorPwrWatts)
> > * power1_input (CurrPwrWatts)
> 
> Just a few random comments as I don't have the time for a complete
> review:
> 
> > 
> > Changes from v1::
> >  - removed deprecated code line,
> >  - fixed comment,
> >  - report power only once per socket (Power information is provided
> >    for the entire package. On multi-node processors it should only be
> >    reported on internal node 0.)
> > 
> > Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com>
> > ---
> >  drivers/hwmon/Kconfig      |   10 ++
> >  drivers/hwmon/Makefile     |    1 +
> >  drivers/hwmon/f15h_power.c |  216 ++++++++++++++++++++++++++++++++++++++++++++
> 
> I would prefer fam15h to f15h. The latter is a little too cryptic for
> me.

Hmm, (unfortunately;-) seconded, e.g.

  linux-2.6 $ grep -rs fam10h arch/x86 | wc
       71     450    7930
  linux-2.6 $ grep -rs f10h arch/x86 | wc
       0       0       0

Will change it.

  [snip]

> > --- a/drivers/hwmon/Makefile
> > +++ b/drivers/hwmon/Makefile
> > @@ -62,6 +62,7 @@ obj-$(CONFIG_SENSORS_JC42)	+= jc42.o
> >  obj-$(CONFIG_SENSORS_JZ4740)	+= jz4740-hwmon.o
> >  obj-$(CONFIG_SENSORS_K8TEMP)	+= k8temp.o
> >  obj-$(CONFIG_SENSORS_K10TEMP)	+= k10temp.o
> > +obj-$(CONFIG_SENSORS_F15H_POWER) += f15h_power.o
> 
> Alphabetic order please.

ok.

[snip]

> > +static ssize_t show_power(struct device *dev,
> > +			  struct device_attribute *attr, char *buf)
> > +{
> > +	u32 val, btdp, tdpl, tdp2w, arange;
> > +	s32 acap;
> > +	u64 ctdp;
> 
> These variable names aren't easy to understand.

Just random names which eventually map to the spec:

btdp - base_tdp
tdpl - tdp_limit
tdp2w - tdp_to_watt
acap - average_accumulator_capture (or even worse how about "processor_tdp_running_average_accumulator":(
arange - average_range

I don't think that changing the names make it much easier to
reconstruct the calculation but if you insist in changing it I'll
adapt it.

> > +	struct pci_dev *f4 = to_pci_dev(dev);
> > +	struct pci_dev *f5;
> > +
> > +	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
> > +	btdp = (val >> 16) & 0xffff;
> 
> Useless masking.

To be removed.

> > +
> > +	f5 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5));
> > +	if (!f5) {
> > +		dev_err(dev, "no function 5 available on this slot\n");
> > +		return 0;
> > +	}
> > +
> > +	pci_read_config_dword(f5, REG_TDP_RUNNING_AVERAGE, &val);
> 
> Does this value change over time?

Yes.

> > +	acap = (val >> 4) & 0x3fffff;
> > +	acap = sign_extend32(acap, 22);
> > +	arange = val & 0xf;
> > +
> > +	pci_read_config_dword(f5, REG_TDP_LIMIT3, &val);
> 
> Same question here. If these values don't change over time, they should
> be stored in a per-device structure to avoid repeated PCI configuration
> space access.

The parts I use from this register shouldn't change => code to be modified.

> > +	pci_dev_put(f5);
> > +
> > +	tdpl = (val >> 16) & 0x1fff;
> > +	tdp2w = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
> > +	ctdp = tdpl - (s32)(acap >> (arange + 1)) + btdp;
> > +	ctdp *= tdp2w;
> > +
> > +	/*
> > +	 * Convert to microWatt
> > +	 *
> > +	 * power is in Watt provided as fixed point integer with
> > +	 * scaling factor 1/(2^16).  For conversion we use
> > +	 * (10^6)/(2^16) = 15625/(2^10)
> > +	 */
> > +	ctdp = (ctdp * 15625) >> 10;
> > +	return sprintf(buf, "%d\n", (u32) ctdp);
> 
> %d and u32 aren't compatible. %lu and long unsigned int would be OK.

already fixed in v3

> > +}
> > +static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL);
> > +
> > +static ssize_t show_power_cap(struct device *dev,
> > +			     struct device_attribute *attr, char *buf)
> > +{
> > +	u32 val, tdp2w;
> > +	u64 ptdp;
> > +	struct pci_dev *f4 = to_pci_dev(dev);
> > +	struct pci_dev *f5;
> > +
> > +	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
> > +	ptdp = val & 0xffff;
> > +
> > +	f5 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5));
> > +	if (!f5) {
> > +		dev_err(dev, "no function 5 available on this slot\n");
> > +		return 0;
> > +	}
> > +
> > +	pci_read_config_dword(f5, REG_TDP_LIMIT3, &val);
> 
> Same question as above, can this value change over time?

Needs to be modified.

> > +	pci_dev_put(f5);
> > +
> > +	tdp2w = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
> > +	ptdp *= tdp2w;
> > +
> > +	/* result not allowed to be >= 256W */
> > +	WARN_ON(ptdp>>16 >= 256);
> > +
> > +	/* convert to microWatt */
> > +	ptdp = (ptdp * 15625) >> 10;
> > +	return sprintf(buf, "%d\n", (u32) ptdp);
> 
> %d and u32 aren't compatible. %lu and long unsigned int would be OK.

fixed in v3.

> > +}
> > +static DEVICE_ATTR(power1_cap, S_IRUGO, show_power_cap, NULL);
> > +
> > +static ssize_t show_name(struct device *dev,
> > +			 struct device_attribute *attr, char *buf)
> > +{
> > +	return sprintf(buf, "f15h_power\n");
> > +}
> > +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
> > +
> > +static int __devinit f15h_power_is_internal_node0(struct pci_dev *f4)
> > +{
> > +	u32 val;
> > +	struct pci_dev *f3;
> > +
> > +	f3 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3));
> > +	if (!f3) {
> > +		dev_err(&f4->dev, "no function 3 available on this slot\n");
> 
> Please use %d and pass the number as a parameter, and do the same for
> the 2 similar messages above. This will let the compiler reuse the
> string.

message removed in v3

> > +		return 0;
> > +	}
> > +
> > +	pci_read_config_dword(f3, REG_NORTHBRIDGE_CAP, &val);
> > +	pci_dev_put(f3);
> > +
> > +	if ((val & BIT(29)) && ((val >> 30) & 3))
> > +		return 0;
> > +
> > +	return 1;
> > +}
> > +
> > +static int __devinit f15h_power_probe(struct pci_dev *pdev,
> > +				      const struct pci_device_id *id)
> > +{
> > +	struct device *hwmon_dev;
> > +	int err = -ENODEV;
> 
> You should move this initialization to the only case which needs it.
> Otherwise you're slowing down the success path with no good reason.

to be done

> > +
> > +	if (!f15h_power_is_internal_node0(pdev))
> > +		goto exit;
> > +
> > +	err = device_create_file(&pdev->dev, &dev_attr_power1_input);
> > +	if (err)
> > +		goto exit;
> > +	err = device_create_file(&pdev->dev, &dev_attr_power1_cap);
> > +	if (err)
> > +		goto exit_remove;
> > +
> > +	err = device_create_file(&pdev->dev, &dev_attr_name);
> > +	if (err)
> > +		goto exit_remove;
> 
> Please consider defining and using an attribute group, it makes the
> code more simple.

Will think about it.

> > +
> > +	hwmon_dev = hwmon_device_register(&pdev->dev);
> > +	if (IS_ERR(hwmon_dev)) {
> > +		err = PTR_ERR(hwmon_dev);
> > +		goto exit_remove;
> > +	}
> > +	dev_set_drvdata(&pdev->dev, hwmon_dev);
> 
> pci_set_drvdata()

Are you sure?
No single hwmon driver is using this wrapper around dev_set_drvdata() so far. (Or I just missed it.)
At the moment only dev_set_drvdata() is used by

 drivers/hwmon/adcxx.c
 drivers/hwmon/lm70.c
 drivers/hwmon/ultra45_env.c
 drivers/hwmon/ibmpex.c
 drivers/hwmon/k10temp.c
 drivers/hwmon/ibmaem.c
 drivers/hwmon/k8temp.c

> > +
> > +	return 0;
> > +
> > +exit_remove:
> > +	device_remove_file(&pdev->dev, &dev_attr_name);
> > +	device_remove_file(&pdev->dev, &dev_attr_power1_input);
> > +	device_remove_file(&pdev->dev, &dev_attr_power1_cap);
> > +exit:
> > +	return err;
> > +}
> > +
> > +static void __devexit f15h_power_remove(struct pci_dev *pdev)
> > +{
> > +	hwmon_device_unregister(dev_get_drvdata(&pdev->dev));
> > +	device_remove_file(&pdev->dev, &dev_attr_name);
> > +	device_remove_file(&pdev->dev, &dev_attr_power1_input);
> > +	device_remove_file(&pdev->dev, &dev_attr_power1_cap);
> > +	dev_set_drvdata(&pdev->dev, NULL);
> 
> pci_set_drvdata()
> 
> > +}
> > +
> > +static const struct pci_device_id f15h_power_id_table[] = {
> > +	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_LINK) },
> 
> Not defined anywhere, causing the driver to fail building:
> 
> drivers/hwmon/f15h_power.c:193:1: error: 'PCI_DEVICE_ID_AMD_15H_NB_LINK' undeclared here (not in a function)
> make[2]: *** [drivers/hwmon/f15h_power.o] Error 1
> make[1]: *** [drivers/hwmon] Error 2
> make[1]: *** Waiting for unfinished jobs....
> make: *** [drivers] Error 2

The macro changed with .39-rc2
Already fixed in v3

Thanks for your review,

Please let me know whether changing the variable names and using
pci_set_drvdata() is strictly required.


Andreas

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h
@ 2011-04-06 15:19       ` Andreas Herrmann
  0 siblings, 0 replies; 43+ messages in thread
From: Andreas Herrmann @ 2011-04-06 15:19 UTC (permalink / raw)
  To: Jean Delvare; +Cc: Guenter Roeck, Thomas Renninger, lm-sensors, linux-kernel

On Wed, Apr 06, 2011 at 04:14:01PM +0200, Jean Delvare wrote:
> On Tue, 5 Apr 2011 16:45:36 +0200, Andreas Herrmann wrote:
> > From: Andreas Herrmann <andreas.herrmann3@amd.com>
> > 
> > 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_cap (ProcessorPwrWatts)
> > * power1_input (CurrPwrWatts)
> 
> Just a few random comments as I don't have the time for a complete
> review:
> 
> > 
> > Changes from v1::
> >  - removed deprecated code line,
> >  - fixed comment,
> >  - report power only once per socket (Power information is provided
> >    for the entire package. On multi-node processors it should only be
> >    reported on internal node 0.)
> > 
> > Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com>
> > ---
> >  drivers/hwmon/Kconfig      |   10 ++
> >  drivers/hwmon/Makefile     |    1 +
> >  drivers/hwmon/f15h_power.c |  216 ++++++++++++++++++++++++++++++++++++++++++++
> 
> I would prefer fam15h to f15h. The latter is a little too cryptic for
> me.

Hmm, (unfortunately;-) seconded, e.g.

  linux-2.6 $ grep -rs fam10h arch/x86 | wc
       71     450    7930
  linux-2.6 $ grep -rs f10h arch/x86 | wc
       0       0       0

Will change it.

  [snip]

> > --- a/drivers/hwmon/Makefile
> > +++ b/drivers/hwmon/Makefile
> > @@ -62,6 +62,7 @@ obj-$(CONFIG_SENSORS_JC42)	+= jc42.o
> >  obj-$(CONFIG_SENSORS_JZ4740)	+= jz4740-hwmon.o
> >  obj-$(CONFIG_SENSORS_K8TEMP)	+= k8temp.o
> >  obj-$(CONFIG_SENSORS_K10TEMP)	+= k10temp.o
> > +obj-$(CONFIG_SENSORS_F15H_POWER) += f15h_power.o
> 
> Alphabetic order please.

ok.

[snip]

> > +static ssize_t show_power(struct device *dev,
> > +			  struct device_attribute *attr, char *buf)
> > +{
> > +	u32 val, btdp, tdpl, tdp2w, arange;
> > +	s32 acap;
> > +	u64 ctdp;
> 
> These variable names aren't easy to understand.

Just random names which eventually map to the spec:

btdp - base_tdp
tdpl - tdp_limit
tdp2w - tdp_to_watt
acap - average_accumulator_capture (or even worse how about "processor_tdp_running_average_accumulator":(
arange - average_range

I don't think that changing the names make it much easier to
reconstruct the calculation but if you insist in changing it I'll
adapt it.

> > +	struct pci_dev *f4 = to_pci_dev(dev);
> > +	struct pci_dev *f5;
> > +
> > +	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
> > +	btdp = (val >> 16) & 0xffff;
> 
> Useless masking.

To be removed.

> > +
> > +	f5 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5));
> > +	if (!f5) {
> > +		dev_err(dev, "no function 5 available on this slot\n");
> > +		return 0;
> > +	}
> > +
> > +	pci_read_config_dword(f5, REG_TDP_RUNNING_AVERAGE, &val);
> 
> Does this value change over time?

Yes.

> > +	acap = (val >> 4) & 0x3fffff;
> > +	acap = sign_extend32(acap, 22);
> > +	arange = val & 0xf;
> > +
> > +	pci_read_config_dword(f5, REG_TDP_LIMIT3, &val);
> 
> Same question here. If these values don't change over time, they should
> be stored in a per-device structure to avoid repeated PCI configuration
> space access.

The parts I use from this register shouldn't change => code to be modified.

> > +	pci_dev_put(f5);
> > +
> > +	tdpl = (val >> 16) & 0x1fff;
> > +	tdp2w = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
> > +	ctdp = tdpl - (s32)(acap >> (arange + 1)) + btdp;
> > +	ctdp *= tdp2w;
> > +
> > +	/*
> > +	 * Convert to microWatt
> > +	 *
> > +	 * power is in Watt provided as fixed point integer with
> > +	 * scaling factor 1/(2^16).  For conversion we use
> > +	 * (10^6)/(2^16) = 15625/(2^10)
> > +	 */
> > +	ctdp = (ctdp * 15625) >> 10;
> > +	return sprintf(buf, "%d\n", (u32) ctdp);
> 
> %d and u32 aren't compatible. %lu and long unsigned int would be OK.

already fixed in v3

> > +}
> > +static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL);
> > +
> > +static ssize_t show_power_cap(struct device *dev,
> > +			     struct device_attribute *attr, char *buf)
> > +{
> > +	u32 val, tdp2w;
> > +	u64 ptdp;
> > +	struct pci_dev *f4 = to_pci_dev(dev);
> > +	struct pci_dev *f5;
> > +
> > +	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
> > +	ptdp = val & 0xffff;
> > +
> > +	f5 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5));
> > +	if (!f5) {
> > +		dev_err(dev, "no function 5 available on this slot\n");
> > +		return 0;
> > +	}
> > +
> > +	pci_read_config_dword(f5, REG_TDP_LIMIT3, &val);
> 
> Same question as above, can this value change over time?

Needs to be modified.

> > +	pci_dev_put(f5);
> > +
> > +	tdp2w = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
> > +	ptdp *= tdp2w;
> > +
> > +	/* result not allowed to be >= 256W */
> > +	WARN_ON(ptdp>>16 >= 256);
> > +
> > +	/* convert to microWatt */
> > +	ptdp = (ptdp * 15625) >> 10;
> > +	return sprintf(buf, "%d\n", (u32) ptdp);
> 
> %d and u32 aren't compatible. %lu and long unsigned int would be OK.

fixed in v3.

> > +}
> > +static DEVICE_ATTR(power1_cap, S_IRUGO, show_power_cap, NULL);
> > +
> > +static ssize_t show_name(struct device *dev,
> > +			 struct device_attribute *attr, char *buf)
> > +{
> > +	return sprintf(buf, "f15h_power\n");
> > +}
> > +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
> > +
> > +static int __devinit f15h_power_is_internal_node0(struct pci_dev *f4)
> > +{
> > +	u32 val;
> > +	struct pci_dev *f3;
> > +
> > +	f3 = pci_get_slot(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3));
> > +	if (!f3) {
> > +		dev_err(&f4->dev, "no function 3 available on this slot\n");
> 
> Please use %d and pass the number as a parameter, and do the same for
> the 2 similar messages above. This will let the compiler reuse the
> string.

message removed in v3

> > +		return 0;
> > +	}
> > +
> > +	pci_read_config_dword(f3, REG_NORTHBRIDGE_CAP, &val);
> > +	pci_dev_put(f3);
> > +
> > +	if ((val & BIT(29)) && ((val >> 30) & 3))
> > +		return 0;
> > +
> > +	return 1;
> > +}
> > +
> > +static int __devinit f15h_power_probe(struct pci_dev *pdev,
> > +				      const struct pci_device_id *id)
> > +{
> > +	struct device *hwmon_dev;
> > +	int err = -ENODEV;
> 
> You should move this initialization to the only case which needs it.
> Otherwise you're slowing down the success path with no good reason.

to be done

> > +
> > +	if (!f15h_power_is_internal_node0(pdev))
> > +		goto exit;
> > +
> > +	err = device_create_file(&pdev->dev, &dev_attr_power1_input);
> > +	if (err)
> > +		goto exit;
> > +	err = device_create_file(&pdev->dev, &dev_attr_power1_cap);
> > +	if (err)
> > +		goto exit_remove;
> > +
> > +	err = device_create_file(&pdev->dev, &dev_attr_name);
> > +	if (err)
> > +		goto exit_remove;
> 
> Please consider defining and using an attribute group, it makes the
> code more simple.

Will think about it.

> > +
> > +	hwmon_dev = hwmon_device_register(&pdev->dev);
> > +	if (IS_ERR(hwmon_dev)) {
> > +		err = PTR_ERR(hwmon_dev);
> > +		goto exit_remove;
> > +	}
> > +	dev_set_drvdata(&pdev->dev, hwmon_dev);
> 
> pci_set_drvdata()

Are you sure?
No single hwmon driver is using this wrapper around dev_set_drvdata() so far. (Or I just missed it.)
At the moment only dev_set_drvdata() is used by

 drivers/hwmon/adcxx.c
 drivers/hwmon/lm70.c
 drivers/hwmon/ultra45_env.c
 drivers/hwmon/ibmpex.c
 drivers/hwmon/k10temp.c
 drivers/hwmon/ibmaem.c
 drivers/hwmon/k8temp.c

> > +
> > +	return 0;
> > +
> > +exit_remove:
> > +	device_remove_file(&pdev->dev, &dev_attr_name);
> > +	device_remove_file(&pdev->dev, &dev_attr_power1_input);
> > +	device_remove_file(&pdev->dev, &dev_attr_power1_cap);
> > +exit:
> > +	return err;
> > +}
> > +
> > +static void __devexit f15h_power_remove(struct pci_dev *pdev)
> > +{
> > +	hwmon_device_unregister(dev_get_drvdata(&pdev->dev));
> > +	device_remove_file(&pdev->dev, &dev_attr_name);
> > +	device_remove_file(&pdev->dev, &dev_attr_power1_input);
> > +	device_remove_file(&pdev->dev, &dev_attr_power1_cap);
> > +	dev_set_drvdata(&pdev->dev, NULL);
> 
> pci_set_drvdata()
> 
> > +}
> > +
> > +static const struct pci_device_id f15h_power_id_table[] = {
> > +	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_LINK) },
> 
> Not defined anywhere, causing the driver to fail building:
> 
> drivers/hwmon/f15h_power.c:193:1: error: 'PCI_DEVICE_ID_AMD_15H_NB_LINK' undeclared here (not in a function)
> make[2]: *** [drivers/hwmon/f15h_power.o] Error 1
> make[1]: *** [drivers/hwmon] Error 2
> make[1]: *** Waiting for unfinished jobs....
> make: *** [drivers] Error 2

The macro changed with .39-rc2
Already fixed in v3

Thanks for your review,

Please let me know whether changing the variable names and using
pci_set_drvdata() is strictly required.


Andreas

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v2] hwmon: Add driver for AMD family 15h processor power information
  2011-04-06 15:19       ` [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h Andreas Herrmann
@ 2011-04-06 15:35         ` Jean Delvare
  -1 siblings, 0 replies; 43+ messages in thread
From: Jean Delvare @ 2011-04-06 15:35 UTC (permalink / raw)
  To: Andreas Herrmann, Guenter Roeck
  Cc: Thomas Renninger, lm-sensors, linux-kernel

On Wed, 6 Apr 2011 17:19:01 +0200, Andreas Herrmann wrote:
> On Wed, Apr 06, 2011 at 04:14:01PM +0200, Jean Delvare wrote:
> > On Tue, 5 Apr 2011 16:45:36 +0200, Andreas Herrmann wrote:
> > > +static ssize_t show_power(struct device *dev,
> > > +			  struct device_attribute *attr, char *buf)
> > > +{
> > > +	u32 val, btdp, tdpl, tdp2w, arange;
> > > +	s32 acap;
> > > +	u64 ctdp;
> > 
> > These variable names aren't easy to understand.
> 
> Just random names which eventually map to the spec:
> 
> btdp - base_tdp
> tdpl - tdp_limit
> tdp2w - tdp_to_watt
> acap - average_accumulator_capture (or even worse how about "processor_tdp_running_average_accumulator":(
> arange - average_range

avg_cap and avg_range would do, respectively, for the last two.

> I don't think that changing the names make it much easier to
> reconstruct the calculation but if you insist in changing it I'll
> adapt it.

I do prefer the "extended" names, really. Sure, this doesn't change the
calculations, but it helps the reader understand what's going on. Which
will be useful if one ever has to fix a bug in the code or extend it
for a different CPU family.

But maybe this is just me. Guenter, do you have an opinion?

> > > (...)
> > > +	dev_set_drvdata(&pdev->dev, hwmon_dev);
> > 
> > pci_set_drvdata()
> 
> Are you sure?

Yes, I'm sure. This function exists, so there is no reason not to use
it.

> No single hwmon driver is using this wrapper around dev_set_drvdata() so far. (Or I just missed it.)

$ grep i2c_set_clientdata drivers/hwmon/*.c | wc -l
67
$ grep spi_set_drvdata drivers/hwmon/*.c | wc -l
2
$ grep platform_set_drvdata drivers/hwmon/*.c | wc -l
66
$

> At the moment only dev_set_drvdata() is used by
> 
>  drivers/hwmon/adcxx.c
>  drivers/hwmon/lm70.c
>  drivers/hwmon/ultra45_env.c
>  drivers/hwmon/ibmpex.c
>  drivers/hwmon/k10temp.c
>  drivers/hwmon/ibmaem.c
>  drivers/hwmon/k8temp.c

Most of these (at least spi and pci drivers) should be fixed. I'll send
a patch later.

-- 
Jean Delvare

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h
@ 2011-04-06 15:35         ` Jean Delvare
  0 siblings, 0 replies; 43+ messages in thread
From: Jean Delvare @ 2011-04-06 15:35 UTC (permalink / raw)
  To: Andreas Herrmann, Guenter Roeck
  Cc: Thomas Renninger, lm-sensors, linux-kernel

On Wed, 6 Apr 2011 17:19:01 +0200, Andreas Herrmann wrote:
> On Wed, Apr 06, 2011 at 04:14:01PM +0200, Jean Delvare wrote:
> > On Tue, 5 Apr 2011 16:45:36 +0200, Andreas Herrmann wrote:
> > > +static ssize_t show_power(struct device *dev,
> > > +			  struct device_attribute *attr, char *buf)
> > > +{
> > > +	u32 val, btdp, tdpl, tdp2w, arange;
> > > +	s32 acap;
> > > +	u64 ctdp;
> > 
> > These variable names aren't easy to understand.
> 
> Just random names which eventually map to the spec:
> 
> btdp - base_tdp
> tdpl - tdp_limit
> tdp2w - tdp_to_watt
> acap - average_accumulator_capture (or even worse how about "processor_tdp_running_average_accumulator":(
> arange - average_range

avg_cap and avg_range would do, respectively, for the last two.

> I don't think that changing the names make it much easier to
> reconstruct the calculation but if you insist in changing it I'll
> adapt it.

I do prefer the "extended" names, really. Sure, this doesn't change the
calculations, but it helps the reader understand what's going on. Which
will be useful if one ever has to fix a bug in the code or extend it
for a different CPU family.

But maybe this is just me. Guenter, do you have an opinion?

> > > (...)
> > > +	dev_set_drvdata(&pdev->dev, hwmon_dev);
> > 
> > pci_set_drvdata()
> 
> Are you sure?

Yes, I'm sure. This function exists, so there is no reason not to use
it.

> No single hwmon driver is using this wrapper around dev_set_drvdata() so far. (Or I just missed it.)

$ grep i2c_set_clientdata drivers/hwmon/*.c | wc -l
67
$ grep spi_set_drvdata drivers/hwmon/*.c | wc -l
2
$ grep platform_set_drvdata drivers/hwmon/*.c | wc -l
66
$

> At the moment only dev_set_drvdata() is used by
> 
>  drivers/hwmon/adcxx.c
>  drivers/hwmon/lm70.c
>  drivers/hwmon/ultra45_env.c
>  drivers/hwmon/ibmpex.c
>  drivers/hwmon/k10temp.c
>  drivers/hwmon/ibmaem.c
>  drivers/hwmon/k8temp.c

Most of these (at least spi and pci drivers) should be fixed. I'll send
a patch later.

-- 
Jean Delvare

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v2] hwmon: Add driver for AMD family 15h processor power information
  2011-04-06 15:35         ` [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h Jean Delvare
@ 2011-04-06 16:14           ` Guenter Roeck
  -1 siblings, 0 replies; 43+ messages in thread
From: Guenter Roeck @ 2011-04-06 16:14 UTC (permalink / raw)
  To: Jean Delvare; +Cc: Andreas Herrmann, Thomas Renninger, lm-sensors, linux-kernel

On Wed, Apr 06, 2011 at 11:35:43AM -0400, Jean Delvare wrote:
> On Wed, 6 Apr 2011 17:19:01 +0200, Andreas Herrmann wrote:
> > On Wed, Apr 06, 2011 at 04:14:01PM +0200, Jean Delvare wrote:
> > > On Tue, 5 Apr 2011 16:45:36 +0200, Andreas Herrmann wrote:
> > > > +static ssize_t show_power(struct device *dev,
> > > > +			  struct device_attribute *attr, char *buf)
> > > > +{
> > > > +	u32 val, btdp, tdpl, tdp2w, arange;
> > > > +	s32 acap;
> > > > +	u64 ctdp;
> > > 
> > > These variable names aren't easy to understand.
> > 
> > Just random names which eventually map to the spec:
> > 
> > btdp - base_tdp
> > tdpl - tdp_limit
> > tdp2w - tdp_to_watt
> > acap - average_accumulator_capture (or even worse how about "processor_tdp_running_average_accumulator":(
> > arange - average_range
> 
> avg_cap and avg_range would do, respectively, for the last two.
> 
> > I don't think that changing the names make it much easier to
> > reconstruct the calculation but if you insist in changing it I'll
> > adapt it.
> 
> I do prefer the "extended" names, really. Sure, this doesn't change the
> calculations, but it helps the reader understand what's going on. Which
> will be useful if one ever has to fix a bug in the code or extend it
> for a different CPU family.
> 
> But maybe this is just me. Guenter, do you have an opinion?
> 
I agree. base_tdp is definitely much better than btdp. Same for the others.

Thanks,
Guenter

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h
@ 2011-04-06 16:14           ` Guenter Roeck
  0 siblings, 0 replies; 43+ messages in thread
From: Guenter Roeck @ 2011-04-06 16:14 UTC (permalink / raw)
  To: Jean Delvare; +Cc: Andreas Herrmann, Thomas Renninger, lm-sensors, linux-kernel

On Wed, Apr 06, 2011 at 11:35:43AM -0400, Jean Delvare wrote:
> On Wed, 6 Apr 2011 17:19:01 +0200, Andreas Herrmann wrote:
> > On Wed, Apr 06, 2011 at 04:14:01PM +0200, Jean Delvare wrote:
> > > On Tue, 5 Apr 2011 16:45:36 +0200, Andreas Herrmann wrote:
> > > > +static ssize_t show_power(struct device *dev,
> > > > +			  struct device_attribute *attr, char *buf)
> > > > +{
> > > > +	u32 val, btdp, tdpl, tdp2w, arange;
> > > > +	s32 acap;
> > > > +	u64 ctdp;
> > > 
> > > These variable names aren't easy to understand.
> > 
> > Just random names which eventually map to the spec:
> > 
> > btdp - base_tdp
> > tdpl - tdp_limit
> > tdp2w - tdp_to_watt
> > acap - average_accumulator_capture (or even worse how about "processor_tdp_running_average_accumulator":(
> > arange - average_range
> 
> avg_cap and avg_range would do, respectively, for the last two.
> 
> > I don't think that changing the names make it much easier to
> > reconstruct the calculation but if you insist in changing it I'll
> > adapt it.
> 
> I do prefer the "extended" names, really. Sure, this doesn't change the
> calculations, but it helps the reader understand what's going on. Which
> will be useful if one ever has to fix a bug in the code or extend it
> for a different CPU family.
> 
> But maybe this is just me. Guenter, do you have an opinion?
> 
I agree. base_tdp is definitely much better than btdp. Same for the others.

Thanks,
Guenter

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v2] hwmon: Add driver for AMD family 15h processor power information
  2011-04-06 16:14           ` [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h Guenter Roeck
@ 2011-04-06 16:20             ` Andreas Herrmann
  -1 siblings, 0 replies; 43+ messages in thread
From: Andreas Herrmann @ 2011-04-06 16:20 UTC (permalink / raw)
  To: Guenter Roeck; +Cc: Jean Delvare, Thomas Renninger, lm-sensors, linux-kernel

On Wed, Apr 06, 2011 at 09:14:45AM -0700, Guenter Roeck wrote:
> On Wed, Apr 06, 2011 at 11:35:43AM -0400, Jean Delvare wrote:
> > On Wed, 6 Apr 2011 17:19:01 +0200, Andreas Herrmann wrote:
> > > On Wed, Apr 06, 2011 at 04:14:01PM +0200, Jean Delvare wrote:
> > > > On Tue, 5 Apr 2011 16:45:36 +0200, Andreas Herrmann wrote:
> > > > > +static ssize_t show_power(struct device *dev,
> > > > > +			  struct device_attribute *attr, char *buf)
> > > > > +{
> > > > > +	u32 val, btdp, tdpl, tdp2w, arange;
> > > > > +	s32 acap;
> > > > > +	u64 ctdp;
> > > > 
> > > > These variable names aren't easy to understand.
> > > 
> > > Just random names which eventually map to the spec:
> > > 
> > > btdp - base_tdp
> > > tdpl - tdp_limit
> > > tdp2w - tdp_to_watt
> > > acap - average_accumulator_capture (or even worse how about "processor_tdp_running_average_accumulator":(
> > > arange - average_range
> > 
> > avg_cap and avg_range would do, respectively, for the last two.
> > 
> > > I don't think that changing the names make it much easier to
> > > reconstruct the calculation but if you insist in changing it I'll
> > > adapt it.
> > 
> > I do prefer the "extended" names, really. Sure, this doesn't change the
> > calculations, but it helps the reader understand what's going on. Which
> > will be useful if one ever has to fix a bug in the code or extend it
> > for a different CPU family.
> > 
> > But maybe this is just me. Guenter, do you have an opinion?
> > 
> I agree. base_tdp is definitely much better than btdp. Same for the others.
> 
> Thanks,
> Guenter

Ok.

Will update the patch soon.


Andreas

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h
@ 2011-04-06 16:20             ` Andreas Herrmann
  0 siblings, 0 replies; 43+ messages in thread
From: Andreas Herrmann @ 2011-04-06 16:20 UTC (permalink / raw)
  To: Guenter Roeck; +Cc: Jean Delvare, Thomas Renninger, lm-sensors, linux-kernel

On Wed, Apr 06, 2011 at 09:14:45AM -0700, Guenter Roeck wrote:
> On Wed, Apr 06, 2011 at 11:35:43AM -0400, Jean Delvare wrote:
> > On Wed, 6 Apr 2011 17:19:01 +0200, Andreas Herrmann wrote:
> > > On Wed, Apr 06, 2011 at 04:14:01PM +0200, Jean Delvare wrote:
> > > > On Tue, 5 Apr 2011 16:45:36 +0200, Andreas Herrmann wrote:
> > > > > +static ssize_t show_power(struct device *dev,
> > > > > +			  struct device_attribute *attr, char *buf)
> > > > > +{
> > > > > +	u32 val, btdp, tdpl, tdp2w, arange;
> > > > > +	s32 acap;
> > > > > +	u64 ctdp;
> > > > 
> > > > These variable names aren't easy to understand.
> > > 
> > > Just random names which eventually map to the spec:
> > > 
> > > btdp - base_tdp
> > > tdpl - tdp_limit
> > > tdp2w - tdp_to_watt
> > > acap - average_accumulator_capture (or even worse how about "processor_tdp_running_average_accumulator":(
> > > arange - average_range
> > 
> > avg_cap and avg_range would do, respectively, for the last two.
> > 
> > > I don't think that changing the names make it much easier to
> > > reconstruct the calculation but if you insist in changing it I'll
> > > adapt it.
> > 
> > I do prefer the "extended" names, really. Sure, this doesn't change the
> > calculations, but it helps the reader understand what's going on. Which
> > will be useful if one ever has to fix a bug in the code or extend it
> > for a different CPU family.
> > 
> > But maybe this is just me. Guenter, do you have an opinion?
> > 
> I agree. base_tdp is definitely much better than btdp. Same for the others.
> 
> Thanks,
> Guenter

Ok.

Will update the patch soon.


Andreas

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3] hwmon: Add driver for AMD family 15h processor power information
  2011-04-06 13:54     ` [lm-sensors] [PATCH v3] hwmon: Add driver for AMD family 15h Andreas Herrmann
@ 2011-04-06 16:50       ` Jean Delvare
  -1 siblings, 0 replies; 43+ messages in thread
From: Jean Delvare @ 2011-04-06 16:50 UTC (permalink / raw)
  To: Andreas Herrmann
  Cc: Guenter Roeck, Thomas Renninger, lm-sensors, linux-kernel,
	Clemens Ladisch

Hi Andreas,

On Wed, 6 Apr 2011 15:54:24 +0200, Andreas Herrmann wrote:
> --- /dev/null
> +++ b/Documentation/hwmon/f15h_power
> @@ -0,0 +1,37 @@
> +Kernel driver f15h_power
> +========================
> +
> +Supported chips:
> +* AMD Family 15h Processors
> +
> +  Prefix: 'f15h_power'
> +  Addresses scanned: PCI space
> +  Datasheets:
> +  BIOS and Kernel Developer's Guide (BKDG) For AMD Family 15h Processors
> +    (not yet published)
> +
> +Author: Andreas Herrmann <andreas.herrmann3@amd.com>

BTW, please consider adding an entry in MAINTAINERS.

> +
> +Description
> +-----------
> +
> +This driver permits reading of registers providing power information
> +of AMD Family 15h processors.
> +
> +For AMD Family 15h processors the following power values can be
> +calculated using different processor northbridge function registers

A trailing ":" would be nice.

> +
> +* BasePwrWatts: Specifies in watts the maximum amount of power
> +  consumed by the processor for NB and logic external to the core.
> +* 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 ProcessorPwrWatts and CurrPwrWatts:
> +* power1_max (ProcessorPwrWatts)

I see you changed this name once already, but... What is expected to
happen if the power consumed exceeds this limit? If you expect the CPU
to get damaged, then power1_crit would be more appropriate.

Another way to decide if _max or _crit is more appropriate is by
answering the question: if a second limit was added in the future,
would it more likely be below or above this one?

> +* power1_input (CurrPwrWatts)
> +
> +On multi-node processors the calculated value is for the entire
> +package and not for a single node. Thus the driver creates sysfs
> +attributes only for internal node0 of a multi-node processor.

> (...)
> +	return sprintf(buf, "%u\n", (u32) ptdp);

Maybe I'm just nitpicking, but I still don't think it's correct. %u
wants unsigned int, not u32. It might be the same but that's by luck.

> --- a/drivers/hwmon/k10temp.c
> +++ b/drivers/hwmon/k10temp.c
> @@ -205,7 +205,7 @@ static void __devexit k10temp_remove(struct pci_dev *pdev)
>  	dev_set_drvdata(&pdev->dev, NULL);
>  }
>  
> -static const struct pci_device_id k10temp_id_table[] = {
> +static DEFINE_PCI_DEVICE_TABLE(k10temp_id_table) = {
>  	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_10H_NB_MISC) },
>  	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_11H_NB_MISC) },
>  	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) },

Such cleanups should go to separate patches, as they don't have
anything to do with your initial effort. And that way you can fix
k8temp i5k_amb too.

-- 
Jean Delvare

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [PATCH v3] hwmon: Add driver for AMD family 15h
@ 2011-04-06 16:50       ` Jean Delvare
  0 siblings, 0 replies; 43+ messages in thread
From: Jean Delvare @ 2011-04-06 16:50 UTC (permalink / raw)
  To: Andreas Herrmann
  Cc: Guenter Roeck, Thomas Renninger, lm-sensors, linux-kernel,
	Clemens Ladisch

Hi Andreas,

On Wed, 6 Apr 2011 15:54:24 +0200, Andreas Herrmann wrote:
> --- /dev/null
> +++ b/Documentation/hwmon/f15h_power
> @@ -0,0 +1,37 @@
> +Kernel driver f15h_power
> +============
> +
> +Supported chips:
> +* AMD Family 15h Processors
> +
> +  Prefix: 'f15h_power'
> +  Addresses scanned: PCI space
> +  Datasheets:
> +  BIOS and Kernel Developer's Guide (BKDG) For AMD Family 15h Processors
> +    (not yet published)
> +
> +Author: Andreas Herrmann <andreas.herrmann3@amd.com>

BTW, please consider adding an entry in MAINTAINERS.

> +
> +Description
> +-----------
> +
> +This driver permits reading of registers providing power information
> +of AMD Family 15h processors.
> +
> +For AMD Family 15h processors the following power values can be
> +calculated using different processor northbridge function registers

A trailing ":" would be nice.

> +
> +* BasePwrWatts: Specifies in watts the maximum amount of power
> +  consumed by the processor for NB and logic external to the core.
> +* 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 ProcessorPwrWatts and CurrPwrWatts:
> +* power1_max (ProcessorPwrWatts)

I see you changed this name once already, but... What is expected to
happen if the power consumed exceeds this limit? If you expect the CPU
to get damaged, then power1_crit would be more appropriate.

Another way to decide if _max or _crit is more appropriate is by
answering the question: if a second limit was added in the future,
would it more likely be below or above this one?

> +* power1_input (CurrPwrWatts)
> +
> +On multi-node processors the calculated value is for the entire
> +package and not for a single node. Thus the driver creates sysfs
> +attributes only for internal node0 of a multi-node processor.

> (...)
> +	return sprintf(buf, "%u\n", (u32) ptdp);

Maybe I'm just nitpicking, but I still don't think it's correct. %u
wants unsigned int, not u32. It might be the same but that's by luck.

> --- a/drivers/hwmon/k10temp.c
> +++ b/drivers/hwmon/k10temp.c
> @@ -205,7 +205,7 @@ static void __devexit k10temp_remove(struct pci_dev *pdev)
>  	dev_set_drvdata(&pdev->dev, NULL);
>  }
>  
> -static const struct pci_device_id k10temp_id_table[] = {
> +static DEFINE_PCI_DEVICE_TABLE(k10temp_id_table) = {
>  	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_10H_NB_MISC) },
>  	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_11H_NB_MISC) },
>  	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) },

Such cleanups should go to separate patches, as they don't have
anything to do with your initial effort. And that way you can fix
k8temp i5k_amb too.

-- 
Jean Delvare

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v3] hwmon: Add driver for AMD family 15h processor power information
  2011-04-06 16:50       ` [lm-sensors] [PATCH v3] hwmon: Add driver for AMD family 15h Jean Delvare
@ 2011-04-07  9:13         ` Andreas Herrmann
  -1 siblings, 0 replies; 43+ messages in thread
From: Andreas Herrmann @ 2011-04-07  9:13 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Guenter Roeck, Thomas Renninger, lm-sensors, linux-kernel,
	Clemens Ladisch

On Wed, Apr 06, 2011 at 06:50:44PM +0200, Jean Delvare wrote:
> Hi Andreas,
> 
> On Wed, 6 Apr 2011 15:54:24 +0200, Andreas Herrmann wrote:
> > --- /dev/null
> > +++ b/Documentation/hwmon/f15h_power
> > @@ -0,0 +1,37 @@
> > +Kernel driver f15h_power
> > +========================
> > +
> > +Supported chips:
> > +* AMD Family 15h Processors
> > +
> > +  Prefix: 'f15h_power'
> > +  Addresses scanned: PCI space
> > +  Datasheets:
> > +  BIOS and Kernel Developer's Guide (BKDG) For AMD Family 15h Processors
> > +    (not yet published)
> > +
> > +Author: Andreas Herrmann <andreas.herrmann3@amd.com>
> 
> BTW, please consider adding an entry in MAINTAINERS.

Yep.

> > +
> > +Description
> > +-----------
> > +
> > +This driver permits reading of registers providing power information
> > +of AMD Family 15h processors.
> > +
> > +For AMD Family 15h processors the following power values can be
> > +calculated using different processor northbridge function registers
> 
> A trailing ":" would be nice.
> 
> > +
> > +* BasePwrWatts: Specifies in watts the maximum amount of power
> > +  consumed by the processor for NB and logic external to the core.
> > +* 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 ProcessorPwrWatts and CurrPwrWatts:
> > +* power1_max (ProcessorPwrWatts)
> 
> I see you changed this name once already, but... What is expected to
> happen if the power consumed exceeds this limit? If you expect the CPU
> to get damaged, then power1_crit would be more appropriate.
> 
> Another way to decide if _max or _crit is more appropriate is by
> answering the question: if a second limit was added in the future,
> would it more likely be below or above this one?

It might make sense to use it as _crit and the interim result of

  (tdp_limit + base_tdp) * tdp_to_watts

being less than ProcessorPwrWatts as _max.

> > +* power1_input (CurrPwrWatts)
> > +
> > +On multi-node processors the calculated value is for the entire
> > +package and not for a single node. Thus the driver creates sysfs
> > +attributes only for internal node0 of a multi-node processor.
> 
> > (...)
> > +	return sprintf(buf, "%u\n", (u32) ptdp);
> 
> Maybe I'm just nitpicking, but I still don't think it's correct. %u
> wants unsigned int, not u32. It might be the same but that's by luck.
> 
> > --- a/drivers/hwmon/k10temp.c
> > +++ b/drivers/hwmon/k10temp.c
> > @@ -205,7 +205,7 @@ static void __devexit k10temp_remove(struct pci_dev *pdev)
> >  	dev_set_drvdata(&pdev->dev, NULL);
> >  }
> >  
> > -static const struct pci_device_id k10temp_id_table[] = {
> > +static DEFINE_PCI_DEVICE_TABLE(k10temp_id_table) = {
> >  	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_10H_NB_MISC) },
> >  	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_11H_NB_MISC) },
> >  	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) },
> 
> Such cleanups should go to separate patches, as they don't have
> anything to do with your initial effort. And that way you can fix
> k8temp i5k_amb too.


This slipped in by accident. I might send a separate patch to clean this
up in several hwmon drivers.


Andreas

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [PATCH v3] hwmon: Add driver for AMD family 15h
@ 2011-04-07  9:13         ` Andreas Herrmann
  0 siblings, 0 replies; 43+ messages in thread
From: Andreas Herrmann @ 2011-04-07  9:13 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Guenter Roeck, Thomas Renninger, lm-sensors, linux-kernel,
	Clemens Ladisch

On Wed, Apr 06, 2011 at 06:50:44PM +0200, Jean Delvare wrote:
> Hi Andreas,
> 
> On Wed, 6 Apr 2011 15:54:24 +0200, Andreas Herrmann wrote:
> > --- /dev/null
> > +++ b/Documentation/hwmon/f15h_power
> > @@ -0,0 +1,37 @@
> > +Kernel driver f15h_power
> > +============
> > +
> > +Supported chips:
> > +* AMD Family 15h Processors
> > +
> > +  Prefix: 'f15h_power'
> > +  Addresses scanned: PCI space
> > +  Datasheets:
> > +  BIOS and Kernel Developer's Guide (BKDG) For AMD Family 15h Processors
> > +    (not yet published)
> > +
> > +Author: Andreas Herrmann <andreas.herrmann3@amd.com>
> 
> BTW, please consider adding an entry in MAINTAINERS.

Yep.

> > +
> > +Description
> > +-----------
> > +
> > +This driver permits reading of registers providing power information
> > +of AMD Family 15h processors.
> > +
> > +For AMD Family 15h processors the following power values can be
> > +calculated using different processor northbridge function registers
> 
> A trailing ":" would be nice.
> 
> > +
> > +* BasePwrWatts: Specifies in watts the maximum amount of power
> > +  consumed by the processor for NB and logic external to the core.
> > +* 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 ProcessorPwrWatts and CurrPwrWatts:
> > +* power1_max (ProcessorPwrWatts)
> 
> I see you changed this name once already, but... What is expected to
> happen if the power consumed exceeds this limit? If you expect the CPU
> to get damaged, then power1_crit would be more appropriate.
> 
> Another way to decide if _max or _crit is more appropriate is by
> answering the question: if a second limit was added in the future,
> would it more likely be below or above this one?

It might make sense to use it as _crit and the interim result of

  (tdp_limit + base_tdp) * tdp_to_watts

being less than ProcessorPwrWatts as _max.

> > +* power1_input (CurrPwrWatts)
> > +
> > +On multi-node processors the calculated value is for the entire
> > +package and not for a single node. Thus the driver creates sysfs
> > +attributes only for internal node0 of a multi-node processor.
> 
> > (...)
> > +	return sprintf(buf, "%u\n", (u32) ptdp);
> 
> Maybe I'm just nitpicking, but I still don't think it's correct. %u
> wants unsigned int, not u32. It might be the same but that's by luck.
> 
> > --- a/drivers/hwmon/k10temp.c
> > +++ b/drivers/hwmon/k10temp.c
> > @@ -205,7 +205,7 @@ static void __devexit k10temp_remove(struct pci_dev *pdev)
> >  	dev_set_drvdata(&pdev->dev, NULL);
> >  }
> >  
> > -static const struct pci_device_id k10temp_id_table[] = {
> > +static DEFINE_PCI_DEVICE_TABLE(k10temp_id_table) = {
> >  	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_10H_NB_MISC) },
> >  	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_11H_NB_MISC) },
> >  	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) },
> 
> Such cleanups should go to separate patches, as they don't have
> anything to do with your initial effort. And that way you can fix
> k8temp i5k_amb too.


This slipped in by accident. I might send a separate patch to clean this
up in several hwmon drivers.


Andreas

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH v4] hwmon: Add driver for AMD family 15h processor power information
  2011-04-06 13:54     ` [lm-sensors] [PATCH v3] hwmon: Add driver for AMD family 15h Andreas Herrmann
@ 2011-04-08 13:54       ` Andreas Herrmann
  -1 siblings, 0 replies; 43+ messages in thread
From: Andreas Herrmann @ 2011-04-08 13:54 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck
  Cc: Thomas Renninger, lm-sensors, linux-kernel, Clemens Ladisch

From: Andreas Herrmann <andreas.herrmann3@amd.com>

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_max (ProcessorPwrWatts)
* power1_input (CurrPwrWatts)

Changes from v2:
 - fix format strings
 - removed paranoid checks for existense of functions 3 and 5
 - changed return type of function f15h_power_is_internal_node0
 - use power1_max instead of power1_cap
 - use dev_warn instead of WARN_ON
 - rebased against 2.6.39-rc2
 - added Documentation/hwmon/f15h_power

Changes from v3:
 - read static power information only once (during driver initialization)
 - made use of attribute groups
 - renamed f15h_power to fam15h_power

Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com>
---
 Documentation/hwmon/fam15h_power |   37 ++++++
 drivers/hwmon/Kconfig            |   10 ++
 drivers/hwmon/Makefile           |    1 +
 drivers/hwmon/fam15h_power.c     |  229 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 277 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/hwmon/fam15h_power
 create mode 100644 drivers/hwmon/fam15h_power.c

At this stage I consider the _max attribute as the right one for
reporting ProcessorPwrWatts.

Hopefully I've addressed all your coments.
Please apply.


Thanks,

Andreas

diff --git a/Documentation/hwmon/fam15h_power b/Documentation/hwmon/fam15h_power
new file mode 100644
index 0000000..2f4d291
--- /dev/null
+++ b/Documentation/hwmon/fam15h_power
@@ -0,0 +1,37 @@
+Kernel driver fam15h_power
+==========================
+
+Supported chips:
+* AMD Family 15h Processors
+
+  Prefix: 'fam15h_power'
+  Addresses scanned: PCI space
+  Datasheets:
+  BIOS and Kernel Developer's Guide (BKDG) For AMD Family 15h Processors
+    (not yet published)
+
+Author: Andreas Herrmann <andreas.herrmann3@amd.com>
+
+Description
+-----------
+
+This driver permits reading of registers providing power information
+of AMD Family 15h processors.
+
+For AMD Family 15h processors the following power values can be
+calculated using different processor northbridge function registers:
+
+* BasePwrWatts: Specifies in watts the maximum amount of power
+  consumed by the processor for NB and logic external to the core.
+* 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 ProcessorPwrWatts and CurrPwrWatts:
+* power1_max (ProcessorPwrWatts)
+* power1_input (CurrPwrWatts)
+
+On multi-node processors the calculated value is for the entire
+package and not for a single node. Thus the driver creates sysfs
+attributes only for internal node0 of a multi-node processor.
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 060ef63..fb3e334 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -249,6 +249,16 @@ config SENSORS_K10TEMP
 	  This driver can also be built as a module.  If so, the module
 	  will be called k10temp.
 
+config SENSORS_FAM15H_POWER
+	tristate "AMD Family 15h processor power"
+	depends on X86 && PCI
+	help
+	  If you say yes here you get support for processor power
+	  information of your AMD family 15h CPU.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called fam15h_power.
+
 config SENSORS_ASB100
 	tristate "Asus ASB100 Bach"
 	depends on X86 && I2C && EXPERIMENTAL
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 967d0ea..236d3f9 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -49,6 +49,7 @@ obj-$(CONFIG_SENSORS_EMC2103)	+= emc2103.o
 obj-$(CONFIG_SENSORS_F71805F)	+= f71805f.o
 obj-$(CONFIG_SENSORS_F71882FG)	+= f71882fg.o
 obj-$(CONFIG_SENSORS_F75375S)	+= f75375s.o
+obj-$(CONFIG_SENSORS_FAM15H_POWER) += fam15h_power.o
 obj-$(CONFIG_SENSORS_FSCHMD)	+= fschmd.o
 obj-$(CONFIG_SENSORS_G760A)	+= g760a.o
 obj-$(CONFIG_SENSORS_GL518SM)	+= gl518sm.o
diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c
new file mode 100644
index 0000000..cb6eb99
--- /dev/null
+++ b/drivers/hwmon/fam15h_power.c
@@ -0,0 +1,229 @@
+/*
+ * fam15h_power.c - AMD Family 15h processor power monitoring
+ *
+ * Copyright (c) 2011 Advanced Micro Devices, Inc.
+ * Author: Andreas Herrmann <andreas.herrmann3@amd.com>
+ *
+ *
+ * This driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This driver 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 driver; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/bitops.h>
+#include <asm/processor.h>
+
+MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor");
+MODULE_AUTHOR("Andreas Herrmann <andreas.herrmann3@amd.com>");
+MODULE_LICENSE("GPL");
+
+/* D18F3 */
+#define REG_NORTHBRIDGE_CAP		0xe8
+
+/* D18F4 */
+#define REG_PROCESSOR_TDP		0x1b8
+
+/* D18F5 */
+#define REG_TDP_RUNNING_AVERAGE		0xe0
+#define REG_TDP_LIMIT3			0xe8
+
+struct fam15h_power_data {
+	struct device *hwmon_dev;
+	unsigned int tdp_to_watt;
+	unsigned int base_tdp;
+	unsigned int processor_pwr_watts;
+};
+
+static ssize_t show_power(struct device *dev,
+			  struct device_attribute *attr, char *buf)
+{
+	u32 val, tdp_limit, running_avg_range;
+	s32 running_avg_capture;
+	u64 curr_pwr_watts;
+	struct pci_dev *f4 = to_pci_dev(dev);
+	struct fam15h_power_data *data = pci_get_drvdata(f4);
+
+	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
+				  REG_TDP_RUNNING_AVERAGE, &val);
+	running_avg_capture = (val >> 4) & 0x3fffff;
+	running_avg_capture = sign_extend32(running_avg_capture, 22);
+	running_avg_range = val & 0xf;
+
+	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
+				  REG_TDP_LIMIT3, &val);
+
+	tdp_limit = val >> 16;
+	curr_pwr_watts = tdp_limit  + data->base_tdp -
+		(s32)(running_avg_capture >> (running_avg_range + 1));
+	curr_pwr_watts *= data->tdp_to_watt;
+
+	/*
+	 * Convert to microWatt
+	 *
+	 * power is in Watt provided as fixed point integer with
+	 * scaling factor 1/(2^16).  For conversion we use
+	 * (10^6)/(2^16) = 15625/(2^10)
+	 */
+	curr_pwr_watts = (curr_pwr_watts * 15625) >> 10;
+	return sprintf(buf, "%u\n", (unsigned int) curr_pwr_watts);
+}
+static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL);
+
+static ssize_t show_power_max(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	struct pci_dev *f4 = to_pci_dev(dev);
+	struct fam15h_power_data *data = pci_get_drvdata(f4);
+
+	return sprintf(buf, "%u\n", data->processor_pwr_watts);
+}
+static DEVICE_ATTR(power1_max, S_IRUGO, show_power_max, NULL);
+
+static ssize_t show_name(struct device *dev,
+			 struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "fam15h_power\n");
+}
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static struct attribute *fam15h_power_attrs[] = {
+	&dev_attr_power1_input.attr,
+	&dev_attr_power1_max.attr,
+	&dev_attr_name.attr,
+	NULL
+};
+
+static struct attribute_group fam15h_power_attr_group = {
+	.attrs	= fam15h_power_attrs,
+};
+
+static bool __devinit fam15h_power_is_internal_node0(struct pci_dev *f4)
+{
+	u32 val;
+
+	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3),
+				  REG_NORTHBRIDGE_CAP, &val);
+	if ((val & BIT(29)) && ((val >> 30) & 3))
+		return false;
+
+	return true;
+}
+
+static void __devinit fam15h_power_init_data(struct pci_dev *f4,
+					     struct fam15h_power_data *data)
+{
+	u32 val;
+	u64 tmp;
+
+	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
+	data->base_tdp = val >> 16;
+	tmp = val & 0xffff;
+
+	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
+				  REG_TDP_LIMIT3, &val);
+
+	data->tdp_to_watt = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
+	tmp *= data->tdp_to_watt;
+
+	/* result not allowed to be >= 256W */
+	if ((tmp>>16) >= 256)
+		dev_warn(&f4->dev, "Bogus value for ProcessorPwrWatts "
+			 "(processor_pwr_watts>=%u)\n",
+			 (unsigned int) (tmp >> 16));
+
+	/* convert to microWatt */
+	data->processor_pwr_watts = (tmp * 15625) >> 10;
+}
+
+static int __devinit fam15h_power_probe(struct pci_dev *pdev,
+					const struct pci_device_id *id)
+{
+	struct fam15h_power_data *data;
+	struct device *dev;
+	int err;
+
+	if (!fam15h_power_is_internal_node0(pdev)) {
+		err = -ENODEV;
+		goto exit;
+	}
+
+	data = kzalloc(sizeof(struct fam15h_power_data), GFP_KERNEL);
+	if (!data) {
+		err = -ENOMEM;
+		goto exit;
+	}
+	fam15h_power_init_data(pdev, data);
+
+	dev = &pdev->dev;
+	err = sysfs_create_group(&dev->kobj, &fam15h_power_attr_group);
+	if (err)
+		goto exit_free_data;
+
+	data->hwmon_dev = hwmon_device_register(&pdev->dev);
+	if (IS_ERR(data->hwmon_dev)) {
+		err = PTR_ERR(data->hwmon_dev);
+		goto exit_remove_group;
+	}
+	pci_set_drvdata(pdev, data);
+
+	return 0;
+
+exit_remove_group:
+	sysfs_remove_group(&dev->kobj, &fam15h_power_attr_group);
+exit_free_data:
+	kfree(data);
+exit:
+	return err;
+}
+
+static void __devexit fam15h_power_remove(struct pci_dev *pdev)
+{
+	struct device *dev;
+	struct fam15h_power_data *data = pci_get_drvdata(pdev);
+
+	dev = &pdev->dev;
+	hwmon_device_unregister(data->hwmon_dev);
+	sysfs_remove_group(&dev->kobj, &fam15h_power_attr_group);
+	pci_set_drvdata(pdev, NULL);
+	kfree(data);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(fam15h_power_id_table) = {
+	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) },
+	{}
+};
+MODULE_DEVICE_TABLE(pci, fam15h_power_id_table);
+
+static struct pci_driver fam15h_power_driver = {
+	.name = "fam15h_power",
+	.id_table = fam15h_power_id_table,
+	.probe = fam15h_power_probe,
+	.remove = __devexit_p(fam15h_power_remove),
+};
+
+static int __init fam15h_power_init(void)
+{
+	return pci_register_driver(&fam15h_power_driver);
+}
+
+static void __exit fam15h_power_exit(void)
+{
+	pci_unregister_driver(&fam15h_power_driver);
+}
+
+module_init(fam15h_power_init)
+module_exit(fam15h_power_exit)
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [lm-sensors] [PATCH v4] hwmon: Add driver for AMD family 15h
@ 2011-04-08 13:54       ` Andreas Herrmann
  0 siblings, 0 replies; 43+ messages in thread
From: Andreas Herrmann @ 2011-04-08 13:54 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck
  Cc: Thomas Renninger, lm-sensors, linux-kernel, Clemens Ladisch

From: Andreas Herrmann <andreas.herrmann3@amd.com>

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_max (ProcessorPwrWatts)
* power1_input (CurrPwrWatts)

Changes from v2:
 - fix format strings
 - removed paranoid checks for existense of functions 3 and 5
 - changed return type of function f15h_power_is_internal_node0
 - use power1_max instead of power1_cap
 - use dev_warn instead of WARN_ON
 - rebased against 2.6.39-rc2
 - added Documentation/hwmon/f15h_power

Changes from v3:
 - read static power information only once (during driver initialization)
 - made use of attribute groups
 - renamed f15h_power to fam15h_power

Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com>
---
 Documentation/hwmon/fam15h_power |   37 ++++++
 drivers/hwmon/Kconfig            |   10 ++
 drivers/hwmon/Makefile           |    1 +
 drivers/hwmon/fam15h_power.c     |  229 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 277 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/hwmon/fam15h_power
 create mode 100644 drivers/hwmon/fam15h_power.c

At this stage I consider the _max attribute as the right one for
reporting ProcessorPwrWatts.

Hopefully I've addressed all your coments.
Please apply.


Thanks,

Andreas

diff --git a/Documentation/hwmon/fam15h_power b/Documentation/hwmon/fam15h_power
new file mode 100644
index 0000000..2f4d291
--- /dev/null
+++ b/Documentation/hwmon/fam15h_power
@@ -0,0 +1,37 @@
+Kernel driver fam15h_power
+=============
+
+Supported chips:
+* AMD Family 15h Processors
+
+  Prefix: 'fam15h_power'
+  Addresses scanned: PCI space
+  Datasheets:
+  BIOS and Kernel Developer's Guide (BKDG) For AMD Family 15h Processors
+    (not yet published)
+
+Author: Andreas Herrmann <andreas.herrmann3@amd.com>
+
+Description
+-----------
+
+This driver permits reading of registers providing power information
+of AMD Family 15h processors.
+
+For AMD Family 15h processors the following power values can be
+calculated using different processor northbridge function registers:
+
+* BasePwrWatts: Specifies in watts the maximum amount of power
+  consumed by the processor for NB and logic external to the core.
+* 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 ProcessorPwrWatts and CurrPwrWatts:
+* power1_max (ProcessorPwrWatts)
+* power1_input (CurrPwrWatts)
+
+On multi-node processors the calculated value is for the entire
+package and not for a single node. Thus the driver creates sysfs
+attributes only for internal node0 of a multi-node processor.
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 060ef63..fb3e334 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -249,6 +249,16 @@ config SENSORS_K10TEMP
 	  This driver can also be built as a module.  If so, the module
 	  will be called k10temp.
 
+config SENSORS_FAM15H_POWER
+	tristate "AMD Family 15h processor power"
+	depends on X86 && PCI
+	help
+	  If you say yes here you get support for processor power
+	  information of your AMD family 15h CPU.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called fam15h_power.
+
 config SENSORS_ASB100
 	tristate "Asus ASB100 Bach"
 	depends on X86 && I2C && EXPERIMENTAL
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 967d0ea..236d3f9 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -49,6 +49,7 @@ obj-$(CONFIG_SENSORS_EMC2103)	+= emc2103.o
 obj-$(CONFIG_SENSORS_F71805F)	+= f71805f.o
 obj-$(CONFIG_SENSORS_F71882FG)	+= f71882fg.o
 obj-$(CONFIG_SENSORS_F75375S)	+= f75375s.o
+obj-$(CONFIG_SENSORS_FAM15H_POWER) += fam15h_power.o
 obj-$(CONFIG_SENSORS_FSCHMD)	+= fschmd.o
 obj-$(CONFIG_SENSORS_G760A)	+= g760a.o
 obj-$(CONFIG_SENSORS_GL518SM)	+= gl518sm.o
diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c
new file mode 100644
index 0000000..cb6eb99
--- /dev/null
+++ b/drivers/hwmon/fam15h_power.c
@@ -0,0 +1,229 @@
+/*
+ * fam15h_power.c - AMD Family 15h processor power monitoring
+ *
+ * Copyright (c) 2011 Advanced Micro Devices, Inc.
+ * Author: Andreas Herrmann <andreas.herrmann3@amd.com>
+ *
+ *
+ * This driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This driver 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 driver; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/bitops.h>
+#include <asm/processor.h>
+
+MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor");
+MODULE_AUTHOR("Andreas Herrmann <andreas.herrmann3@amd.com>");
+MODULE_LICENSE("GPL");
+
+/* D18F3 */
+#define REG_NORTHBRIDGE_CAP		0xe8
+
+/* D18F4 */
+#define REG_PROCESSOR_TDP		0x1b8
+
+/* D18F5 */
+#define REG_TDP_RUNNING_AVERAGE		0xe0
+#define REG_TDP_LIMIT3			0xe8
+
+struct fam15h_power_data {
+	struct device *hwmon_dev;
+	unsigned int tdp_to_watt;
+	unsigned int base_tdp;
+	unsigned int processor_pwr_watts;
+};
+
+static ssize_t show_power(struct device *dev,
+			  struct device_attribute *attr, char *buf)
+{
+	u32 val, tdp_limit, running_avg_range;
+	s32 running_avg_capture;
+	u64 curr_pwr_watts;
+	struct pci_dev *f4 = to_pci_dev(dev);
+	struct fam15h_power_data *data = pci_get_drvdata(f4);
+
+	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
+				  REG_TDP_RUNNING_AVERAGE, &val);
+	running_avg_capture = (val >> 4) & 0x3fffff;
+	running_avg_capture = sign_extend32(running_avg_capture, 22);
+	running_avg_range = val & 0xf;
+
+	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
+				  REG_TDP_LIMIT3, &val);
+
+	tdp_limit = val >> 16;
+	curr_pwr_watts = tdp_limit  + data->base_tdp -
+		(s32)(running_avg_capture >> (running_avg_range + 1));
+	curr_pwr_watts *= data->tdp_to_watt;
+
+	/*
+	 * Convert to microWatt
+	 *
+	 * power is in Watt provided as fixed point integer with
+	 * scaling factor 1/(2^16).  For conversion we use
+	 * (10^6)/(2^16) = 15625/(2^10)
+	 */
+	curr_pwr_watts = (curr_pwr_watts * 15625) >> 10;
+	return sprintf(buf, "%u\n", (unsigned int) curr_pwr_watts);
+}
+static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL);
+
+static ssize_t show_power_max(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	struct pci_dev *f4 = to_pci_dev(dev);
+	struct fam15h_power_data *data = pci_get_drvdata(f4);
+
+	return sprintf(buf, "%u\n", data->processor_pwr_watts);
+}
+static DEVICE_ATTR(power1_max, S_IRUGO, show_power_max, NULL);
+
+static ssize_t show_name(struct device *dev,
+			 struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "fam15h_power\n");
+}
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static struct attribute *fam15h_power_attrs[] = {
+	&dev_attr_power1_input.attr,
+	&dev_attr_power1_max.attr,
+	&dev_attr_name.attr,
+	NULL
+};
+
+static struct attribute_group fam15h_power_attr_group = {
+	.attrs	= fam15h_power_attrs,
+};
+
+static bool __devinit fam15h_power_is_internal_node0(struct pci_dev *f4)
+{
+	u32 val;
+
+	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3),
+				  REG_NORTHBRIDGE_CAP, &val);
+	if ((val & BIT(29)) && ((val >> 30) & 3))
+		return false;
+
+	return true;
+}
+
+static void __devinit fam15h_power_init_data(struct pci_dev *f4,
+					     struct fam15h_power_data *data)
+{
+	u32 val;
+	u64 tmp;
+
+	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
+	data->base_tdp = val >> 16;
+	tmp = val & 0xffff;
+
+	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
+				  REG_TDP_LIMIT3, &val);
+
+	data->tdp_to_watt = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
+	tmp *= data->tdp_to_watt;
+
+	/* result not allowed to be >= 256W */
+	if ((tmp>>16) >= 256)
+		dev_warn(&f4->dev, "Bogus value for ProcessorPwrWatts "
+			 "(processor_pwr_watts>=%u)\n",
+			 (unsigned int) (tmp >> 16));
+
+	/* convert to microWatt */
+	data->processor_pwr_watts = (tmp * 15625) >> 10;
+}
+
+static int __devinit fam15h_power_probe(struct pci_dev *pdev,
+					const struct pci_device_id *id)
+{
+	struct fam15h_power_data *data;
+	struct device *dev;
+	int err;
+
+	if (!fam15h_power_is_internal_node0(pdev)) {
+		err = -ENODEV;
+		goto exit;
+	}
+
+	data = kzalloc(sizeof(struct fam15h_power_data), GFP_KERNEL);
+	if (!data) {
+		err = -ENOMEM;
+		goto exit;
+	}
+	fam15h_power_init_data(pdev, data);
+
+	dev = &pdev->dev;
+	err = sysfs_create_group(&dev->kobj, &fam15h_power_attr_group);
+	if (err)
+		goto exit_free_data;
+
+	data->hwmon_dev = hwmon_device_register(&pdev->dev);
+	if (IS_ERR(data->hwmon_dev)) {
+		err = PTR_ERR(data->hwmon_dev);
+		goto exit_remove_group;
+	}
+	pci_set_drvdata(pdev, data);
+
+	return 0;
+
+exit_remove_group:
+	sysfs_remove_group(&dev->kobj, &fam15h_power_attr_group);
+exit_free_data:
+	kfree(data);
+exit:
+	return err;
+}
+
+static void __devexit fam15h_power_remove(struct pci_dev *pdev)
+{
+	struct device *dev;
+	struct fam15h_power_data *data = pci_get_drvdata(pdev);
+
+	dev = &pdev->dev;
+	hwmon_device_unregister(data->hwmon_dev);
+	sysfs_remove_group(&dev->kobj, &fam15h_power_attr_group);
+	pci_set_drvdata(pdev, NULL);
+	kfree(data);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(fam15h_power_id_table) = {
+	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) },
+	{}
+};
+MODULE_DEVICE_TABLE(pci, fam15h_power_id_table);
+
+static struct pci_driver fam15h_power_driver = {
+	.name = "fam15h_power",
+	.id_table = fam15h_power_id_table,
+	.probe = fam15h_power_probe,
+	.remove = __devexit_p(fam15h_power_remove),
+};
+
+static int __init fam15h_power_init(void)
+{
+	return pci_register_driver(&fam15h_power_driver);
+}
+
+static void __exit fam15h_power_exit(void)
+{
+	pci_unregister_driver(&fam15h_power_driver);
+}
+
+module_init(fam15h_power_init)
+module_exit(fam15h_power_exit)
-- 
1.7.4.1


_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH] hwmon, fam15h_power: Add maintainers entry
  2011-04-06 13:54     ` [lm-sensors] [PATCH v3] hwmon: Add driver for AMD family 15h Andreas Herrmann
@ 2011-04-08 13:57       ` Andreas Herrmann
  -1 siblings, 0 replies; 43+ messages in thread
From: Andreas Herrmann @ 2011-04-08 13:57 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck
  Cc: Thomas Renninger, lm-sensors, linux-kernel, Clemens Ladisch

From: Andreas Herrmann <andreas.herrmann3@amd.com>

Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com>
---
 MAINTAINERS |    7 +++++++
 1 files changed, 7 insertions(+), 0 deletions(-)

As suggested by Jean, here's a patch to add a maintainers
entry for the new driver.


Regards,

Andreas

diff --git a/MAINTAINERS b/MAINTAINERS
index 6b4b9cd..af8421d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -483,6 +483,13 @@ F:	drivers/tty/serial/altera_jtaguart.c
 F:	include/linux/altera_uart.h
 F:	include/linux/altera_jtaguart.h
 
+AMD FAM15H PROCESSOR POWER HARDWARE MONITORING DRIVER
+M:	Andreas Herrmann <andreas.herrmann3@amd.com>
+L:	lm-sensors@lm-sensors.org
+S:	Maintained
+F:	Documentation/hwmon/fam15h_power
+F:	drivers/hwmon/fam15h_power.c
+
 AMD GEODE CS5536 USB DEVICE CONTROLLER DRIVER
 M:	Thomas Dahlmann <dahlmann.thomas@arcor.de>
 L:	linux-geode@lists.infradead.org (moderated for non-subscribers)
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [lm-sensors] [PATCH] hwmon, fam15h_power: Add maintainers entry
@ 2011-04-08 13:57       ` Andreas Herrmann
  0 siblings, 0 replies; 43+ messages in thread
From: Andreas Herrmann @ 2011-04-08 13:57 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck
  Cc: Thomas Renninger, lm-sensors, linux-kernel, Clemens Ladisch

From: Andreas Herrmann <andreas.herrmann3@amd.com>

Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com>
---
 MAINTAINERS |    7 +++++++
 1 files changed, 7 insertions(+), 0 deletions(-)

As suggested by Jean, here's a patch to add a maintainers
entry for the new driver.


Regards,

Andreas

diff --git a/MAINTAINERS b/MAINTAINERS
index 6b4b9cd..af8421d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -483,6 +483,13 @@ F:	drivers/tty/serial/altera_jtaguart.c
 F:	include/linux/altera_uart.h
 F:	include/linux/altera_jtaguart.h
 
+AMD FAM15H PROCESSOR POWER HARDWARE MONITORING DRIVER
+M:	Andreas Herrmann <andreas.herrmann3@amd.com>
+L:	lm-sensors@lm-sensors.org
+S:	Maintained
+F:	Documentation/hwmon/fam15h_power
+F:	drivers/hwmon/fam15h_power.c
+
 AMD GEODE CS5536 USB DEVICE CONTROLLER DRIVER
 M:	Thomas Dahlmann <dahlmann.thomas@arcor.de>
 L:	linux-geode@lists.infradead.org (moderated for non-subscribers)
-- 
1.7.4.1


_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* Re: [PATCH v4] hwmon: Add driver for AMD family 15h processor power information
  2011-04-08 13:54       ` [lm-sensors] [PATCH v4] hwmon: Add driver for AMD family 15h Andreas Herrmann
@ 2011-04-13 13:06         ` Jean Delvare
  -1 siblings, 0 replies; 43+ messages in thread
From: Jean Delvare @ 2011-04-13 13:06 UTC (permalink / raw)
  To: Andreas Herrmann
  Cc: Guenter Roeck, Thomas Renninger, lm-sensors, linux-kernel,
	Clemens Ladisch

Hi Andreas,

On Fri, 8 Apr 2011 15:54:07 +0200, Andreas Herrmann wrote:
> From: Andreas Herrmann <andreas.herrmann3@amd.com>
> 
> 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_max (ProcessorPwrWatts)

Still questionable whether power1_crit would be more appropriate...

> * power1_input (CurrPwrWatts)
> 
> Changes from v2:
>  - fix format strings
>  - removed paranoid checks for existense of functions 3 and 5
>  - changed return type of function f15h_power_is_internal_node0
>  - use power1_max instead of power1_cap
>  - use dev_warn instead of WARN_ON
>  - rebased against 2.6.39-rc2
>  - added Documentation/hwmon/f15h_power
> 
> Changes from v3:
>  - read static power information only once (during driver initialization)
>  - made use of attribute groups
>  - renamed f15h_power to fam15h_power
> 
> Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com>
> ---
>  Documentation/hwmon/fam15h_power |   37 ++++++
>  drivers/hwmon/Kconfig            |   10 ++
>  drivers/hwmon/Makefile           |    1 +
>  drivers/hwmon/fam15h_power.c     |  229 ++++++++++++++++++++++++++++++++++++++
>  4 files changed, 277 insertions(+), 0 deletions(-)
>  create mode 100644 Documentation/hwmon/fam15h_power
>  create mode 100644 drivers/hwmon/fam15h_power.c
> 
> At this stage I consider the _max attribute as the right one for
> reporting ProcessorPwrWatts.
> 
> Hopefully I've addressed all your coments.
> Please apply.

It seems I have some more comments, but these are very small things you
should have no problem addressing quickly:

> diff --git a/Documentation/hwmon/fam15h_power b/Documentation/hwmon/fam15h_power
> new file mode 100644
> index 0000000..2f4d291
> --- /dev/null
> +++ b/Documentation/hwmon/fam15h_power
> @@ -0,0 +1,37 @@
> +Kernel driver fam15h_power
> +==========================
> +
> +Supported chips:
> +* AMD Family 15h Processors
> +
> +  Prefix: 'fam15h_power'
> +  Addresses scanned: PCI space
> +  Datasheets:
> +  BIOS and Kernel Developer's Guide (BKDG) For AMD Family 15h Processors
> +    (not yet published)
> +
> +Author: Andreas Herrmann <andreas.herrmann3@amd.com>
> +
> +Description
> +-----------
> +
> +This driver permits reading of registers providing power information
> +of AMD Family 15h processors.
> +
> +For AMD Family 15h processors the following power values can be
> +calculated using different processor northbridge function registers:
> +
> +* BasePwrWatts: Specifies in watts the maximum amount of power
> +  consumed by the processor for NB and logic external to the core.
> +* 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 ProcessorPwrWatts and CurrPwrWatts:
> +* power1_max (ProcessorPwrWatts)
> +* power1_input (CurrPwrWatts)
> +
> +On multi-node processors the calculated value is for the entire
> +package and not for a single node. Thus the driver creates sysfs
> +attributes only for internal node0 of a multi-node processor.
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 060ef63..fb3e334 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -249,6 +249,16 @@ config SENSORS_K10TEMP
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called k10temp.
>  
> +config SENSORS_FAM15H_POWER
> +	tristate "AMD Family 15h processor power"
> +	depends on X86 && PCI
> +	help
> +	  If you say yes here you get support for processor power
> +	  information of your AMD family 15h CPU.
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called fam15h_power.
> +
>  config SENSORS_ASB100
>  	tristate "Asus ASB100 Bach"
>  	depends on X86 && I2C && EXPERIMENTAL
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index 967d0ea..236d3f9 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -49,6 +49,7 @@ obj-$(CONFIG_SENSORS_EMC2103)	+= emc2103.o
>  obj-$(CONFIG_SENSORS_F71805F)	+= f71805f.o
>  obj-$(CONFIG_SENSORS_F71882FG)	+= f71882fg.o
>  obj-$(CONFIG_SENSORS_F75375S)	+= f75375s.o
> +obj-$(CONFIG_SENSORS_FAM15H_POWER) += fam15h_power.o
>  obj-$(CONFIG_SENSORS_FSCHMD)	+= fschmd.o
>  obj-$(CONFIG_SENSORS_G760A)	+= g760a.o
>  obj-$(CONFIG_SENSORS_GL518SM)	+= gl518sm.o
> diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c
> new file mode 100644
> index 0000000..cb6eb99
> --- /dev/null
> +++ b/drivers/hwmon/fam15h_power.c
> @@ -0,0 +1,229 @@
> +/*
> + * fam15h_power.c - AMD Family 15h processor power monitoring
> + *
> + * Copyright (c) 2011 Advanced Micro Devices, Inc.
> + * Author: Andreas Herrmann <andreas.herrmann3@amd.com>
> + *
> + *
> + * This driver is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This driver 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 driver; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/err.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <linux/bitops.h>
> +#include <asm/processor.h>
> +
> +MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor");
> +MODULE_AUTHOR("Andreas Herrmann <andreas.herrmann3@amd.com>");
> +MODULE_LICENSE("GPL");
> +
> +/* D18F3 */
> +#define REG_NORTHBRIDGE_CAP		0xe8
> +
> +/* D18F4 */
> +#define REG_PROCESSOR_TDP		0x1b8
> +
> +/* D18F5 */
> +#define REG_TDP_RUNNING_AVERAGE		0xe0
> +#define REG_TDP_LIMIT3			0xe8
> +
> +struct fam15h_power_data {
> +	struct device *hwmon_dev;
> +	unsigned int tdp_to_watt;
> +	unsigned int base_tdp;
> +	unsigned int processor_pwr_watts;

watt vs. watts is inconsistent, isn't it?

> +};
> +
> +static ssize_t show_power(struct device *dev,
> +			  struct device_attribute *attr, char *buf)
> +{
> +	u32 val, tdp_limit, running_avg_range;
> +	s32 running_avg_capture;
> +	u64 curr_pwr_watts;
> +	struct pci_dev *f4 = to_pci_dev(dev);
> +	struct fam15h_power_data *data = pci_get_drvdata(f4);

dev_get_drvdata(dev) would be more efficient.

> +
> +	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
> +				  REG_TDP_RUNNING_AVERAGE, &val);
> +	running_avg_capture = (val >> 4) & 0x3fffff;
> +	running_avg_capture = sign_extend32(running_avg_capture, 22);
> +	running_avg_range = val & 0xf;
> +
> +	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
> +				  REG_TDP_LIMIT3, &val);
> +
> +	tdp_limit = val >> 16;
> +	curr_pwr_watts = tdp_limit  + data->base_tdp -

Doubled space.

> +		(s32)(running_avg_capture >> (running_avg_range + 1));
> +	curr_pwr_watts *= data->tdp_to_watt;
> +
> +	/*
> +	 * Convert to microWatt
> +	 *
> +	 * power is in Watt provided as fixed point integer with
> +	 * scaling factor 1/(2^16).  For conversion we use
> +	 * (10^6)/(2^16) = 15625/(2^10)
> +	 */
> +	curr_pwr_watts = (curr_pwr_watts * 15625) >> 10;
> +	return sprintf(buf, "%u\n", (unsigned int) curr_pwr_watts);
> +}
> +static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL);
> +
> +static ssize_t show_power_max(struct device *dev,
> +			     struct device_attribute *attr, char *buf)
> +{
> +	struct pci_dev *f4 = to_pci_dev(dev);
> +	struct fam15h_power_data *data = pci_get_drvdata(f4);

dev_get_drvdata(dev) would be more efficient (you don't even need f4
then.)

> +	return sprintf(buf, "%u\n", data->processor_pwr_watts);
> +}
> +static DEVICE_ATTR(power1_max, S_IRUGO, show_power_max, NULL);
> +
> +static ssize_t show_name(struct device *dev,
> +			 struct device_attribute *attr, char *buf)
> +{
> +	return sprintf(buf, "fam15h_power\n");
> +}
> +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
> +
> +static struct attribute *fam15h_power_attrs[] = {
> +	&dev_attr_power1_input.attr,
> +	&dev_attr_power1_max.attr,
> +	&dev_attr_name.attr,
> +	NULL
> +};
> +
> +static struct attribute_group fam15h_power_attr_group = {

Can be made const.

> +	.attrs	= fam15h_power_attrs,
> +};
> +
> +static bool __devinit fam15h_power_is_internal_node0(struct pci_dev *f4)
> +{
> +	u32 val;
> +
> +	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3),
> +				  REG_NORTHBRIDGE_CAP, &val);
> +	if ((val & BIT(29)) && ((val >> 30) & 3))
> +		return false;
> +
> +	return true;
> +}
> +
> +static void __devinit fam15h_power_init_data(struct pci_dev *f4,
> +					     struct fam15h_power_data *data)
> +{
> +	u32 val;
> +	u64 tmp;
> +
> +	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
> +	data->base_tdp = val >> 16;
> +	tmp = val & 0xffff;
> +
> +	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
> +				  REG_TDP_LIMIT3, &val);
> +
> +	data->tdp_to_watt = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
> +	tmp *= data->tdp_to_watt;
> +
> +	/* result not allowed to be >= 256W */
> +	if ((tmp>>16) >= 256)

Please add spaces around ">>" for consistency.

> +		dev_warn(&f4->dev, "Bogus value for ProcessorPwrWatts "
> +			 "(processor_pwr_watts>=%u)\n",
> +			 (unsigned int) (tmp >> 16));
> +
> +	/* convert to microWatt */
> +	data->processor_pwr_watts = (tmp * 15625) >> 10;
> +}
> +
> +static int __devinit fam15h_power_probe(struct pci_dev *pdev,
> +					const struct pci_device_id *id)
> +{
> +	struct fam15h_power_data *data;
> +	struct device *dev;
> +	int err;
> +
> +	if (!fam15h_power_is_internal_node0(pdev)) {
> +		err = -ENODEV;
> +		goto exit;
> +	}
> +
> +	data = kzalloc(sizeof(struct fam15h_power_data), GFP_KERNEL);
> +	if (!data) {
> +		err = -ENOMEM;
> +		goto exit;
> +	}
> +	fam15h_power_init_data(pdev, data);
> +
> +	dev = &pdev->dev;
> +	err = sysfs_create_group(&dev->kobj, &fam15h_power_attr_group);
> +	if (err)
> +		goto exit_free_data;
> +
> +	data->hwmon_dev = hwmon_device_register(&pdev->dev);
> +	if (IS_ERR(data->hwmon_dev)) {
> +		err = PTR_ERR(data->hwmon_dev);
> +		goto exit_remove_group;
> +	}
> +	pci_set_drvdata(pdev, data);

This is racy. pci_set_drvdata() should be called before creating the
sysfs attributes, because the sysfs callbacks need it.

> +
> +	return 0;
> +
> +exit_remove_group:
> +	sysfs_remove_group(&dev->kobj, &fam15h_power_attr_group);
> +exit_free_data:
> +	kfree(data);
> +exit:
> +	return err;
> +}
> +
> +static void __devexit fam15h_power_remove(struct pci_dev *pdev)
> +{
> +	struct device *dev;
> +	struct fam15h_power_data *data = pci_get_drvdata(pdev);
> +
> +	dev = &pdev->dev;
> +	hwmon_device_unregister(data->hwmon_dev);
> +	sysfs_remove_group(&dev->kobj, &fam15h_power_attr_group);
> +	pci_set_drvdata(pdev, NULL);
> +	kfree(data);
> +}
> +
> +static DEFINE_PCI_DEVICE_TABLE(fam15h_power_id_table) = {
> +	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(pci, fam15h_power_id_table);
> +
> +static struct pci_driver fam15h_power_driver = {
> +	.name = "fam15h_power",
> +	.id_table = fam15h_power_id_table,
> +	.probe = fam15h_power_probe,
> +	.remove = __devexit_p(fam15h_power_remove),
> +};
> +
> +static int __init fam15h_power_init(void)
> +{
> +	return pci_register_driver(&fam15h_power_driver);
> +}
> +
> +static void __exit fam15h_power_exit(void)
> +{
> +	pci_unregister_driver(&fam15h_power_driver);
> +}
> +
> +module_init(fam15h_power_init)
> +module_exit(fam15h_power_exit)

Thanks,
-- 
Jean Delvare

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [PATCH v4] hwmon: Add driver for AMD family 15h
@ 2011-04-13 13:06         ` Jean Delvare
  0 siblings, 0 replies; 43+ messages in thread
From: Jean Delvare @ 2011-04-13 13:06 UTC (permalink / raw)
  To: Andreas Herrmann
  Cc: Guenter Roeck, Thomas Renninger, lm-sensors, linux-kernel,
	Clemens Ladisch

Hi Andreas,

On Fri, 8 Apr 2011 15:54:07 +0200, Andreas Herrmann wrote:
> From: Andreas Herrmann <andreas.herrmann3@amd.com>
> 
> 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_max (ProcessorPwrWatts)

Still questionable whether power1_crit would be more appropriate...

> * power1_input (CurrPwrWatts)
> 
> Changes from v2:
>  - fix format strings
>  - removed paranoid checks for existense of functions 3 and 5
>  - changed return type of function f15h_power_is_internal_node0
>  - use power1_max instead of power1_cap
>  - use dev_warn instead of WARN_ON
>  - rebased against 2.6.39-rc2
>  - added Documentation/hwmon/f15h_power
> 
> Changes from v3:
>  - read static power information only once (during driver initialization)
>  - made use of attribute groups
>  - renamed f15h_power to fam15h_power
> 
> Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com>
> ---
>  Documentation/hwmon/fam15h_power |   37 ++++++
>  drivers/hwmon/Kconfig            |   10 ++
>  drivers/hwmon/Makefile           |    1 +
>  drivers/hwmon/fam15h_power.c     |  229 ++++++++++++++++++++++++++++++++++++++
>  4 files changed, 277 insertions(+), 0 deletions(-)
>  create mode 100644 Documentation/hwmon/fam15h_power
>  create mode 100644 drivers/hwmon/fam15h_power.c
> 
> At this stage I consider the _max attribute as the right one for
> reporting ProcessorPwrWatts.
> 
> Hopefully I've addressed all your coments.
> Please apply.

It seems I have some more comments, but these are very small things you
should have no problem addressing quickly:

> diff --git a/Documentation/hwmon/fam15h_power b/Documentation/hwmon/fam15h_power
> new file mode 100644
> index 0000000..2f4d291
> --- /dev/null
> +++ b/Documentation/hwmon/fam15h_power
> @@ -0,0 +1,37 @@
> +Kernel driver fam15h_power
> +=============
> +
> +Supported chips:
> +* AMD Family 15h Processors
> +
> +  Prefix: 'fam15h_power'
> +  Addresses scanned: PCI space
> +  Datasheets:
> +  BIOS and Kernel Developer's Guide (BKDG) For AMD Family 15h Processors
> +    (not yet published)
> +
> +Author: Andreas Herrmann <andreas.herrmann3@amd.com>
> +
> +Description
> +-----------
> +
> +This driver permits reading of registers providing power information
> +of AMD Family 15h processors.
> +
> +For AMD Family 15h processors the following power values can be
> +calculated using different processor northbridge function registers:
> +
> +* BasePwrWatts: Specifies in watts the maximum amount of power
> +  consumed by the processor for NB and logic external to the core.
> +* 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 ProcessorPwrWatts and CurrPwrWatts:
> +* power1_max (ProcessorPwrWatts)
> +* power1_input (CurrPwrWatts)
> +
> +On multi-node processors the calculated value is for the entire
> +package and not for a single node. Thus the driver creates sysfs
> +attributes only for internal node0 of a multi-node processor.
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 060ef63..fb3e334 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -249,6 +249,16 @@ config SENSORS_K10TEMP
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called k10temp.
>  
> +config SENSORS_FAM15H_POWER
> +	tristate "AMD Family 15h processor power"
> +	depends on X86 && PCI
> +	help
> +	  If you say yes here you get support for processor power
> +	  information of your AMD family 15h CPU.
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called fam15h_power.
> +
>  config SENSORS_ASB100
>  	tristate "Asus ASB100 Bach"
>  	depends on X86 && I2C && EXPERIMENTAL
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index 967d0ea..236d3f9 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -49,6 +49,7 @@ obj-$(CONFIG_SENSORS_EMC2103)	+= emc2103.o
>  obj-$(CONFIG_SENSORS_F71805F)	+= f71805f.o
>  obj-$(CONFIG_SENSORS_F71882FG)	+= f71882fg.o
>  obj-$(CONFIG_SENSORS_F75375S)	+= f75375s.o
> +obj-$(CONFIG_SENSORS_FAM15H_POWER) += fam15h_power.o
>  obj-$(CONFIG_SENSORS_FSCHMD)	+= fschmd.o
>  obj-$(CONFIG_SENSORS_G760A)	+= g760a.o
>  obj-$(CONFIG_SENSORS_GL518SM)	+= gl518sm.o
> diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c
> new file mode 100644
> index 0000000..cb6eb99
> --- /dev/null
> +++ b/drivers/hwmon/fam15h_power.c
> @@ -0,0 +1,229 @@
> +/*
> + * fam15h_power.c - AMD Family 15h processor power monitoring
> + *
> + * Copyright (c) 2011 Advanced Micro Devices, Inc.
> + * Author: Andreas Herrmann <andreas.herrmann3@amd.com>
> + *
> + *
> + * This driver is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This driver 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 driver; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/err.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <linux/bitops.h>
> +#include <asm/processor.h>
> +
> +MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor");
> +MODULE_AUTHOR("Andreas Herrmann <andreas.herrmann3@amd.com>");
> +MODULE_LICENSE("GPL");
> +
> +/* D18F3 */
> +#define REG_NORTHBRIDGE_CAP		0xe8
> +
> +/* D18F4 */
> +#define REG_PROCESSOR_TDP		0x1b8
> +
> +/* D18F5 */
> +#define REG_TDP_RUNNING_AVERAGE		0xe0
> +#define REG_TDP_LIMIT3			0xe8
> +
> +struct fam15h_power_data {
> +	struct device *hwmon_dev;
> +	unsigned int tdp_to_watt;
> +	unsigned int base_tdp;
> +	unsigned int processor_pwr_watts;

watt vs. watts is inconsistent, isn't it?

> +};
> +
> +static ssize_t show_power(struct device *dev,
> +			  struct device_attribute *attr, char *buf)
> +{
> +	u32 val, tdp_limit, running_avg_range;
> +	s32 running_avg_capture;
> +	u64 curr_pwr_watts;
> +	struct pci_dev *f4 = to_pci_dev(dev);
> +	struct fam15h_power_data *data = pci_get_drvdata(f4);

dev_get_drvdata(dev) would be more efficient.

> +
> +	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
> +				  REG_TDP_RUNNING_AVERAGE, &val);
> +	running_avg_capture = (val >> 4) & 0x3fffff;
> +	running_avg_capture = sign_extend32(running_avg_capture, 22);
> +	running_avg_range = val & 0xf;
> +
> +	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
> +				  REG_TDP_LIMIT3, &val);
> +
> +	tdp_limit = val >> 16;
> +	curr_pwr_watts = tdp_limit  + data->base_tdp -

Doubled space.

> +		(s32)(running_avg_capture >> (running_avg_range + 1));
> +	curr_pwr_watts *= data->tdp_to_watt;
> +
> +	/*
> +	 * Convert to microWatt
> +	 *
> +	 * power is in Watt provided as fixed point integer with
> +	 * scaling factor 1/(2^16).  For conversion we use
> +	 * (10^6)/(2^16) = 15625/(2^10)
> +	 */
> +	curr_pwr_watts = (curr_pwr_watts * 15625) >> 10;
> +	return sprintf(buf, "%u\n", (unsigned int) curr_pwr_watts);
> +}
> +static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL);
> +
> +static ssize_t show_power_max(struct device *dev,
> +			     struct device_attribute *attr, char *buf)
> +{
> +	struct pci_dev *f4 = to_pci_dev(dev);
> +	struct fam15h_power_data *data = pci_get_drvdata(f4);

dev_get_drvdata(dev) would be more efficient (you don't even need f4
then.)

> +	return sprintf(buf, "%u\n", data->processor_pwr_watts);
> +}
> +static DEVICE_ATTR(power1_max, S_IRUGO, show_power_max, NULL);
> +
> +static ssize_t show_name(struct device *dev,
> +			 struct device_attribute *attr, char *buf)
> +{
> +	return sprintf(buf, "fam15h_power\n");
> +}
> +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
> +
> +static struct attribute *fam15h_power_attrs[] = {
> +	&dev_attr_power1_input.attr,
> +	&dev_attr_power1_max.attr,
> +	&dev_attr_name.attr,
> +	NULL
> +};
> +
> +static struct attribute_group fam15h_power_attr_group = {

Can be made const.

> +	.attrs	= fam15h_power_attrs,
> +};
> +
> +static bool __devinit fam15h_power_is_internal_node0(struct pci_dev *f4)
> +{
> +	u32 val;
> +
> +	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3),
> +				  REG_NORTHBRIDGE_CAP, &val);
> +	if ((val & BIT(29)) && ((val >> 30) & 3))
> +		return false;
> +
> +	return true;
> +}
> +
> +static void __devinit fam15h_power_init_data(struct pci_dev *f4,
> +					     struct fam15h_power_data *data)
> +{
> +	u32 val;
> +	u64 tmp;
> +
> +	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
> +	data->base_tdp = val >> 16;
> +	tmp = val & 0xffff;
> +
> +	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
> +				  REG_TDP_LIMIT3, &val);
> +
> +	data->tdp_to_watt = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
> +	tmp *= data->tdp_to_watt;
> +
> +	/* result not allowed to be >= 256W */
> +	if ((tmp>>16) >= 256)

Please add spaces around ">>" for consistency.

> +		dev_warn(&f4->dev, "Bogus value for ProcessorPwrWatts "
> +			 "(processor_pwr_watts>=%u)\n",
> +			 (unsigned int) (tmp >> 16));
> +
> +	/* convert to microWatt */
> +	data->processor_pwr_watts = (tmp * 15625) >> 10;
> +}
> +
> +static int __devinit fam15h_power_probe(struct pci_dev *pdev,
> +					const struct pci_device_id *id)
> +{
> +	struct fam15h_power_data *data;
> +	struct device *dev;
> +	int err;
> +
> +	if (!fam15h_power_is_internal_node0(pdev)) {
> +		err = -ENODEV;
> +		goto exit;
> +	}
> +
> +	data = kzalloc(sizeof(struct fam15h_power_data), GFP_KERNEL);
> +	if (!data) {
> +		err = -ENOMEM;
> +		goto exit;
> +	}
> +	fam15h_power_init_data(pdev, data);
> +
> +	dev = &pdev->dev;
> +	err = sysfs_create_group(&dev->kobj, &fam15h_power_attr_group);
> +	if (err)
> +		goto exit_free_data;
> +
> +	data->hwmon_dev = hwmon_device_register(&pdev->dev);
> +	if (IS_ERR(data->hwmon_dev)) {
> +		err = PTR_ERR(data->hwmon_dev);
> +		goto exit_remove_group;
> +	}
> +	pci_set_drvdata(pdev, data);

This is racy. pci_set_drvdata() should be called before creating the
sysfs attributes, because the sysfs callbacks need it.

> +
> +	return 0;
> +
> +exit_remove_group:
> +	sysfs_remove_group(&dev->kobj, &fam15h_power_attr_group);
> +exit_free_data:
> +	kfree(data);
> +exit:
> +	return err;
> +}
> +
> +static void __devexit fam15h_power_remove(struct pci_dev *pdev)
> +{
> +	struct device *dev;
> +	struct fam15h_power_data *data = pci_get_drvdata(pdev);
> +
> +	dev = &pdev->dev;
> +	hwmon_device_unregister(data->hwmon_dev);
> +	sysfs_remove_group(&dev->kobj, &fam15h_power_attr_group);
> +	pci_set_drvdata(pdev, NULL);
> +	kfree(data);
> +}
> +
> +static DEFINE_PCI_DEVICE_TABLE(fam15h_power_id_table) = {
> +	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(pci, fam15h_power_id_table);
> +
> +static struct pci_driver fam15h_power_driver = {
> +	.name = "fam15h_power",
> +	.id_table = fam15h_power_id_table,
> +	.probe = fam15h_power_probe,
> +	.remove = __devexit_p(fam15h_power_remove),
> +};
> +
> +static int __init fam15h_power_init(void)
> +{
> +	return pci_register_driver(&fam15h_power_driver);
> +}
> +
> +static void __exit fam15h_power_exit(void)
> +{
> +	pci_unregister_driver(&fam15h_power_driver);
> +}
> +
> +module_init(fam15h_power_init)
> +module_exit(fam15h_power_exit)

Thanks,
-- 
Jean Delvare

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH] hwmon, fam15h_power: Add maintainers entry
  2011-04-08 13:57       ` [lm-sensors] " Andreas Herrmann
@ 2011-04-13 13:08         ` Jean Delvare
  -1 siblings, 0 replies; 43+ messages in thread
From: Jean Delvare @ 2011-04-13 13:08 UTC (permalink / raw)
  To: Andreas Herrmann
  Cc: Guenter Roeck, Thomas Renninger, lm-sensors, linux-kernel,
	Clemens Ladisch

Hi Andreas,

On Fri, 8 Apr 2011 15:57:46 +0200, Andreas Herrmann wrote:
> From: Andreas Herrmann <andreas.herrmann3@amd.com>
> 
> Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com>
> ---
>  MAINTAINERS |    7 +++++++
>  1 files changed, 7 insertions(+), 0 deletions(-)
> 
> As suggested by Jean, here's a patch to add a maintainers
> entry for the new driver.
> 
> 
> Regards,
> 
> Andreas
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 6b4b9cd..af8421d 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -483,6 +483,13 @@ F:	drivers/tty/serial/altera_jtaguart.c
>  F:	include/linux/altera_uart.h
>  F:	include/linux/altera_jtaguart.h
>  
> +AMD FAM15H PROCESSOR POWER HARDWARE MONITORING DRIVER

I'd remove "HARDWARE" as the target hardware is already described.

> +M:	Andreas Herrmann <andreas.herrmann3@amd.com>
> +L:	lm-sensors@lm-sensors.org
> +S:	Maintained
> +F:	Documentation/hwmon/fam15h_power
> +F:	drivers/hwmon/fam15h_power.c
> +
>  AMD GEODE CS5536 USB DEVICE CONTROLLER DRIVER
>  M:	Thomas Dahlmann <dahlmann.thomas@arcor.de>
>  L:	linux-geode@lists.infradead.org (moderated for non-subscribers)

Other than this small detail, which I can fix myself if you agree with
me, this looks good, thanks for stepping in!

-- 
Jean Delvare

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [PATCH] hwmon, fam15h_power: Add maintainers entry
@ 2011-04-13 13:08         ` Jean Delvare
  0 siblings, 0 replies; 43+ messages in thread
From: Jean Delvare @ 2011-04-13 13:08 UTC (permalink / raw)
  To: Andreas Herrmann
  Cc: Guenter Roeck, Thomas Renninger, lm-sensors, linux-kernel,
	Clemens Ladisch

Hi Andreas,

On Fri, 8 Apr 2011 15:57:46 +0200, Andreas Herrmann wrote:
> From: Andreas Herrmann <andreas.herrmann3@amd.com>
> 
> Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com>
> ---
>  MAINTAINERS |    7 +++++++
>  1 files changed, 7 insertions(+), 0 deletions(-)
> 
> As suggested by Jean, here's a patch to add a maintainers
> entry for the new driver.
> 
> 
> Regards,
> 
> Andreas
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 6b4b9cd..af8421d 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -483,6 +483,13 @@ F:	drivers/tty/serial/altera_jtaguart.c
>  F:	include/linux/altera_uart.h
>  F:	include/linux/altera_jtaguart.h
>  
> +AMD FAM15H PROCESSOR POWER HARDWARE MONITORING DRIVER

I'd remove "HARDWARE" as the target hardware is already described.

> +M:	Andreas Herrmann <andreas.herrmann3@amd.com>
> +L:	lm-sensors@lm-sensors.org
> +S:	Maintained
> +F:	Documentation/hwmon/fam15h_power
> +F:	drivers/hwmon/fam15h_power.c
> +
>  AMD GEODE CS5536 USB DEVICE CONTROLLER DRIVER
>  M:	Thomas Dahlmann <dahlmann.thomas@arcor.de>
>  L:	linux-geode@lists.infradead.org (moderated for non-subscribers)

Other than this small detail, which I can fix myself if you agree with
me, this looks good, thanks for stepping in!

-- 
Jean Delvare

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH] hwmon, fam15h_power: Add maintainers entry
  2011-04-13 13:08         ` [lm-sensors] " Jean Delvare
@ 2011-04-13 14:51           ` Andreas Herrmann
  -1 siblings, 0 replies; 43+ messages in thread
From: Andreas Herrmann @ 2011-04-13 14:51 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Guenter Roeck, Thomas Renninger, lm-sensors, linux-kernel,
	Clemens Ladisch

On Wed, Apr 13, 2011 at 03:08:55PM +0200, Jean Delvare wrote:
> Hi Andreas,
> 
> On Fri, 8 Apr 2011 15:57:46 +0200, Andreas Herrmann wrote:
> > From: Andreas Herrmann <andreas.herrmann3@amd.com>
> > 
> > Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com>
> > ---
> >  MAINTAINERS |    7 +++++++
> >  1 files changed, 7 insertions(+), 0 deletions(-)
> > 
> > As suggested by Jean, here's a patch to add a maintainers
> > entry for the new driver.
> > 
> > 
> > Regards,
> > 
> > Andreas
> > 
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 6b4b9cd..af8421d 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -483,6 +483,13 @@ F:	drivers/tty/serial/altera_jtaguart.c
> >  F:	include/linux/altera_uart.h
> >  F:	include/linux/altera_jtaguart.h
> >  
> > +AMD FAM15H PROCESSOR POWER HARDWARE MONITORING DRIVER
> 
> I'd remove "HARDWARE" as the target hardware is already described.
> 
> > +M:	Andreas Herrmann <andreas.herrmann3@amd.com>
> > +L:	lm-sensors@lm-sensors.org
> > +S:	Maintained
> > +F:	Documentation/hwmon/fam15h_power
> > +F:	drivers/hwmon/fam15h_power.c
> > +
> >  AMD GEODE CS5536 USB DEVICE CONTROLLER DRIVER
> >  M:	Thomas Dahlmann <dahlmann.thomas@arcor.de>
> >  L:	linux-geode@lists.infradead.org (moderated for non-subscribers)
> 
> Other than this small detail, which I can fix myself if you agree with
> me, this looks good, thanks for stepping in!

I agree.


Thanks,

Andreas

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [PATCH] hwmon, fam15h_power: Add maintainers entry
@ 2011-04-13 14:51           ` Andreas Herrmann
  0 siblings, 0 replies; 43+ messages in thread
From: Andreas Herrmann @ 2011-04-13 14:51 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Guenter Roeck, Thomas Renninger, lm-sensors, linux-kernel,
	Clemens Ladisch

On Wed, Apr 13, 2011 at 03:08:55PM +0200, Jean Delvare wrote:
> Hi Andreas,
> 
> On Fri, 8 Apr 2011 15:57:46 +0200, Andreas Herrmann wrote:
> > From: Andreas Herrmann <andreas.herrmann3@amd.com>
> > 
> > Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com>
> > ---
> >  MAINTAINERS |    7 +++++++
> >  1 files changed, 7 insertions(+), 0 deletions(-)
> > 
> > As suggested by Jean, here's a patch to add a maintainers
> > entry for the new driver.
> > 
> > 
> > Regards,
> > 
> > Andreas
> > 
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 6b4b9cd..af8421d 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -483,6 +483,13 @@ F:	drivers/tty/serial/altera_jtaguart.c
> >  F:	include/linux/altera_uart.h
> >  F:	include/linux/altera_jtaguart.h
> >  
> > +AMD FAM15H PROCESSOR POWER HARDWARE MONITORING DRIVER
> 
> I'd remove "HARDWARE" as the target hardware is already described.
> 
> > +M:	Andreas Herrmann <andreas.herrmann3@amd.com>
> > +L:	lm-sensors@lm-sensors.org
> > +S:	Maintained
> > +F:	Documentation/hwmon/fam15h_power
> > +F:	drivers/hwmon/fam15h_power.c
> > +
> >  AMD GEODE CS5536 USB DEVICE CONTROLLER DRIVER
> >  M:	Thomas Dahlmann <dahlmann.thomas@arcor.de>
> >  L:	linux-geode@lists.infradead.org (moderated for non-subscribers)
> 
> Other than this small detail, which I can fix myself if you agree with
> me, this looks good, thanks for stepping in!

I agree.


Thanks,

Andreas

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH v4] hwmon: Add driver for AMD family 15h processor power information
  2011-04-13 13:06         ` [lm-sensors] [PATCH v4] hwmon: Add driver for AMD family 15h Jean Delvare
@ 2011-04-14 19:16           ` Andreas Herrmann
  -1 siblings, 0 replies; 43+ messages in thread
From: Andreas Herrmann @ 2011-04-14 19:16 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Guenter Roeck, Thomas Renninger, lm-sensors, linux-kernel,
	Clemens Ladisch

On Wed, Apr 13, 2011 at 03:06:33PM +0200, Jean Delvare wrote:
> Hi Andreas,
> 
> On Fri, 8 Apr 2011 15:54:07 +0200, Andreas Herrmann wrote:
> > From: Andreas Herrmann <andreas.herrmann3@amd.com>
> > 
> > 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_max (ProcessorPwrWatts)
> 
> Still questionable whether power1_crit would be more appropriate...

Finally, I've changed it.

> > * power1_input (CurrPwrWatts)
> > 
> > Changes from v2:
> >  - fix format strings
> >  - removed paranoid checks for existense of functions 3 and 5
> >  - changed return type of function f15h_power_is_internal_node0
> >  - use power1_max instead of power1_cap
> >  - use dev_warn instead of WARN_ON
> >  - rebased against 2.6.39-rc2
> >  - added Documentation/hwmon/f15h_power
> > 
> > Changes from v3:
> >  - read static power information only once (during driver initialization)
> >  - made use of attribute groups
> >  - renamed f15h_power to fam15h_power
> > 
> > Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com>
> > ---
> >  Documentation/hwmon/fam15h_power |   37 ++++++
> >  drivers/hwmon/Kconfig            |   10 ++
> >  drivers/hwmon/Makefile           |    1 +
> >  drivers/hwmon/fam15h_power.c     |  229 ++++++++++++++++++++++++++++++++++++++
> >  4 files changed, 277 insertions(+), 0 deletions(-)
> >  create mode 100644 Documentation/hwmon/fam15h_power
> >  create mode 100644 drivers/hwmon/fam15h_power.c
> > 
> > At this stage I consider the _max attribute as the right one for
> > reporting ProcessorPwrWatts.
> > 
> > Hopefully I've addressed all your coments.
> > Please apply.
> 
> It seems I have some more comments, but these are very small things you
> should have no problem addressing quickly:
> 
> > diff --git a/Documentation/hwmon/fam15h_power b/Documentation/hwmon/fam15h_power
> > new file mode 100644
> > index 0000000..2f4d291
> > --- /dev/null
> > +++ b/Documentation/hwmon/fam15h_power
> > @@ -0,0 +1,37 @@
> > +Kernel driver fam15h_power
> > +==========================
> > +
> > +Supported chips:
> > +* AMD Family 15h Processors
> > +
> > +  Prefix: 'fam15h_power'
> > +  Addresses scanned: PCI space
> > +  Datasheets:
> > +  BIOS and Kernel Developer's Guide (BKDG) For AMD Family 15h Processors
> > +    (not yet published)
> > +
> > +Author: Andreas Herrmann <andreas.herrmann3@amd.com>
> > +
> > +Description
> > +-----------
> > +
> > +This driver permits reading of registers providing power information
> > +of AMD Family 15h processors.
> > +
> > +For AMD Family 15h processors the following power values can be
> > +calculated using different processor northbridge function registers:
> > +
> > +* BasePwrWatts: Specifies in watts the maximum amount of power
> > +  consumed by the processor for NB and logic external to the core.
> > +* 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 ProcessorPwrWatts and CurrPwrWatts:
> > +* power1_max (ProcessorPwrWatts)
> > +* power1_input (CurrPwrWatts)
> > +
> > +On multi-node processors the calculated value is for the entire
> > +package and not for a single node. Thus the driver creates sysfs
> > +attributes only for internal node0 of a multi-node processor.
> > diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> > index 060ef63..fb3e334 100644
> > --- a/drivers/hwmon/Kconfig
> > +++ b/drivers/hwmon/Kconfig
> > @@ -249,6 +249,16 @@ config SENSORS_K10TEMP
> >  	  This driver can also be built as a module.  If so, the module
> >  	  will be called k10temp.
> >  
> > +config SENSORS_FAM15H_POWER
> > +	tristate "AMD Family 15h processor power"
> > +	depends on X86 && PCI
> > +	help
> > +	  If you say yes here you get support for processor power
> > +	  information of your AMD family 15h CPU.
> > +
> > +	  This driver can also be built as a module.  If so, the module
> > +	  will be called fam15h_power.
> > +
> >  config SENSORS_ASB100
> >  	tristate "Asus ASB100 Bach"
> >  	depends on X86 && I2C && EXPERIMENTAL
> > diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> > index 967d0ea..236d3f9 100644
> > --- a/drivers/hwmon/Makefile
> > +++ b/drivers/hwmon/Makefile
> > @@ -49,6 +49,7 @@ obj-$(CONFIG_SENSORS_EMC2103)	+= emc2103.o
> >  obj-$(CONFIG_SENSORS_F71805F)	+= f71805f.o
> >  obj-$(CONFIG_SENSORS_F71882FG)	+= f71882fg.o
> >  obj-$(CONFIG_SENSORS_F75375S)	+= f75375s.o
> > +obj-$(CONFIG_SENSORS_FAM15H_POWER) += fam15h_power.o
> >  obj-$(CONFIG_SENSORS_FSCHMD)	+= fschmd.o
> >  obj-$(CONFIG_SENSORS_G760A)	+= g760a.o
> >  obj-$(CONFIG_SENSORS_GL518SM)	+= gl518sm.o
> > diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c
> > new file mode 100644
> > index 0000000..cb6eb99
> > --- /dev/null
> > +++ b/drivers/hwmon/fam15h_power.c
> > @@ -0,0 +1,229 @@
> > +/*
> > + * fam15h_power.c - AMD Family 15h processor power monitoring
> > + *
> > + * Copyright (c) 2011 Advanced Micro Devices, Inc.
> > + * Author: Andreas Herrmann <andreas.herrmann3@amd.com>
> > + *
> > + *
> > + * This driver is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU General Public License; either
> > + * version 2 of the License, or (at your option) any later version.
> > + *
> > + * This driver 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 driver; if not, see <http://www.gnu.org/licenses/>.
> > + */
> > +
> > +#include <linux/err.h>
> > +#include <linux/hwmon.h>
> > +#include <linux/hwmon-sysfs.h>
> > +#include <linux/init.h>
> > +#include <linux/module.h>
> > +#include <linux/pci.h>
> > +#include <linux/bitops.h>
> > +#include <asm/processor.h>
> > +
> > +MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor");
> > +MODULE_AUTHOR("Andreas Herrmann <andreas.herrmann3@amd.com>");
> > +MODULE_LICENSE("GPL");
> > +
> > +/* D18F3 */
> > +#define REG_NORTHBRIDGE_CAP		0xe8
> > +
> > +/* D18F4 */
> > +#define REG_PROCESSOR_TDP		0x1b8
> > +
> > +/* D18F5 */
> > +#define REG_TDP_RUNNING_AVERAGE		0xe0
> > +#define REG_TDP_LIMIT3			0xe8
> > +
> > +struct fam15h_power_data {
> > +	struct device *hwmon_dev;
> > +	unsigned int tdp_to_watt;
> > +	unsigned int base_tdp;
> > +	unsigned int processor_pwr_watts;
> 
> watt vs. watts is inconsistent, isn't it?

Changed.

> > +};
> > +
> > +static ssize_t show_power(struct device *dev,
> > +			  struct device_attribute *attr, char *buf)
> > +{
> > +	u32 val, tdp_limit, running_avg_range;
> > +	s32 running_avg_capture;
> > +	u64 curr_pwr_watts;
> > +	struct pci_dev *f4 = to_pci_dev(dev);
> > +	struct fam15h_power_data *data = pci_get_drvdata(f4);
> 
> dev_get_drvdata(dev) would be more efficient.
> 
> > +
> > +	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
> > +				  REG_TDP_RUNNING_AVERAGE, &val);
> > +	running_avg_capture = (val >> 4) & 0x3fffff;
> > +	running_avg_capture = sign_extend32(running_avg_capture, 22);
> > +	running_avg_range = val & 0xf;
> > +
> > +	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
> > +				  REG_TDP_LIMIT3, &val);
> > +
> > +	tdp_limit = val >> 16;
> > +	curr_pwr_watts = tdp_limit  + data->base_tdp -
> 
> Doubled space.

Whoops!

> > +		(s32)(running_avg_capture >> (running_avg_range + 1));
> > +	curr_pwr_watts *= data->tdp_to_watt;
> > +
> > +	/*
> > +	 * Convert to microWatt
> > +	 *
> > +	 * power is in Watt provided as fixed point integer with
> > +	 * scaling factor 1/(2^16).  For conversion we use
> > +	 * (10^6)/(2^16) = 15625/(2^10)
> > +	 */
> > +	curr_pwr_watts = (curr_pwr_watts * 15625) >> 10;
> > +	return sprintf(buf, "%u\n", (unsigned int) curr_pwr_watts);
> > +}
> > +static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL);
> > +
> > +static ssize_t show_power_max(struct device *dev,
> > +			     struct device_attribute *attr, char *buf)
> > +{
> > +	struct pci_dev *f4 = to_pci_dev(dev);
> > +	struct fam15h_power_data *data = pci_get_drvdata(f4);
> 
> dev_get_drvdata(dev) would be more efficient (you don't even need f4
> then.)
> 
> > +	return sprintf(buf, "%u\n", data->processor_pwr_watts);
> > +}
> > +static DEVICE_ATTR(power1_max, S_IRUGO, show_power_max, NULL);
> > +
> > +static ssize_t show_name(struct device *dev,
> > +			 struct device_attribute *attr, char *buf)
> > +{
> > +	return sprintf(buf, "fam15h_power\n");
> > +}
> > +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
> > +
> > +static struct attribute *fam15h_power_attrs[] = {
> > +	&dev_attr_power1_input.attr,
> > +	&dev_attr_power1_max.attr,
> > +	&dev_attr_name.attr,
> > +	NULL
> > +};
> > +
> > +static struct attribute_group fam15h_power_attr_group = {
> 
> Can be made const.

Done this ....

> > +	.attrs	= fam15h_power_attrs,
> > +};
> > +
> > +static bool __devinit fam15h_power_is_internal_node0(struct pci_dev *f4)
> > +{
> > +	u32 val;
> > +
> > +	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3),
> > +				  REG_NORTHBRIDGE_CAP, &val);
> > +	if ((val & BIT(29)) && ((val >> 30) & 3))
> > +		return false;
> > +
> > +	return true;
> > +}
> > +
> > +static void __devinit fam15h_power_init_data(struct pci_dev *f4,
> > +					     struct fam15h_power_data *data)
> > +{
> > +	u32 val;
> > +	u64 tmp;
> > +
> > +	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
> > +	data->base_tdp = val >> 16;
> > +	tmp = val & 0xffff;
> > +
> > +	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
> > +				  REG_TDP_LIMIT3, &val);
> > +
> > +	data->tdp_to_watt = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
> > +	tmp *= data->tdp_to_watt;
> > +
> > +	/* result not allowed to be >= 256W */
> > +	if ((tmp>>16) >= 256)
> 
> Please add spaces around ">>" for consistency.

... and that.

> > +		dev_warn(&f4->dev, "Bogus value for ProcessorPwrWatts "
> > +			 "(processor_pwr_watts>=%u)\n",
> > +			 (unsigned int) (tmp >> 16));
> > +
> > +	/* convert to microWatt */
> > +	data->processor_pwr_watts = (tmp * 15625) >> 10;
> > +}
> > +
> > +static int __devinit fam15h_power_probe(struct pci_dev *pdev,
> > +					const struct pci_device_id *id)
> > +{
> > +	struct fam15h_power_data *data;
> > +	struct device *dev;
> > +	int err;
> > +
> > +	if (!fam15h_power_is_internal_node0(pdev)) {
> > +		err = -ENODEV;
> > +		goto exit;
> > +	}
> > +
> > +	data = kzalloc(sizeof(struct fam15h_power_data), GFP_KERNEL);
> > +	if (!data) {
> > +		err = -ENOMEM;
> > +		goto exit;
> > +	}
> > +	fam15h_power_init_data(pdev, data);
> > +
> > +	dev = &pdev->dev;
> > +	err = sysfs_create_group(&dev->kobj, &fam15h_power_attr_group);
> > +	if (err)
> > +		goto exit_free_data;
> > +
> > +	data->hwmon_dev = hwmon_device_register(&pdev->dev);
> > +	if (IS_ERR(data->hwmon_dev)) {
> > +		err = PTR_ERR(data->hwmon_dev);
> > +		goto exit_remove_group;
> > +	}
> > +	pci_set_drvdata(pdev, data);
> 
> This is racy. pci_set_drvdata() should be called before creating the
> sysfs attributes, because the sysfs callbacks need it.

Oops -- fixed.


> > +
> > +	return 0;

[snip]


Thanks again for the review


Andreas

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [PATCH v4] hwmon: Add driver for AMD family 15h
@ 2011-04-14 19:16           ` Andreas Herrmann
  0 siblings, 0 replies; 43+ messages in thread
From: Andreas Herrmann @ 2011-04-14 19:16 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Guenter Roeck, Thomas Renninger, lm-sensors, linux-kernel,
	Clemens Ladisch

On Wed, Apr 13, 2011 at 03:06:33PM +0200, Jean Delvare wrote:
> Hi Andreas,
> 
> On Fri, 8 Apr 2011 15:54:07 +0200, Andreas Herrmann wrote:
> > From: Andreas Herrmann <andreas.herrmann3@amd.com>
> > 
> > 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_max (ProcessorPwrWatts)
> 
> Still questionable whether power1_crit would be more appropriate...

Finally, I've changed it.

> > * power1_input (CurrPwrWatts)
> > 
> > Changes from v2:
> >  - fix format strings
> >  - removed paranoid checks for existense of functions 3 and 5
> >  - changed return type of function f15h_power_is_internal_node0
> >  - use power1_max instead of power1_cap
> >  - use dev_warn instead of WARN_ON
> >  - rebased against 2.6.39-rc2
> >  - added Documentation/hwmon/f15h_power
> > 
> > Changes from v3:
> >  - read static power information only once (during driver initialization)
> >  - made use of attribute groups
> >  - renamed f15h_power to fam15h_power
> > 
> > Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com>
> > ---
> >  Documentation/hwmon/fam15h_power |   37 ++++++
> >  drivers/hwmon/Kconfig            |   10 ++
> >  drivers/hwmon/Makefile           |    1 +
> >  drivers/hwmon/fam15h_power.c     |  229 ++++++++++++++++++++++++++++++++++++++
> >  4 files changed, 277 insertions(+), 0 deletions(-)
> >  create mode 100644 Documentation/hwmon/fam15h_power
> >  create mode 100644 drivers/hwmon/fam15h_power.c
> > 
> > At this stage I consider the _max attribute as the right one for
> > reporting ProcessorPwrWatts.
> > 
> > Hopefully I've addressed all your coments.
> > Please apply.
> 
> It seems I have some more comments, but these are very small things you
> should have no problem addressing quickly:
> 
> > diff --git a/Documentation/hwmon/fam15h_power b/Documentation/hwmon/fam15h_power
> > new file mode 100644
> > index 0000000..2f4d291
> > --- /dev/null
> > +++ b/Documentation/hwmon/fam15h_power
> > @@ -0,0 +1,37 @@
> > +Kernel driver fam15h_power
> > +=============
> > +
> > +Supported chips:
> > +* AMD Family 15h Processors
> > +
> > +  Prefix: 'fam15h_power'
> > +  Addresses scanned: PCI space
> > +  Datasheets:
> > +  BIOS and Kernel Developer's Guide (BKDG) For AMD Family 15h Processors
> > +    (not yet published)
> > +
> > +Author: Andreas Herrmann <andreas.herrmann3@amd.com>
> > +
> > +Description
> > +-----------
> > +
> > +This driver permits reading of registers providing power information
> > +of AMD Family 15h processors.
> > +
> > +For AMD Family 15h processors the following power values can be
> > +calculated using different processor northbridge function registers:
> > +
> > +* BasePwrWatts: Specifies in watts the maximum amount of power
> > +  consumed by the processor for NB and logic external to the core.
> > +* 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 ProcessorPwrWatts and CurrPwrWatts:
> > +* power1_max (ProcessorPwrWatts)
> > +* power1_input (CurrPwrWatts)
> > +
> > +On multi-node processors the calculated value is for the entire
> > +package and not for a single node. Thus the driver creates sysfs
> > +attributes only for internal node0 of a multi-node processor.
> > diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> > index 060ef63..fb3e334 100644
> > --- a/drivers/hwmon/Kconfig
> > +++ b/drivers/hwmon/Kconfig
> > @@ -249,6 +249,16 @@ config SENSORS_K10TEMP
> >  	  This driver can also be built as a module.  If so, the module
> >  	  will be called k10temp.
> >  
> > +config SENSORS_FAM15H_POWER
> > +	tristate "AMD Family 15h processor power"
> > +	depends on X86 && PCI
> > +	help
> > +	  If you say yes here you get support for processor power
> > +	  information of your AMD family 15h CPU.
> > +
> > +	  This driver can also be built as a module.  If so, the module
> > +	  will be called fam15h_power.
> > +
> >  config SENSORS_ASB100
> >  	tristate "Asus ASB100 Bach"
> >  	depends on X86 && I2C && EXPERIMENTAL
> > diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> > index 967d0ea..236d3f9 100644
> > --- a/drivers/hwmon/Makefile
> > +++ b/drivers/hwmon/Makefile
> > @@ -49,6 +49,7 @@ obj-$(CONFIG_SENSORS_EMC2103)	+= emc2103.o
> >  obj-$(CONFIG_SENSORS_F71805F)	+= f71805f.o
> >  obj-$(CONFIG_SENSORS_F71882FG)	+= f71882fg.o
> >  obj-$(CONFIG_SENSORS_F75375S)	+= f75375s.o
> > +obj-$(CONFIG_SENSORS_FAM15H_POWER) += fam15h_power.o
> >  obj-$(CONFIG_SENSORS_FSCHMD)	+= fschmd.o
> >  obj-$(CONFIG_SENSORS_G760A)	+= g760a.o
> >  obj-$(CONFIG_SENSORS_GL518SM)	+= gl518sm.o
> > diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c
> > new file mode 100644
> > index 0000000..cb6eb99
> > --- /dev/null
> > +++ b/drivers/hwmon/fam15h_power.c
> > @@ -0,0 +1,229 @@
> > +/*
> > + * fam15h_power.c - AMD Family 15h processor power monitoring
> > + *
> > + * Copyright (c) 2011 Advanced Micro Devices, Inc.
> > + * Author: Andreas Herrmann <andreas.herrmann3@amd.com>
> > + *
> > + *
> > + * This driver is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU General Public License; either
> > + * version 2 of the License, or (at your option) any later version.
> > + *
> > + * This driver 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 driver; if not, see <http://www.gnu.org/licenses/>.
> > + */
> > +
> > +#include <linux/err.h>
> > +#include <linux/hwmon.h>
> > +#include <linux/hwmon-sysfs.h>
> > +#include <linux/init.h>
> > +#include <linux/module.h>
> > +#include <linux/pci.h>
> > +#include <linux/bitops.h>
> > +#include <asm/processor.h>
> > +
> > +MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor");
> > +MODULE_AUTHOR("Andreas Herrmann <andreas.herrmann3@amd.com>");
> > +MODULE_LICENSE("GPL");
> > +
> > +/* D18F3 */
> > +#define REG_NORTHBRIDGE_CAP		0xe8
> > +
> > +/* D18F4 */
> > +#define REG_PROCESSOR_TDP		0x1b8
> > +
> > +/* D18F5 */
> > +#define REG_TDP_RUNNING_AVERAGE		0xe0
> > +#define REG_TDP_LIMIT3			0xe8
> > +
> > +struct fam15h_power_data {
> > +	struct device *hwmon_dev;
> > +	unsigned int tdp_to_watt;
> > +	unsigned int base_tdp;
> > +	unsigned int processor_pwr_watts;
> 
> watt vs. watts is inconsistent, isn't it?

Changed.

> > +};
> > +
> > +static ssize_t show_power(struct device *dev,
> > +			  struct device_attribute *attr, char *buf)
> > +{
> > +	u32 val, tdp_limit, running_avg_range;
> > +	s32 running_avg_capture;
> > +	u64 curr_pwr_watts;
> > +	struct pci_dev *f4 = to_pci_dev(dev);
> > +	struct fam15h_power_data *data = pci_get_drvdata(f4);
> 
> dev_get_drvdata(dev) would be more efficient.
> 
> > +
> > +	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
> > +				  REG_TDP_RUNNING_AVERAGE, &val);
> > +	running_avg_capture = (val >> 4) & 0x3fffff;
> > +	running_avg_capture = sign_extend32(running_avg_capture, 22);
> > +	running_avg_range = val & 0xf;
> > +
> > +	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
> > +				  REG_TDP_LIMIT3, &val);
> > +
> > +	tdp_limit = val >> 16;
> > +	curr_pwr_watts = tdp_limit  + data->base_tdp -
> 
> Doubled space.

Whoops!

> > +		(s32)(running_avg_capture >> (running_avg_range + 1));
> > +	curr_pwr_watts *= data->tdp_to_watt;
> > +
> > +	/*
> > +	 * Convert to microWatt
> > +	 *
> > +	 * power is in Watt provided as fixed point integer with
> > +	 * scaling factor 1/(2^16).  For conversion we use
> > +	 * (10^6)/(2^16) = 15625/(2^10)
> > +	 */
> > +	curr_pwr_watts = (curr_pwr_watts * 15625) >> 10;
> > +	return sprintf(buf, "%u\n", (unsigned int) curr_pwr_watts);
> > +}
> > +static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL);
> > +
> > +static ssize_t show_power_max(struct device *dev,
> > +			     struct device_attribute *attr, char *buf)
> > +{
> > +	struct pci_dev *f4 = to_pci_dev(dev);
> > +	struct fam15h_power_data *data = pci_get_drvdata(f4);
> 
> dev_get_drvdata(dev) would be more efficient (you don't even need f4
> then.)
> 
> > +	return sprintf(buf, "%u\n", data->processor_pwr_watts);
> > +}
> > +static DEVICE_ATTR(power1_max, S_IRUGO, show_power_max, NULL);
> > +
> > +static ssize_t show_name(struct device *dev,
> > +			 struct device_attribute *attr, char *buf)
> > +{
> > +	return sprintf(buf, "fam15h_power\n");
> > +}
> > +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
> > +
> > +static struct attribute *fam15h_power_attrs[] = {
> > +	&dev_attr_power1_input.attr,
> > +	&dev_attr_power1_max.attr,
> > +	&dev_attr_name.attr,
> > +	NULL
> > +};
> > +
> > +static struct attribute_group fam15h_power_attr_group = {
> 
> Can be made const.

Done this ....

> > +	.attrs	= fam15h_power_attrs,
> > +};
> > +
> > +static bool __devinit fam15h_power_is_internal_node0(struct pci_dev *f4)
> > +{
> > +	u32 val;
> > +
> > +	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3),
> > +				  REG_NORTHBRIDGE_CAP, &val);
> > +	if ((val & BIT(29)) && ((val >> 30) & 3))
> > +		return false;
> > +
> > +	return true;
> > +}
> > +
> > +static void __devinit fam15h_power_init_data(struct pci_dev *f4,
> > +					     struct fam15h_power_data *data)
> > +{
> > +	u32 val;
> > +	u64 tmp;
> > +
> > +	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
> > +	data->base_tdp = val >> 16;
> > +	tmp = val & 0xffff;
> > +
> > +	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
> > +				  REG_TDP_LIMIT3, &val);
> > +
> > +	data->tdp_to_watt = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
> > +	tmp *= data->tdp_to_watt;
> > +
> > +	/* result not allowed to be >= 256W */
> > +	if ((tmp>>16) >= 256)
> 
> Please add spaces around ">>" for consistency.

... and that.

> > +		dev_warn(&f4->dev, "Bogus value for ProcessorPwrWatts "
> > +			 "(processor_pwr_watts>=%u)\n",
> > +			 (unsigned int) (tmp >> 16));
> > +
> > +	/* convert to microWatt */
> > +	data->processor_pwr_watts = (tmp * 15625) >> 10;
> > +}
> > +
> > +static int __devinit fam15h_power_probe(struct pci_dev *pdev,
> > +					const struct pci_device_id *id)
> > +{
> > +	struct fam15h_power_data *data;
> > +	struct device *dev;
> > +	int err;
> > +
> > +	if (!fam15h_power_is_internal_node0(pdev)) {
> > +		err = -ENODEV;
> > +		goto exit;
> > +	}
> > +
> > +	data = kzalloc(sizeof(struct fam15h_power_data), GFP_KERNEL);
> > +	if (!data) {
> > +		err = -ENOMEM;
> > +		goto exit;
> > +	}
> > +	fam15h_power_init_data(pdev, data);
> > +
> > +	dev = &pdev->dev;
> > +	err = sysfs_create_group(&dev->kobj, &fam15h_power_attr_group);
> > +	if (err)
> > +		goto exit_free_data;
> > +
> > +	data->hwmon_dev = hwmon_device_register(&pdev->dev);
> > +	if (IS_ERR(data->hwmon_dev)) {
> > +		err = PTR_ERR(data->hwmon_dev);
> > +		goto exit_remove_group;
> > +	}
> > +	pci_set_drvdata(pdev, data);
> 
> This is racy. pci_set_drvdata() should be called before creating the
> sysfs attributes, because the sysfs callbacks need it.

Oops -- fixed.


> > +
> > +	return 0;

[snip]


Thanks again for the review


Andreas

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH v5] hwmon: Add driver for AMD family 15h processor power information
  2011-04-08 13:54       ` [lm-sensors] [PATCH v4] hwmon: Add driver for AMD family 15h Andreas Herrmann
@ 2011-04-14 19:20         ` Andreas Herrmann
  -1 siblings, 0 replies; 43+ messages in thread
From: Andreas Herrmann @ 2011-04-14 19:20 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck
  Cc: Thomas Renninger, lm-sensors, linux-kernel, Clemens Ladisch

From: Andreas Herrmann <andreas.herrmann3@amd.com>

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)

Changes from v2:
 - fix format strings
 - removed paranoid checks for existense of functions 3 and 5
 - changed return type of function f15h_power_is_internal_node0
 - use power1_max instead of power1_cap
 - use dev_warn instead of WARN_ON
 - rebased against 2.6.39-rc2
 - added Documentation/hwmon/f15h_power

Changes from v3:
 - read static power information only once (during driver initialization)
 - made use of attribute groups
 - renamed f15h_power to fam15h_power

Changes from v4:
 - fixed race in probe function
 - use power1_crit instead of powe1_max
 - coding style changes

Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com>
---
 Documentation/hwmon/fam15h_power |   37 ++++++
 drivers/hwmon/Kconfig            |   10 ++
 drivers/hwmon/Makefile           |    1 +
 drivers/hwmon/fam15h_power.c     |  229 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 277 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/hwmon/fam15h_power
 create mode 100644 drivers/hwmon/fam15h_power.c

Patch is rebased on v2.6.39-rc3-82-g85f2e68


Andreas

diff --git a/Documentation/hwmon/fam15h_power b/Documentation/hwmon/fam15h_power
new file mode 100644
index 0000000..a92918e
--- /dev/null
+++ b/Documentation/hwmon/fam15h_power
@@ -0,0 +1,37 @@
+Kernel driver fam15h_power
+==========================
+
+Supported chips:
+* AMD Family 15h Processors
+
+  Prefix: 'fam15h_power'
+  Addresses scanned: PCI space
+  Datasheets:
+  BIOS and Kernel Developer's Guide (BKDG) For AMD Family 15h Processors
+    (not yet published)
+
+Author: Andreas Herrmann <andreas.herrmann3@amd.com>
+
+Description
+-----------
+
+This driver permits reading of registers providing power information
+of AMD Family 15h processors.
+
+For AMD Family 15h processors the following power values can be
+calculated using different processor northbridge function registers:
+
+* BasePwrWatts: Specifies in watts the maximum amount of power
+  consumed by the processor for NB and logic external to the core.
+* 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 ProcessorPwrWatts and CurrPwrWatts:
+* power1_crit (ProcessorPwrWatts)
+* power1_input (CurrPwrWatts)
+
+On multi-node processors the calculated value is for the entire
+package and not for a single node. Thus the driver creates sysfs
+attributes only for internal node0 of a multi-node processor.
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 060ef63..fb3e334 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -249,6 +249,16 @@ config SENSORS_K10TEMP
 	  This driver can also be built as a module.  If so, the module
 	  will be called k10temp.
 
+config SENSORS_FAM15H_POWER
+	tristate "AMD Family 15h processor power"
+	depends on X86 && PCI
+	help
+	  If you say yes here you get support for processor power
+	  information of your AMD family 15h CPU.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called fam15h_power.
+
 config SENSORS_ASB100
 	tristate "Asus ASB100 Bach"
 	depends on X86 && I2C && EXPERIMENTAL
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 967d0ea..236d3f9 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -49,6 +49,7 @@ obj-$(CONFIG_SENSORS_EMC2103)	+= emc2103.o
 obj-$(CONFIG_SENSORS_F71805F)	+= f71805f.o
 obj-$(CONFIG_SENSORS_F71882FG)	+= f71882fg.o
 obj-$(CONFIG_SENSORS_F75375S)	+= f75375s.o
+obj-$(CONFIG_SENSORS_FAM15H_POWER) += fam15h_power.o
 obj-$(CONFIG_SENSORS_FSCHMD)	+= fschmd.o
 obj-$(CONFIG_SENSORS_G760A)	+= g760a.o
 obj-$(CONFIG_SENSORS_GL518SM)	+= gl518sm.o
diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c
new file mode 100644
index 0000000..523f8fb
--- /dev/null
+++ b/drivers/hwmon/fam15h_power.c
@@ -0,0 +1,229 @@
+/*
+ * fam15h_power.c - AMD Family 15h processor power monitoring
+ *
+ * Copyright (c) 2011 Advanced Micro Devices, Inc.
+ * Author: Andreas Herrmann <andreas.herrmann3@amd.com>
+ *
+ *
+ * This driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This driver 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 driver; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/bitops.h>
+#include <asm/processor.h>
+
+MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor");
+MODULE_AUTHOR("Andreas Herrmann <andreas.herrmann3@amd.com>");
+MODULE_LICENSE("GPL");
+
+/* D18F3 */
+#define REG_NORTHBRIDGE_CAP		0xe8
+
+/* D18F4 */
+#define REG_PROCESSOR_TDP		0x1b8
+
+/* D18F5 */
+#define REG_TDP_RUNNING_AVERAGE		0xe0
+#define REG_TDP_LIMIT3			0xe8
+
+struct fam15h_power_data {
+	struct device *hwmon_dev;
+	unsigned int tdp_to_watts;
+	unsigned int base_tdp;
+	unsigned int processor_pwr_watts;
+};
+
+static ssize_t show_power(struct device *dev,
+			  struct device_attribute *attr, char *buf)
+{
+	u32 val, tdp_limit, running_avg_range;
+	s32 running_avg_capture;
+	u64 curr_pwr_watts;
+	struct pci_dev *f4 = to_pci_dev(dev);
+	struct fam15h_power_data *data = dev_get_drvdata(dev);
+
+	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
+				  REG_TDP_RUNNING_AVERAGE, &val);
+	running_avg_capture = (val >> 4) & 0x3fffff;
+	running_avg_capture = sign_extend32(running_avg_capture, 22);
+	running_avg_range = val & 0xf;
+
+	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
+				  REG_TDP_LIMIT3, &val);
+
+	tdp_limit = val >> 16;
+	curr_pwr_watts = tdp_limit + data->base_tdp -
+		(s32)(running_avg_capture >> (running_avg_range + 1));
+	curr_pwr_watts *= data->tdp_to_watts;
+
+	/*
+	 * Convert to microWatt
+	 *
+	 * power is in Watt provided as fixed point integer with
+	 * scaling factor 1/(2^16).  For conversion we use
+	 * (10^6)/(2^16) = 15625/(2^10)
+	 */
+	curr_pwr_watts = (curr_pwr_watts * 15625) >> 10;
+	return sprintf(buf, "%u\n", (unsigned int) curr_pwr_watts);
+}
+static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL);
+
+static ssize_t show_power_crit(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct fam15h_power_data *data = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", data->processor_pwr_watts);
+}
+static DEVICE_ATTR(power1_crit, S_IRUGO, show_power_crit, NULL);
+
+static ssize_t show_name(struct device *dev,
+			 struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "fam15h_power\n");
+}
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static struct attribute *fam15h_power_attrs[] = {
+	&dev_attr_power1_input.attr,
+	&dev_attr_power1_crit.attr,
+	&dev_attr_name.attr,
+	NULL
+};
+
+static const struct attribute_group fam15h_power_attr_group = {
+	.attrs	= fam15h_power_attrs,
+};
+
+static bool __devinit fam15h_power_is_internal_node0(struct pci_dev *f4)
+{
+	u32 val;
+
+	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3),
+				  REG_NORTHBRIDGE_CAP, &val);
+	if ((val & BIT(29)) && ((val >> 30) & 3))
+		return false;
+
+	return true;
+}
+
+static void __devinit fam15h_power_init_data(struct pci_dev *f4,
+					     struct fam15h_power_data *data)
+{
+	u32 val;
+	u64 tmp;
+
+	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
+	data->base_tdp = val >> 16;
+	tmp = val & 0xffff;
+
+	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
+				  REG_TDP_LIMIT3, &val);
+
+	data->tdp_to_watts = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
+	tmp *= data->tdp_to_watts;
+
+	/* result not allowed to be >= 256W */
+	if ((tmp >> 16) >= 256)
+		dev_warn(&f4->dev, "Bogus value for ProcessorPwrWatts "
+			 "(processor_pwr_watts>=%u)\n",
+			 (unsigned int) (tmp >> 16));
+
+	/* convert to microWatt */
+	data->processor_pwr_watts = (tmp * 15625) >> 10;
+}
+
+static int __devinit fam15h_power_probe(struct pci_dev *pdev,
+					const struct pci_device_id *id)
+{
+	struct fam15h_power_data *data;
+	struct device *dev;
+	int err;
+
+	if (!fam15h_power_is_internal_node0(pdev)) {
+		err = -ENODEV;
+		goto exit;
+	}
+
+	data = kzalloc(sizeof(struct fam15h_power_data), GFP_KERNEL);
+	if (!data) {
+		err = -ENOMEM;
+		goto exit;
+	}
+	fam15h_power_init_data(pdev, data);
+	dev = &pdev->dev;
+
+	dev_set_drvdata(dev, data);
+	err = sysfs_create_group(&dev->kobj, &fam15h_power_attr_group);
+	if (err)
+		goto exit_free_data;
+
+	data->hwmon_dev = hwmon_device_register(dev);
+	if (IS_ERR(data->hwmon_dev)) {
+		err = PTR_ERR(data->hwmon_dev);
+		goto exit_remove_group;
+	}
+
+	return 0;
+
+exit_remove_group:
+	sysfs_remove_group(&dev->kobj, &fam15h_power_attr_group);
+exit_free_data:
+	kfree(data);
+exit:
+	return err;
+}
+
+static void __devexit fam15h_power_remove(struct pci_dev *pdev)
+{
+	struct device *dev;
+	struct fam15h_power_data *data;
+
+	dev = &pdev->dev;
+	data = dev_get_drvdata(dev);
+	hwmon_device_unregister(data->hwmon_dev);
+	sysfs_remove_group(&dev->kobj, &fam15h_power_attr_group);
+	dev_set_drvdata(dev, NULL);
+	kfree(data);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(fam15h_power_id_table) = {
+	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) },
+	{}
+};
+MODULE_DEVICE_TABLE(pci, fam15h_power_id_table);
+
+static struct pci_driver fam15h_power_driver = {
+	.name = "fam15h_power",
+	.id_table = fam15h_power_id_table,
+	.probe = fam15h_power_probe,
+	.remove = __devexit_p(fam15h_power_remove),
+};
+
+static int __init fam15h_power_init(void)
+{
+	return pci_register_driver(&fam15h_power_driver);
+}
+
+static void __exit fam15h_power_exit(void)
+{
+	pci_unregister_driver(&fam15h_power_driver);
+}
+
+module_init(fam15h_power_init)
+module_exit(fam15h_power_exit)
-- 
1.7.5.rc1


^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [lm-sensors] [PATCH v5] hwmon: Add driver for AMD family 15h
@ 2011-04-14 19:20         ` Andreas Herrmann
  0 siblings, 0 replies; 43+ messages in thread
From: Andreas Herrmann @ 2011-04-14 19:20 UTC (permalink / raw)
  To: Jean Delvare, Guenter Roeck
  Cc: Thomas Renninger, lm-sensors, linux-kernel, Clemens Ladisch

From: Andreas Herrmann <andreas.herrmann3@amd.com>

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)

Changes from v2:
 - fix format strings
 - removed paranoid checks for existense of functions 3 and 5
 - changed return type of function f15h_power_is_internal_node0
 - use power1_max instead of power1_cap
 - use dev_warn instead of WARN_ON
 - rebased against 2.6.39-rc2
 - added Documentation/hwmon/f15h_power

Changes from v3:
 - read static power information only once (during driver initialization)
 - made use of attribute groups
 - renamed f15h_power to fam15h_power

Changes from v4:
 - fixed race in probe function
 - use power1_crit instead of powe1_max
 - coding style changes

Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com>
---
 Documentation/hwmon/fam15h_power |   37 ++++++
 drivers/hwmon/Kconfig            |   10 ++
 drivers/hwmon/Makefile           |    1 +
 drivers/hwmon/fam15h_power.c     |  229 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 277 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/hwmon/fam15h_power
 create mode 100644 drivers/hwmon/fam15h_power.c

Patch is rebased on v2.6.39-rc3-82-g85f2e68


Andreas

diff --git a/Documentation/hwmon/fam15h_power b/Documentation/hwmon/fam15h_power
new file mode 100644
index 0000000..a92918e
--- /dev/null
+++ b/Documentation/hwmon/fam15h_power
@@ -0,0 +1,37 @@
+Kernel driver fam15h_power
+=============
+
+Supported chips:
+* AMD Family 15h Processors
+
+  Prefix: 'fam15h_power'
+  Addresses scanned: PCI space
+  Datasheets:
+  BIOS and Kernel Developer's Guide (BKDG) For AMD Family 15h Processors
+    (not yet published)
+
+Author: Andreas Herrmann <andreas.herrmann3@amd.com>
+
+Description
+-----------
+
+This driver permits reading of registers providing power information
+of AMD Family 15h processors.
+
+For AMD Family 15h processors the following power values can be
+calculated using different processor northbridge function registers:
+
+* BasePwrWatts: Specifies in watts the maximum amount of power
+  consumed by the processor for NB and logic external to the core.
+* 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 ProcessorPwrWatts and CurrPwrWatts:
+* power1_crit (ProcessorPwrWatts)
+* power1_input (CurrPwrWatts)
+
+On multi-node processors the calculated value is for the entire
+package and not for a single node. Thus the driver creates sysfs
+attributes only for internal node0 of a multi-node processor.
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 060ef63..fb3e334 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -249,6 +249,16 @@ config SENSORS_K10TEMP
 	  This driver can also be built as a module.  If so, the module
 	  will be called k10temp.
 
+config SENSORS_FAM15H_POWER
+	tristate "AMD Family 15h processor power"
+	depends on X86 && PCI
+	help
+	  If you say yes here you get support for processor power
+	  information of your AMD family 15h CPU.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called fam15h_power.
+
 config SENSORS_ASB100
 	tristate "Asus ASB100 Bach"
 	depends on X86 && I2C && EXPERIMENTAL
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 967d0ea..236d3f9 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -49,6 +49,7 @@ obj-$(CONFIG_SENSORS_EMC2103)	+= emc2103.o
 obj-$(CONFIG_SENSORS_F71805F)	+= f71805f.o
 obj-$(CONFIG_SENSORS_F71882FG)	+= f71882fg.o
 obj-$(CONFIG_SENSORS_F75375S)	+= f75375s.o
+obj-$(CONFIG_SENSORS_FAM15H_POWER) += fam15h_power.o
 obj-$(CONFIG_SENSORS_FSCHMD)	+= fschmd.o
 obj-$(CONFIG_SENSORS_G760A)	+= g760a.o
 obj-$(CONFIG_SENSORS_GL518SM)	+= gl518sm.o
diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c
new file mode 100644
index 0000000..523f8fb
--- /dev/null
+++ b/drivers/hwmon/fam15h_power.c
@@ -0,0 +1,229 @@
+/*
+ * fam15h_power.c - AMD Family 15h processor power monitoring
+ *
+ * Copyright (c) 2011 Advanced Micro Devices, Inc.
+ * Author: Andreas Herrmann <andreas.herrmann3@amd.com>
+ *
+ *
+ * This driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This driver 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 driver; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/bitops.h>
+#include <asm/processor.h>
+
+MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor");
+MODULE_AUTHOR("Andreas Herrmann <andreas.herrmann3@amd.com>");
+MODULE_LICENSE("GPL");
+
+/* D18F3 */
+#define REG_NORTHBRIDGE_CAP		0xe8
+
+/* D18F4 */
+#define REG_PROCESSOR_TDP		0x1b8
+
+/* D18F5 */
+#define REG_TDP_RUNNING_AVERAGE		0xe0
+#define REG_TDP_LIMIT3			0xe8
+
+struct fam15h_power_data {
+	struct device *hwmon_dev;
+	unsigned int tdp_to_watts;
+	unsigned int base_tdp;
+	unsigned int processor_pwr_watts;
+};
+
+static ssize_t show_power(struct device *dev,
+			  struct device_attribute *attr, char *buf)
+{
+	u32 val, tdp_limit, running_avg_range;
+	s32 running_avg_capture;
+	u64 curr_pwr_watts;
+	struct pci_dev *f4 = to_pci_dev(dev);
+	struct fam15h_power_data *data = dev_get_drvdata(dev);
+
+	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
+				  REG_TDP_RUNNING_AVERAGE, &val);
+	running_avg_capture = (val >> 4) & 0x3fffff;
+	running_avg_capture = sign_extend32(running_avg_capture, 22);
+	running_avg_range = val & 0xf;
+
+	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
+				  REG_TDP_LIMIT3, &val);
+
+	tdp_limit = val >> 16;
+	curr_pwr_watts = tdp_limit + data->base_tdp -
+		(s32)(running_avg_capture >> (running_avg_range + 1));
+	curr_pwr_watts *= data->tdp_to_watts;
+
+	/*
+	 * Convert to microWatt
+	 *
+	 * power is in Watt provided as fixed point integer with
+	 * scaling factor 1/(2^16).  For conversion we use
+	 * (10^6)/(2^16) = 15625/(2^10)
+	 */
+	curr_pwr_watts = (curr_pwr_watts * 15625) >> 10;
+	return sprintf(buf, "%u\n", (unsigned int) curr_pwr_watts);
+}
+static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL);
+
+static ssize_t show_power_crit(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct fam15h_power_data *data = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", data->processor_pwr_watts);
+}
+static DEVICE_ATTR(power1_crit, S_IRUGO, show_power_crit, NULL);
+
+static ssize_t show_name(struct device *dev,
+			 struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "fam15h_power\n");
+}
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static struct attribute *fam15h_power_attrs[] = {
+	&dev_attr_power1_input.attr,
+	&dev_attr_power1_crit.attr,
+	&dev_attr_name.attr,
+	NULL
+};
+
+static const struct attribute_group fam15h_power_attr_group = {
+	.attrs	= fam15h_power_attrs,
+};
+
+static bool __devinit fam15h_power_is_internal_node0(struct pci_dev *f4)
+{
+	u32 val;
+
+	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3),
+				  REG_NORTHBRIDGE_CAP, &val);
+	if ((val & BIT(29)) && ((val >> 30) & 3))
+		return false;
+
+	return true;
+}
+
+static void __devinit fam15h_power_init_data(struct pci_dev *f4,
+					     struct fam15h_power_data *data)
+{
+	u32 val;
+	u64 tmp;
+
+	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
+	data->base_tdp = val >> 16;
+	tmp = val & 0xffff;
+
+	pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
+				  REG_TDP_LIMIT3, &val);
+
+	data->tdp_to_watts = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
+	tmp *= data->tdp_to_watts;
+
+	/* result not allowed to be >= 256W */
+	if ((tmp >> 16) >= 256)
+		dev_warn(&f4->dev, "Bogus value for ProcessorPwrWatts "
+			 "(processor_pwr_watts>=%u)\n",
+			 (unsigned int) (tmp >> 16));
+
+	/* convert to microWatt */
+	data->processor_pwr_watts = (tmp * 15625) >> 10;
+}
+
+static int __devinit fam15h_power_probe(struct pci_dev *pdev,
+					const struct pci_device_id *id)
+{
+	struct fam15h_power_data *data;
+	struct device *dev;
+	int err;
+
+	if (!fam15h_power_is_internal_node0(pdev)) {
+		err = -ENODEV;
+		goto exit;
+	}
+
+	data = kzalloc(sizeof(struct fam15h_power_data), GFP_KERNEL);
+	if (!data) {
+		err = -ENOMEM;
+		goto exit;
+	}
+	fam15h_power_init_data(pdev, data);
+	dev = &pdev->dev;
+
+	dev_set_drvdata(dev, data);
+	err = sysfs_create_group(&dev->kobj, &fam15h_power_attr_group);
+	if (err)
+		goto exit_free_data;
+
+	data->hwmon_dev = hwmon_device_register(dev);
+	if (IS_ERR(data->hwmon_dev)) {
+		err = PTR_ERR(data->hwmon_dev);
+		goto exit_remove_group;
+	}
+
+	return 0;
+
+exit_remove_group:
+	sysfs_remove_group(&dev->kobj, &fam15h_power_attr_group);
+exit_free_data:
+	kfree(data);
+exit:
+	return err;
+}
+
+static void __devexit fam15h_power_remove(struct pci_dev *pdev)
+{
+	struct device *dev;
+	struct fam15h_power_data *data;
+
+	dev = &pdev->dev;
+	data = dev_get_drvdata(dev);
+	hwmon_device_unregister(data->hwmon_dev);
+	sysfs_remove_group(&dev->kobj, &fam15h_power_attr_group);
+	dev_set_drvdata(dev, NULL);
+	kfree(data);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(fam15h_power_id_table) = {
+	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) },
+	{}
+};
+MODULE_DEVICE_TABLE(pci, fam15h_power_id_table);
+
+static struct pci_driver fam15h_power_driver = {
+	.name = "fam15h_power",
+	.id_table = fam15h_power_id_table,
+	.probe = fam15h_power_probe,
+	.remove = __devexit_p(fam15h_power_remove),
+};
+
+static int __init fam15h_power_init(void)
+{
+	return pci_register_driver(&fam15h_power_driver);
+}
+
+static void __exit fam15h_power_exit(void)
+{
+	pci_unregister_driver(&fam15h_power_driver);
+}
+
+module_init(fam15h_power_init)
+module_exit(fam15h_power_exit)
-- 
1.7.5.rc1


_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* Re: [PATCH v5] hwmon: Add driver for AMD family 15h processor power information
  2011-04-14 19:20         ` [lm-sensors] [PATCH v5] hwmon: Add driver for AMD family 15h Andreas Herrmann
@ 2011-04-15  9:31           ` Jean Delvare
  -1 siblings, 0 replies; 43+ messages in thread
From: Jean Delvare @ 2011-04-15  9:31 UTC (permalink / raw)
  To: Andreas Herrmann
  Cc: Guenter Roeck, Thomas Renninger, lm-sensors, linux-kernel,
	Clemens Ladisch

On Thu, 14 Apr 2011 21:20:11 +0200, Andreas Herrmann wrote:
> From: Andreas Herrmann <andreas.herrmann3@amd.com>
> 
> 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)
> (...)

Applied, thanks.

-- 
Jean Delvare

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [PATCH v5] hwmon: Add driver for AMD family 15h
@ 2011-04-15  9:31           ` Jean Delvare
  0 siblings, 0 replies; 43+ messages in thread
From: Jean Delvare @ 2011-04-15  9:31 UTC (permalink / raw)
  To: Andreas Herrmann
  Cc: Guenter Roeck, Thomas Renninger, lm-sensors, linux-kernel,
	Clemens Ladisch

On Thu, 14 Apr 2011 21:20:11 +0200, Andreas Herrmann wrote:
> From: Andreas Herrmann <andreas.herrmann3@amd.com>
> 
> 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)
> (...)

Applied, thanks.

-- 
Jean Delvare

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 43+ messages in thread

end of thread, other threads:[~2011-04-15  9:32 UTC | newest]

Thread overview: 43+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-04-04 16:07 [lm-sensors] [PATCH] hwmon: Add driver for AMD family 15h processor Andreas Herrmann
2011-04-05 14:45 ` [PATCH v2] hwmon: Add driver for AMD family 15h processor power information Andreas Herrmann
2011-04-05 14:45   ` [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h Andreas Herrmann
2011-04-05 20:12   ` [PATCH v2] hwmon: Add driver for AMD family 15h processor power information Guenter Roeck
2011-04-05 20:12     ` [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h Guenter Roeck
2011-04-06  7:14     ` [PATCH v2] hwmon: Add driver for AMD family 15h processor power information Clemens Ladisch
2011-04-06  7:14       ` [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h Clemens Ladisch
2011-04-06  9:38       ` [PATCH v2] hwmon: Add driver for AMD family 15h processor power information Andreas Herrmann
2011-04-06  9:38         ` [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h Andreas Herrmann
2011-04-06  9:31     ` [PATCH v2] hwmon: Add driver for AMD family 15h processor power information Andreas Herrmann
2011-04-06  9:31       ` [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h Andreas Herrmann
2011-04-06 13:54   ` [PATCH v3] hwmon: Add driver for AMD family 15h processor power information Andreas Herrmann
2011-04-06 13:54     ` [lm-sensors] [PATCH v3] hwmon: Add driver for AMD family 15h Andreas Herrmann
2011-04-06 16:50     ` [PATCH v3] hwmon: Add driver for AMD family 15h processor power information Jean Delvare
2011-04-06 16:50       ` [lm-sensors] [PATCH v3] hwmon: Add driver for AMD family 15h Jean Delvare
2011-04-07  9:13       ` [PATCH v3] hwmon: Add driver for AMD family 15h processor power information Andreas Herrmann
2011-04-07  9:13         ` [lm-sensors] [PATCH v3] hwmon: Add driver for AMD family 15h Andreas Herrmann
2011-04-08 13:54     ` [PATCH v4] hwmon: Add driver for AMD family 15h processor power information Andreas Herrmann
2011-04-08 13:54       ` [lm-sensors] [PATCH v4] hwmon: Add driver for AMD family 15h Andreas Herrmann
2011-04-13 13:06       ` [PATCH v4] hwmon: Add driver for AMD family 15h processor power information Jean Delvare
2011-04-13 13:06         ` [lm-sensors] [PATCH v4] hwmon: Add driver for AMD family 15h Jean Delvare
2011-04-14 19:16         ` [PATCH v4] hwmon: Add driver for AMD family 15h processor power information Andreas Herrmann
2011-04-14 19:16           ` [lm-sensors] [PATCH v4] hwmon: Add driver for AMD family 15h Andreas Herrmann
2011-04-14 19:20       ` [PATCH v5] hwmon: Add driver for AMD family 15h processor power information Andreas Herrmann
2011-04-14 19:20         ` [lm-sensors] [PATCH v5] hwmon: Add driver for AMD family 15h Andreas Herrmann
2011-04-15  9:31         ` [PATCH v5] hwmon: Add driver for AMD family 15h processor power information Jean Delvare
2011-04-15  9:31           ` [lm-sensors] [PATCH v5] hwmon: Add driver for AMD family 15h Jean Delvare
2011-04-08 13:57     ` [PATCH] hwmon, fam15h_power: Add maintainers entry Andreas Herrmann
2011-04-08 13:57       ` [lm-sensors] " Andreas Herrmann
2011-04-13 13:08       ` Jean Delvare
2011-04-13 13:08         ` [lm-sensors] " Jean Delvare
2011-04-13 14:51         ` Andreas Herrmann
2011-04-13 14:51           ` [lm-sensors] " Andreas Herrmann
2011-04-06 14:14   ` [PATCH v2] hwmon: Add driver for AMD family 15h processor power information Jean Delvare
2011-04-06 14:14     ` [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h Jean Delvare
2011-04-06 15:19     ` [PATCH v2] hwmon: Add driver for AMD family 15h processor power information Andreas Herrmann
2011-04-06 15:19       ` [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h Andreas Herrmann
2011-04-06 15:35       ` [PATCH v2] hwmon: Add driver for AMD family 15h processor power information Jean Delvare
2011-04-06 15:35         ` [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h Jean Delvare
2011-04-06 16:14         ` [PATCH v2] hwmon: Add driver for AMD family 15h processor power information Guenter Roeck
2011-04-06 16:14           ` [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h Guenter Roeck
2011-04-06 16:20           ` [PATCH v2] hwmon: Add driver for AMD family 15h processor power information Andreas Herrmann
2011-04-06 16:20             ` [lm-sensors] [PATCH v2] hwmon: Add driver for AMD family 15h Andreas Herrmann

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.