Skip to content

Commit b00647c

Browse files
committed
hwmon: (k10temp) Show core and SoC current and voltages on Ryzen CPUs
Ryzen CPUs report core and SoC voltages and currents. Add support for it to the k10temp driver. For the time being, only report voltages and currents for Ryzen CPUs. Threadripper and EPYC appear to use a different mechanism. Tested-by: Brad Campbell <[email protected]> Tested-by: Bernhard Gebetsberger <[email protected]> Tested-by: Holger Kiehl <[email protected]> Tested-by: Michael Larabel <[email protected]> Tested-by: Jonathan McDowell <[email protected]> Tested-by: Ken Moffat <[email protected]> Tested-by: Darren Salt <[email protected]> Signed-off-by: Guenter Roeck <[email protected]>
1 parent c757938 commit b00647c

File tree

1 file changed

+131
-3
lines changed

1 file changed

+131
-3
lines changed

drivers/hwmon/k10temp.c

Lines changed: 131 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,18 @@
1111
* convert raw register values is from https://github.com/ocerman/zenpower.
1212
* The information is not confirmed from chip datasheets, but experiments
1313
* suggest that it provides reasonable temperature values.
14+
* - Register addresses to read chip voltage and current are also from
15+
* https://github.com/ocerman/zenpower, and not confirmed from chip
16+
* datasheets. Current calibration is board specific and not typically
17+
* shared by board vendors. For this reason, current values are
18+
* normalized to report 1A/LSB for core current and and 0.25A/LSB for SoC
19+
* current. Reported values can be adjusted using the sensors configuration
20+
* file.
21+
* - It is unknown if the mechanism to read CCD1/CCD2 temperature as well as
22+
* current and voltage information works on higher-end Ryzen CPUs.
23+
* Information reported by Windows tools suggests that additional sensors
24+
* (both temperature and voltage/current) are supported, but their register
25+
* location is currently unknown.
1426
*/
1527

1628
#include <linux/bitops.h>
@@ -70,9 +82,16 @@ static DEFINE_MUTEX(nb_smu_ind_mutex);
7082
#define F17H_M70H_CCD1_TEMP 0x00059954
7183
#define F17H_M70H_CCD2_TEMP 0x00059958
7284

85+
#define F17H_M01H_SVI 0x0005A000
86+
#define F17H_M01H_SVI_TEL_PLANE0 (F17H_M01H_SVI + 0xc)
87+
#define F17H_M01H_SVI_TEL_PLANE1 (F17H_M01H_SVI + 0x10)
88+
7389
#define CUR_TEMP_SHIFT 21
7490
#define CUR_TEMP_RANGE_SEL_MASK BIT(19)
7591

