Commit | Line | Data |
---|---|---|
fb1b7060 BN |
1 | // SPDX-License-Identifier: MIT |
2 | /* | |
3 | * Copyright © 2023 Intel Corporation | |
4 | */ | |
5 | ||
fbcdc9d3 | 6 | #include <linux/hwmon-sysfs.h> |
fb1b7060 | 7 | #include <linux/hwmon.h> |
fbcdc9d3 | 8 | #include <linux/types.h> |
fb1b7060 BN |
9 | |
10 | #include <drm/drm_managed.h> | |
11 | #include "regs/xe_gt_regs.h" | |
12 | #include "regs/xe_mchbar_regs.h" | |
13 | #include "xe_device.h" | |
14 | #include "xe_gt.h" | |
15 | #include "xe_hwmon.h" | |
16 | #include "xe_mmio.h" | |
92d44a42 BN |
17 | #include "xe_pcode.h" |
18 | #include "xe_pcode_api.h" | |
fb1b7060 BN |
19 | |
20 | enum xe_hwmon_reg { | |
21 | REG_PKG_RAPL_LIMIT, | |
22 | REG_PKG_POWER_SKU, | |
23 | REG_PKG_POWER_SKU_UNIT, | |
fbcdc9d3 | 24 | REG_GT_PERF_STATUS, |
fb1b7060 BN |
25 | }; |
26 | ||
27 | enum xe_hwmon_reg_operation { | |
28 | REG_READ, | |
29 | REG_WRITE, | |
30 | REG_RMW, | |
31 | }; | |
32 | ||
33 | /* | |
34 | * SF_* - scale factors for particular quantities according to hwmon spec. | |
35 | */ | |
36 | #define SF_POWER 1000000 /* microwatts */ | |
92d44a42 | 37 | #define SF_CURR 1000 /* milliamperes */ |
fbcdc9d3 | 38 | #define SF_VOLTAGE 1000 /* millivolts */ |
fb1b7060 BN |
39 | |
40 | struct xe_hwmon { | |
41 | struct device *hwmon_dev; | |
42 | struct xe_gt *gt; | |
43 | struct mutex hwmon_lock; /* rmw operations*/ | |
44 | int scl_shift_power; | |
45 | }; | |
46 | ||
47 | static u32 xe_hwmon_get_reg(struct xe_hwmon *hwmon, enum xe_hwmon_reg hwmon_reg) | |
48 | { | |
49 | struct xe_device *xe = gt_to_xe(hwmon->gt); | |
50 | struct xe_reg reg = XE_REG(0); | |
51 | ||
52 | switch (hwmon_reg) { | |
53 | case REG_PKG_RAPL_LIMIT: | |
54 | if (xe->info.platform == XE_DG2) | |
55 | reg = PCU_CR_PACKAGE_RAPL_LIMIT; | |
56 | else if (xe->info.platform == XE_PVC) | |
57 | reg = PVC_GT0_PACKAGE_RAPL_LIMIT; | |
58 | break; | |
59 | case REG_PKG_POWER_SKU: | |
60 | if (xe->info.platform == XE_DG2) | |
61 | reg = PCU_CR_PACKAGE_POWER_SKU; | |
62 | else if (xe->info.platform == XE_PVC) | |
63 | reg = PVC_GT0_PACKAGE_POWER_SKU; | |
64 | break; | |
65 | case REG_PKG_POWER_SKU_UNIT: | |
66 | if (xe->info.platform == XE_DG2) | |
67 | reg = PCU_CR_PACKAGE_POWER_SKU_UNIT; | |
68 | else if (xe->info.platform == XE_PVC) | |
69 | reg = PVC_GT0_PACKAGE_POWER_SKU_UNIT; | |
70 | break; | |
fbcdc9d3 BN |
71 | case REG_GT_PERF_STATUS: |
72 | if (xe->info.platform == XE_DG2) | |
73 | reg = GT_PERF_STATUS; | |
74 | break; | |
fb1b7060 BN |
75 | default: |
76 | drm_warn(&xe->drm, "Unknown xe hwmon reg id: %d\n", hwmon_reg); | |
77 | break; | |
78 | } | |
79 | ||
80 | return reg.raw; | |
81 | } | |
82 | ||
83 | static int xe_hwmon_process_reg(struct xe_hwmon *hwmon, enum xe_hwmon_reg hwmon_reg, | |
84 | enum xe_hwmon_reg_operation operation, u32 *value, | |
85 | u32 clr, u32 set) | |
86 | { | |
87 | struct xe_reg reg; | |
88 | ||
89 | reg.raw = xe_hwmon_get_reg(hwmon, hwmon_reg); | |
90 | ||
91 | if (!reg.raw) | |
92 | return -EOPNOTSUPP; | |
93 | ||
94 | switch (operation) { | |
95 | case REG_READ: | |
96 | *value = xe_mmio_read32(hwmon->gt, reg); | |
97 | return 0; | |
98 | case REG_WRITE: | |
99 | xe_mmio_write32(hwmon->gt, reg, *value); | |
100 | return 0; | |
101 | case REG_RMW: | |
102 | *value = xe_mmio_rmw32(hwmon->gt, reg, clr, set); | |
103 | return 0; | |
104 | default: | |
105 | drm_warn(>_to_xe(hwmon->gt)->drm, "Invalid xe hwmon reg operation: %d\n", | |
106 | operation); | |
107 | return -EOPNOTSUPP; | |
108 | } | |
109 | } | |
110 | ||
111 | static int xe_hwmon_process_reg_read64(struct xe_hwmon *hwmon, | |
112 | enum xe_hwmon_reg hwmon_reg, u64 *value) | |
113 | { | |
114 | struct xe_reg reg; | |
115 | ||
116 | reg.raw = xe_hwmon_get_reg(hwmon, hwmon_reg); | |
117 | ||
118 | if (!reg.raw) | |
119 | return -EOPNOTSUPP; | |
120 | ||
121 | *value = xe_mmio_read64_2x32(hwmon->gt, reg); | |
122 | ||
123 | return 0; | |
124 | } | |
125 | ||
126 | #define PL1_DISABLE 0 | |
127 | ||
128 | /* | |
129 | * HW allows arbitrary PL1 limits to be set but silently clamps these values to | |
130 | * "typical but not guaranteed" min/max values in REG_PKG_POWER_SKU. Follow the | |
131 | * same pattern for sysfs, allow arbitrary PL1 limits to be set but display | |
132 | * clamped values when read. | |
133 | */ | |
134 | static int xe_hwmon_power_max_read(struct xe_hwmon *hwmon, long *value) | |
135 | { | |
136 | u32 reg_val; | |
137 | u64 reg_val64, min, max; | |
138 | ||
139 | xe_hwmon_process_reg(hwmon, REG_PKG_RAPL_LIMIT, REG_READ, ®_val, 0, 0); | |
140 | /* Check if PL1 limit is disabled */ | |
141 | if (!(reg_val & PKG_PWR_LIM_1_EN)) { | |
142 | *value = PL1_DISABLE; | |
143 | return 0; | |
144 | } | |
145 | ||
146 | reg_val = REG_FIELD_GET(PKG_PWR_LIM_1, reg_val); | |
147 | *value = mul_u64_u32_shr(reg_val, SF_POWER, hwmon->scl_shift_power); | |
148 | ||
149 | xe_hwmon_process_reg_read64(hwmon, REG_PKG_POWER_SKU, ®_val64); | |
150 | min = REG_FIELD_GET(PKG_MIN_PWR, reg_val64); | |
151 | min = mul_u64_u32_shr(min, SF_POWER, hwmon->scl_shift_power); | |
152 | max = REG_FIELD_GET(PKG_MAX_PWR, reg_val64); | |
153 | max = mul_u64_u32_shr(max, SF_POWER, hwmon->scl_shift_power); | |
154 | ||
155 | if (min && max) | |
156 | *value = clamp_t(u64, *value, min, max); | |
157 | ||
158 | return 0; | |
159 | } | |
160 | ||
161 | static int xe_hwmon_power_max_write(struct xe_hwmon *hwmon, long value) | |
162 | { | |
163 | u32 reg_val; | |
164 | ||
165 | /* Disable PL1 limit and verify, as limit cannot be disabled on all platforms */ | |
166 | if (value == PL1_DISABLE) { | |
167 | xe_hwmon_process_reg(hwmon, REG_PKG_RAPL_LIMIT, REG_RMW, ®_val, | |
168 | PKG_PWR_LIM_1_EN, 0); | |
169 | xe_hwmon_process_reg(hwmon, REG_PKG_RAPL_LIMIT, REG_READ, ®_val, | |
170 | PKG_PWR_LIM_1_EN, 0); | |
171 | ||
172 | if (reg_val & PKG_PWR_LIM_1_EN) | |
173 | return -EOPNOTSUPP; | |
174 | } | |
175 | ||
176 | /* Computation in 64-bits to avoid overflow. Round to nearest. */ | |
177 | reg_val = DIV_ROUND_CLOSEST_ULL((u64)value << hwmon->scl_shift_power, SF_POWER); | |
178 | reg_val = PKG_PWR_LIM_1_EN | REG_FIELD_PREP(PKG_PWR_LIM_1, reg_val); | |
179 | ||
180 | xe_hwmon_process_reg(hwmon, REG_PKG_RAPL_LIMIT, REG_RMW, ®_val, | |
181 | PKG_PWR_LIM_1_EN | PKG_PWR_LIM_1, reg_val); | |
182 | ||
183 | return 0; | |
184 | } | |
185 | ||
186 | static int xe_hwmon_power_rated_max_read(struct xe_hwmon *hwmon, long *value) | |
187 | { | |
188 | u32 reg_val; | |
189 | ||
190 | xe_hwmon_process_reg(hwmon, REG_PKG_POWER_SKU, REG_READ, ®_val, 0, 0); | |
191 | reg_val = REG_FIELD_GET(PKG_TDP, reg_val); | |
192 | *value = mul_u64_u32_shr(reg_val, SF_POWER, hwmon->scl_shift_power); | |
193 | ||
194 | return 0; | |
195 | } | |
196 | ||
197 | static const struct hwmon_channel_info *hwmon_info[] = { | |
92d44a42 BN |
198 | HWMON_CHANNEL_INFO(power, HWMON_P_MAX | HWMON_P_RATED_MAX | HWMON_P_CRIT), |
199 | HWMON_CHANNEL_INFO(curr, HWMON_C_CRIT), | |
fbcdc9d3 | 200 | HWMON_CHANNEL_INFO(in, HWMON_I_INPUT), |
fb1b7060 BN |
201 | NULL |
202 | }; | |
203 | ||
92d44a42 BN |
204 | /* I1 is exposed as power_crit or as curr_crit depending on bit 31 */ |
205 | static int xe_hwmon_pcode_read_i1(struct xe_gt *gt, u32 *uval) | |
206 | { | |
207 | /* Avoid Illegal Subcommand error */ | |
208 | if (gt_to_xe(gt)->info.platform == XE_DG2) | |
209 | return -ENXIO; | |
210 | ||
211 | return xe_pcode_read(gt, PCODE_MBOX(PCODE_POWER_SETUP, | |
212 | POWER_SETUP_SUBCOMMAND_READ_I1, 0), | |
213 | uval, 0); | |
214 | } | |
215 | ||
216 | static int xe_hwmon_pcode_write_i1(struct xe_gt *gt, u32 uval) | |
217 | { | |
218 | return xe_pcode_write(gt, PCODE_MBOX(PCODE_POWER_SETUP, | |
219 | POWER_SETUP_SUBCOMMAND_WRITE_I1, 0), | |
220 | uval); | |
221 | } | |
222 | ||
fbcdc9d3 BN |
223 | static int xe_hwmon_get_voltage(struct xe_hwmon *hwmon, long *value) |
224 | { | |
225 | u32 reg_val; | |
226 | ||
227 | xe_hwmon_process_reg(hwmon, REG_GT_PERF_STATUS, | |
228 | REG_READ, ®_val, 0, 0); | |
229 | /* HW register value in units of 2.5 millivolt */ | |
230 | *value = DIV_ROUND_CLOSEST(REG_FIELD_GET(VOLTAGE_MASK, reg_val) * 2500, SF_VOLTAGE); | |
231 | ||
232 | return 0; | |
233 | } | |
234 | ||
fb1b7060 BN |
235 | static umode_t |
236 | xe_hwmon_power_is_visible(struct xe_hwmon *hwmon, u32 attr, int chan) | |
237 | { | |
92d44a42 BN |
238 | u32 uval; |
239 | ||
fb1b7060 BN |
240 | switch (attr) { |
241 | case hwmon_power_max: | |
242 | return xe_hwmon_get_reg(hwmon, REG_PKG_RAPL_LIMIT) ? 0664 : 0; | |
243 | case hwmon_power_rated_max: | |
244 | return xe_hwmon_get_reg(hwmon, REG_PKG_POWER_SKU) ? 0444 : 0; | |
92d44a42 BN |
245 | case hwmon_power_crit: |
246 | return (xe_hwmon_pcode_read_i1(hwmon->gt, &uval) || | |
247 | !(uval & POWER_SETUP_I1_WATTS)) ? 0 : 0644; | |
fb1b7060 BN |
248 | default: |
249 | return 0; | |
250 | } | |
251 | } | |
252 | ||
253 | static int | |
254 | xe_hwmon_power_read(struct xe_hwmon *hwmon, u32 attr, int chan, long *val) | |
255 | { | |
92d44a42 BN |
256 | int ret; |
257 | u32 uval; | |
258 | ||
fb1b7060 BN |
259 | switch (attr) { |
260 | case hwmon_power_max: | |
261 | return xe_hwmon_power_max_read(hwmon, val); | |
262 | case hwmon_power_rated_max: | |
263 | return xe_hwmon_power_rated_max_read(hwmon, val); | |
92d44a42 BN |
264 | case hwmon_power_crit: |
265 | ret = xe_hwmon_pcode_read_i1(hwmon->gt, &uval); | |
266 | if (ret) | |
267 | return ret; | |
268 | if (!(uval & POWER_SETUP_I1_WATTS)) | |
269 | return -ENODEV; | |
270 | *val = mul_u64_u32_shr(REG_FIELD_GET(POWER_SETUP_I1_DATA_MASK, uval), | |
271 | SF_POWER, POWER_SETUP_I1_SHIFT); | |
272 | return 0; | |
fb1b7060 BN |
273 | default: |
274 | return -EOPNOTSUPP; | |
275 | } | |
276 | } | |
277 | ||
278 | static int | |
279 | xe_hwmon_power_write(struct xe_hwmon *hwmon, u32 attr, int chan, long val) | |
280 | { | |
92d44a42 BN |
281 | u32 uval; |
282 | ||
fb1b7060 BN |
283 | switch (attr) { |
284 | case hwmon_power_max: | |
285 | return xe_hwmon_power_max_write(hwmon, val); | |
92d44a42 BN |
286 | case hwmon_power_crit: |
287 | uval = DIV_ROUND_CLOSEST_ULL(val << POWER_SETUP_I1_SHIFT, SF_POWER); | |
288 | return xe_hwmon_pcode_write_i1(hwmon->gt, uval); | |
289 | default: | |
290 | return -EOPNOTSUPP; | |
291 | } | |
292 | } | |
293 | ||
294 | static umode_t | |
295 | xe_hwmon_curr_is_visible(const struct xe_hwmon *hwmon, u32 attr) | |
296 | { | |
297 | u32 uval; | |
298 | ||
299 | switch (attr) { | |
300 | case hwmon_curr_crit: | |
301 | return (xe_hwmon_pcode_read_i1(hwmon->gt, &uval) || | |
302 | (uval & POWER_SETUP_I1_WATTS)) ? 0 : 0644; | |
303 | default: | |
304 | return 0; | |
305 | } | |
306 | } | |
307 | ||
308 | static int | |
309 | xe_hwmon_curr_read(struct xe_hwmon *hwmon, u32 attr, long *val) | |
310 | { | |
311 | int ret; | |
312 | u32 uval; | |
313 | ||
314 | switch (attr) { | |
315 | case hwmon_curr_crit: | |
316 | ret = xe_hwmon_pcode_read_i1(hwmon->gt, &uval); | |
317 | if (ret) | |
318 | return ret; | |
319 | if (uval & POWER_SETUP_I1_WATTS) | |
320 | return -ENODEV; | |
321 | *val = mul_u64_u32_shr(REG_FIELD_GET(POWER_SETUP_I1_DATA_MASK, uval), | |
322 | SF_CURR, POWER_SETUP_I1_SHIFT); | |
323 | return 0; | |
324 | default: | |
325 | return -EOPNOTSUPP; | |
326 | } | |
327 | } | |
328 | ||
329 | static int | |
330 | xe_hwmon_curr_write(struct xe_hwmon *hwmon, u32 attr, long val) | |
331 | { | |
332 | u32 uval; | |
333 | ||
334 | switch (attr) { | |
335 | case hwmon_curr_crit: | |
336 | uval = DIV_ROUND_CLOSEST_ULL(val << POWER_SETUP_I1_SHIFT, SF_CURR); | |
337 | return xe_hwmon_pcode_write_i1(hwmon->gt, uval); | |
fb1b7060 BN |
338 | default: |
339 | return -EOPNOTSUPP; | |
340 | } | |
341 | } | |
342 | ||
fbcdc9d3 BN |
343 | static umode_t |
344 | xe_hwmon_in_is_visible(struct xe_hwmon *hwmon, u32 attr) | |
345 | { | |
346 | switch (attr) { | |
347 | case hwmon_in_input: | |
348 | return xe_hwmon_get_reg(hwmon, REG_GT_PERF_STATUS) ? 0444 : 0; | |
349 | default: | |
350 | return 0; | |
351 | } | |
352 | } | |
353 | ||
354 | static int | |
355 | xe_hwmon_in_read(struct xe_hwmon *hwmon, u32 attr, long *val) | |
356 | { | |
357 | int ret; | |
358 | ||
359 | xe_device_mem_access_get(gt_to_xe(hwmon->gt)); | |
360 | ||
361 | switch (attr) { | |
362 | case hwmon_in_input: | |
363 | ret = xe_hwmon_get_voltage(hwmon, val); | |
364 | break; | |
365 | default: | |
366 | ret = -EOPNOTSUPP; | |
367 | } | |
368 | ||
369 | xe_device_mem_access_put(gt_to_xe(hwmon->gt)); | |
370 | ||
371 | return ret; | |
372 | } | |
373 | ||
fb1b7060 BN |
374 | static umode_t |
375 | xe_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type, | |
376 | u32 attr, int channel) | |
377 | { | |
378 | struct xe_hwmon *hwmon = (struct xe_hwmon *)drvdata; | |
379 | int ret; | |
380 | ||
381 | xe_device_mem_access_get(gt_to_xe(hwmon->gt)); | |
382 | ||
383 | switch (type) { | |
384 | case hwmon_power: | |
385 | ret = xe_hwmon_power_is_visible(hwmon, attr, channel); | |
386 | break; | |
92d44a42 BN |
387 | case hwmon_curr: |
388 | ret = xe_hwmon_curr_is_visible(hwmon, attr); | |
389 | break; | |
fbcdc9d3 BN |
390 | case hwmon_in: |
391 | ret = xe_hwmon_in_is_visible(hwmon, attr); | |
392 | break; | |
fb1b7060 BN |
393 | default: |
394 | ret = 0; | |
395 | break; | |
396 | } | |
397 | ||
398 | xe_device_mem_access_put(gt_to_xe(hwmon->gt)); | |
399 | ||
400 | return ret; | |
401 | } | |
402 | ||
403 | static int | |
404 | xe_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, | |
405 | int channel, long *val) | |
406 | { | |
407 | struct xe_hwmon *hwmon = dev_get_drvdata(dev); | |
408 | int ret; | |
409 | ||
410 | xe_device_mem_access_get(gt_to_xe(hwmon->gt)); | |
411 | ||
412 | switch (type) { | |
413 | case hwmon_power: | |
414 | ret = xe_hwmon_power_read(hwmon, attr, channel, val); | |
415 | break; | |
92d44a42 BN |
416 | case hwmon_curr: |
417 | ret = xe_hwmon_curr_read(hwmon, attr, val); | |
418 | break; | |
fbcdc9d3 BN |
419 | case hwmon_in: |
420 | ret = xe_hwmon_in_read(hwmon, attr, val); | |
421 | break; | |
fb1b7060 BN |
422 | default: |
423 | ret = -EOPNOTSUPP; | |
424 | break; | |
425 | } | |
426 | ||
427 | xe_device_mem_access_put(gt_to_xe(hwmon->gt)); | |
428 | ||
429 | return ret; | |
430 | } | |
431 | ||
432 | static int | |
433 | xe_hwmon_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, | |
434 | int channel, long val) | |
435 | { | |
436 | struct xe_hwmon *hwmon = dev_get_drvdata(dev); | |
437 | int ret; | |
438 | ||
439 | xe_device_mem_access_get(gt_to_xe(hwmon->gt)); | |
440 | ||
441 | switch (type) { | |
442 | case hwmon_power: | |
443 | ret = xe_hwmon_power_write(hwmon, attr, channel, val); | |
444 | break; | |
92d44a42 BN |
445 | case hwmon_curr: |
446 | ret = xe_hwmon_curr_write(hwmon, attr, val); | |
447 | break; | |
fb1b7060 BN |
448 | default: |
449 | ret = -EOPNOTSUPP; | |
450 | break; | |
451 | } | |
452 | ||
453 | xe_device_mem_access_put(gt_to_xe(hwmon->gt)); | |
454 | ||
455 | return ret; | |
456 | } | |
457 | ||
458 | static const struct hwmon_ops hwmon_ops = { | |
459 | .is_visible = xe_hwmon_is_visible, | |
460 | .read = xe_hwmon_read, | |
461 | .write = xe_hwmon_write, | |
462 | }; | |
463 | ||
464 | static const struct hwmon_chip_info hwmon_chip_info = { | |
465 | .ops = &hwmon_ops, | |
466 | .info = hwmon_info, | |
467 | }; | |
468 | ||
469 | static void | |
470 | xe_hwmon_get_preregistration_info(struct xe_device *xe) | |
471 | { | |
472 | struct xe_hwmon *hwmon = xe->hwmon; | |
473 | u32 val_sku_unit = 0; | |
474 | int ret; | |
475 | ||
476 | ret = xe_hwmon_process_reg(hwmon, REG_PKG_POWER_SKU_UNIT, REG_READ, &val_sku_unit, 0, 0); | |
477 | /* | |
478 | * The contents of register PKG_POWER_SKU_UNIT do not change, | |
479 | * so read it once and store the shift values. | |
480 | */ | |
481 | if (!ret) | |
482 | hwmon->scl_shift_power = REG_FIELD_GET(PKG_PWR_UNIT, val_sku_unit); | |
483 | } | |
484 | ||
485 | void xe_hwmon_register(struct xe_device *xe) | |
486 | { | |
487 | struct device *dev = xe->drm.dev; | |
488 | struct xe_hwmon *hwmon; | |
489 | ||
490 | /* hwmon is available only for dGfx */ | |
491 | if (!IS_DGFX(xe)) | |
492 | return; | |
493 | ||
494 | hwmon = devm_kzalloc(dev, sizeof(*hwmon), GFP_KERNEL); | |
495 | if (!hwmon) | |
496 | return; | |
497 | ||
498 | xe->hwmon = hwmon; | |
499 | ||
500 | drmm_mutex_init(&xe->drm, &hwmon->hwmon_lock); | |
501 | ||
502 | /* primary GT to access device level properties */ | |
503 | hwmon->gt = xe->tiles[0].primary_gt; | |
504 | ||
505 | xe_hwmon_get_preregistration_info(xe); | |
506 | ||
507 | drm_dbg(&xe->drm, "Register xe hwmon interface\n"); | |
508 | ||
509 | /* hwmon_dev points to device hwmon<i> */ | |
510 | hwmon->hwmon_dev = devm_hwmon_device_register_with_info(dev, "xe", hwmon, | |
511 | &hwmon_chip_info, | |
512 | NULL); | |
513 | if (IS_ERR(hwmon->hwmon_dev)) { | |
514 | drm_warn(&xe->drm, "Failed to register xe hwmon (%pe)\n", hwmon->hwmon_dev); | |
515 | xe->hwmon = NULL; | |
516 | return; | |
517 | } | |
518 | } | |
519 |