Merge branch 'ver5'
This commit is contained in:
commit
3406cda35c
3 changed files with 122 additions and 12 deletions
2
Makefile
2
Makefile
|
@ -1,4 +1,4 @@
|
||||||
VERSION := 0.1.4
|
VERSION := 0.1.5
|
||||||
TARGET := $(shell uname -r)
|
TARGET := $(shell uname -r)
|
||||||
DKMS_ROOT_PATH := /usr/src/zenpower-$(VERSION)
|
DKMS_ROOT_PATH := /usr/src/zenpower-$(VERSION)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Zenpower
|
# Zenpower
|
||||||
Zenpower is Linux kernel driver for reading temperature, voltage(SVI2), current(SVI2) and power(SVI2) for AMD Zen family CPUs.
|
Zenpower is Linux kernel driver for reading temperature, voltage(SVI2), current(SVI2) and power(SVI2) for AMD Zen family CPUs.
|
||||||
|
|
||||||
Make sure that your Linux kernel have support for your CPUs as Zenpower is using kernel calls to read values from SMN.
|
Make sure that your Linux kernel have support for your CPUs as Zenpower is using kernel function `amd_smn_read` to read values from SMN. A fallback method (which may or may not work!) will be used when it is detected that kernel function `amd_smn_read` lacks support for your CPU.
|
||||||
For AMD family 17h Model 70h (Ryzen 3000) CPUs you need kernel version 5.3.4 or newer or kernel with this patch: https://patchwork.kernel.org/patch/11043277/
|
For AMD family 17h Model 70h (Ryzen 3000) CPUs you need kernel version 5.3.4 or newer or kernel with this patch: https://patchwork.kernel.org/patch/11043277/
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
130
zenpower.c
130
zenpower.c
|
@ -7,7 +7,7 @@
|
||||||
MODULE_DESCRIPTION("AMD ZEN family CPU Sensors Driver");
|
MODULE_DESCRIPTION("AMD ZEN family CPU Sensors Driver");
|
||||||
MODULE_AUTHOR("Ondrej Čerman");
|
MODULE_AUTHOR("Ondrej Čerman");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_VERSION("0.1.4");
|
MODULE_VERSION("0.1.5");
|
||||||
|
|
||||||
// based on k10temp - GPL - (c) 2009 Clemens Ladisch <clemens@ladisch.de>
|
// based on k10temp - GPL - (c) 2009 Clemens Ladisch <clemens@ladisch.de>
|
||||||
//
|
//
|
||||||
|
@ -41,6 +41,8 @@ MODULE_VERSION("0.1.4");
|
||||||
#define F17H_M01H_SVI 0x0005A000
|
#define F17H_M01H_SVI 0x0005A000
|
||||||
#define F17H_M01H_SVI_TEL_PLANE0 F17H_M01H_SVI + 0xc
|
#define F17H_M01H_SVI_TEL_PLANE0 F17H_M01H_SVI + 0xc
|
||||||
#define F17H_M01H_SVI_TEL_PLANE1 F17H_M01H_SVI + 0x10
|
#define F17H_M01H_SVI_TEL_PLANE1 F17H_M01H_SVI + 0x10
|
||||||
|
#define F17H_M70H_CCD1_TEMP 0x00059954
|
||||||
|
#define F17H_M70H_CCD2_TEMP 0x00059958
|
||||||
|
|
||||||
#define F17H_TEMP_ADJUST_MASK 0x80000
|
#define F17H_TEMP_ADJUST_MASK 0x80000
|
||||||
|
|
||||||
|
@ -51,6 +53,9 @@ struct zenpower_data {
|
||||||
u32 svi_soc_addr;
|
u32 svi_soc_addr;
|
||||||
int temp_offset;
|
int temp_offset;
|
||||||
bool zen2;
|
bool zen2;
|
||||||
|
bool kernel_smn_support;
|
||||||
|
bool amps_visible;
|
||||||
|
bool ccd1_visible, ccd2_visible;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct tctl_offset {
|
struct tctl_offset {
|
||||||
|
@ -68,9 +73,29 @@ static const struct tctl_offset tctl_offset_table[] = {
|
||||||
{ 0x17, "AMD Ryzen Threadripper 29", 27000 }, /* 29{20,50,70,90}[W]X */
|
{ 0x17, "AMD Ryzen Threadripper 29", 27000 }, /* 29{20,50,70,90}[W]X */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static DEFINE_MUTEX(nb_smu_ind_mutex);
|
||||||
|
|
||||||
static umode_t zenpower_is_visible(struct kobject *kobj,
|
static umode_t zenpower_is_visible(struct kobject *kobj,
|
||||||
struct attribute *attr, int index)
|
struct attribute *attr, int index)
|
||||||
{
|
{
|
||||||
|
struct device *dev = container_of(kobj, struct device, kobj);
|
||||||
|
struct zenpower_data *data = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
switch (index) {
|
||||||
|
case 4 ... 11: // amperage and wattage
|
||||||
|
if (!data->amps_visible)
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
case 17 ... 18: // CCD1 temperature
|
||||||
|
if (!data->ccd1_visible)
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
case 19 ... 20: // CCD2 temperature
|
||||||
|
if (!data->ccd2_visible)
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return attr->mode;
|
return attr->mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +132,7 @@ static u32 get_soc_current(u32 plane, bool zen2)
|
||||||
return (fc * idd_cor) / 1000;
|
return (fc * idd_cor) / 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int get_raw_temp(struct zenpower_data *data)
|
static unsigned int get_ctl_temp(struct zenpower_data *data)
|
||||||
{
|
{
|
||||||
unsigned int temp;
|
unsigned int temp;
|
||||||
u32 regval;
|
u32 regval;
|
||||||
|
@ -119,11 +144,19 @@ static unsigned int get_raw_temp(struct zenpower_data *data)
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned int get_ccd_temp(struct zenpower_data *data, u32 ccd_addr)
|
||||||
|
{
|
||||||
|
u32 regval;
|
||||||
|
data->read_amdsmn_addr(data->pdev, ccd_addr, ®val);
|
||||||
|
|
||||||
|
return (regval & 0xfff) * 125 - 305000;
|
||||||
|
}
|
||||||
|
|
||||||
static ssize_t temp1_input_show(struct device *dev,
|
static ssize_t temp1_input_show(struct device *dev,
|
||||||
struct device_attribute *attr, char *buf)
|
struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
struct zenpower_data *data = dev_get_drvdata(dev);
|
struct zenpower_data *data = dev_get_drvdata(dev);
|
||||||
unsigned int temp = get_raw_temp(data);
|
unsigned int temp = get_ctl_temp(data);
|
||||||
|
|
||||||
if (temp > data->temp_offset)
|
if (temp > data->temp_offset)
|
||||||
temp -= data->temp_offset;
|
temp -= data->temp_offset;
|
||||||
|
@ -143,7 +176,25 @@ static ssize_t temp2_input_show(struct device *dev,
|
||||||
struct device_attribute *devattr, char *buf)
|
struct device_attribute *devattr, char *buf)
|
||||||
{
|
{
|
||||||
struct zenpower_data *data = dev_get_drvdata(dev);
|
struct zenpower_data *data = dev_get_drvdata(dev);
|
||||||
unsigned int temp = get_raw_temp(data);
|
unsigned int temp = get_ctl_temp(data);
|
||||||
|
|
||||||
|
return sprintf(buf, "%u\n", temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t temp3_input_show(struct device *dev,
|
||||||
|
struct device_attribute *devattr, char *buf)
|
||||||
|
{
|
||||||
|
struct zenpower_data *data = dev_get_drvdata(dev);
|
||||||
|
unsigned int temp = get_ccd_temp(data, F17H_M70H_CCD1_TEMP);
|
||||||
|
|
||||||
|
return sprintf(buf, "%u\n", temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t temp4_input_show(struct device *dev,
|
||||||
|
struct device_attribute *devattr, char *buf)
|
||||||
|
{
|
||||||
|
struct zenpower_data *data = dev_get_drvdata(dev);
|
||||||
|
unsigned int temp = get_ccd_temp(data, F17H_M70H_CCD2_TEMP);
|
||||||
|
|
||||||
return sprintf(buf, "%u\n", temp);
|
return sprintf(buf, "%u\n", temp);
|
||||||
}
|
}
|
||||||
|
@ -216,7 +267,7 @@ static ssize_t power2_input_show(struct device *dev,
|
||||||
|
|
||||||
int static debug_addrs_arr[] = {
|
int static debug_addrs_arr[] = {
|
||||||
F17H_M01H_SVI + 0x8, F17H_M01H_SVI_TEL_PLANE0, F17H_M01H_SVI_TEL_PLANE1,
|
F17H_M01H_SVI + 0x8, F17H_M01H_SVI_TEL_PLANE0, F17H_M01H_SVI_TEL_PLANE1,
|
||||||
0x000598BC, 0x0005994C, 0x00059954, 0x00059958, 0x0005995C
|
0x000598BC, 0x0005994C, F17H_M70H_CCD1_TEMP, F17H_M70H_CCD2_TEMP, 0x0005995C
|
||||||
};
|
};
|
||||||
|
|
||||||
static ssize_t debug_data_show(struct device *dev,
|
static ssize_t debug_data_show(struct device *dev,
|
||||||
|
@ -226,6 +277,7 @@ static ssize_t debug_data_show(struct device *dev,
|
||||||
struct zenpower_data *data = dev_get_drvdata(dev);
|
struct zenpower_data *data = dev_get_drvdata(dev);
|
||||||
u32 smndata;
|
u32 smndata;
|
||||||
|
|
||||||
|
len += sprintf(buf, "kernel_smn_support = %d\n", data->kernel_smn_support);
|
||||||
for (i = 0; i < ARRAY_SIZE(debug_addrs_arr); i++){
|
for (i = 0; i < ARRAY_SIZE(debug_addrs_arr); i++){
|
||||||
data->read_amdsmn_addr(data->pdev, debug_addrs_arr[i], &smndata);
|
data->read_amdsmn_addr(data->pdev, debug_addrs_arr[i], &smndata);
|
||||||
len += sprintf(buf + len, "%08x = %08x\n", debug_addrs_arr[i], smndata);
|
len += sprintf(buf + len, "%08x = %08x\n", debug_addrs_arr[i], smndata);
|
||||||
|
@ -257,6 +309,10 @@ static ssize_t zen_label_show(struct device *dev,
|
||||||
return sprintf(buf, "Tdie\n");
|
return sprintf(buf, "Tdie\n");
|
||||||
case 32:
|
case 32:
|
||||||
return sprintf(buf, "Tctl\n");
|
return sprintf(buf, "Tctl\n");
|
||||||
|
case 33:
|
||||||
|
return sprintf(buf, "Tccd1\n");
|
||||||
|
case 34:
|
||||||
|
return sprintf(buf, "Tccd2\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -279,8 +335,13 @@ static DEVICE_ATTR_RO(temp1_max);
|
||||||
static SENSOR_DEVICE_ATTR(temp1_label, 0444, zen_label_show, NULL, 31);
|
static SENSOR_DEVICE_ATTR(temp1_label, 0444, zen_label_show, NULL, 31);
|
||||||
static DEVICE_ATTR_RO(temp2_input);
|
static DEVICE_ATTR_RO(temp2_input);
|
||||||
static SENSOR_DEVICE_ATTR(temp2_label, 0444, zen_label_show, NULL, 32);
|
static SENSOR_DEVICE_ATTR(temp2_label, 0444, zen_label_show, NULL, 32);
|
||||||
|
static DEVICE_ATTR_RO(temp3_input);
|
||||||
|
static SENSOR_DEVICE_ATTR(temp3_label, 0444, zen_label_show, NULL, 33);
|
||||||
|
static DEVICE_ATTR_RO(temp4_input);
|
||||||
|
static SENSOR_DEVICE_ATTR(temp4_label, 0444, zen_label_show, NULL, 34);
|
||||||
static DEVICE_ATTR_RO(debug_data);
|
static DEVICE_ATTR_RO(debug_data);
|
||||||
|
|
||||||
|
|
||||||
static struct attribute *zenpower_attrs[] = {
|
static struct attribute *zenpower_attrs[] = {
|
||||||
&dev_attr_in1_input.attr,
|
&dev_attr_in1_input.attr,
|
||||||
&sensor_dev_attr_in1_label.dev_attr.attr,
|
&sensor_dev_attr_in1_label.dev_attr.attr,
|
||||||
|
@ -299,6 +360,10 @@ static struct attribute *zenpower_attrs[] = {
|
||||||
&sensor_dev_attr_temp1_label.dev_attr.attr,
|
&sensor_dev_attr_temp1_label.dev_attr.attr,
|
||||||
&dev_attr_temp2_input.attr,
|
&dev_attr_temp2_input.attr,
|
||||||
&sensor_dev_attr_temp2_label.dev_attr.attr,
|
&sensor_dev_attr_temp2_label.dev_attr.attr,
|
||||||
|
&dev_attr_temp3_input.attr,
|
||||||
|
&sensor_dev_attr_temp3_label.dev_attr.attr,
|
||||||
|
&dev_attr_temp4_input.attr,
|
||||||
|
&sensor_dev_attr_temp4_label.dev_attr.attr,
|
||||||
&dev_attr_debug_data.attr,
|
&dev_attr_debug_data.attr,
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
@ -310,17 +375,28 @@ static const struct attribute_group zenpower_group = {
|
||||||
__ATTRIBUTE_GROUPS(zenpower);
|
__ATTRIBUTE_GROUPS(zenpower);
|
||||||
|
|
||||||
|
|
||||||
static void read_amdsmn_addr(struct pci_dev *pdev, u32 address, u32 *regval)
|
static void kernel_smn_read(struct pci_dev *pdev, u32 address, u32 *regval)
|
||||||
{
|
{
|
||||||
amd_smn_read(amd_pci_dev_to_node_id(pdev), address, regval);
|
amd_smn_read(amd_pci_dev_to_node_id(pdev), address, regval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fallback method from k10temp
|
||||||
|
// may return inaccurate results on multi-die chips
|
||||||
|
static void nb_index_read(struct pci_dev *pdev, u32 address, u32 *regval)
|
||||||
|
{
|
||||||
|
mutex_lock(&nb_smu_ind_mutex);
|
||||||
|
pci_bus_write_config_dword(pdev->bus, PCI_DEVFN(0, 0), 0x60, address);
|
||||||
|
pci_bus_read_config_dword(pdev->bus, PCI_DEVFN(0, 0), 0x64, regval);
|
||||||
|
mutex_unlock(&nb_smu_ind_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
static int zenpower_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
static int zenpower_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct zenpower_data *data;
|
struct zenpower_data *data;
|
||||||
struct device *hwmon_dev;
|
struct device *hwmon_dev;
|
||||||
bool swapped_addr = false;
|
bool swapped_addr = false;
|
||||||
|
u32 tmp;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||||
|
@ -329,11 +405,45 @@ static int zenpower_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||||
|
|
||||||
data->zen2 = false;
|
data->zen2 = false;
|
||||||
data->pdev = pdev;
|
data->pdev = pdev;
|
||||||
data->read_amdsmn_addr = read_amdsmn_addr;
|
data->read_amdsmn_addr = nb_index_read;
|
||||||
|
data->kernel_smn_support = false;
|
||||||
|
data->amps_visible = false;
|
||||||
|
data->ccd1_visible = false;
|
||||||
|
data->ccd2_visible = false;
|
||||||
|
|
||||||
if (boot_cpu_data.x86 == 0x17 && boot_cpu_data.x86_model == 0x71) {
|
for (id = amd_nb_misc_ids; id->vendor; id++) {
|
||||||
data->zen2 = true;
|
if (pdev->vendor == id->vendor && pdev->device == id->device) {
|
||||||
swapped_addr = true;
|
data->kernel_smn_support = true;
|
||||||
|
data->read_amdsmn_addr = kernel_smn_read;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (boot_cpu_data.x86 == 0x17) {
|
||||||
|
switch (boot_cpu_data.x86_model) {
|
||||||
|
case 0x1: // Zen
|
||||||
|
case 0x8: // Zen+
|
||||||
|
case 0x11: // Zen APU
|
||||||
|
case 0x18: // Zen+ APU
|
||||||
|
data->amps_visible = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x71: // Zen2
|
||||||
|
data->amps_visible = true;
|
||||||
|
data->zen2 = true;
|
||||||
|
swapped_addr = true;
|
||||||
|
|
||||||
|
data->read_amdsmn_addr(pdev, F17H_M70H_CCD1_TEMP, &tmp);
|
||||||
|
if ((tmp & 0xfff) > 0) {
|
||||||
|
data->ccd1_visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->read_amdsmn_addr(pdev, F17H_M70H_CCD2_TEMP, &tmp);
|
||||||
|
if ((tmp & 0xfff) > 0) {
|
||||||
|
data->ccd2_visible = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SWAP_CORE_SOC
|
#ifdef SWAP_CORE_SOC
|
||||||
|
|
Loading…
Reference in a new issue