92+
#define CFACTOR_ICORE 1000000 /* 1A / LSB */
93+
#define CFACTOR_ISOC 250000 /* 0.25A / LSB */
94+
7695
struct k10temp_data {
7796
struct pci_dev *pdev;
7897
void (*read_htcreg)(struct pci_dev *pdev, u32 *regval);
@@ -82,6 +101,9 @@ struct k10temp_data {
82101
bool show_tdie;
83102
bool show_tccd1;
84103
bool show_tccd2;
104+
u32 svi_addr[2];
105+
bool show_current;
106+
int cfactor[2];
85107
};
86108

87109
struct tctl_offset {
@@ -99,6 +121,16 @@ static const struct tctl_offset tctl_offset_table[] = {
99121
{ 0x17, "AMD Ryzen Threadripper 29", 27000 }, /* 29{20,50,70,90}[W]X */
100122
};
101123

124+
static bool is_threadripper(void)
125+
{
126+
return strstr(boot_cpu_data.x86_model_id, "Threadripper");
127+
}
128+
129+
static bool is_epyc(void)
130+
{
131+
return strstr(boot_cpu_data.x86_model_id, "EPYC");
132+
}
133+
102134
static void read_htcreg_pci(struct pci_dev *pdev, u32 *regval)
103135
{
104136
pci_read_config_dword(pdev, REG_HARDWARE_THERMAL_CONTROL, regval);
@@ -157,16 +189,76 @@ const char *k10temp_temp_label[] = {
157189
"Tccd2",
158190
};
159191

192+
const char *k10temp_in_label[] = {
193+
"Vcore",
194+
"Vsoc",
195+
};
196+
197+
const char *k10temp_curr_label[] = {
198+
"Icore",
199+
"Isoc",
200+
};
201+
160202
static int k10temp_read_labels(struct device *dev,
161203
enum hwmon_sensor_types type,
162204
u32 attr, int channel, const char **str)
163205
{
164-
*str = k10temp_temp_label[channel];
206+
switch (type) {
207+
case hwmon_temp:
208+
*str = k10temp_temp_label[channel];
209+
break;
210+
case hwmon_in:
211+
*str = k10temp_in_label[channel];
212+
break;
213+
case hwmon_curr:
214+
*str = k10temp_curr_label[channel];
215+
break;
216+
default:
217+
return -EOPNOTSUPP;
218+
}
165219
return 0;
166220
}
167221

168-
static int k10temp_read(struct device *dev, enum hwmon_sensor_types type,
169-
u32 attr, int channel, long *val)
222+
static int k10temp_read_curr(struct device *dev, u32 attr, int channel,
223+
long *val)
224+
{
225+
struct k10temp_data *data = dev_get_drvdata(dev);
226+
u32 regval;
227+
228+
switch (attr) {
229+
case hwmon_curr_input:
230+
amd_smn_read(amd_pci_dev_to_node_id(data->pdev),
231+
data->svi_addr[channel], &regval);
232+
*val = DIV_ROUND_CLOSEST(data->cfactor[channel] *
233+
(regval & 0xff),
234+
1000);
235+
break;
236+
default:
237+
return -EOPNOTSUPP;
238+
}
239+
return 0;
240+
}
241+
242+
static int k10temp_read_in(struct device *dev, u32 attr, int channel, long *val)
243+
{
244+
struct k10temp_data *data = dev_get_drvdata(dev);
245+
u32 regval;
246+
247+
switch (attr) {
248+
case hwmon_in_input:
249+
amd_smn_read(amd_pci_dev_to_node_id(data->pdev),
250+
data->svi_addr[channel], &regval);
251+
regval = (regval >> 16) & 0xff;
252+
*val = DIV_ROUND_CLOSEST(155000 - regval * 625, 100);
253+
break;
254+
default:
255+
return -EOPNOTSUPP;
256+
}
257+
return 0;
258+
}
259+
260+
static int k10temp_read_temp(struct device *dev, u32 attr, int channel,
261+
long *val)
170262
{
171263
struct k10temp_data *data = dev_get_drvdata(dev);
172264
u32 regval;
@@ -216,6 +308,21 @@ static int k10temp_read(struct device *dev, enum hwmon_sensor_types type,
216308
return 0;
217309
}
218310

311+
static int k10temp_read(struct device *dev, enum hwmon_sensor_types type,
312+
u32 attr, int channel, long *val)
313+
{
314+
switch (type) {
315+
case hwmon_temp:
316+
return k10temp_read_temp(dev, attr, channel, val);
317+
case hwmon_in:
318+
return k10temp_read_in(dev, attr, channel, val);
319+
case hwmon_curr:
320+
return k10temp_read_curr(dev, attr, channel, val);
321+
default:
322+
return -EOPNOTSUPP;
323+
}
324+
}
325+
219326
static umode_t k10temp_is_visible(const void *_data,
220327
enum hwmon_sensor_types type,
221328
u32 attr, int channel)
@@ -290,6 +397,11 @@ static umode_t k10temp_is_visible(const void *_data,
290397
return 0;
291398
}
292399
break;
400+
case hwmon_in:
401+
case hwmon_curr:
402+
if (!data->show_current)
403+
return 0;
404+
break;
293405
default:
294406
return 0;
295407
}
@@ -338,6 +450,12 @@ static const struct hwmon_channel_info *k10temp_info[] = {
338450
HWMON_T_INPUT | HWMON_T_LABEL,
339451
HWMON_T_INPUT | HWMON_T_LABEL,
340452
HWMON_T_INPUT | HWMON_T_LABEL),
453+
HWMON_CHANNEL_INFO(in,
454+
HWMON_I_INPUT | HWMON_I_LABEL,
455+
HWMON_I_INPUT | HWMON_I_LABEL),
456+
HWMON_CHANNEL_INFO(curr,
457+
HWMON_C_INPUT | HWMON_C_LABEL,
458+
HWMON_C_INPUT | HWMON_C_LABEL),
341459
NULL
342460
};
343461

@@ -393,9 +511,19 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
393511
case 0x8: /* Zen+ */
394512
case 0x11: /* Zen APU */
395513
case 0x18: /* Zen+ APU */
514+
data->show_current = !is_threadripper() && !is_epyc();
515+
data->svi_addr[0] = F17H_M01H_SVI_TEL_PLANE0;
516+
data->svi_addr[1] = F17H_M01H_SVI_TEL_PLANE1;
517+
data->cfactor[0] = CFACTOR_ICORE;
518+
data->cfactor[1] = CFACTOR_ISOC;
396519
break;
397520
case 0x31: /* Zen2 Threadripper */
398521
case 0x71: /* Zen2 */
522+
data->show_current = !is_threadripper() && !is_epyc();
523+
data->cfactor[0] = CFACTOR_ICORE;
524+
data->cfactor[1] = CFACTOR_ISOC;
525+
data->svi_addr[0] = F17H_M01H_SVI_TEL_PLANE1;
526+
data->svi_addr[1] = F17H_M01H_SVI_TEL_PLANE0;
399527
amd_smn_read(amd_pci_dev_to_node_id(pdev),
400528
F17H_M70H_CCD1_TEMP, &regval);
401529
if (regval & 0xfff)

0 commit comments

Comments
 (0)