|
|
@@ -41,12 +41,17 @@ MODULE_LICENSE("GPL");
|
|
|
#define REG_TDP_RUNNING_AVERAGE 0xe0
|
|
|
#define REG_TDP_LIMIT3 0xe8
|
|
|
|
|
|
+#define FAM15H_MIN_NUM_ATTRS 2
|
|
|
+#define FAM15H_NUM_GROUPS 2
|
|
|
+
|
|
|
struct fam15h_power_data {
|
|
|
struct pci_dev *pdev;
|
|
|
unsigned int tdp_to_watts;
|
|
|
unsigned int base_tdp;
|
|
|
unsigned int processor_pwr_watts;
|
|
|
unsigned int cpu_pwr_sample_ratio;
|
|
|
+ const struct attribute_group *groups[FAM15H_NUM_GROUPS];
|
|
|
+ struct attribute_group group;
|
|
|
};
|
|
|
|
|
|
static ssize_t show_power(struct device *dev,
|
|
|
@@ -105,29 +110,31 @@ static ssize_t show_power_crit(struct device *dev,
|
|
|
}
|
|
|
static DEVICE_ATTR(power1_crit, S_IRUGO, show_power_crit, NULL);
|
|
|
|
|
|
-static umode_t fam15h_power_is_visible(struct kobject *kobj,
|
|
|
- struct attribute *attr,
|
|
|
- int index)
|
|
|
+static int fam15h_power_init_attrs(struct pci_dev *pdev,
|
|
|
+ struct fam15h_power_data *data)
|
|
|
{
|
|
|
- /* power1_input is only reported for Fam15h, Models 00h-0fh */
|
|
|
- if (attr == &dev_attr_power1_input.attr &&
|
|
|
- (boot_cpu_data.x86 != 0x15 || boot_cpu_data.x86_model > 0xf))
|
|
|
- return 0;
|
|
|
+ int n = FAM15H_MIN_NUM_ATTRS;
|
|
|
+ struct attribute **fam15h_power_attrs;
|
|
|
|
|
|
- return attr->mode;
|
|
|
-}
|
|
|
+ if (boot_cpu_data.x86 == 0x15 && boot_cpu_data.x86_model <= 0xf)
|
|
|
+ n += 1;
|
|
|
|
|
|
-static struct attribute *fam15h_power_attrs[] = {
|
|
|
- &dev_attr_power1_input.attr,
|
|
|
- &dev_attr_power1_crit.attr,
|
|
|
- NULL
|
|
|
-};
|
|
|
+ fam15h_power_attrs = devm_kcalloc(&pdev->dev, n,
|
|
|
+ sizeof(*fam15h_power_attrs),
|
|
|
+ GFP_KERNEL);
|
|
|
|
|
|
-static const struct attribute_group fam15h_power_group = {
|
|
|
- .attrs = fam15h_power_attrs,
|
|
|
- .is_visible = fam15h_power_is_visible,
|
|
|
-};
|
|
|
-__ATTRIBUTE_GROUPS(fam15h_power);
|
|
|
+ if (!fam15h_power_attrs)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ n = 0;
|
|
|
+ fam15h_power_attrs[n++] = &dev_attr_power1_crit.attr;
|
|
|
+ if (boot_cpu_data.x86 == 0x15 && boot_cpu_data.x86_model <= 0xf)
|
|
|
+ fam15h_power_attrs[n++] = &dev_attr_power1_input.attr;
|
|
|
+
|
|
|
+ data->group.attrs = fam15h_power_attrs;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
static bool should_load_on_this_node(struct pci_dev *f4)
|
|
|
{
|
|
|
@@ -186,11 +193,12 @@ static int fam15h_power_resume(struct pci_dev *pdev)
|
|
|
#define fam15h_power_resume NULL
|
|
|
#endif
|
|
|
|
|
|
-static void fam15h_power_init_data(struct pci_dev *f4,
|
|
|
- struct fam15h_power_data *data)
|
|
|
+static int fam15h_power_init_data(struct pci_dev *f4,
|
|
|
+ struct fam15h_power_data *data)
|
|
|
{
|
|
|
u32 val, eax, ebx, ecx, edx;
|
|
|
u64 tmp;
|
|
|
+ int ret;
|
|
|
|
|
|
pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
|
|
|
data->base_tdp = val >> 16;
|
|
|
@@ -211,11 +219,15 @@ static void fam15h_power_init_data(struct pci_dev *f4,
|
|
|
/* convert to microWatt */
|
|
|
data->processor_pwr_watts = (tmp * 15625) >> 10;
|
|
|
|
|
|
+ ret = fam15h_power_init_attrs(f4, data);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
cpuid(0x80000007, &eax, &ebx, &ecx, &edx);
|
|
|
|
|
|
/* CPUID Fn8000_0007:EDX[12] indicates to support accumulated power */
|
|
|
if (!(edx & BIT(12)))
|
|
|
- return;
|
|
|
+ return 0;
|
|
|
|
|
|
/*
|
|
|
* determine the ratio of the compute unit power accumulator
|
|
|
@@ -223,14 +235,17 @@ static void fam15h_power_init_data(struct pci_dev *f4,
|
|
|
* Fn8000_0007:ECX
|
|
|
*/
|
|
|
data->cpu_pwr_sample_ratio = ecx;
|
|
|
+
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
static int fam15h_power_probe(struct pci_dev *pdev,
|
|
|
- const struct pci_device_id *id)
|
|
|
+ const struct pci_device_id *id)
|
|
|
{
|
|
|
struct fam15h_power_data *data;
|
|
|
struct device *dev = &pdev->dev;
|
|
|
struct device *hwmon_dev;
|
|
|
+ int ret;
|
|
|
|
|
|
/*
|
|
|
* though we ignore every other northbridge, we still have to
|
|
|
@@ -246,12 +261,17 @@ static int fam15h_power_probe(struct pci_dev *pdev,
|
|
|
if (!data)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
- fam15h_power_init_data(pdev, data);
|
|
|
+ ret = fam15h_power_init_data(pdev, data);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
data->pdev = pdev;
|
|
|
|
|
|
+ data->groups[0] = &data->group;
|
|
|
+
|
|
|
hwmon_dev = devm_hwmon_device_register_with_groups(dev, "fam15h_power",
|
|
|
data,
|
|
|
- fam15h_power_groups);
|
|
|
+ &data->groups[0]);
|
|
|
return PTR_ERR_OR_ZERO(hwmon_dev);
|
|
|
}
|
|
|
|