* ✓ CI.KUnit: success for Add fan control support (rev2)
2026-07-17 4:17 [PATCH v2 0/9] Add fan control support Karthik Poosa
@ 2026-07-17 4:16 ` Patchwork
2026-07-17 4:17 ` [PATCH v2 1/9] drm/xe/pcode: Introduce xe_pcode_read_timeout() API Karthik Poosa
` (10 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Patchwork @ 2026-07-17 4:16 UTC (permalink / raw)
To: Poosa, Karthik; +Cc: intel-xe
== Series Details ==
Series: Add fan control support (rev2)
URL : https://patchwork.freedesktop.org/series/168027/
State : success
== Summary ==
+ trap cleanup EXIT
+ /kernel/tools/testing/kunit/kunit.py run --kunitconfig /kernel/drivers/gpu/drm/xe/.kunitconfig
[04:15:19] Configuring KUnit Kernel ...
Generating .config ...
Populating config with:
$ make ARCH=um O=.kunit olddefconfig
[04:15:23] Building KUnit Kernel ...
Populating config with:
$ make ARCH=um O=.kunit olddefconfig
Building with:
$ make all compile_commands.json scripts_gdb ARCH=um O=.kunit --jobs=48
[04:15:55] Starting KUnit Kernel (1/1)...
[04:15:55] ============================================================
Running tests with:
$ .kunit/linux kunit.enable=1 mem=1G console=tty kunit_shutdown=halt
[04:15:55] ================== guc_buf (11 subtests) ===================
[04:15:55] [PASSED] test_smallest
[04:15:55] [PASSED] test_largest
[04:15:55] [PASSED] test_granular
[04:15:55] [PASSED] test_unique
[04:15:55] [PASSED] test_overlap
[04:15:55] [PASSED] test_reusable
[04:15:55] [PASSED] test_too_big
[04:15:55] [PASSED] test_flush
[04:15:55] [PASSED] test_lookup
[04:15:55] [PASSED] test_data
[04:15:55] [PASSED] test_class
[04:15:55] ===================== [PASSED] guc_buf =====================
[04:15:55] =================== guc_dbm (7 subtests) ===================
[04:15:55] [PASSED] test_empty
[04:15:55] [PASSED] test_default
[04:15:55] ======================== test_size ========================
[04:15:55] [PASSED] 4
[04:15:55] [PASSED] 8
[04:15:55] [PASSED] 32
[04:15:55] [PASSED] 256
[04:15:55] ==================== [PASSED] test_size ====================
[04:15:55] ======================= test_reuse ========================
[04:15:55] [PASSED] 4
[04:15:55] [PASSED] 8
[04:15:55] [PASSED] 32
[04:15:55] [PASSED] 256
[04:15:55] =================== [PASSED] test_reuse ====================
[04:15:55] =================== test_range_overlap ====================
[04:15:55] [PASSED] 4
[04:15:55] [PASSED] 8
[04:15:55] [PASSED] 32
[04:15:55] [PASSED] 256
[04:15:55] =============== [PASSED] test_range_overlap ================
[04:15:55] =================== test_range_compact ====================
[04:15:55] [PASSED] 4
[04:15:55] [PASSED] 8
[04:15:55] [PASSED] 32
[04:15:55] [PASSED] 256
[04:15:55] =============== [PASSED] test_range_compact ================
[04:15:55] ==================== test_range_spare =====================
[04:15:55] [PASSED] 4
[04:15:55] [PASSED] 8
[04:15:55] [PASSED] 32
[04:15:55] [PASSED] 256
[04:15:55] ================ [PASSED] test_range_spare =================
[04:15:55] ===================== [PASSED] guc_dbm =====================
[04:15:55] =================== guc_idm (6 subtests) ===================
[04:15:55] [PASSED] bad_init
[04:15:55] [PASSED] no_init
[04:15:55] [PASSED] init_fini
[04:15:55] [PASSED] check_used
[04:15:55] [PASSED] check_quota
[04:15:55] [PASSED] check_all
[04:15:55] ===================== [PASSED] guc_idm =====================
[04:15:55] =============== guc_klv_helpers (9 subtests) ===============
[04:15:55] [PASSED] test_count
[04:15:55] [PASSED] test_encode_u32
[04:15:55] [PASSED] test_encode_u64
[04:15:55] [PASSED] test_encode_string
[04:15:55] [PASSED] test_encode_object_raw
[04:15:55] [PASSED] test_encode_object_klv
[04:15:55] [PASSED] test_encode_object_nested
[04:15:55] [PASSED] test_encode_object_basic
[04:15:55] [PASSED] test_print
[04:15:55] ================= [PASSED] guc_klv_helpers =================
[04:15:55] ================== no_relay (3 subtests) ===================
[04:15:55] [PASSED] xe_drops_guc2pf_if_not_ready
[04:15:55] [PASSED] xe_drops_guc2vf_if_not_ready
[04:15:55] [PASSED] xe_rejects_send_if_not_ready
[04:15:55] ==================== [PASSED] no_relay =====================
[04:15:55] ================== pf_relay (14 subtests) ==================
[04:15:55] [PASSED] pf_rejects_guc2pf_too_short
[04:15:55] [PASSED] pf_rejects_guc2pf_too_long
[04:15:55] [PASSED] pf_rejects_guc2pf_no_payload
[04:15:55] [PASSED] pf_fails_no_payload
[04:15:55] [PASSED] pf_fails_bad_origin
[04:15:55] [PASSED] pf_fails_bad_type
[04:15:55] [PASSED] pf_txn_reports_error
[04:15:55] [PASSED] pf_txn_sends_pf2guc
[04:15:55] [PASSED] pf_sends_pf2guc
[04:15:55] [SKIPPED] pf_loopback_nop (requires CONFIG_DRM_XE_DEBUG_SRIOV)
[04:15:55] [SKIPPED] pf_loopback_echo (requires CONFIG_DRM_XE_DEBUG_SRIOV)
[04:15:55] [SKIPPED] pf_loopback_fail (requires CONFIG_DRM_XE_DEBUG_SRIOV)
[04:15:55] [SKIPPED] pf_loopback_busy (requires CONFIG_DRM_XE_DEBUG_SRIOV)
[04:15:55] [SKIPPED] pf_loopback_retry (requires CONFIG_DRM_XE_DEBUG_SRIOV)
[04:15:55] ==================== [PASSED] pf_relay =====================
[04:15:55] ================== vf_relay (3 subtests) ===================
[04:15:55] [PASSED] vf_rejects_guc2vf_too_short
[04:15:55] [PASSED] vf_rejects_guc2vf_too_long
[04:15:55] [PASSED] vf_rejects_guc2vf_no_payload
[04:15:55] ==================== [PASSED] vf_relay =====================
[04:15:55] ================ pf_gt_config (9 subtests) =================
[04:15:55] [PASSED] fair_contexts_1vf
[04:15:55] [PASSED] fair_doorbells_1vf
[04:15:55] [PASSED] fair_ggtt_1vf
[04:15:55] ====================== fair_vram_1vf ======================
[04:15:55] [PASSED] 3.50 GiB
[04:15:55] [PASSED] 11.5 GiB
[04:15:55] [PASSED] 15.5 GiB
[04:15:55] [PASSED] 31.5 GiB
[04:15:55] [PASSED] 63.5 GiB
[04:15:55] [PASSED] 1.91 GiB
[04:15:55] ================== [PASSED] fair_vram_1vf ==================
[04:15:55] ================ fair_vram_1vf_admin_only =================
[04:15:55] [PASSED] 3.50 GiB
[04:15:55] [PASSED] 11.5 GiB
[04:15:55] [PASSED] 15.5 GiB
[04:15:55] [PASSED] 31.5 GiB
[04:15:55] [PASSED] 63.5 GiB
[04:15:55] [PASSED] 1.91 GiB
[04:15:55] ============ [PASSED] fair_vram_1vf_admin_only =============
[04:15:55] ====================== fair_contexts ======================
[04:15:55] [PASSED] 1 VF
[04:15:55] [PASSED] 2 VFs
[04:15:55] [PASSED] 3 VFs
[04:15:55] [PASSED] 4 VFs
[04:15:55] [PASSED] 5 VFs
[04:15:55] [PASSED] 6 VFs
[04:15:55] [PASSED] 7 VFs
[04:15:55] [PASSED] 8 VFs
[04:15:55] [PASSED] 9 VFs
[04:15:55] [PASSED] 10 VFs
[04:15:55] [PASSED] 11 VFs
[04:15:55] [PASSED] 12 VFs
[04:15:55] [PASSED] 13 VFs
[04:15:55] [PASSED] 14 VFs
[04:15:55] [PASSED] 15 VFs
[04:15:55] [PASSED] 16 VFs
[04:15:55] [PASSED] 17 VFs
[04:15:55] [PASSED] 18 VFs
[04:15:55] [PASSED] 19 VFs
[04:15:55] [PASSED] 20 VFs
[04:15:55] [PASSED] 21 VFs
[04:15:55] [PASSED] 22 VFs
[04:15:55] [PASSED] 23 VFs
[04:15:55] [PASSED] 24 VFs
[04:15:55] [PASSED] 25 VFs
[04:15:55] [PASSED] 26 VFs
[04:15:55] [PASSED] 27 VFs
[04:15:55] [PASSED] 28 VFs
[04:15:55] [PASSED] 29 VFs
[04:15:55] [PASSED] 30 VFs
[04:15:55] [PASSED] 31 VFs
[04:15:55] [PASSED] 32 VFs
[04:15:55] [PASSED] 33 VFs
[04:15:55] [PASSED] 34 VFs
[04:15:55] [PASSED] 35 VFs
[04:15:55] [PASSED] 36 VFs
[04:15:55] [PASSED] 37 VFs
[04:15:55] [PASSED] 38 VFs
[04:15:55] [PASSED] 39 VFs
[04:15:55] [PASSED] 40 VFs
[04:15:55] [PASSED] 41 VFs
[04:15:55] [PASSED] 42 VFs
[04:15:55] [PASSED] 43 VFs
[04:15:55] [PASSED] 44 VFs
[04:15:55] [PASSED] 45 VFs
[04:15:55] [PASSED] 46 VFs
[04:15:55] [PASSED] 47 VFs
[04:15:55] [PASSED] 48 VFs
[04:15:55] [PASSED] 49 VFs
[04:15:55] [PASSED] 50 VFs
[04:15:55] [PASSED] 51 VFs
[04:15:55] [PASSED] 52 VFs
[04:15:55] [PASSED] 53 VFs
[04:15:55] [PASSED] 54 VFs
[04:15:55] [PASSED] 55 VFs
[04:15:55] [PASSED] 56 VFs
[04:15:55] [PASSED] 57 VFs
[04:15:55] [PASSED] 58 VFs
[04:15:55] [PASSED] 59 VFs
[04:15:55] [PASSED] 60 VFs
[04:15:55] [PASSED] 61 VFs
[04:15:55] [PASSED] 62 VFs
[04:15:55] [PASSED] 63 VFs
[04:15:55] ================== [PASSED] fair_contexts ==================
[04:15:55] ===================== fair_doorbells ======================
[04:15:55] [PASSED] 1 VF
[04:15:55] [PASSED] 2 VFs
[04:15:55] [PASSED] 3 VFs
[04:15:55] [PASSED] 4 VFs
[04:15:55] [PASSED] 5 VFs
[04:15:55] [PASSED] 6 VFs
[04:15:55] [PASSED] 7 VFs
[04:15:55] [PASSED] 8 VFs
[04:15:55] [PASSED] 9 VFs
[04:15:55] [PASSED] 10 VFs
[04:15:55] [PASSED] 11 VFs
[04:15:55] [PASSED] 12 VFs
[04:15:55] [PASSED] 13 VFs
[04:15:55] [PASSED] 14 VFs
[04:15:55] [PASSED] 15 VFs
[04:15:55] [PASSED] 16 VFs
[04:15:55] [PASSED] 17 VFs
[04:15:55] [PASSED] 18 VFs
[04:15:55] [PASSED] 19 VFs
[04:15:55] [PASSED] 20 VFs
[04:15:55] [PASSED] 21 VFs
[04:15:55] [PASSED] 22 VFs
[04:15:55] [PASSED] 23 VFs
[04:15:55] [PASSED] 24 VFs
[04:15:55] [PASSED] 25 VFs
[04:15:55] [PASSED] 26 VFs
[04:15:55] [PASSED] 27 VFs
[04:15:55] [PASSED] 28 VFs
[04:15:55] [PASSED] 29 VFs
[04:15:55] [PASSED] 30 VFs
[04:15:55] [PASSED] 31 VFs
[04:15:55] [PASSED] 32 VFs
[04:15:55] [PASSED] 33 VFs
[04:15:55] [PASSED] 34 VFs
[04:15:55] [PASSED] 35 VFs
[04:15:55] [PASSED] 36 VFs
[04:15:55] [PASSED] 37 VFs
[04:15:55] [PASSED] 38 VFs
[04:15:55] [PASSED] 39 VFs
[04:15:55] [PASSED] 40 VFs
[04:15:55] [PASSED] 41 VFs
[04:15:55] [PASSED] 42 VFs
[04:15:55] [PASSED] 43 VFs
[04:15:55] [PASSED] 44 VFs
[04:15:55] [PASSED] 45 VFs
[04:15:55] [PASSED] 46 VFs
[04:15:55] [PASSED] 47 VFs
[04:15:55] [PASSED] 48 VFs
[04:15:55] [PASSED] 49 VFs
[04:15:55] [PASSED] 50 VFs
[04:15:55] [PASSED] 51 VFs
[04:15:55] [PASSED] 52 VFs
[04:15:55] [PASSED] 53 VFs
[04:15:55] [PASSED] 54 VFs
[04:15:55] [PASSED] 55 VFs
[04:15:55] [PASSED] 56 VFs
[04:15:55] [PASSED] 57 VFs
[04:15:55] [PASSED] 58 VFs
[04:15:55] [PASSED] 59 VFs
[04:15:55] [PASSED] 60 VFs
[04:15:55] [PASSED] 61 VFs
[04:15:55] [PASSED] 62 VFs
[04:15:55] [PASSED] 63 VFs
[04:15:55] ================= [PASSED] fair_doorbells ==================
[04:15:55] ======================== fair_ggtt ========================
[04:15:55] [PASSED] 1 VF
[04:15:55] [PASSED] 2 VFs
[04:15:55] [PASSED] 3 VFs
[04:15:55] [PASSED] 4 VFs
[04:15:55] [PASSED] 5 VFs
[04:15:55] [PASSED] 6 VFs
[04:15:55] [PASSED] 7 VFs
[04:15:55] [PASSED] 8 VFs
[04:15:55] [PASSED] 9 VFs
[04:15:55] [PASSED] 10 VFs
[04:15:55] [PASSED] 11 VFs
[04:15:55] [PASSED] 12 VFs
[04:15:55] [PASSED] 13 VFs
[04:15:55] [PASSED] 14 VFs
[04:15:55] [PASSED] 15 VFs
[04:15:55] [PASSED] 16 VFs
[04:15:55] [PASSED] 17 VFs
[04:15:55] [PASSED] 18 VFs
[04:15:55] [PASSED] 19 VFs
[04:15:55] [PASSED] 20 VFs
[04:15:55] [PASSED] 21 VFs
[04:15:55] [PASSED] 22 VFs
[04:15:55] [PASSED] 23 VFs
[04:15:55] [PASSED] 24 VFs
[04:15:55] [PASSED] 25 VFs
[04:15:55] [PASSED] 26 VFs
[04:15:55] [PASSED] 27 VFs
[04:15:55] [PASSED] 28 VFs
[04:15:55] [PASSED] 29 VFs
[04:15:55] [PASSED] 30 VFs
[04:15:55] [PASSED] 31 VFs
[04:15:55] [PASSED] 32 VFs
[04:15:55] [PASSED] 33 VFs
[04:15:55] [PASSED] 34 VFs
[04:15:55] [PASSED] 35 VFs
[04:15:55] [PASSED] 36 VFs
[04:15:55] [PASSED] 37 VFs
[04:15:55] [PASSED] 38 VFs
[04:15:55] [PASSED] 39 VFs
[04:15:55] [PASSED] 40 VFs
[04:15:55] [PASSED] 41 VFs
[04:15:55] [PASSED] 42 VFs
[04:15:55] [PASSED] 43 VFs
[04:15:55] [PASSED] 44 VFs
[04:15:55] [PASSED] 45 VFs
[04:15:55] [PASSED] 46 VFs
[04:15:55] [PASSED] 47 VFs
[04:15:55] [PASSED] 48 VFs
[04:15:55] [PASSED] 49 VFs
[04:15:55] [PASSED] 50 VFs
[04:15:55] [PASSED] 51 VFs
[04:15:55] [PASSED] 52 VFs
[04:15:55] [PASSED] 53 VFs
[04:15:55] [PASSED] 54 VFs
[04:15:55] [PASSED] 55 VFs
[04:15:55] [PASSED] 56 VFs
[04:15:55] [PASSED] 57 VFs
[04:15:55] [PASSED] 58 VFs
[04:15:55] [PASSED] 59 VFs
[04:15:55] [PASSED] 60 VFs
[04:15:55] [PASSED] 61 VFs
[04:15:55] [PASSED] 62 VFs
[04:15:55] [PASSED] 63 VFs
[04:15:55] ==================== [PASSED] fair_ggtt ====================
[04:15:55] ======================== fair_vram ========================
[04:15:55] [PASSED] 1 VF
[04:15:55] [PASSED] 2 VFs
[04:15:55] [PASSED] 3 VFs
[04:15:55] [PASSED] 4 VFs
[04:15:55] [PASSED] 5 VFs
[04:15:55] [PASSED] 6 VFs
[04:15:55] [PASSED] 7 VFs
[04:15:55] [PASSED] 8 VFs
[04:15:55] [PASSED] 9 VFs
[04:15:55] [PASSED] 10 VFs
[04:15:55] [PASSED] 11 VFs
[04:15:55] [PASSED] 12 VFs
[04:15:55] [PASSED] 13 VFs
[04:15:55] [PASSED] 14 VFs
[04:15:55] [PASSED] 15 VFs
[04:15:55] [PASSED] 16 VFs
[04:15:55] [PASSED] 17 VFs
[04:15:55] [PASSED] 18 VFs
[04:15:55] [PASSED] 19 VFs
[04:15:55] [PASSED] 20 VFs
[04:15:55] [PASSED] 21 VFs
[04:15:55] [PASSED] 22 VFs
[04:15:55] [PASSED] 23 VFs
[04:15:55] [PASSED] 24 VFs
[04:15:55] [PASSED] 25 VFs
[04:15:55] [PASSED] 26 VFs
[04:15:55] [PASSED] 27 VFs
[04:15:55] [PASSED] 28 VFs
[04:15:55] [PASSED] 29 VFs
[04:15:55] [PASSED] 30 VFs
[04:15:55] [PASSED] 31 VFs
[04:15:55] [PASSED] 32 VFs
[04:15:55] [PASSED] 33 VFs
[04:15:55] [PASSED] 34 VFs
[04:15:55] [PASSED] 35 VFs
[04:15:55] [PASSED] 36 VFs
[04:15:55] [PASSED] 37 VFs
[04:15:55] [PASSED] 38 VFs
[04:15:55] [PASSED] 39 VFs
[04:15:55] [PASSED] 40 VFs
[04:15:55] [PASSED] 41 VFs
[04:15:55] [PASSED] 42 VFs
[04:15:55] [PASSED] 43 VFs
[04:15:55] [PASSED] 44 VFs
[04:15:55] [PASSED] 45 VFs
[04:15:55] [PASSED] 46 VFs
[04:15:55] [PASSED] 47 VFs
[04:15:55] [PASSED] 48 VFs
[04:15:55] [PASSED] 49 VFs
[04:15:55] [PASSED] 50 VFs
[04:15:55] [PASSED] 51 VFs
[04:15:55] [PASSED] 52 VFs
[04:15:55] [PASSED] 53 VFs
[04:15:55] [PASSED] 54 VFs
[04:15:55] [PASSED] 55 VFs
[04:15:55] [PASSED] 56 VFs
[04:15:55] [PASSED] 57 VFs
[04:15:55] [PASSED] 58 VFs
[04:15:55] [PASSED] 59 VFs
[04:15:55] [PASSED] 60 VFs
[04:15:55] [PASSED] 61 VFs
[04:15:55] [PASSED] 62 VFs
[04:15:55] [PASSED] 63 VFs
[04:15:55] ==================== [PASSED] fair_vram ====================
[04:15:55] ================== [PASSED] pf_gt_config ===================
[04:15:55] ===================== lmtt (1 subtest) =====================
[04:15:55] ======================== test_ops =========================
[04:15:55] [PASSED] 2-level
[04:15:55] [PASSED] multi-level
[04:15:55] ==================== [PASSED] test_ops =====================
[04:15:55] ====================== [PASSED] lmtt =======================
[04:15:55] ================= sriov_packet (1 subtest) =================
[04:15:55] [PASSED] test_descriptor_init
[04:15:55] ================== [PASSED] sriov_packet ===================
[04:15:55] ================= pf_service (11 subtests) =================
[04:15:55] [PASSED] pf_negotiate_any
[04:15:55] [PASSED] pf_negotiate_base_match
[04:15:55] [PASSED] pf_negotiate_base_newer
[04:15:55] [PASSED] pf_negotiate_base_next
[04:15:55] [SKIPPED] pf_negotiate_base_older (no older minor)
[04:15:55] [PASSED] pf_negotiate_base_prev
[04:15:55] [PASSED] pf_negotiate_latest_match
[04:15:55] [PASSED] pf_negotiate_latest_newer
[04:15:55] [PASSED] pf_negotiate_latest_next
[04:15:55] [SKIPPED] pf_negotiate_latest_older (no older minor)
[04:15:55] [SKIPPED] pf_negotiate_latest_prev (no prev major)
[04:15:55] =================== [PASSED] pf_service ====================
[04:15:55] ================= xe_guc_g2g (2 subtests) ==================
[04:15:55] ============== xe_live_guc_g2g_kunit_default ==============
[04:15:55] ========= [SKIPPED] xe_live_guc_g2g_kunit_default ==========
[04:15:55] ============== xe_live_guc_g2g_kunit_allmem ===============
[04:15:55] ========== [SKIPPED] xe_live_guc_g2g_kunit_allmem ==========
[04:15:55] =================== [SKIPPED] xe_guc_g2g ===================
[04:15:55] =================== xe_mocs (2 subtests) ===================
[04:15:55] ================ xe_live_mocs_kernel_kunit ================
[04:15:55] =========== [SKIPPED] xe_live_mocs_kernel_kunit ============
[04:15:55] ================ xe_live_mocs_reset_kunit =================
[04:15:55] ============ [SKIPPED] xe_live_mocs_reset_kunit ============
[04:15:55] ==================== [SKIPPED] xe_mocs =====================
[04:15:55] ================= xe_migrate (2 subtests) ==================
[04:15:55] ================= xe_migrate_sanity_kunit =================
[04:15:55] ============ [SKIPPED] xe_migrate_sanity_kunit =============
[04:15:55] ================== xe_validate_ccs_kunit ==================
[04:15:55] ============= [SKIPPED] xe_validate_ccs_kunit ==============
[04:15:55] =================== [SKIPPED] xe_migrate ===================
[04:15:55] ================== xe_dma_buf (1 subtest) ==================
[04:15:55] ==================== xe_dma_buf_kunit =====================
[04:15:55] ================ [SKIPPED] xe_dma_buf_kunit ================
[04:15:55] =================== [SKIPPED] xe_dma_buf ===================
[04:15:55] ================= xe_bo_shrink (1 subtest) =================
[04:15:55] =================== xe_bo_shrink_kunit ====================
[04:15:55] =============== [SKIPPED] xe_bo_shrink_kunit ===============
[04:15:55] ================== [SKIPPED] xe_bo_shrink ==================
[04:15:55] ==================== xe_bo (2 subtests) ====================
[04:15:55] ================== xe_ccs_migrate_kunit ===================
[04:15:55] ============== [SKIPPED] xe_ccs_migrate_kunit ==============
[04:15:55] ==================== xe_bo_evict_kunit ====================
[04:15:55] =============== [SKIPPED] xe_bo_evict_kunit ================
[04:15:55] ===================== [SKIPPED] xe_bo ======================
[04:15:55] ==================== args (13 subtests) ====================
[04:15:55] [PASSED] count_args_test
[04:15:55] [PASSED] call_args_example
[04:15:55] [PASSED] call_args_test
[04:15:55] [PASSED] drop_first_arg_example
[04:15:55] [PASSED] drop_first_arg_test
[04:15:55] [PASSED] first_arg_example
[04:15:55] [PASSED] first_arg_test
[04:15:55] [PASSED] last_arg_example
[04:15:55] [PASSED] last_arg_test
[04:15:55] [PASSED] pick_arg_example
[04:15:55] [PASSED] if_args_example
[04:15:55] [PASSED] if_args_test
[04:15:55] [PASSED] sep_comma_example
[04:15:55] ====================== [PASSED] args =======================
[04:15:55] =================== xe_pci (3 subtests) ====================
[04:15:55] ==================== check_graphics_ip ====================
[04:15:55] [PASSED] 12.00 Xe_LP
[04:15:55] [PASSED] 12.10 Xe_LP+
[04:15:55] [PASSED] 12.55 Xe_HPG
[04:15:55] [PASSED] 12.60 Xe_HPC
[04:15:55] [PASSED] 12.70 Xe_LPG
[04:15:55] [PASSED] 12.71 Xe_LPG
[04:15:55] [PASSED] 12.74 Xe_LPG+
[04:15:55] [PASSED] 20.01 Xe2_HPG
[04:15:55] [PASSED] 20.02 Xe2_HPG
[04:15:55] [PASSED] 20.04 Xe2_LPG
[04:15:55] [PASSED] 30.00 Xe3_LPG
[04:15:55] [PASSED] 30.01 Xe3_LPG
[04:15:55] [PASSED] 30.03 Xe3_LPG
[04:15:55] [PASSED] 30.04 Xe3_LPG
[04:15:55] [PASSED] 30.05 Xe3_LPG
[04:15:55] [PASSED] 35.10 Xe3p_LPG
[04:15:55] [PASSED] 35.11 Xe3p_XPC
[04:15:55] ================ [PASSED] check_graphics_ip ================
[04:15:55] ===================== check_media_ip ======================
[04:15:55] [PASSED] 12.00 Xe_M
[04:15:55] [PASSED] 12.55 Xe_HPM
[04:15:55] [PASSED] 13.00 Xe_LPM+
[04:15:55] [PASSED] 13.01 Xe2_HPM
[04:15:55] [PASSED] 20.00 Xe2_LPM
[04:15:55] [PASSED] 30.00 Xe3_LPM
[04:15:55] [PASSED] 30.02 Xe3_LPM
[04:15:55] [PASSED] 35.00 Xe3p_LPM
[04:15:55] [PASSED] 35.03 Xe3p_HPM
[04:15:55] ================= [PASSED] check_media_ip ==================
[04:15:55] =================== check_platform_desc ===================
[04:15:55] [PASSED] 0x9A60 (TIGERLAKE)
[04:15:55] [PASSED] 0x9A68 (TIGERLAKE)
[04:15:55] [PASSED] 0x9A70 (TIGERLAKE)
[04:15:55] [PASSED] 0x9A40 (TIGERLAKE)
[04:15:55] [PASSED] 0x9A49 (TIGERLAKE)
[04:15:55] [PASSED] 0x9A59 (TIGERLAKE)
[04:15:55] [PASSED] 0x9A78 (TIGERLAKE)
[04:15:55] [PASSED] 0x9AC0 (TIGERLAKE)
[04:15:55] [PASSED] 0x9AC9 (TIGERLAKE)
[04:15:55] [PASSED] 0x9AD9 (TIGERLAKE)
[04:15:55] [PASSED] 0x9AF8 (TIGERLAKE)
[04:15:55] [PASSED] 0x4C80 (ROCKETLAKE)
[04:15:55] [PASSED] 0x4C8A (ROCKETLAKE)
[04:15:55] [PASSED] 0x4C8B (ROCKETLAKE)
[04:15:55] [PASSED] 0x4C8C (ROCKETLAKE)
[04:15:55] [PASSED] 0x4C90 (ROCKETLAKE)
[04:15:55] [PASSED] 0x4C9A (ROCKETLAKE)
[04:15:55] [PASSED] 0x4680 (ALDERLAKE_S)
[04:15:55] [PASSED] 0x4682 (ALDERLAKE_S)
[04:15:55] [PASSED] 0x4688 (ALDERLAKE_S)
[04:15:55] [PASSED] 0x468A (ALDERLAKE_S)
[04:15:55] [PASSED] 0x468B (ALDERLAKE_S)
[04:15:55] [PASSED] 0x4690 (ALDERLAKE_S)
[04:15:55] [PASSED] 0x4692 (ALDERLAKE_S)
[04:15:55] [PASSED] 0x4693 (ALDERLAKE_S)
[04:15:55] [PASSED] 0x46A0 (ALDERLAKE_P)
[04:15:55] [PASSED] 0x46A1 (ALDERLAKE_P)
[04:15:55] [PASSED] 0x46A2 (ALDERLAKE_P)
[04:15:55] [PASSED] 0x46A3 (ALDERLAKE_P)
[04:15:55] [PASSED] 0x46A6 (ALDERLAKE_P)
[04:15:55] [PASSED] 0x46A8 (ALDERLAKE_P)
[04:15:55] [PASSED] 0x46AA (ALDERLAKE_P)
[04:15:55] [PASSED] 0x462A (ALDERLAKE_P)
[04:15:55] [PASSED] 0x4626 (ALDERLAKE_P)
[04:15:55] [PASSED] 0x4628 (ALDERLAKE_P)
[04:15:55] [PASSED] 0x46B0 (ALDERLAKE_P)
[04:15:55] [PASSED] 0x46B1 (ALDERLAKE_P)
[04:15:55] [PASSED] 0x46B2 (ALDERLAKE_P)
[04:15:55] [PASSED] 0x46B3 (ALDERLAKE_P)
[04:15:55] [PASSED] 0x46C0 (ALDERLAKE_P)
[04:15:55] [PASSED] 0x46C1 (ALDERLAKE_P)
[04:15:55] [PASSED] 0x46C2 (ALDERLAKE_P)
[04:15:55] [PASSED] 0x46C3 (ALDERLAKE_P)
[04:15:55] [PASSED] 0x46D0 (ALDERLAKE_N)
[04:15:55] [PASSED] 0x46D1 (ALDERLAKE_N)
[04:15:55] [PASSED] 0x46D2 (ALDERLAKE_N)
[04:15:55] [PASSED] 0x46D3 (ALDERLAKE_N)
[04:15:55] [PASSED] 0x46D4 (ALDERLAKE_N)
[04:15:55] [PASSED] 0xA721 (ALDERLAKE_P)
[04:15:55] [PASSED] 0xA7A1 (ALDERLAKE_P)
[04:15:55] [PASSED] 0xA7A9 (ALDERLAKE_P)
[04:15:55] [PASSED] 0xA7AC (ALDERLAKE_P)
[04:15:55] [PASSED] 0xA7AD (ALDERLAKE_P)
[04:15:55] [PASSED] 0xA720 (ALDERLAKE_P)
[04:15:55] [PASSED] 0xA7A0 (ALDERLAKE_P)
[04:15:55] [PASSED] 0xA7A8 (ALDERLAKE_P)
[04:15:55] [PASSED] 0xA7AA (ALDERLAKE_P)
[04:15:55] [PASSED] 0xA7AB (ALDERLAKE_P)
[04:15:55] [PASSED] 0xA780 (ALDERLAKE_S)
[04:15:55] [PASSED] 0xA781 (ALDERLAKE_S)
[04:15:55] [PASSED] 0xA782 (ALDERLAKE_S)
[04:15:55] [PASSED] 0xA783 (ALDERLAKE_S)
[04:15:55] [PASSED] 0xA788 (ALDERLAKE_S)
[04:15:55] [PASSED] 0xA789 (ALDERLAKE_S)
[04:15:55] [PASSED] 0xA78A (ALDERLAKE_S)
[04:15:55] [PASSED] 0xA78B (ALDERLAKE_S)
[04:15:55] [PASSED] 0x4905 (DG1)
[04:15:55] [PASSED] 0x4906 (DG1)
[04:15:55] [PASSED] 0x4907 (DG1)
[04:15:55] [PASSED] 0x4908 (DG1)
[04:15:55] [PASSED] 0x4909 (DG1)
[04:15:55] [PASSED] 0x56C0 (DG2)
[04:15:55] [PASSED] 0x56C2 (DG2)
[04:15:55] [PASSED] 0x56C1 (DG2)
[04:15:55] [PASSED] 0x7D51 (METEORLAKE)
[04:15:55] [PASSED] 0x7DD1 (METEORLAKE)
[04:15:55] [PASSED] 0x7D41 (METEORLAKE)
[04:15:55] [PASSED] 0x7D67 (METEORLAKE)
[04:15:55] [PASSED] 0xB640 (METEORLAKE)
[04:15:55] [PASSED] 0x56A0 (DG2)
[04:15:55] [PASSED] 0x56A1 (DG2)
[04:15:55] [PASSED] 0x56A2 (DG2)
[04:15:55] [PASSED] 0x56BE (DG2)
[04:15:55] [PASSED] 0x56BF (DG2)
[04:15:55] [PASSED] 0x5690 (DG2)
[04:15:55] [PASSED] 0x5691 (DG2)
[04:15:55] [PASSED] 0x5692 (DG2)
[04:15:55] [PASSED] 0x56A5 (DG2)
[04:15:55] [PASSED] 0x56A6 (DG2)
[04:15:55] [PASSED] 0x56B0 (DG2)
[04:15:55] [PASSED] 0x56B1 (DG2)
[04:15:55] [PASSED] 0x56BA (DG2)
[04:15:55] [PASSED] 0x56BB (DG2)
[04:15:55] [PASSED] 0x56BC (DG2)
[04:15:55] [PASSED] 0x56BD (DG2)
[04:15:55] [PASSED] 0x5693 (DG2)
[04:15:55] [PASSED] 0x5694 (DG2)
[04:15:55] [PASSED] 0x5695 (DG2)
[04:15:55] [PASSED] 0x56A3 (DG2)
[04:15:55] [PASSED] 0x56A4 (DG2)
[04:15:55] [PASSED] 0x56B2 (DG2)
[04:15:55] [PASSED] 0x56B3 (DG2)
[04:15:55] [PASSED] 0x5696 (DG2)
[04:15:55] [PASSED] 0x5697 (DG2)
[04:15:55] [PASSED] 0xB69 (PVC)
[04:15:55] [PASSED] 0xB6E (PVC)
[04:15:55] [PASSED] 0xBD4 (PVC)
[04:15:55] [PASSED] 0xBD5 (PVC)
[04:15:55] [PASSED] 0xBD6 (PVC)
[04:15:55] [PASSED] 0xBD7 (PVC)
[04:15:55] [PASSED] 0xBD8 (PVC)
[04:15:55] [PASSED] 0xBD9 (PVC)
[04:15:55] [PASSED] 0xBDA (PVC)
[04:15:55] [PASSED] 0xBDB (PVC)
[04:15:55] [PASSED] 0xBE0 (PVC)
[04:15:55] [PASSED] 0xBE1 (PVC)
[04:15:55] [PASSED] 0xBE5 (PVC)
[04:15:55] [PASSED] 0x7D40 (METEORLAKE)
[04:15:55] [PASSED] 0x7D45 (METEORLAKE)
[04:15:55] [PASSED] 0x7D55 (METEORLAKE)
[04:15:55] [PASSED] 0x7D60 (METEORLAKE)
[04:15:55] [PASSED] 0x7DD5 (METEORLAKE)
[04:15:55] [PASSED] 0x6420 (LUNARLAKE)
[04:15:55] [PASSED] 0x64A0 (LUNARLAKE)
[04:15:55] [PASSED] 0x64B0 (LUNARLAKE)
[04:15:55] [PASSED] 0xE202 (BATTLEMAGE)
[04:15:55] [PASSED] 0xE209 (BATTLEMAGE)
[04:15:55] [PASSED] 0xE20B (BATTLEMAGE)
[04:15:55] [PASSED] 0xE20C (BATTLEMAGE)
[04:15:55] [PASSED] 0xE20D (BATTLEMAGE)
[04:15:55] [PASSED] 0xE210 (BATTLEMAGE)
[04:15:55] [PASSED] 0xE211 (BATTLEMAGE)
[04:15:55] [PASSED] 0xE212 (BATTLEMAGE)
[04:15:55] [PASSED] 0xE216 (BATTLEMAGE)
[04:15:55] [PASSED] 0xE220 (BATTLEMAGE)
[04:15:55] [PASSED] 0xE221 (BATTLEMAGE)
[04:15:55] [PASSED] 0xE222 (BATTLEMAGE)
[04:15:55] [PASSED] 0xE223 (BATTLEMAGE)
[04:15:55] [PASSED] 0xB080 (PANTHERLAKE)
[04:15:55] [PASSED] 0xB081 (PANTHERLAKE)
[04:15:55] [PASSED] 0xB082 (PANTHERLAKE)
[04:15:55] [PASSED] 0xB083 (PANTHERLAKE)
[04:15:55] [PASSED] 0xB084 (PANTHERLAKE)
[04:15:55] [PASSED] 0xB085 (PANTHERLAKE)
[04:15:55] [PASSED] 0xB086 (PANTHERLAKE)
[04:15:55] [PASSED] 0xB087 (PANTHERLAKE)
[04:15:55] [PASSED] 0xB08F (PANTHERLAKE)
[04:15:55] [PASSED] 0xB090 (PANTHERLAKE)
[04:15:55] [PASSED] 0xB0A0 (PANTHERLAKE)
[04:15:55] [PASSED] 0xB0B0 (PANTHERLAKE)
[04:15:55] [PASSED] 0xFD80 (PANTHERLAKE)
[04:15:55] [PASSED] 0xFD81 (PANTHERLAKE)
[04:15:55] [PASSED] 0xD740 (NOVALAKE_S)
[04:15:55] [PASSED] 0xD741 (NOVALAKE_S)
[04:15:55] [PASSED] 0xD742 (NOVALAKE_S)
[04:15:55] [PASSED] 0xD743 (NOVALAKE_S)
[04:15:55] [PASSED] 0xD745 (NOVALAKE_S)
[04:15:55] [PASSED] 0xD74A (NOVALAKE_S)
[04:15:55] [PASSED] 0xD74B (NOVALAKE_S)
[04:15:55] [PASSED] 0x674C (CRESCENTISLAND)
[04:15:55] [PASSED] 0x674D (CRESCENTISLAND)
[04:15:55] [PASSED] 0x674E (CRESCENTISLAND)
[04:15:55] [PASSED] 0x674F (CRESCENTISLAND)
[04:15:55] [PASSED] 0x6750 (CRESCENTISLAND)
[04:15:55] [PASSED] 0xD750 (NOVALAKE_P)
[04:15:55] [PASSED] 0xD751 (NOVALAKE_P)
[04:15:55] [PASSED] 0xD752 (NOVALAKE_P)
[04:15:55] [PASSED] 0xD753 (NOVALAKE_P)
[04:15:55] [PASSED] 0xD754 (NOVALAKE_P)
[04:15:55] [PASSED] 0xD755 (NOVALAKE_P)
[04:15:55] [PASSED] 0xD756 (NOVALAKE_P)
[04:15:55] [PASSED] 0xD757 (NOVALAKE_P)
[04:15:55] [PASSED] 0xD75F (NOVALAKE_P)
[04:15:55] =============== [PASSED] check_platform_desc ===============
[04:15:55] ===================== [PASSED] xe_pci ======================
[04:15:55] ============= xe_rtp_tables_test (5 subtests) ==============
[04:15:55] ================== xe_rtp_table_gt_test ===================
[04:15:55] [PASSED] gt_was/14011060649
[04:15:55] [PASSED] gt_was/14011059788
[04:15:55] [PASSED] gt_was/14015795083
[04:15:55] [PASSED] gt_was/16021867713
[04:15:55] [PASSED] gt_was/14019449301
[04:15:55] [PASSED] gt_was/16028005424
[04:15:55] [PASSED] gt_was/14026578760
[04:15:55] [PASSED] gt_was/1409420604
[04:15:55] [PASSED] gt_was/1408615072
[04:15:55] [PASSED] gt_was/22010523718
[04:15:55] [PASSED] gt_was/14011006942
[04:15:55] [PASSED] gt_was/14014830051
[04:15:55] [PASSED] gt_was/18018781329
[04:15:55] [PASSED] gt_was/1509235366
[04:15:55] [PASSED] gt_was/18018781329
[04:15:55] [PASSED] gt_was/16016694945
[04:15:55] [PASSED] gt_was/14018575942
[04:15:55] [PASSED] gt_was/22016670082
[04:15:55] [PASSED] gt_was/22016670082
[04:15:55] [PASSED] gt_was/14017421178
[04:15:55] [PASSED] gt_was/16025250150
[04:15:55] [PASSED] gt_was/14021871409
[04:15:55] [PASSED] gt_was/16021865536
[04:15:55] [PASSED] gt_was/14021486841
[04:15:55] [PASSED] gt_was/14025160223
[04:15:55] [PASSED] gt_was/14026144927, 16029437861, 14026127056
[04:15:55] [PASSED] gt_was/14025635424
[04:15:55] [PASSED] gt_was/16028005424
[04:15:55] ============== [PASSED] xe_rtp_table_gt_test ===============
[04:15:55] ================== xe_rtp_table_gt_test ===================
[04:15:55] [PASSED] gt_tunings/Tuning: Blend Fill Caching Optimization Disable
[04:15:55] [PASSED] gt_tunings/Tuning: 32B Access Enable
[04:15:55] [PASSED] gt_tunings/Tuning: L3 cache
[04:15:55] [PASSED] gt_tunings/Tuning: L3 cache - media
[04:15:55] [PASSED] gt_tunings/Tuning: Compression Overfetch
[04:15:55] [PASSED] gt_tunings/Tuning: Compression Overfetch - media
[04:15:55] [PASSED] gt_tunings/Tuning: Enable compressible partial write overfetch in L3
[04:15:55] [PASSED] gt_tunings/Tuning: Enable compressible partial write overfetch in L3 - media
[04:15:55] [PASSED] gt_tunings/Tuning: L2 Overfetch Compressible Only
[04:15:55] [PASSED] gt_tunings/Tuning: L2 Overfetch Compressible Only - media
[04:15:55] [PASSED] gt_tunings/Tuning: Stateless compression control
[04:15:55] [PASSED] gt_tunings/Tuning: Stateless compression control - media
[04:15:55] [PASSED] gt_tunings/Tuning: L3 RW flush all Cache
[04:15:55] [PASSED] gt_tunings/Tuning: L3 RW flush all cache - media
[04:15:55] [PASSED] gt_tunings/Tuning: Set STLB Bank Hash Mode to 4KB
[04:15:55] ============== [PASSED] xe_rtp_table_gt_test ===============
[04:15:55] ================== xe_rtp_table_oob_test ==================
[04:15:55] [PASSED] oob_was/1607983814
[04:15:55] [PASSED] oob_was/16010904313
[04:15:55] [PASSED] oob_was/18022495364
[04:15:55] [PASSED] oob_was/22012773006
[04:15:55] [PASSED] oob_was/14014475959
[04:15:55] [PASSED] oob_was/22011391025
[04:15:55] [PASSED] oob_was/22012727170
[04:15:55] [PASSED] oob_was/22012727685
[04:15:55] [PASSED] oob_was/22016596838
[04:15:55] [PASSED] oob_was/18020744125
[04:15:55] [PASSED] oob_was/1409600907
[04:15:55] [PASSED] oob_was/22014953428
[04:15:55] [PASSED] oob_was/16017236439
[04:15:55] [PASSED] oob_was/14019821291
[04:15:55] [PASSED] oob_was/14015076503
[04:15:55] [PASSED] oob_was/14018913170
[04:15:55] [PASSED] oob_was/14018094691
[04:15:55] [PASSED] oob_was/18024947630
[04:15:55] [PASSED] oob_was/16022287689
[04:15:55] [PASSED] oob_was/13011645652
[04:15:55] [PASSED] oob_was/14022293748
[04:15:55] [PASSED] oob_was/22019794406
[04:15:55] [PASSED] oob_was/22019338487
[04:15:55] [PASSED] oob_was/16023588340
[04:15:55] [PASSED] oob_was/14019789679
[04:15:55] [PASSED] oob_was/14022866841
[04:15:55] [PASSED] oob_was/16021333562
[04:15:55] [PASSED] oob_was/14016712196
[04:15:55] [PASSED] oob_was/14015568240
[04:15:55] [PASSED] oob_was/18013179988
[04:15:55] [PASSED] oob_was/1508761755
[04:15:55] [PASSED] oob_was/16023105232
[04:15:55] [PASSED] oob_was/16026508708
[04:15:55] [PASSED] oob_was/14020001231
[04:15:55] [PASSED] oob_was/16023683509
[04:15:55] [PASSED] oob_was/14025515070
[04:15:55] [PASSED] oob_was/15015404425_disable
[04:15:55] [PASSED] oob_was/16026007364
[04:15:55] [PASSED] oob_was/14020316580
[04:15:55] [PASSED] oob_was/14025883347
[04:15:55] [PASSED] oob_was/16029380221
[04:15:55] [PASSED] oob_was/22022079272
[04:15:55] [PASSED] oob_was/16029897822
[04:15:55] ============== [PASSED] xe_rtp_table_oob_test ==============
[04:15:55] ================ xe_rtp_table_dev_oob_test ================
[04:15:55] [PASSED] device_oob_was/22010954014
[04:15:55] [PASSED] device_oob_was/15015404425
[04:15:55] [PASSED] device_oob_was/22019338487_display
[04:15:55] [PASSED] device_oob_was/14022085890
[04:15:55] [PASSED] device_oob_was/14026539277
[04:15:55] [PASSED] device_oob_was/14026633728
[04:15:55] [PASSED] device_oob_was/14026746987
[04:15:55] [PASSED] device_oob_was/14026779378
[04:15:55] ============ [PASSED] xe_rtp_table_dev_oob_test ============
[04:15:55] ========== xe_rtp_table_missing_upper_bound_test ==========
[04:15:55] [PASSED] register_whitelist/WaAllowPMDepthAndInvocationCountAccessFromUMD, 1408556865
[04:15:55] [PASSED] register_whitelist/1508744258, 14012131227, 1808121037
[04:15:55] [PASSED] register_whitelist/1806527549
[04:15:55] [PASSED] register_whitelist/allow_read_ctx_timestamp
[04:15:55] [PASSED] register_whitelist/allow_read_queue_timestamp
[04:15:55] [PASSED] register_whitelist/16014440446
[04:15:55] [PASSED] register_whitelist/16017236439
[04:15:55] [PASSED] register_whitelist/16020183090
[04:15:55] [PASSED] register_whitelist/14024997852
[04:15:55] [PASSED] register_whitelist/14024997852
[04:15:55] ====== [PASSED] xe_rtp_table_missing_upper_bound_test ======
[04:15:55] =============== [PASSED] xe_rtp_tables_test ================
[04:15:55] =================== xe_rtp (3 subtests) ====================
[04:15:55] =================== xe_rtp_rules_tests ====================
[04:15:55] [PASSED] no
[04:15:55] [PASSED] yes
[04:15:55] [PASSED] no-and-no
[04:15:55] [PASSED] no-and-yes
[04:15:55] [PASSED] yes-and-no
[04:15:55] [PASSED] yes-and-yes
[04:15:55] [PASSED] no-or-no
[04:15:55] [PASSED] no-or-yes
[04:15:55] [PASSED] yes-or-no
[04:15:55] [PASSED] yes-or-yes
[04:15:55] [PASSED] no-yes-or-yes-no
[04:15:55] [PASSED] no-yes-or-yes-yes
[04:15:55] [PASSED] yes-yes-or-no-yes
[04:15:55] [PASSED] yes-yes-or-yes-yes
[04:15:55] [PASSED] no-no-or-yes-or-no
[04:15:55] [PASSED] or
[04:15:55] [PASSED] or-yes
[04:15:55] [PASSED] or-no
[04:15:55] [PASSED] yes-or
[04:15:55] [PASSED] no-or
[04:15:55] [PASSED] no-or-or-yes
[04:15:55] [PASSED] yes-or-or-no
[04:15:55] [PASSED] no-or-or-no
[04:15:55] [PASSED] missing-context-engine-class
[04:15:55] [PASSED] missing-context-engine-class-or-yes
[04:15:55] [PASSED] missing-context-engine-class-or-or-yes
[04:15:55] =============== [PASSED] xe_rtp_rules_tests ================
[04:15:55] =============== xe_rtp_process_to_sr_tests ================
[04:15:55] [PASSED] coalesce-same-reg
[04:15:55] [PASSED] coalesce-same-reg-literal-and-func
[04:15:55] [PASSED] no-match-no-add
[04:15:55] [PASSED] two-regs-two-entries
[04:15:55] [PASSED] clr-one-set-other
[04:15:55] [PASSED] set-field
[04:15:55] [PASSED] conflict-duplicate
[04:15:55] [PASSED] conflict-not-disjoint
[04:15:55] [PASSED] conflict-not-disjoint-literal-and-func
[04:15:55] [PASSED] conflict-reg-type
[04:15:55] [PASSED] bad-mcr-reg-forced-to-regular
[04:15:55] [PASSED] bad-regular-reg-forced-to-mcr
[04:15:55] =========== [PASSED] xe_rtp_process_to_sr_tests ============
[04:15:55] ================== xe_rtp_process_tests ===================
[04:15:55] [PASSED] active1
[04:15:55] [PASSED] active2
[04:15:55] [PASSED] active-inactive
[04:15:55] [PASSED] inactive-active
[04:15:55] [PASSED] inactive-active-inactive
[04:15:55] [PASSED] inactive-inactive-inactive
[04:15:55] ============== [PASSED] xe_rtp_process_tests ===============
[04:15:55] ===================== [PASSED] xe_rtp ======================
[04:15:55] ==================== xe_wa (1 subtest) =====================
[04:15:55] ======================== xe_wa_gt =========================
[04:15:55] [PASSED] TIGERLAKE B0
[04:15:55] [PASSED] DG1 A0
[04:15:55] [PASSED] DG1 B0
[04:15:55] [PASSED] ALDERLAKE_S A0
[04:15:55] [PASSED] ALDERLAKE_S B0
[04:15:55] [PASSED] ALDERLAKE_S C0
[04:15:55] [PASSED] ALDERLAKE_S D0
[04:15:55] [PASSED] ALDERLAKE_P A0
[04:15:55] [PASSED] ALDERLAKE_P B0
[04:15:55] [PASSED] ALDERLAKE_P C0
[04:15:55] [PASSED] ALDERLAKE_S RPLS D0
[04:15:55] [PASSED] ALDERLAKE_P RPLU E0
[04:15:55] [PASSED] DG2 G10 C0
[04:15:55] [PASSED] DG2 G11 B1
[04:15:55] [PASSED] DG2 G12 A1
[04:15:55] [PASSED] METEORLAKE 12.70(Xe_LPG) A0 13.00(Xe_LPM+) A0
[04:15:55] [PASSED] METEORLAKE 12.71(Xe_LPG) A0 13.00(Xe_LPM+) A0
[04:15:55] [PASSED] METEORLAKE 12.74(Xe_LPG+) A0 13.00(Xe_LPM+) A0
[04:15:55] [PASSED] LUNARLAKE 20.04(Xe2_LPG) A0 20.00(Xe2_LPM) A0
[04:15:55] [PASSED] LUNARLAKE 20.04(Xe2_LPG) B0 20.00(Xe2_LPM) A0
[04:15:55] [PASSED] BATTLEMAGE 20.01(Xe2_HPG) A0 13.01(Xe2_HPM) A1
[04:15:55] [PASSED] PANTHERLAKE 30.00(Xe3_LPG) A0 30.00(Xe3_LPM) A0
[04:15:55] ==================== [PASSED] xe_wa_gt =====================
[04:15:55] ====================== [PASSED] xe_wa ======================
[04:15:55] ============================================================
[04:15:55] Testing complete. Ran 741 tests: passed: 723, skipped: 18
[04:15:55] Elapsed time: 36.586s total, 4.397s configuring, 31.517s building, 0.647s running
+ /kernel/tools/testing/kunit/kunit.py run --kunitconfig /kernel/drivers/gpu/drm/tests/.kunitconfig
[04:15:55] Configuring KUnit Kernel ...
Regenerating .config ...
Populating config with:
$ make ARCH=um O=.kunit olddefconfig
[04:15:57] Building KUnit Kernel ...
Populating config with:
$ make ARCH=um O=.kunit olddefconfig
Building with:
$ make all compile_commands.json scripts_gdb ARCH=um O=.kunit --jobs=48
[04:16:22] Starting KUnit Kernel (1/1)...
[04:16:22] ============================================================
Running tests with:
$ .kunit/linux kunit.enable=1 mem=1G console=tty kunit_shutdown=halt
[04:16:22] ============ drm_test_pick_cmdline (2 subtests) ============
[04:16:22] [PASSED] drm_test_pick_cmdline_res_1920_1080_60
[04:16:22] =============== drm_test_pick_cmdline_named ===============
[04:16:22] [PASSED] NTSC
[04:16:22] [PASSED] NTSC-J
[04:16:22] [PASSED] PAL
[04:16:22] [PASSED] PAL-M
[04:16:22] =========== [PASSED] drm_test_pick_cmdline_named ===========
[04:16:22] ============== [PASSED] drm_test_pick_cmdline ==============
[04:16:22] == drm_test_atomic_get_connector_for_encoder (1 subtest) ===
[04:16:22] [PASSED] drm_test_drm_atomic_get_connector_for_encoder
[04:16:22] ==== [PASSED] drm_test_atomic_get_connector_for_encoder ====
[04:16:22] =========== drm_validate_clone_mode (2 subtests) ===========
[04:16:22] ============== drm_test_check_in_clone_mode ===============
[04:16:22] [PASSED] in_clone_mode
[04:16:22] [PASSED] not_in_clone_mode
[04:16:22] ========== [PASSED] drm_test_check_in_clone_mode ===========
[04:16:22] =============== drm_test_check_valid_clones ===============
[04:16:22] [PASSED] not_in_clone_mode
[04:16:22] [PASSED] valid_clone
[04:16:22] [PASSED] invalid_clone
[04:16:22] =========== [PASSED] drm_test_check_valid_clones ===========
[04:16:22] ============= [PASSED] drm_validate_clone_mode =============
[04:16:22] ============= drm_validate_modeset (1 subtest) =============
[04:16:22] [PASSED] drm_test_check_connector_changed_modeset
[04:16:22] ============== [PASSED] drm_validate_modeset ===============
[04:16:22] ====== drm_test_bridge_get_current_state (1 subtest) =======
[04:16:22] [PASSED] drm_test_drm_bridge_get_current_state_atomic
[04:16:22] ======== [PASSED] drm_test_bridge_get_current_state ========
[04:16:22] ====== drm_test_bridge_helper_reset_crtc (3 subtests) ======
[04:16:22] [PASSED] drm_test_drm_bridge_helper_reset_crtc_atomic
[04:16:22] [PASSED] drm_test_drm_bridge_helper_reset_crtc_atomic_disabled
[04:16:22] [PASSED] drm_test_drm_bridge_helper_hdmi_output_bus_fmts
[04:16:22] ======== [PASSED] drm_test_bridge_helper_reset_crtc ========
[04:16:22] ============== drm_bridge_alloc (2 subtests) ===============
[04:16:22] [PASSED] drm_test_drm_bridge_alloc_basic
[04:16:22] [PASSED] drm_test_drm_bridge_alloc_get_put
[04:16:22] ================ [PASSED] drm_bridge_alloc =================
[04:16:22] ============= drm_bridge_bus_fmt (5 subtests) ==============
[04:16:22] [PASSED] drm_test_bridge_rgb_yuv_rgb
[04:16:22] [PASSED] drm_test_bridge_must_convert_to_yuv444
[04:16:22] [PASSED] drm_test_bridge_hdmi_auto_rgb
[04:16:22] [PASSED] drm_test_bridge_auto_first
[04:16:22] [PASSED] drm_test_bridge_rgb_yuv_no_path
[04:16:22] =============== [PASSED] drm_bridge_bus_fmt ================
[04:16:22] ============= drm_cmdline_parser (40 subtests) =============
[04:16:22] [PASSED] drm_test_cmdline_force_d_only
[04:16:22] [PASSED] drm_test_cmdline_force_D_only_dvi
[04:16:22] [PASSED] drm_test_cmdline_force_D_only_hdmi
[04:16:22] [PASSED] drm_test_cmdline_force_D_only_not_digital
[04:16:22] [PASSED] drm_test_cmdline_force_e_only
[04:16:22] [PASSED] drm_test_cmdline_res
[04:16:22] [PASSED] drm_test_cmdline_res_vesa
[04:16:22] [PASSED] drm_test_cmdline_res_vesa_rblank
[04:16:22] [PASSED] drm_test_cmdline_res_rblank
[04:16:22] [PASSED] drm_test_cmdline_res_bpp
[04:16:22] [PASSED] drm_test_cmdline_res_refresh
[04:16:22] [PASSED] drm_test_cmdline_res_bpp_refresh
[04:16:22] [PASSED] drm_test_cmdline_res_bpp_refresh_interlaced
[04:16:22] [PASSED] drm_test_cmdline_res_bpp_refresh_margins
[04:16:22] [PASSED] drm_test_cmdline_res_bpp_refresh_force_off
[04:16:22] [PASSED] drm_test_cmdline_res_bpp_refresh_force_on
[04:16:22] [PASSED] drm_test_cmdline_res_bpp_refresh_force_on_analog
[04:16:22] [PASSED] drm_test_cmdline_res_bpp_refresh_force_on_digital
[04:16:22] [PASSED] drm_test_cmdline_res_bpp_refresh_interlaced_margins_force_on
[04:16:22] [PASSED] drm_test_cmdline_res_margins_force_on
[04:16:22] [PASSED] drm_test_cmdline_res_vesa_margins
[04:16:22] [PASSED] drm_test_cmdline_name
[04:16:22] [PASSED] drm_test_cmdline_name_bpp
[04:16:22] [PASSED] drm_test_cmdline_name_option
[04:16:22] [PASSED] drm_test_cmdline_name_bpp_option
[04:16:22] [PASSED] drm_test_cmdline_rotate_0
[04:16:22] [PASSED] drm_test_cmdline_rotate_90
[04:16:22] [PASSED] drm_test_cmdline_rotate_180
[04:16:22] [PASSED] drm_test_cmdline_rotate_270
[04:16:22] [PASSED] drm_test_cmdline_hmirror
[04:16:22] [PASSED] drm_test_cmdline_vmirror
[04:16:22] [PASSED] drm_test_cmdline_margin_options
[04:16:22] [PASSED] drm_test_cmdline_multiple_options
[04:16:22] [PASSED] drm_test_cmdline_bpp_extra_and_option
[04:16:22] [PASSED] drm_test_cmdline_extra_and_option
[04:16:22] [PASSED] drm_test_cmdline_freestanding_options
[04:16:22] [PASSED] drm_test_cmdline_freestanding_force_e_and_options
[04:16:22] [PASSED] drm_test_cmdline_panel_orientation
[04:16:22] ================ drm_test_cmdline_invalid =================
[04:16:22] [PASSED] margin_only
[04:16:22] [PASSED] interlace_only
[04:16:22] [PASSED] res_missing_x
[04:16:22] [PASSED] res_missing_y
[04:16:22] [PASSED] res_bad_y
[04:16:22] [PASSED] res_missing_y_bpp
[04:16:22] [PASSED] res_bad_bpp
[04:16:22] [PASSED] res_bad_refresh
[04:16:22] [PASSED] res_bpp_refresh_force_on_off
[04:16:22] [PASSED] res_invalid_mode
[04:16:22] [PASSED] res_bpp_wrong_place_mode
[04:16:22] [PASSED] name_bpp_refresh
[04:16:22] [PASSED] name_refresh
[04:16:22] [PASSED] name_refresh_wrong_mode
[04:16:22] [PASSED] name_refresh_invalid_mode
[04:16:22] [PASSED] rotate_multiple
[04:16:22] [PASSED] rotate_invalid_val
[04:16:22] [PASSED] rotate_truncated
[04:16:22] [PASSED] invalid_option
[04:16:22] [PASSED] invalid_tv_option
[04:16:22] [PASSED] truncated_tv_option
[04:16:22] ============ [PASSED] drm_test_cmdline_invalid =============
[04:16:22] =============== drm_test_cmdline_tv_options ===============
[04:16:22] [PASSED] NTSC
[04:16:22] [PASSED] NTSC_443
[04:16:22] [PASSED] NTSC_J
[04:16:22] [PASSED] PAL
[04:16:22] [PASSED] PAL_M
[04:16:22] [PASSED] PAL_N
[04:16:22] [PASSED] SECAM
[04:16:22] [PASSED] MONO_525
[04:16:22] [PASSED] MONO_625
[04:16:22] =========== [PASSED] drm_test_cmdline_tv_options ===========
[04:16:22] =============== [PASSED] drm_cmdline_parser ================
[04:16:22] ========== drmm_connector_hdmi_init (20 subtests) ==========
[04:16:22] [PASSED] drm_test_connector_hdmi_init_valid
[04:16:22] [PASSED] drm_test_connector_hdmi_init_bpc_8
[04:16:22] [PASSED] drm_test_connector_hdmi_init_bpc_10
[04:16:22] [PASSED] drm_test_connector_hdmi_init_bpc_12
[04:16:22] [PASSED] drm_test_connector_hdmi_init_bpc_invalid
[04:16:22] [PASSED] drm_test_connector_hdmi_init_bpc_null
[04:16:22] [PASSED] drm_test_connector_hdmi_init_formats_empty
[04:16:22] [PASSED] drm_test_connector_hdmi_init_formats_no_rgb
[04:16:22] === drm_test_connector_hdmi_init_formats_yuv420_allowed ===
[04:16:22] [PASSED] supported_formats=0x9 yuv420_allowed=1
[04:16:22] [PASSED] supported_formats=0x9 yuv420_allowed=0
[04:16:22] [PASSED] supported_formats=0x5 yuv420_allowed=1
[04:16:22] [PASSED] supported_formats=0x5 yuv420_allowed=0
[04:16:22] === [PASSED] drm_test_connector_hdmi_init_formats_yuv420_allowed ===
[04:16:22] [PASSED] drm_test_connector_hdmi_init_null_ddc
[04:16:22] [PASSED] drm_test_connector_hdmi_init_null_product
[04:16:22] [PASSED] drm_test_connector_hdmi_init_null_vendor
[04:16:22] [PASSED] drm_test_connector_hdmi_init_product_length_exact
[04:16:22] [PASSED] drm_test_connector_hdmi_init_product_length_too_long
[04:16:22] [PASSED] drm_test_connector_hdmi_init_product_valid
[04:16:22] [PASSED] drm_test_connector_hdmi_init_vendor_length_exact
[04:16:22] [PASSED] drm_test_connector_hdmi_init_vendor_length_too_long
[04:16:22] [PASSED] drm_test_connector_hdmi_init_vendor_valid
[04:16:22] ========= drm_test_connector_hdmi_init_type_valid =========
[04:16:22] [PASSED] HDMI-A
[04:16:22] [PASSED] HDMI-B
[04:16:22] ===== [PASSED] drm_test_connector_hdmi_init_type_valid =====
[04:16:22] ======== drm_test_connector_hdmi_init_type_invalid ========
[04:16:22] [PASSED] Unknown
[04:16:22] [PASSED] VGA
[04:16:22] [PASSED] DVI-I
[04:16:22] [PASSED] DVI-D
[04:16:22] [PASSED] DVI-A
[04:16:22] [PASSED] Composite
[04:16:22] [PASSED] SVIDEO
[04:16:22] [PASSED] LVDS
[04:16:22] [PASSED] Component
[04:16:22] [PASSED] DIN
[04:16:22] [PASSED] DP
[04:16:22] [PASSED] TV
[04:16:22] [PASSED] eDP
[04:16:22] [PASSED] Virtual
[04:16:22] [PASSED] DSI
[04:16:22] [PASSED] DPI
[04:16:22] [PASSED] Writeback
[04:16:22] [PASSED] SPI
[04:16:22] [PASSED] USB
[04:16:22] ==== [PASSED] drm_test_connector_hdmi_init_type_invalid ====
[04:16:22] ============ [PASSED] drmm_connector_hdmi_init =============
[04:16:22] ============= drmm_connector_init (3 subtests) =============
[04:16:22] [PASSED] drm_test_drmm_connector_init
[04:16:22] [PASSED] drm_test_drmm_connector_init_null_ddc
[04:16:22] ========= drm_test_drmm_connector_init_type_valid =========
[04:16:22] [PASSED] Unknown
[04:16:22] [PASSED] VGA
[04:16:22] [PASSED] DVI-I
[04:16:22] [PASSED] DVI-D
[04:16:22] [PASSED] DVI-A
[04:16:22] [PASSED] Composite
[04:16:22] [PASSED] SVIDEO
[04:16:22] [PASSED] LVDS
[04:16:22] [PASSED] Component
[04:16:22] [PASSED] DIN
[04:16:22] [PASSED] DP
[04:16:22] [PASSED] HDMI-A
[04:16:22] [PASSED] HDMI-B
[04:16:22] [PASSED] TV
[04:16:22] [PASSED] eDP
[04:16:22] [PASSED] Virtual
[04:16:22] [PASSED] DSI
[04:16:22] [PASSED] DPI
[04:16:22] [PASSED] Writeback
[04:16:22] [PASSED] SPI
[04:16:22] [PASSED] USB
[04:16:22] ===== [PASSED] drm_test_drmm_connector_init_type_valid =====
[04:16:22] =============== [PASSED] drmm_connector_init ===============
[04:16:22] ========= drm_connector_dynamic_init (6 subtests) ==========
[04:16:22] [PASSED] drm_test_drm_connector_dynamic_init
[04:16:22] [PASSED] drm_test_drm_connector_dynamic_init_null_ddc
[04:16:22] [PASSED] drm_test_drm_connector_dynamic_init_not_added
[04:16:22] [PASSED] drm_test_drm_connector_dynamic_init_properties
[04:16:22] ===== drm_test_drm_connector_dynamic_init_type_valid ======
[04:16:22] [PASSED] Unknown
[04:16:22] [PASSED] VGA
[04:16:22] [PASSED] DVI-I
[04:16:22] [PASSED] DVI-D
[04:16:22] [PASSED] DVI-A
[04:16:22] [PASSED] Composite
[04:16:22] [PASSED] SVIDEO
[04:16:22] [PASSED] LVDS
[04:16:22] [PASSED] Component
[04:16:22] [PASSED] DIN
[04:16:22] [PASSED] DP
[04:16:22] [PASSED] HDMI-A
[04:16:22] [PASSED] HDMI-B
[04:16:22] [PASSED] TV
[04:16:22] [PASSED] eDP
[04:16:22] [PASSED] Virtual
[04:16:22] [PASSED] DSI
[04:16:22] [PASSED] DPI
[04:16:22] [PASSED] Writeback
[04:16:22] [PASSED] SPI
[04:16:22] [PASSED] USB
[04:16:22] = [PASSED] drm_test_drm_connector_dynamic_init_type_valid ==
[04:16:22] ======== drm_test_drm_connector_dynamic_init_name =========
[04:16:22] [PASSED] Unknown
[04:16:22] [PASSED] VGA
[04:16:22] [PASSED] DVI-I
[04:16:22] [PASSED] DVI-D
[04:16:22] [PASSED] DVI-A
[04:16:22] [PASSED] Composite
[04:16:22] [PASSED] SVIDEO
[04:16:22] [PASSED] LVDS
[04:16:22] [PASSED] Component
[04:16:22] [PASSED] DIN
[04:16:22] [PASSED] DP
[04:16:22] [PASSED] HDMI-A
[04:16:22] [PASSED] HDMI-B
[04:16:22] [PASSED] TV
[04:16:22] [PASSED] eDP
[04:16:22] [PASSED] Virtual
[04:16:22] [PASSED] DSI
[04:16:22] [PASSED] DPI
[04:16:22] [PASSED] Writeback
[04:16:22] [PASSED] SPI
[04:16:22] [PASSED] USB
[04:16:22] ==== [PASSED] drm_test_drm_connector_dynamic_init_name =====
[04:16:22] =========== [PASSED] drm_connector_dynamic_init ============
[04:16:22] ==== drm_connector_dynamic_register_early (4 subtests) =====
[04:16:22] [PASSED] drm_test_drm_connector_dynamic_register_early_on_list
[04:16:22] [PASSED] drm_test_drm_connector_dynamic_register_early_defer
[04:16:22] [PASSED] drm_test_drm_connector_dynamic_register_early_no_init
[04:16:22] [PASSED] drm_test_drm_connector_dynamic_register_early_no_mode_object
[04:16:22] ====== [PASSED] drm_connector_dynamic_register_early =======
[04:16:22] ======= drm_connector_dynamic_register (7 subtests) ========
[04:16:22] [PASSED] drm_test_drm_connector_dynamic_register_on_list
[04:16:22] [PASSED] drm_test_drm_connector_dynamic_register_no_defer
[04:16:22] [PASSED] drm_test_drm_connector_dynamic_register_no_init
[04:16:22] [PASSED] drm_test_drm_connector_dynamic_register_mode_object
[04:16:22] [PASSED] drm_test_drm_connector_dynamic_register_sysfs
[04:16:22] [PASSED] drm_test_drm_connector_dynamic_register_sysfs_name
[04:16:22] [PASSED] drm_test_drm_connector_dynamic_register_debugfs
[04:16:22] ========= [PASSED] drm_connector_dynamic_register ==========
[04:16:22] = drm_connector_attach_broadcast_rgb_property (2 subtests) =
[04:16:22] [PASSED] drm_test_drm_connector_attach_broadcast_rgb_property
[04:16:22] [PASSED] drm_test_drm_connector_attach_broadcast_rgb_property_hdmi_connector
[04:16:22] === [PASSED] drm_connector_attach_broadcast_rgb_property ===
[04:16:22] ========== drm_get_tv_mode_from_name (2 subtests) ==========
[04:16:22] ========== drm_test_get_tv_mode_from_name_valid ===========
[04:16:22] [PASSED] NTSC
[04:16:22] [PASSED] NTSC-443
[04:16:22] [PASSED] NTSC-J
[04:16:22] [PASSED] PAL
[04:16:22] [PASSED] PAL-M
[04:16:22] [PASSED] PAL-N
[04:16:22] [PASSED] SECAM
[04:16:22] [PASSED] Mono
[04:16:22] ====== [PASSED] drm_test_get_tv_mode_from_name_valid =======
[04:16:22] [PASSED] drm_test_get_tv_mode_from_name_truncated
[04:16:22] ============ [PASSED] drm_get_tv_mode_from_name ============
[04:16:22] = drm_test_connector_hdmi_compute_mode_clock (12 subtests) =
[04:16:22] [PASSED] drm_test_drm_hdmi_compute_mode_clock_rgb
[04:16:22] [PASSED] drm_test_drm_hdmi_compute_mode_clock_rgb_10bpc
[04:16:22] [PASSED] drm_test_drm_hdmi_compute_mode_clock_rgb_10bpc_vic_1
[04:16:22] [PASSED] drm_test_drm_hdmi_compute_mode_clock_rgb_12bpc
[04:16:22] [PASSED] drm_test_drm_hdmi_compute_mode_clock_rgb_12bpc_vic_1
[04:16:22] [PASSED] drm_test_drm_hdmi_compute_mode_clock_rgb_double
[04:16:22] = drm_test_connector_hdmi_compute_mode_clock_yuv420_valid =
[04:16:22] [PASSED] VIC 96
[04:16:22] [PASSED] VIC 97
[04:16:22] [PASSED] VIC 101
[04:16:22] [PASSED] VIC 102
[04:16:22] [PASSED] VIC 106
[04:16:22] [PASSED] VIC 107
[04:16:22] === [PASSED] drm_test_connector_hdmi_compute_mode_clock_yuv420_valid ===
[04:16:22] [PASSED] drm_test_connector_hdmi_compute_mode_clock_yuv420_10_bpc
[04:16:22] [PASSED] drm_test_connector_hdmi_compute_mode_clock_yuv420_12_bpc
[04:16:22] [PASSED] drm_test_connector_hdmi_compute_mode_clock_yuv422_8_bpc
[04:16:22] [PASSED] drm_test_connector_hdmi_compute_mode_clock_yuv422_10_bpc
[04:16:22] [PASSED] drm_test_connector_hdmi_compute_mode_clock_yuv422_12_bpc
[04:16:22] === [PASSED] drm_test_connector_hdmi_compute_mode_clock ====
[04:16:22] == drm_hdmi_connector_get_broadcast_rgb_name (2 subtests) ==
[04:16:22] === drm_test_drm_hdmi_connector_get_broadcast_rgb_name ====
[04:16:22] [PASSED] Automatic
[04:16:22] [PASSED] Full
[04:16:22] [PASSED] Limited 16:235
[04:16:22] === [PASSED] drm_test_drm_hdmi_connector_get_broadcast_rgb_name ===
[04:16:22] [PASSED] drm_test_drm_hdmi_connector_get_broadcast_rgb_name_invalid
[04:16:22] ==== [PASSED] drm_hdmi_connector_get_broadcast_rgb_name ====
[04:16:22] == drm_hdmi_connector_get_output_format_name (2 subtests) ==
[04:16:22] === drm_test_drm_hdmi_connector_get_output_format_name ====
[04:16:22] [PASSED] RGB
[04:16:22] [PASSED] YUV 4:2:0
[04:16:22] [PASSED] YUV 4:2:2
[04:16:22] [PASSED] YUV 4:4:4
[04:16:22] === [PASSED] drm_test_drm_hdmi_connector_get_output_format_name ===
[04:16:22] [PASSED] drm_test_drm_hdmi_connector_get_output_format_name_invalid
[04:16:22] ==== [PASSED] drm_hdmi_connector_get_output_format_name ====
[04:16:22] ============= drm_damage_helper (21 subtests) ==============
[04:16:22] [PASSED] drm_test_damage_iter_no_damage
[04:16:22] [PASSED] drm_test_damage_iter_no_damage_fractional_src
[04:16:22] [PASSED] drm_test_damage_iter_no_damage_src_moved
[04:16:22] [PASSED] drm_test_damage_iter_no_damage_fractional_src_moved
[04:16:22] [PASSED] drm_test_damage_iter_no_damage_not_visible
[04:16:22] [PASSED] drm_test_damage_iter_no_damage_no_crtc
[04:16:22] [PASSED] drm_test_damage_iter_no_damage_no_fb
[04:16:22] [PASSED] drm_test_damage_iter_simple_damage
[04:16:22] [PASSED] drm_test_damage_iter_single_damage
[04:16:22] [PASSED] drm_test_damage_iter_single_damage_intersect_src
[04:16:22] [PASSED] drm_test_damage_iter_single_damage_outside_src
[04:16:22] [PASSED] drm_test_damage_iter_single_damage_fractional_src
[04:16:22] [PASSED] drm_test_damage_iter_single_damage_intersect_fractional_src
[04:16:22] [PASSED] drm_test_damage_iter_single_damage_outside_fractional_src
[04:16:22] [PASSED] drm_test_damage_iter_single_damage_src_moved
[04:16:22] [PASSED] drm_test_damage_iter_single_damage_fractional_src_moved
[04:16:22] [PASSED] drm_test_damage_iter_damage
[04:16:22] [PASSED] drm_test_damage_iter_damage_one_intersect
[04:16:22] [PASSED] drm_test_damage_iter_damage_one_outside
[04:16:22] [PASSED] drm_test_damage_iter_damage_src_moved
[04:16:22] [PASSED] drm_test_damage_iter_damage_not_visible
[04:16:22] ================ [PASSED] drm_damage_helper ================
[04:16:22] ============== drm_dp_mst_helper (3 subtests) ==============
[04:16:22] ============== drm_test_dp_mst_calc_pbn_mode ==============
[04:16:22] [PASSED] Clock 154000 BPP 30 DSC disabled
[04:16:22] [PASSED] Clock 234000 BPP 30 DSC disabled
[04:16:22] [PASSED] Clock 297000 BPP 24 DSC disabled
[04:16:22] [PASSED] Clock 332880 BPP 24 DSC enabled
[04:16:22] [PASSED] Clock 324540 BPP 24 DSC enabled
[04:16:22] ========== [PASSED] drm_test_dp_mst_calc_pbn_mode ==========
[04:16:22] ============== drm_test_dp_mst_calc_pbn_div ===============
[04:16:22] [PASSED] Link rate 2000000 lane count 4
[04:16:22] [PASSED] Link rate 2000000 lane count 2
[04:16:22] [PASSED] Link rate 2000000 lane count 1
[04:16:22] [PASSED] Link rate 1350000 lane count 4
[04:16:22] [PASSED] Link rate 1350000 lane count 2
[04:16:22] [PASSED] Link rate 1350000 lane count 1
[04:16:22] [PASSED] Link rate 1000000 lane count 4
[04:16:22] [PASSED] Link rate 1000000 lane count 2
[04:16:22] [PASSED] Link rate 1000000 lane count 1
[04:16:22] [PASSED] Link rate 810000 lane count 4
[04:16:22] [PASSED] Link rate 810000 lane count 2
[04:16:22] [PASSED] Link rate 810000 lane count 1
[04:16:22] [PASSED] Link rate 540000 lane count 4
[04:16:22] [PASSED] Link rate 540000 lane count 2
[04:16:22] [PASSED] Link rate 540000 lane count 1
[04:16:22] [PASSED] Link rate 270000 lane count 4
[04:16:22] [PASSED] Link rate 270000 lane count 2
[04:16:22] [PASSED] Link rate 270000 lane count 1
[04:16:22] [PASSED] Link rate 162000 lane count 4
[04:16:22] [PASSED] Link rate 162000 lane count 2
[04:16:22] [PASSED] Link rate 162000 lane count 1
[04:16:22] ========== [PASSED] drm_test_dp_mst_calc_pbn_div ===========
[04:16:22] ========= drm_test_dp_mst_sideband_msg_req_decode =========
[04:16:22] [PASSED] DP_ENUM_PATH_RESOURCES with port number
[04:16:22] [PASSED] DP_POWER_UP_PHY with port number
[04:16:22] [PASSED] DP_POWER_DOWN_PHY with port number
[04:16:22] [PASSED] DP_ALLOCATE_PAYLOAD with SDP stream sinks
[04:16:22] [PASSED] DP_ALLOCATE_PAYLOAD with port number
[04:16:22] [PASSED] DP_ALLOCATE_PAYLOAD with VCPI
[04:16:22] [PASSED] DP_ALLOCATE_PAYLOAD with PBN
[04:16:22] [PASSED] DP_QUERY_PAYLOAD with port number
[04:16:22] [PASSED] DP_QUERY_PAYLOAD with VCPI
[04:16:22] [PASSED] DP_REMOTE_DPCD_READ with port number
[04:16:22] [PASSED] DP_REMOTE_DPCD_READ with DPCD address
[04:16:22] [PASSED] DP_REMOTE_DPCD_READ with max number of bytes
[04:16:22] [PASSED] DP_REMOTE_DPCD_WRITE with port number
[04:16:22] [PASSED] DP_REMOTE_DPCD_WRITE with DPCD address
[04:16:22] [PASSED] DP_REMOTE_DPCD_WRITE with data array
[04:16:22] [PASSED] DP_REMOTE_I2C_READ with port number
[04:16:22] [PASSED] DP_REMOTE_I2C_READ with I2C device ID
[04:16:22] [PASSED] DP_REMOTE_I2C_READ with transactions array
[04:16:22] [PASSED] DP_REMOTE_I2C_WRITE with port number
[04:16:22] [PASSED] DP_REMOTE_I2C_WRITE with I2C device ID
[04:16:22] [PASSED] DP_REMOTE_I2C_WRITE with data array
[04:16:22] [PASSED] DP_QUERY_STREAM_ENC_STATUS with stream ID
[04:16:22] [PASSED] DP_QUERY_STREAM_ENC_STATUS with client ID
[04:16:22] [PASSED] DP_QUERY_STREAM_ENC_STATUS with stream event
[04:16:22] [PASSED] DP_QUERY_STREAM_ENC_STATUS with valid stream event
[04:16:22] [PASSED] DP_QUERY_STREAM_ENC_STATUS with stream behavior
[04:16:22] [PASSED] DP_QUERY_STREAM_ENC_STATUS with a valid stream behavior
[04:16:22] ===== [PASSED] drm_test_dp_mst_sideband_msg_req_decode =====
[04:16:22] ================ [PASSED] drm_dp_mst_helper ================
[04:16:22] ================== drm_exec (7 subtests) ===================
[04:16:22] [PASSED] sanitycheck
[04:16:22] [PASSED] test_lock
[04:16:22] [PASSED] test_lock_unlock
[04:16:22] [PASSED] test_duplicates
[04:16:22] [PASSED] test_prepare
[04:16:22] [PASSED] test_prepare_array
[04:16:22] [PASSED] test_multiple_loops
[04:16:22] ==================== [PASSED] drm_exec =====================
[04:16:22] =========== drm_format_helper_test (17 subtests) ===========
[04:16:22] ============== drm_test_fb_xrgb8888_to_gray8 ==============
[04:16:22] [PASSED] single_pixel_source_buffer
[04:16:22] [PASSED] single_pixel_clip_rectangle
[04:16:22] [PASSED] well_known_colors
[04:16:22] [PASSED] destination_pitch
[04:16:22] ========== [PASSED] drm_test_fb_xrgb8888_to_gray8 ==========
[04:16:22] ============= drm_test_fb_xrgb8888_to_rgb332 ==============
[04:16:22] [PASSED] single_pixel_source_buffer
[04:16:22] [PASSED] single_pixel_clip_rectangle
[04:16:22] [PASSED] well_known_colors
[04:16:22] [PASSED] destination_pitch
[04:16:22] ========= [PASSED] drm_test_fb_xrgb8888_to_rgb332 ==========
[04:16:22] ============= drm_test_fb_xrgb8888_to_rgb565 ==============
[04:16:22] [PASSED] single_pixel_source_buffer
[04:16:22] [PASSED] single_pixel_clip_rectangle
[04:16:22] [PASSED] well_known_colors
[04:16:22] [PASSED] destination_pitch
[04:16:22] ========= [PASSED] drm_test_fb_xrgb8888_to_rgb565 ==========
[04:16:22] ============ drm_test_fb_xrgb8888_to_xrgb1555 =============
[04:16:22] [PASSED] single_pixel_source_buffer
[04:16:22] [PASSED] single_pixel_clip_rectangle
[04:16:22] [PASSED] well_known_colors
[04:16:22] [PASSED] destination_pitch
[04:16:22] ======== [PASSED] drm_test_fb_xrgb8888_to_xrgb1555 =========
[04:16:22] ============ drm_test_fb_xrgb8888_to_argb1555 =============
[04:16:22] [PASSED] single_pixel_source_buffer
[04:16:22] [PASSED] single_pixel_clip_rectangle
[04:16:22] [PASSED] well_known_colors
[04:16:22] [PASSED] destination_pitch
[04:16:22] ======== [PASSED] drm_test_fb_xrgb8888_to_argb1555 =========
[04:16:22] ============ drm_test_fb_xrgb8888_to_rgba5551 =============
[04:16:22] [PASSED] single_pixel_source_buffer
[04:16:22] [PASSED] single_pixel_clip_rectangle
[04:16:22] [PASSED] well_known_colors
[04:16:22] [PASSED] destination_pitch
[04:16:22] ======== [PASSED] drm_test_fb_xrgb8888_to_rgba5551 =========
[04:16:22] ============= drm_test_fb_xrgb8888_to_rgb888 ==============
[04:16:22] [PASSED] single_pixel_source_buffer
[04:16:22] [PASSED] single_pixel_clip_rectangle
[04:16:22] [PASSED] well_known_colors
[04:16:22] [PASSED] destination_pitch
[04:16:22] ========= [PASSED] drm_test_fb_xrgb8888_to_rgb888 ==========
[04:16:22] ============= drm_test_fb_xrgb8888_to_bgr888 ==============
[04:16:22] [PASSED] single_pixel_source_buffer
[04:16:22] [PASSED] single_pixel_clip_rectangle
[04:16:22] [PASSED] well_known_colors
[04:16:22] [PASSED] destination_pitch
[04:16:22] ========= [PASSED] drm_test_fb_xrgb8888_to_bgr888 ==========
[04:16:22] ============ drm_test_fb_xrgb8888_to_argb8888 =============
[04:16:22] [PASSED] single_pixel_source_buffer
[04:16:22] [PASSED] single_pixel_clip_rectangle
[04:16:22] [PASSED] well_known_colors
[04:16:22] [PASSED] destination_pitch
[04:16:22] ======== [PASSED] drm_test_fb_xrgb8888_to_argb8888 =========
[04:16:22] =========== drm_test_fb_xrgb8888_to_xrgb2101010 ===========
[04:16:22] [PASSED] single_pixel_source_buffer
[04:16:22] [PASSED] single_pixel_clip_rectangle
[04:16:22] [PASSED] well_known_colors
[04:16:22] [PASSED] destination_pitch
[04:16:22] ======= [PASSED] drm_test_fb_xrgb8888_to_xrgb2101010 =======
[04:16:22] =========== drm_test_fb_xrgb8888_to_argb2101010 ===========
[04:16:22] [PASSED] single_pixel_source_buffer
[04:16:22] [PASSED] single_pixel_clip_rectangle
[04:16:22] [PASSED] well_known_colors
[04:16:22] [PASSED] destination_pitch
[04:16:22] ======= [PASSED] drm_test_fb_xrgb8888_to_argb2101010 =======
[04:16:22] ============== drm_test_fb_xrgb8888_to_mono ===============
[04:16:22] [PASSED] single_pixel_source_buffer
[04:16:22] [PASSED] single_pixel_clip_rectangle
[04:16:22] [PASSED] well_known_colors
[04:16:22] [PASSED] destination_pitch
[04:16:22] ========== [PASSED] drm_test_fb_xrgb8888_to_mono ===========
[04:16:22] ==================== drm_test_fb_swab =====================
[04:16:22] [PASSED] single_pixel_source_buffer
[04:16:22] [PASSED] single_pixel_clip_rectangle
[04:16:22] [PASSED] well_known_colors
[04:16:22] [PASSED] destination_pitch
[04:16:22] ================ [PASSED] drm_test_fb_swab =================
[04:16:22] ============ drm_test_fb_xrgb8888_to_xbgr8888 =============
[04:16:22] [PASSED] single_pixel_source_buffer
[04:16:22] [PASSED] single_pixel_clip_rectangle
[04:16:22] [PASSED] well_known_colors
[04:16:22] [PASSED] destination_pitch
[04:16:22] ======== [PASSED] drm_test_fb_xrgb8888_to_xbgr8888 =========
[04:16:22] ============ drm_test_fb_xrgb8888_to_abgr8888 =============
[04:16:22] [PASSED] single_pixel_source_buffer
[04:16:22] [PASSED] single_pixel_clip_rectangle
[04:16:22] [PASSED] well_known_colors
[04:16:22] [PASSED] destination_pitch
[04:16:22] ======== [PASSED] drm_test_fb_xrgb8888_to_abgr8888 =========
[04:16:22] ================= drm_test_fb_clip_offset =================
[04:16:22] [PASSED] pass through
[04:16:22] [PASSED] horizontal offset
[04:16:22] [PASSED] vertical offset
[04:16:22] [PASSED] horizontal and vertical offset
[04:16:22] [PASSED] horizontal offset (custom pitch)
[04:16:22] [PASSED] vertical offset (custom pitch)
[04:16:22] [PASSED] horizontal and vertical offset (custom pitch)
[04:16:22] ============= [PASSED] drm_test_fb_clip_offset =============
[04:16:22] =================== drm_test_fb_memcpy ====================
[04:16:22] [PASSED] single_pixel_source_buffer: XR24 little-endian (0x34325258)
[04:16:22] [PASSED] single_pixel_source_buffer: XRA8 little-endian (0x38415258)
[04:16:22] [PASSED] single_pixel_source_buffer: YU24 little-endian (0x34325559)
[04:16:22] [PASSED] single_pixel_clip_rectangle: XB24 little-endian (0x34324258)
[04:16:22] [PASSED] single_pixel_clip_rectangle: XRA8 little-endian (0x38415258)
[04:16:22] [PASSED] single_pixel_clip_rectangle: YU24 little-endian (0x34325559)
[04:16:22] [PASSED] well_known_colors: XB24 little-endian (0x34324258)
[04:16:22] [PASSED] well_known_colors: XRA8 little-endian (0x38415258)
[04:16:22] [PASSED] well_known_colors: YU24 little-endian (0x34325559)
[04:16:22] [PASSED] destination_pitch: XB24 little-endian (0x34324258)
[04:16:22] [PASSED] destination_pitch: XRA8 little-endian (0x38415258)
[04:16:22] [PASSED] destination_pitch: YU24 little-endian (0x34325559)
[04:16:22] =============== [PASSED] drm_test_fb_memcpy ================
[04:16:22] ============= [PASSED] drm_format_helper_test ==============
[04:16:22] ================= drm_format (18 subtests) =================
[04:16:22] [PASSED] drm_test_format_block_width_invalid
[04:16:22] [PASSED] drm_test_format_block_width_one_plane
[04:16:22] [PASSED] drm_test_format_block_width_two_plane
[04:16:22] [PASSED] drm_test_format_block_width_three_plane
[04:16:22] [PASSED] drm_test_format_block_width_tiled
[04:16:22] [PASSED] drm_test_format_block_height_invalid
[04:16:22] [PASSED] drm_test_format_block_height_one_plane
[04:16:22] [PASSED] drm_test_format_block_height_two_plane
[04:16:22] [PASSED] drm_test_format_block_height_three_plane
[04:16:22] [PASSED] drm_test_format_block_height_tiled
[04:16:22] [PASSED] drm_test_format_min_pitch_invalid
[04:16:22] [PASSED] drm_test_format_min_pitch_one_plane_8bpp
[04:16:22] [PASSED] drm_test_format_min_pitch_one_plane_16bpp
[04:16:22] [PASSED] drm_test_format_min_pitch_one_plane_24bpp
[04:16:22] [PASSED] drm_test_format_min_pitch_one_plane_32bpp
[04:16:22] [PASSED] drm_test_format_min_pitch_two_plane
[04:16:22] [PASSED] drm_test_format_min_pitch_three_plane_8bpp
[04:16:22] [PASSED] drm_test_format_min_pitch_tiled
[04:16:22] =================== [PASSED] drm_format ====================
[04:16:22] ============== drm_framebuffer (10 subtests) ===============
[04:16:22] ========== drm_test_framebuffer_check_src_coords ==========
[04:16:22] [PASSED] Success: source fits into fb
[04:16:22] [PASSED] Fail: overflowing fb with x-axis coordinate
[04:16:22] [PASSED] Fail: overflowing fb with y-axis coordinate
[04:16:22] [PASSED] Fail: overflowing fb with source width
[04:16:22] [PASSED] Fail: overflowing fb with source height
[04:16:22] ====== [PASSED] drm_test_framebuffer_check_src_coords ======
[04:16:22] [PASSED] drm_test_framebuffer_cleanup
[04:16:22] =============== drm_test_framebuffer_create ===============
[04:16:22] [PASSED] ABGR8888 normal sizes
[04:16:22] [PASSED] ABGR8888 max sizes
[04:16:22] [PASSED] ABGR8888 pitch greater than min required
[04:16:22] [PASSED] ABGR8888 pitch less than min required
[04:16:22] [PASSED] ABGR8888 Invalid width
[04:16:22] [PASSED] ABGR8888 Invalid buffer handle
[04:16:22] [PASSED] No pixel format
[04:16:22] [PASSED] ABGR8888 Width 0
[04:16:22] [PASSED] ABGR8888 Height 0
[04:16:22] [PASSED] ABGR8888 Out of bound height * pitch combination
[04:16:22] [PASSED] ABGR8888 Large buffer offset
[04:16:22] [PASSED] ABGR8888 Buffer offset for inexistent plane
[04:16:22] [PASSED] ABGR8888 Invalid flag
[04:16:22] [PASSED] ABGR8888 Set DRM_MODE_FB_MODIFIERS without modifiers
[04:16:22] [PASSED] ABGR8888 Valid buffer modifier
[04:16:22] [PASSED] ABGR8888 Invalid buffer modifier(DRM_FORMAT_MOD_SAMSUNG_64_32_TILE)
[04:16:22] [PASSED] ABGR8888 Extra pitches without DRM_MODE_FB_MODIFIERS
[04:16:22] [PASSED] ABGR8888 Extra pitches with DRM_MODE_FB_MODIFIERS
[04:16:22] [PASSED] NV12 Normal sizes
[04:16:22] [PASSED] NV12 Max sizes
[04:16:22] [PASSED] NV12 Invalid pitch
[04:16:22] [PASSED] NV12 Invalid modifier/missing DRM_MODE_FB_MODIFIERS flag
[04:16:22] [PASSED] NV12 different modifier per-plane
[04:16:22] [PASSED] NV12 with DRM_FORMAT_MOD_SAMSUNG_64_32_TILE
[04:16:22] [PASSED] NV12 Valid modifiers without DRM_MODE_FB_MODIFIERS
[04:16:22] [PASSED] NV12 Modifier for inexistent plane
[04:16:22] [PASSED] NV12 Handle for inexistent plane
[04:16:22] [PASSED] NV12 Handle for inexistent plane without DRM_MODE_FB_MODIFIERS
[04:16:22] [PASSED] YVU420 DRM_MODE_FB_MODIFIERS set without modifier
[04:16:22] [PASSED] YVU420 Normal sizes
[04:16:22] [PASSED] YVU420 Max sizes
[04:16:22] [PASSED] YVU420 Invalid pitch
[04:16:22] [PASSED] YVU420 Different pitches
[04:16:22] [PASSED] YVU420 Different buffer offsets/pitches
[04:16:22] [PASSED] YVU420 Modifier set just for plane 0, without DRM_MODE_FB_MODIFIERS
[04:16:22] [PASSED] YVU420 Modifier set just for planes 0, 1, without DRM_MODE_FB_MODIFIERS
[04:16:22] [PASSED] YVU420 Modifier set just for plane 0, 1, with DRM_MODE_FB_MODIFIERS
[04:16:22] [PASSED] YVU420 Valid modifier
[04:16:22] [PASSED] YVU420 Different modifiers per plane
[04:16:22] [PASSED] YVU420 Modifier for inexistent plane
[04:16:22] [PASSED] YUV420_10BIT Invalid modifier(DRM_FORMAT_MOD_LINEAR)
[04:16:22] [PASSED] X0L2 Normal sizes
[04:16:22] [PASSED] X0L2 Max sizes
[04:16:22] [PASSED] X0L2 Invalid pitch
[04:16:22] [PASSED] X0L2 Pitch greater than minimum required
[04:16:22] [PASSED] X0L2 Handle for inexistent plane
[04:16:22] [PASSED] X0L2 Offset for inexistent plane, without DRM_MODE_FB_MODIFIERS set
[04:16:22] [PASSED] X0L2 Modifier without DRM_MODE_FB_MODIFIERS set
[04:16:22] [PASSED] X0L2 Valid modifier
[04:16:22] [PASSED] X0L2 Modifier for inexistent plane
[04:16:22] =========== [PASSED] drm_test_framebuffer_create ===========
[04:16:22] [PASSED] drm_test_framebuffer_free
[04:16:22] [PASSED] drm_test_framebuffer_init
[04:16:22] [PASSED] drm_test_framebuffer_init_bad_format
[04:16:22] [PASSED] drm_test_framebuffer_init_dev_mismatch
[04:16:22] [PASSED] drm_test_framebuffer_lookup
[04:16:22] [PASSED] drm_test_framebuffer_lookup_inexistent
[04:16:22] [PASSED] drm_test_framebuffer_modifiers_not_supported
[04:16:22] ================= [PASSED] drm_framebuffer =================
[04:16:22] ================ drm_gem_shmem (8 subtests) ================
[04:16:22] [PASSED] drm_gem_shmem_test_obj_create
[04:16:22] [PASSED] drm_gem_shmem_test_obj_create_private
[04:16:22] [PASSED] drm_gem_shmem_test_pin_pages
[04:16:22] [PASSED] drm_gem_shmem_test_vmap
[04:16:22] [PASSED] drm_gem_shmem_test_get_sg_table
[04:16:22] [PASSED] drm_gem_shmem_test_get_pages_sgt
[04:16:22] [PASSED] drm_gem_shmem_test_madvise
[04:16:22] [PASSED] drm_gem_shmem_test_purge
[04:16:22] ================== [PASSED] drm_gem_shmem ==================
[04:16:22] === drm_atomic_helper_connector_hdmi_check (29 subtests) ===
[04:16:22] [PASSED] drm_test_check_broadcast_rgb_auto_cea_mode
[04:16:22] [PASSED] drm_test_check_broadcast_rgb_auto_cea_mode_vic_1
[04:16:22] [PASSED] drm_test_check_broadcast_rgb_full_cea_mode
[04:16:22] [PASSED] drm_test_check_broadcast_rgb_full_cea_mode_vic_1
[04:16:22] [PASSED] drm_test_check_broadcast_rgb_limited_cea_mode
[04:16:22] [PASSED] drm_test_check_broadcast_rgb_limited_cea_mode_vic_1
[04:16:22] ====== drm_test_check_broadcast_rgb_cea_mode_yuv420 =======
[04:16:22] [PASSED] Automatic
[04:16:22] [PASSED] Full
[04:16:22] [PASSED] Limited 16:235
[04:16:22] == [PASSED] drm_test_check_broadcast_rgb_cea_mode_yuv420 ===
[04:16:22] [PASSED] drm_test_check_broadcast_rgb_crtc_mode_changed
[04:16:22] [PASSED] drm_test_check_broadcast_rgb_crtc_mode_not_changed
[04:16:22] [PASSED] drm_test_check_disable_connector
[04:16:22] [PASSED] drm_test_check_hdmi_funcs_reject_rate
[04:16:22] [PASSED] drm_test_check_max_tmds_rate_bpc_fallback_rgb
[04:16:22] [PASSED] drm_test_check_max_tmds_rate_bpc_fallback_yuv420
[04:16:22] [PASSED] drm_test_check_max_tmds_rate_bpc_fallback_ignore_yuv422
[04:16:22] [PASSED] drm_test_check_max_tmds_rate_bpc_fallback_ignore_yuv420
[04:16:22] [PASSED] drm_test_check_driver_unsupported_fallback_yuv420
[04:16:22] [PASSED] drm_test_check_output_bpc_crtc_mode_changed
[04:16:22] [PASSED] drm_test_check_output_bpc_crtc_mode_not_changed
[04:16:22] [PASSED] drm_test_check_output_bpc_dvi
[04:16:22] [PASSED] drm_test_check_output_bpc_format_vic_1
[04:16:22] [PASSED] drm_test_check_output_bpc_format_display_8bpc_only
[04:16:22] [PASSED] drm_test_check_output_bpc_format_display_rgb_only
[04:16:22] [PASSED] drm_test_check_output_bpc_format_driver_8bpc_only
[04:16:22] [PASSED] drm_test_check_output_bpc_format_driver_rgb_only
[04:16:22] [PASSED] drm_test_check_tmds_char_rate_rgb_8bpc
[04:16:22] [PASSED] drm_test_check_tmds_char_rate_rgb_10bpc
[04:16:22] [PASSED] drm_test_check_tmds_char_rate_rgb_12bpc
[04:16:22] ============ drm_test_check_hdmi_color_format =============
[04:16:22] [PASSED] AUTO -> RGB
[04:16:22] [PASSED] YCBCR422 -> YUV422
[04:16:22] [PASSED] YCBCR420 -> YUV420
[04:16:22] [PASSED] YCBCR444 -> YUV444
[04:16:22] [PASSED] RGB -> RGB
[04:16:22] ======== [PASSED] drm_test_check_hdmi_color_format =========
[04:16:22] ======== drm_test_check_hdmi_color_format_420_only ========
[04:16:22] [PASSED] RGB should fail
[04:16:22] [PASSED] YUV444 should fail
[04:16:22] [PASSED] YUV422 should fail
[04:16:22] [PASSED] YUV420 should work
[04:16:22] ==== [PASSED] drm_test_check_hdmi_color_format_420_only ====
[04:16:22] ===== [PASSED] drm_atomic_helper_connector_hdmi_check ======
[04:16:22] === drm_atomic_helper_connector_hdmi_reset (6 subtests) ====
[04:16:22] [PASSED] drm_test_check_broadcast_rgb_value
[04:16:22] [PASSED] drm_test_check_bpc_8_value
[04:16:22] [PASSED] drm_test_check_bpc_10_value
[04:16:22] [PASSED] drm_test_check_bpc_12_value
[04:16:22] [PASSED] drm_test_check_format_value
[04:16:22] [PASSED] drm_test_check_tmds_char_value
[04:16:22] ===== [PASSED] drm_atomic_helper_connector_hdmi_reset ======
[04:16:22] = drm_atomic_helper_connector_hdmi_mode_valid (7 subtests) =
[04:16:22] [PASSED] drm_test_check_mode_valid
[04:16:22] [PASSED] drm_test_check_mode_valid_reject
[04:16:22] [PASSED] drm_test_check_mode_valid_reject_rate
[04:16:22] [PASSED] drm_test_check_mode_valid_reject_max_clock
[04:16:22] [PASSED] drm_test_check_mode_valid_yuv420_only_max_clock
[04:16:22] [PASSED] drm_test_check_mode_valid_reject_yuv420_only_connector
[04:16:22] [PASSED] drm_test_check_mode_valid_accept_yuv420_also_connector_rgb
[04:16:22] === [PASSED] drm_atomic_helper_connector_hdmi_mode_valid ===
[04:16:22] = drm_atomic_helper_connector_hdmi_infoframes (5 subtests) =
[04:16:22] [PASSED] drm_test_check_infoframes
[04:16:22] [PASSED] drm_test_check_reject_avi_infoframe
[04:16:22] [PASSED] drm_test_check_reject_hdr_infoframe_bpc_8
[04:16:22] [PASSED] drm_test_check_reject_hdr_infoframe_bpc_10
[04:16:22] [PASSED] drm_test_check_reject_audio_infoframe
[04:16:22] === [PASSED] drm_atomic_helper_connector_hdmi_infoframes ===
[04:16:22] ================= drm_managed (2 subtests) =================
[04:16:22] [PASSED] drm_test_managed_release_action
[04:16:22] [PASSED] drm_test_managed_run_action
[04:16:22] =================== [PASSED] drm_managed ===================
[04:16:22] =================== drm_mm (6 subtests) ====================
[04:16:22] [PASSED] drm_test_mm_init
[04:16:22] [PASSED] drm_test_mm_debug
[04:16:22] [PASSED] drm_test_mm_align32
[04:16:22] [PASSED] drm_test_mm_align64
[04:16:22] [PASSED] drm_test_mm_lowest
[04:16:22] [PASSED] drm_test_mm_highest
[04:16:22] ===================== [PASSED] drm_mm ======================
[04:16:22] ============= drm_modes_analog_tv (5 subtests) =============
[04:16:22] [PASSED] drm_test_modes_analog_tv_mono_576i
[04:16:22] [PASSED] drm_test_modes_analog_tv_ntsc_480i
[04:16:22] [PASSED] drm_test_modes_analog_tv_ntsc_480i_inlined
[04:16:22] [PASSED] drm_test_modes_analog_tv_pal_576i
[04:16:22] [PASSED] drm_test_modes_analog_tv_pal_576i_inlined
[04:16:22] =============== [PASSED] drm_modes_analog_tv ===============
[04:16:22] ============== drm_plane_helper (2 subtests) ===============
[04:16:22] =============== drm_test_check_plane_state ================
[04:16:22] [PASSED] clipping_simple
[04:16:22] [PASSED] clipping_rotate_reflect
[04:16:22] [PASSED] positioning_simple
[04:16:22] [PASSED] upscaling
[04:16:22] [PASSED] downscaling
[04:16:22] [PASSED] rounding1
[04:16:22] [PASSED] rounding2
[04:16:22] [PASSED] rounding3
[04:16:22] [PASSED] rounding4
[04:16:22] =========== [PASSED] drm_test_check_plane_state ============
[04:16:22] =========== drm_test_check_invalid_plane_state ============
[04:16:22] [PASSED] positioning_invalid
[04:16:22] [PASSED] upscaling_invalid
[04:16:22] [PASSED] downscaling_invalid
[04:16:22] ======= [PASSED] drm_test_check_invalid_plane_state ========
[04:16:22] ================ [PASSED] drm_plane_helper =================
[04:16:22] ====== drm_connector_helper_tv_get_modes (1 subtest) =======
[04:16:22] ====== drm_test_connector_helper_tv_get_modes_check =======
[04:16:22] [PASSED] None
[04:16:22] [PASSED] PAL
[04:16:22] [PASSED] NTSC
[04:16:22] [PASSED] Both, NTSC Default
[04:16:22] [PASSED] Both, PAL Default
[04:16:22] [PASSED] Both, NTSC Default, with PAL on command-line
[04:16:22] [PASSED] Both, PAL Default, with NTSC on command-line
[04:16:22] == [PASSED] drm_test_connector_helper_tv_get_modes_check ===
[04:16:22] ======== [PASSED] drm_connector_helper_tv_get_modes ========
[04:16:22] ================== drm_rect (9 subtests) ===================
[04:16:22] [PASSED] drm_test_rect_clip_scaled_div_by_zero
[04:16:22] [PASSED] drm_test_rect_clip_scaled_not_clipped
[04:16:22] [PASSED] drm_test_rect_clip_scaled_clipped
[04:16:22] [PASSED] drm_test_rect_clip_scaled_signed_vs_unsigned
[04:16:22] ================= drm_test_rect_intersect =================
[04:16:22] [PASSED] top-left x bottom-right: 2x2+1+1 x 2x2+0+0
[04:16:22] [PASSED] top-right x bottom-left: 2x2+0+0 x 2x2+1-1
[04:16:22] [PASSED] bottom-left x top-right: 2x2+1-1 x 2x2+0+0
[04:16:22] [PASSED] bottom-right x top-left: 2x2+0+0 x 2x2+1+1
[04:16:22] [PASSED] right x left: 2x1+0+0 x 3x1+1+0
[04:16:22] [PASSED] left x right: 3x1+1+0 x 2x1+0+0
[04:16:22] [PASSED] up x bottom: 1x2+0+0 x 1x3+0-1
[04:16:22] [PASSED] bottom x up: 1x3+0-1 x 1x2+0+0
[04:16:22] [PASSED] touching corner: 1x1+0+0 x 2x2+1+1
[04:16:22] [PASSED] touching side: 1x1+0+0 x 1x1+1+0
[04:16:22] [PASSED] equal rects: 2x2+0+0 x 2x2+0+0
[04:16:22] [PASSED] inside another: 2x2+0+0 x 1x1+1+1
[04:16:22] [PASSED] far away: 1x1+0+0 x 1x1+3+6
[04:16:22] [PASSED] points intersecting: 0x0+5+10 x 0x0+5+10
[04:16:22] [PASSED] points not intersecting: 0x0+0+0 x 0x0+5+10
[04:16:22] ============= [PASSED] drm_test_rect_intersect =============
[04:16:22] ================ drm_test_rect_calc_hscale ================
[04:16:22] [PASSED] normal use
[04:16:22] [PASSED] out of max range
[04:16:22] [PASSED] out of min range
[04:16:22] [PASSED] zero dst
[04:16:22] [PASSED] negative src
[04:16:22] [PASSED] negative dst
[04:16:22] ============ [PASSED] drm_test_rect_calc_hscale ============
[04:16:22] ================ drm_test_rect_calc_vscale ================
[04:16:22] [PASSED] normal use
[04:16:22] [PASSED] out of max range
[04:16:22] [PASSED] out of min range
[04:16:22] [PASSED] zero dst
[04:16:22] [PASSED] negative src
[04:16:22] [PASSED] negative dst
[04:16:22] ============ [PASSED] drm_test_rect_calc_vscale ============
[04:16:22] ================== drm_test_rect_rotate ===================
[04:16:22] [PASSED] reflect-x
[04:16:22] [PASSED] reflect-y
[04:16:22] [PASSED] rotate-0
[04:16:22] [PASSED] rotate-90
[04:16:22] [PASSED] rotate-180
[04:16:22] [PASSED] rotate-270
[04:16:22] ============== [PASSED] drm_test_rect_rotate ===============
[04:16:22] ================ drm_test_rect_rotate_inv =================
[04:16:22] [PASSED] reflect-x
[04:16:22] [PASSED] reflect-y
[04:16:22] [PASSED] rotate-0
[04:16:22] [PASSED] rotate-90
[04:16:22] [PASSED] rotate-180
[04:16:22] [PASSED] rotate-270
[04:16:22] ============ [PASSED] drm_test_rect_rotate_inv =============
[04:16:22] ==================== [PASSED] drm_rect =====================
[04:16:22] ============ drm_sysfb_modeset_test (1 subtest) ============
[04:16:22] ============ drm_test_sysfb_build_fourcc_list =============
[04:16:22] [PASSED] no native formats
[04:16:22] [PASSED] XRGB8888 as native format
[04:16:22] [PASSED] remove duplicates
[04:16:22] [PASSED] convert alpha formats
[04:16:22] [PASSED] random formats
[04:16:22] ======== [PASSED] drm_test_sysfb_build_fourcc_list =========
[04:16:22] ============= [PASSED] drm_sysfb_modeset_test ==============
[04:16:22] ================== drm_fixp (2 subtests) ===================
[04:16:22] [PASSED] drm_test_int2fixp
[04:16:22] [PASSED] drm_test_sm2fixp
[04:16:22] ==================== [PASSED] drm_fixp =====================
[04:16:22] ============================================================
[04:16:22] Testing complete. Ran 637 tests: passed: 637
[04:16:22] Elapsed time: 26.593s total, 1.807s configuring, 24.616s building, 0.152s running
+ /kernel/tools/testing/kunit/kunit.py run --kunitconfig /kernel/drivers/gpu/drm/ttm/tests/.kunitconfig
[04:16:22] Configuring KUnit Kernel ...
Regenerating .config ...
Populating config with:
$ make ARCH=um O=.kunit olddefconfig
[04:16:24] Building KUnit Kernel ...
Populating config with:
$ make ARCH=um O=.kunit olddefconfig
Building with:
$ make all compile_commands.json scripts_gdb ARCH=um O=.kunit --jobs=48
[04:16:34] Starting KUnit Kernel (1/1)...
[04:16:34] ============================================================
Running tests with:
$ .kunit/linux kunit.enable=1 mem=1G console=tty kunit_shutdown=halt
[04:16:34] ================= ttm_device (5 subtests) ==================
[04:16:34] [PASSED] ttm_device_init_basic
[04:16:34] [PASSED] ttm_device_init_multiple
[04:16:34] [PASSED] ttm_device_fini_basic
[04:16:34] [PASSED] ttm_device_init_no_vma_man
[04:16:34] ================== ttm_device_init_pools ==================
[04:16:34] [PASSED] No DMA allocations, no DMA32 required
[04:16:34] [PASSED] DMA allocations, DMA32 required
[04:16:34] [PASSED] No DMA allocations, DMA32 required
[04:16:34] [PASSED] DMA allocations, no DMA32 required
[04:16:34] ============== [PASSED] ttm_device_init_pools ==============
[04:16:34] =================== [PASSED] ttm_device ====================
[04:16:34] ================== ttm_pool (8 subtests) ===================
[04:16:34] ================== ttm_pool_alloc_basic ===================
[04:16:34] [PASSED] One page
[04:16:34] [PASSED] More than one page
[04:16:34] [PASSED] Above the allocation limit
[04:16:34] [PASSED] One page, with coherent DMA mappings enabled
[04:16:34] [PASSED] Above the allocation limit, with coherent DMA mappings enabled
[04:16:34] ============== [PASSED] ttm_pool_alloc_basic ===============
[04:16:34] ============== ttm_pool_alloc_basic_dma_addr ==============
[04:16:34] [PASSED] One page
[04:16:34] [PASSED] More than one page
[04:16:34] [PASSED] Above the allocation limit
[04:16:34] [PASSED] One page, with coherent DMA mappings enabled
[04:16:34] [PASSED] Above the allocation limit, with coherent DMA mappings enabled
[04:16:34] ========== [PASSED] ttm_pool_alloc_basic_dma_addr ==========
[04:16:34] [PASSED] ttm_pool_alloc_order_caching_match
[04:16:34] [PASSED] ttm_pool_alloc_caching_mismatch
[04:16:34] [PASSED] ttm_pool_alloc_order_mismatch
[04:16:34] [PASSED] ttm_pool_free_dma_alloc
[04:16:34] [PASSED] ttm_pool_free_no_dma_alloc
[04:16:34] [PASSED] ttm_pool_fini_basic
[04:16:34] ==================== [PASSED] ttm_pool =====================
[04:16:34] ================ ttm_resource (8 subtests) =================
[04:16:34] ================= ttm_resource_init_basic =================
[04:16:34] [PASSED] Init resource in TTM_PL_SYSTEM
[04:16:34] [PASSED] Init resource in TTM_PL_VRAM
[04:16:34] [PASSED] Init resource in a private placement
[04:16:34] [PASSED] Init resource in TTM_PL_SYSTEM, set placement flags
[04:16:34] ============= [PASSED] ttm_resource_init_basic =============
[04:16:34] [PASSED] ttm_resource_init_pinned
[04:16:34] [PASSED] ttm_resource_fini_basic
[04:16:34] [PASSED] ttm_resource_manager_init_basic
[04:16:34] [PASSED] ttm_resource_manager_usage_basic
[04:16:34] [PASSED] ttm_resource_manager_set_used_basic
[04:16:34] [PASSED] ttm_sys_man_alloc_basic
[04:16:34] [PASSED] ttm_sys_man_free_basic
[04:16:34] ================== [PASSED] ttm_resource ===================
[04:16:34] =================== ttm_tt (15 subtests) ===================
[04:16:34] ==================== ttm_tt_init_basic ====================
[04:16:34] [PASSED] Page-aligned size
[04:16:34] [PASSED] Extra pages requested
[04:16:34] ================ [PASSED] ttm_tt_init_basic ================
[04:16:34] [PASSED] ttm_tt_init_misaligned
[04:16:34] [PASSED] ttm_tt_fini_basic
[04:16:34] [PASSED] ttm_tt_fini_sg
[04:16:34] [PASSED] ttm_tt_fini_shmem
[04:16:34] [PASSED] ttm_tt_create_basic
[04:16:34] [PASSED] ttm_tt_create_invalid_bo_type
[04:16:34] [PASSED] ttm_tt_create_ttm_exists
[04:16:34] [PASSED] ttm_tt_create_failed
[04:16:34] [PASSED] ttm_tt_destroy_basic
[04:16:34] [PASSED] ttm_tt_populate_null_ttm
[04:16:34] [PASSED] ttm_tt_populate_populated_ttm
[04:16:34] [PASSED] ttm_tt_unpopulate_basic
[04:16:34] [PASSED] ttm_tt_unpopulate_empty_ttm
[04:16:34] [PASSED] ttm_tt_swapin_basic
[04:16:34] ===================== [PASSED] ttm_tt ======================
[04:16:34] =================== ttm_bo (14 subtests) ===================
[04:16:34] =========== ttm_bo_reserve_optimistic_no_ticket ===========
[04:16:34] [PASSED] Cannot be interrupted and sleeps
[04:16:34] [PASSED] Cannot be interrupted, locks straight away
[04:16:34] [PASSED] Can be interrupted, sleeps
[04:16:34] ======= [PASSED] ttm_bo_reserve_optimistic_no_ticket =======
[04:16:34] [PASSED] ttm_bo_reserve_locked_no_sleep
[04:16:34] [PASSED] ttm_bo_reserve_no_wait_ticket
[04:16:34] [PASSED] ttm_bo_reserve_double_resv
[04:16:34] [PASSED] ttm_bo_reserve_interrupted
[04:16:34] [PASSED] ttm_bo_reserve_deadlock
[04:16:34] [PASSED] ttm_bo_unreserve_basic
[04:16:34] [PASSED] ttm_bo_unreserve_pinned
[04:16:34] [PASSED] ttm_bo_unreserve_bulk
[04:16:34] [PASSED] ttm_bo_fini_basic
[04:16:34] [PASSED] ttm_bo_fini_shared_resv
[04:16:34] [PASSED] ttm_bo_pin_basic
[04:16:34] [PASSED] ttm_bo_pin_unpin_resource
[04:16:34] [PASSED] ttm_bo_multiple_pin_one_unpin
[04:16:34] ===================== [PASSED] ttm_bo ======================
[04:16:34] ============== ttm_bo_validate (22 subtests) ===============
[04:16:34] ============== ttm_bo_init_reserved_sys_man ===============
[04:16:34] [PASSED] Buffer object for userspace
[04:16:34] [PASSED] Kernel buffer object
[04:16:34] [PASSED] Shared buffer object
[04:16:34] ========== [PASSED] ttm_bo_init_reserved_sys_man ===========
[04:16:34] ============== ttm_bo_init_reserved_mock_man ==============
[04:16:34] [PASSED] Buffer object for userspace
[04:16:34] [PASSED] Kernel buffer object
[04:16:34] [PASSED] Shared buffer object
[04:16:34] ========== [PASSED] ttm_bo_init_reserved_mock_man ==========
[04:16:34] [PASSED] ttm_bo_init_reserved_resv
[04:16:34] ================== ttm_bo_validate_basic ==================
[04:16:34] [PASSED] Buffer object for userspace
[04:16:34] [PASSED] Kernel buffer object
[04:16:34] [PASSED] Shared buffer object
[04:16:34] ============== [PASSED] ttm_bo_validate_basic ==============
[04:16:34] [PASSED] ttm_bo_validate_invalid_placement
[04:16:34] ============= ttm_bo_validate_same_placement ==============
[04:16:34] [PASSED] System manager
[04:16:34] [PASSED] VRAM manager
[04:16:34] ========= [PASSED] ttm_bo_validate_same_placement ==========
[04:16:34] [PASSED] ttm_bo_validate_failed_alloc
[04:16:34] [PASSED] ttm_bo_validate_pinned
[04:16:34] [PASSED] ttm_bo_validate_busy_placement
[04:16:34] ================ ttm_bo_validate_multihop =================
[04:16:34] [PASSED] Buffer object for userspace
[04:16:34] [PASSED] Kernel buffer object
[04:16:34] [PASSED] Shared buffer object
[04:16:34] ============ [PASSED] ttm_bo_validate_multihop =============
[04:16:34] ========== ttm_bo_validate_no_placement_signaled ==========
[04:16:34] [PASSED] Buffer object in system domain, no page vector
[04:16:34] [PASSED] Buffer object in system domain with an existing page vector
[04:16:34] ====== [PASSED] ttm_bo_validate_no_placement_signaled ======
[04:16:34] ======== ttm_bo_validate_no_placement_not_signaled ========
[04:16:34] [PASSED] Buffer object for userspace
[04:16:34] [PASSED] Kernel buffer object
[04:16:34] [PASSED] Shared buffer object
[04:16:34] ==== [PASSED] ttm_bo_validate_no_placement_not_signaled ====
[04:16:34] [PASSED] ttm_bo_validate_move_fence_signaled
[04:16:34] ========= ttm_bo_validate_move_fence_not_signaled =========
[04:16:34] [PASSED] Waits for GPU
[04:16:34] [PASSED] Tries to lock straight away
[04:16:34] ===== [PASSED] ttm_bo_validate_move_fence_not_signaled =====
[04:16:34] [PASSED] ttm_bo_validate_swapout
[04:16:34] [PASSED] ttm_bo_validate_happy_evict
[04:16:34] [PASSED] ttm_bo_validate_all_pinned_evict
[04:16:34] [PASSED] ttm_bo_validate_allowed_only_evict
[04:16:34] [PASSED] ttm_bo_validate_deleted_evict
[04:16:34] [PASSED] ttm_bo_validate_busy_domain_evict
[04:16:34] [PASSED] ttm_bo_validate_evict_gutting
[04:16:34] [PASSED] ttm_bo_validate_recrusive_evict
[04:16:34] ================= [PASSED] ttm_bo_validate =================
[04:16:34] ============================================================
[04:16:34] Testing complete. Ran 102 tests: passed: 102
[04:16:34] Elapsed time: 11.811s total, 1.809s configuring, 9.787s building, 0.180s running
+ cleanup
++ stat -c %u:%g /kernel
+ chown -R 1003:1003 /kernel
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2 0/9] Add fan control support
@ 2026-07-17 4:17 Karthik Poosa
2026-07-17 4:16 ` ✓ CI.KUnit: success for Add fan control support (rev2) Patchwork
` (11 more replies)
0 siblings, 12 replies; 21+ messages in thread
From: Karthik Poosa @ 2026-07-17 4:17 UTC (permalink / raw)
To: intel-xe
Cc: rodrigo.vivi, anshuman.gupta, badal.nilawar, raag.jadav,
riana.tauro, sk.anirban, mallesh.koujalagi, soham.purkait,
Karthik Poosa
This series adds Intel Xe fan control support through the hwmon sysfs ABI,
including fan maximum reporting, direct PWM control, mode control, and
programmable fan-curve points.
The patch set is ordered from backend enablement to user-visible controls
and full curve programming support.
Series overview
drm/xe/hwmon: initialize fan-control backend and runtime state
Initializes the fan-control backend and runtime state needed to safely
expose fan controls through hwmon.
drm/xe/hwmon: expose fanN_max
Adds fan maximum RPM reporting through fan1_max, fan2_max, and fan3_max.
drm/xe/hwmon: expose pwm1-3
Adds direct PWM duty control through pwm1-3 with hwmon-compatible 0..255
scaling.
drm/xe/hwmon: expose pwm1-3_enable
Adds PWM mode control through pwm1-3_enable:
0 = full speed
1 = manual user control
2 = firmware automatic control
drm/xe/hwmon: enable fan curve control support
Adds programmable per-fan curve points through:
pwm1-3_auto_point1-10_temp
pwm1-3_auto_point1-10_pwm
ABI updates are included in
Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon, with implementation
in drivers/gpu/drm/xe/xe_hwmon.c.
Sysfs nodes and usage
Read fan speed and max RPM:
cat /sys/class/hwmon/hwmonX/fan1_input
cat /sys/class/hwmon/hwmonX/fan1_max
Set manual mode and direct PWM:
echo 1 > /sys/class/hwmon/hwmonX/pwm1_enable
echo 120 > /sys/class/hwmon/hwmonX/pwm1
Return to firmware automatic control:
echo 2 > /sys/class/hwmon/hwmonX/pwm1_enable
Program a fan curve:
echo 1 > /sys/class/hwmon/hwmonX/pwm1_enable
echo 30000 > /sys/class/hwmon/hwmonX/pwm1_auto_point1_temp
echo 20 > /sys/class/hwmon/hwmonX/pwm1_auto_point1_pwm
...
echo 85000 > /sys/class/hwmon/hwmonX/pwm1_auto_point10_temp
echo 200 > /sys/class/hwmon/hwmonX/pwm1_auto_point10_pwm
Note:
Package temperature (temp2_input) is used as the temperature reference for
fan table control.
For hardware acceptance, fan-curve temperatures must be strictly
increasing.
The user fan table is committed to hardware only after the last curve point
is written.
Fan curve currently supports only 10 fan curve points.
v2:
- Added kernel-doc for fan control flow and sysfs behavior.
- Added save/restore support for user fan table across suspend/resume.
- Added late-binding handling to refresh fan information after late
binding completes.
- Preserved user-selected PWM mode across late-binding updates (do not
force auto mode).
- Fan curve points show stock table points when pwmX_enable=2.
Karthik Poosa (9):
drm/xe/pcode: Introduce xe_pcode_read_timeout() API
drm/xe/hwmon: initialize fan-control backend and table cache
drm/xe/hwmon: expose fanN_max
drm/xe/hwmon: expose pwm[1-3]
drm/xe/hwmon: expose pwm[1-3]_enable
drm/xe/hwmon: Enable fan curve control support
drm/xe/hwmon: Add kernel-doc for fan control
drm/xe/hwmon: preserve fan user table across suspend resume
drm/xe/hwmon: Update fan info after late binding
.../ABI/testing/sysfs-driver-intel-xe-hwmon | 44 +
drivers/gpu/drm/xe/xe_hwmon.c | 1408 ++++++++++++++++-
drivers/gpu/drm/xe/xe_hwmon.h | 6 +
drivers/gpu/drm/xe/xe_late_bind_fw.c | 3 +
drivers/gpu/drm/xe/xe_pcode.c | 11 +
drivers/gpu/drm/xe/xe_pcode.h | 1 +
drivers/gpu/drm/xe/xe_pcode_api.h | 9 +
drivers/gpu/drm/xe/xe_pm.c | 9 +
8 files changed, 1476 insertions(+), 15 deletions(-)
--
2.25.1
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2 1/9] drm/xe/pcode: Introduce xe_pcode_read_timeout() API
2026-07-17 4:17 [PATCH v2 0/9] Add fan control support Karthik Poosa
2026-07-17 4:16 ` ✓ CI.KUnit: success for Add fan control support (rev2) Patchwork
@ 2026-07-17 4:17 ` Karthik Poosa
2026-07-17 4:17 ` [PATCH v2 2/9] drm/xe/hwmon: initialize fan-control backend and table cache Karthik Poosa
` (9 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Karthik Poosa @ 2026-07-17 4:17 UTC (permalink / raw)
To: intel-xe
Cc: rodrigo.vivi, anshuman.gupta, badal.nilawar, raag.jadav,
riana.tauro, sk.anirban, mallesh.koujalagi, soham.purkait,
Karthik Poosa
Add the xe_pcode_read_timeout() helper to allow callers to specify
custom timeout values for pcode read operations.
Signed-off-by: Karthik Poosa <karthik.poosa@intel.com>
---
drivers/gpu/drm/xe/xe_pcode.c | 11 +++++++++++
drivers/gpu/drm/xe/xe_pcode.h | 1 +
2 files changed, 12 insertions(+)
diff --git a/drivers/gpu/drm/xe/xe_pcode.c b/drivers/gpu/drm/xe/xe_pcode.c
index ccc3bdeed6bb..7386113e60b3 100644
--- a/drivers/gpu/drm/xe/xe_pcode.c
+++ b/drivers/gpu/drm/xe/xe_pcode.c
@@ -147,6 +147,17 @@ int xe_pcode_read(struct xe_tile *tile, u32 mbox, u32 *val, u32 *val1)
return err;
}
+int xe_pcode_read_timeout(struct xe_tile *tile, u32 mbox, u32 *val, u32 *val1, u32 timeout_ms)
+{
+ int err;
+
+ mutex_lock(&tile->pcode.lock);
+ err = pcode_mailbox_rw(tile, mbox, val, val1, timeout_ms, true, false);
+ mutex_unlock(&tile->pcode.lock);
+
+ return err;
+}
+
static int pcode_try_request(struct xe_tile *tile, u32 mbox,
u32 request, u32 reply_mask, u32 reply,
u32 *status, bool atomic, int timeout_us, bool locked)
diff --git a/drivers/gpu/drm/xe/xe_pcode.h b/drivers/gpu/drm/xe/xe_pcode.h
index 8fb3e4ba13a6..c9647c5f305e 100644
--- a/drivers/gpu/drm/xe/xe_pcode.h
+++ b/drivers/gpu/drm/xe/xe_pcode.h
@@ -24,6 +24,7 @@ int xe_pcode_ready(struct xe_device *xe, bool locked);
int xe_pcode_init_min_freq_table(struct xe_tile *tile, u32 min_gt_freq,
u32 max_gt_freq);
int xe_pcode_read(struct xe_tile *tile, u32 mbox, u32 *val, u32 *val1);
+int xe_pcode_read_timeout(struct xe_tile *tile, u32 mbox, u32 *val, u32 *val1, u32 timeout_ms);
int xe_pcode_write_timeout(struct xe_tile *tile, u32 mbox, u32 val,
int timeout_ms);
int xe_pcode_write64_timeout(struct xe_tile *tile, u32 mbox, u32 data0,
--
2.25.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 2/9] drm/xe/hwmon: initialize fan-control backend and table cache
2026-07-17 4:17 [PATCH v2 0/9] Add fan control support Karthik Poosa
2026-07-17 4:16 ` ✓ CI.KUnit: success for Add fan control support (rev2) Patchwork
2026-07-17 4:17 ` [PATCH v2 1/9] drm/xe/pcode: Introduce xe_pcode_read_timeout() API Karthik Poosa
@ 2026-07-17 4:17 ` Karthik Poosa
2026-08-10 6:03 ` Nilawar, Badal
2026-08-11 13:11 ` Nilawar, Badal
2026-07-17 4:17 ` [PATCH v2 3/9] drm/xe/hwmon: expose fanN_max Karthik Poosa
` (8 subsequent siblings)
11 siblings, 2 replies; 21+ messages in thread
From: Karthik Poosa @ 2026-07-17 4:17 UTC (permalink / raw)
To: intel-xe
Cc: rodrigo.vivi, anshuman.gupta, badal.nilawar, raag.jadav,
riana.tauro, sk.anirban, mallesh.koujalagi, soham.purkait,
Karthik Poosa
Initialize Xe hwmon fan-control support by detecting fan count,
reading stock fan control points and min PWM.
v2:
- Avoid user table initialization during probe. (Badal)
- Move unused code to appropriate patches.
- Use xe helpers for dmesg logs.
Signed-off-by: Karthik Poosa <karthik.poosa@intel.com>
Assisted-by: Codex:gpt-5-4
---
drivers/gpu/drm/xe/xe_hwmon.c | 159 +++++++++++++++++++++++++++---
drivers/gpu/drm/xe/xe_pcode_api.h | 5 +
2 files changed, 149 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c
index de3f2aeffc3f..0a416e3e7b8c 100644
--- a/drivers/gpu/drm/xe/xe_hwmon.c
+++ b/drivers/gpu/drm/xe/xe_hwmon.c
@@ -20,6 +20,7 @@
#include "xe_pcode_api.h"
#include "xe_sriov.h"
#include "xe_pm.h"
+#include "xe_printk.h"
#include "xe_vsec.h"
#include "regs/xe_pmt.h"
@@ -122,14 +123,50 @@ struct xe_hwmon_energy_info {
long accum_energy;
};
+enum fan_table_type {
+ /** @STOCK_FAN_TABLE: firmware-provided fan table */
+ STOCK_FAN_TABLE,
+ /** @USER_FAN_TABLE: user fan table written through sysfs */
+ USER_FAN_TABLE,
+ /** @FAN_TABLE_MAX: number of fan table slots tracked per fan */
+ FAN_TABLE_MAX,
+};
+
+/* Maximum number of fan control points supported by each fan */
+#define MAX_FAN_CONTROL_POINTS (10)
+
+/* Fan control point index: bit31 selects fan table type; bits30:0 control point index */
+#define FCP_INDEX(FAN_TABLE_TYPE, POINT_NUM) (((FAN_TABLE_TYPE) << 31) | \
+ ((POINT_NUM) & REG_GENMASK(30, 0)))
+
+/* PCODE operations timeout for fan control commands */
+#define XE_PCODE_FAN_CONTROL_TIMEOUT_MS (10)
+
/**
- * struct xe_hwmon_fan_info - to cache previous fan reading
+ * struct xe_hwmon_fan_info - cached fan telemetry and control state
+ *
+ * Each fan keeps the latest tachometer sampling state along with two fan
+ * tables: the stock table discovered from firmware and the user table managed
+ * by hwmon sysfs writes.
*/
struct xe_hwmon_fan_info {
/** @reg_val_prev: previous fan reg val */
u32 reg_val_prev;
/** @time_prev: previous timestamp */
u64 time_prev;
+ /** @fan_table: fan control tables */
+ struct fan_table {
+ /** @fan_control_point_count: number of supported fan control points */
+ u8 fan_control_point_count;
+ struct fan_control_point {
+ /** @temp: temperature in degree celsius */
+ u8 temp;
+ /** @speed: fan speed in percentage */
+ u8 speed;
+ } fcp[MAX_FAN_CONTROL_POINTS];
+ } fan_table[FAN_TABLE_MAX];
+ /** @min_pwm: minimum fan PWM */
+ u32 min_pwm;
};
/**
@@ -168,6 +205,8 @@ struct xe_hwmon {
int scl_shift_time;
/** @ei: Energy info for energyN_input */
struct xe_hwmon_energy_info ei[CHANNEL_MAX];
+ /** @num_fans: number of fans available */
+ u8 num_fans;
/** @fi: Fan info for fanN_input */
struct xe_hwmon_fan_info fi[FAN_MAX];
/** @boot_power_limit_read: is boot power limits read */
@@ -856,17 +895,100 @@ static int xe_hwmon_pcode_write_i1(const struct xe_hwmon *hwmon, u32 uval)
(uval & POWER_SETUP_I1_DATA_MASK));
}
-static int xe_hwmon_pcode_read_fan_control(const struct xe_hwmon *hwmon, u32 subcmd, u32 *uval)
+static int xe_hwmon_pcode_read_fan_control(const struct xe_hwmon *hwmon, u32 subcmd, u8 fan_num,
+ u32 *uval)
{
struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
+ return xe_pcode_read_timeout(root_tile, PCODE_MBOX(FAN_SPEED_CONTROL, subcmd, fan_num),
+ uval, NULL, XE_PCODE_FAN_CONTROL_TIMEOUT_MS);
+}
+
+static int xe_hwmon_get_num_fans(const struct xe_hwmon *hwmon, u32 *num_fans)
+{
+ u32 fan_mask = 0;
+ int ret;
/* Platforms that don't return correct value */
- if (hwmon->xe->info.platform == XE_DG2 && subcmd == FSC_READ_NUM_FANS) {
- *uval = 2;
+ if (hwmon->xe->info.platform == XE_DG2) {
+ *num_fans = 2;
return 0;
}
- return xe_pcode_read(root_tile, PCODE_MBOX(FAN_SPEED_CONTROL, subcmd, 0), uval, NULL);
+ ret = xe_hwmon_pcode_read_fan_control(hwmon, FSC_READ_NUM_FANS, 0, &fan_mask);
+ if (ret) {
+ xe_warn(hwmon->xe, "failed to read number of fans, ret=%d\n", ret);
+ return ret;
+ }
+
+ *num_fans = min_t(u32, hweight32(fan_mask), FAN_MAX);
+
+ return 0;
+}
+
+static int xe_hwmon_read_fan_control_info(struct xe_hwmon *hwmon)
+{
+ struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
+ int point;
+ int fan;
+ int ret;
+
+ for (fan = 0; fan < hwmon->num_fans; fan++) {
+ u32 stock_fcp_count = 0;
+
+ struct xe_hwmon_fan_info *fi = &hwmon->fi[fan];
+
+ ret = xe_hwmon_pcode_read_fan_control(hwmon,
+ FSC_READ_STOCK_FAN_CONTROL_POINTS,
+ fan, &stock_fcp_count);
+ if (ret) {
+ xe_err(hwmon->xe,
+ "failed to read fan %d stock control point count, ret=%d\n",
+ fan, ret);
+ return ret;
+ }
+ fi->fan_table[STOCK_FAN_TABLE].fan_control_point_count =
+ min_t(u8, stock_fcp_count, MAX_FAN_CONTROL_POINTS);
+
+ xe_dbg(hwmon->xe, "fan %d stock points %u\n", fan,
+ fi->fan_table[STOCK_FAN_TABLE].fan_control_point_count);
+
+ /* Dump the stock fan control points for debugging purposes. */
+ for (point = 0; point < fi->fan_table[STOCK_FAN_TABLE].fan_control_point_count;
+ point++) {
+ u32 fcp = 0;
+
+ fcp = FCP_INDEX(STOCK_FAN_TABLE, point);
+ ret = xe_pcode_read_timeout(root_tile,
+ PCODE_MBOX(FAN_SPEED_CONTROL,
+ FSC_READ_FAN_TABLE, fan),
+ &fcp, NULL, XE_PCODE_FAN_CONTROL_TIMEOUT_MS);
+ if (ret) {
+ xe_err(hwmon->xe, "failed to read fan %d stock point %d, ret=%d\n",
+ fan, point, ret);
+ continue;
+ }
+
+ /* Cache the stock fan control points in local structure for later use. */
+ fi->fan_table[STOCK_FAN_TABLE].fcp[point].temp =
+ REG_FIELD_GET(FAN_CONTROL_POINT_TEMP_MASK, fcp);
+ fi->fan_table[STOCK_FAN_TABLE].fcp[point].speed =
+ REG_FIELD_GET(FAN_CONTROL_POINT_SPEED_MASK, fcp);
+ xe_dbg(hwmon->xe, "fan %d stock point %d: temp %u C, speed %u %%\n",
+ fan, point, fi->fan_table[STOCK_FAN_TABLE].fcp[point].temp,
+ fi->fan_table[STOCK_FAN_TABLE].fcp[point].speed);
+ }
+
+ /* Read minimum fan PWM */
+ ret = xe_hwmon_pcode_read_fan_control(hwmon, FSC_READ_FAN_MIN_PWM, fan,
+ &fi->min_pwm);
+ if (ret) {
+ xe_err(hwmon->xe, "failed to read fan %d min PWM, ret=%d\n", fan, ret);
+ continue;
+ }
+
+ xe_dbg(hwmon->xe, "fan %d min PWM %u\n", fan, fi->min_pwm);
+ }
+ return 0;
}
static int xe_hwmon_power_curr_crit_read(struct xe_hwmon *hwmon, int channel,
@@ -1279,17 +1401,12 @@ xe_hwmon_energy_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *val)
static umode_t
xe_hwmon_fan_is_visible(struct xe_hwmon *hwmon, u32 attr, int channel)
{
- u32 uval = 0;
-
if (!hwmon->xe->info.has_fan_control)
return 0;
switch (attr) {
case hwmon_fan_input:
- if (xe_hwmon_pcode_read_fan_control(hwmon, FSC_READ_NUM_FANS, &uval))
- return 0;
-
- return channel < uval ? 0444 : 0;
+ return channel < hwmon->num_fans ? 0444 : 0;
default:
return 0;
}
@@ -1476,6 +1593,7 @@ xe_hwmon_get_preregistration_info(struct xe_hwmon *hwmon)
u64 val_sku_unit = 0;
int channel;
struct xe_reg pkg_power_sku_unit;
+ u32 num_fans = 0;
if (hwmon->xe->info.has_mbx_power_limits) {
/* Check if GPU firmware support mailbox power limits commands. */
@@ -1531,10 +1649,21 @@ xe_hwmon_get_preregistration_info(struct xe_hwmon *hwmon)
if (xe_hwmon_is_visible(hwmon, hwmon_energy, hwmon_energy_input, channel))
xe_hwmon_energy_get(hwmon, channel, &energy);
- /* Initialize 'struct xe_hwmon_fan_info' with initial fan register reading. */
- for (channel = 0; channel < FAN_MAX; channel++)
- if (xe_hwmon_is_visible(hwmon, hwmon_fan, hwmon_fan_input, channel))
- xe_hwmon_fan_input_read(hwmon, channel, &fan_speed);
+ if (hwmon->xe->info.has_fan_control) {
+ xe_hwmon_get_num_fans(hwmon, &num_fans);
+
+ xe_info(hwmon->xe, "Number of fans detected: %u\n", num_fans);
+ hwmon->num_fans = num_fans;
+
+ /* Initialize 'struct xe_hwmon_fan_info' with initial fan register reading. */
+ for (channel = 0; channel < hwmon->num_fans; channel++)
+ if (xe_hwmon_is_visible(hwmon, hwmon_fan, hwmon_fan_input, channel))
+ xe_hwmon_fan_input_read(hwmon, channel, &fan_speed);
+
+ /* Fan control tables initialization */
+ if (xe_hwmon_read_fan_control_info(hwmon))
+ xe_warn(hwmon->xe, "Fan control tables are not available\n");
+ }
if (hwmon->xe->info.has_mbx_thermal_info && xe_hwmon_pcode_read_thermal_info(hwmon))
drm_warn(&hwmon->xe->drm, "Thermal mailbox not supported by card firmware\n");
diff --git a/drivers/gpu/drm/xe/xe_pcode_api.h b/drivers/gpu/drm/xe/xe_pcode_api.h
index 94575c476e3d..669010f1e2d0 100644
--- a/drivers/gpu/drm/xe/xe_pcode_api.h
+++ b/drivers/gpu/drm/xe/xe_pcode_api.h
@@ -85,6 +85,11 @@
#define FAN_SPEED_CONTROL 0x7D
#define FSC_READ_NUM_FANS 0x4
+#define FSC_READ_STOCK_FAN_CONTROL_POINTS 0x5
+#define FSC_READ_FAN_TABLE 0x7
+#define FAN_CONTROL_POINT_TEMP_MASK REG_GENMASK(7, 0)
+#define FAN_CONTROL_POINT_SPEED_MASK REG_GENMASK(15, 8)
+#define FSC_READ_FAN_MIN_PWM 0x8
#define PCODE_SCRATCH(x) XE_REG(0x138320 + ((x) * 4))
/* PCODE_SCRATCH0 */
--
2.25.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 3/9] drm/xe/hwmon: expose fanN_max
2026-07-17 4:17 [PATCH v2 0/9] Add fan control support Karthik Poosa
` (2 preceding siblings ...)
2026-07-17 4:17 ` [PATCH v2 2/9] drm/xe/hwmon: initialize fan-control backend and table cache Karthik Poosa
@ 2026-07-17 4:17 ` Karthik Poosa
2026-07-23 5:34 ` Purkait, Soham
2026-07-17 4:17 ` [PATCH v2 4/9] drm/xe/hwmon: expose pwm[1-3] Karthik Poosa
` (7 subsequent siblings)
11 siblings, 1 reply; 21+ messages in thread
From: Karthik Poosa @ 2026-07-17 4:17 UTC (permalink / raw)
To: intel-xe
Cc: rodrigo.vivi, anshuman.gupta, badal.nilawar, raag.jadav,
riana.tauro, sk.anirban, mallesh.koujalagi, soham.purkait,
Karthik Poosa
Expose fan maximum RPM via fanN_max.
The value is derived from the firmware-reported maximum fan RPS and
converted to RPM in the fan hwmon read path.
Update Xe hwmon ABI documentation for fanN_max.
v2: Use xe helper for dmesg logs.
Signed-off-by: Karthik Poosa <karthik.poosa@intel.com>
Assisted-by: Codex:gpt-5-4
---
.../ABI/testing/sysfs-driver-intel-xe-hwmon | 8 ++++++++
drivers/gpu/drm/xe/xe_hwmon.c | 18 +++++++++++++++++-
drivers/gpu/drm/xe/xe_pcode_api.h | 1 +
3 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon b/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon
index 0da739d9a816..ec0b94d76e22 100644
--- a/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon
+++ b/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon
@@ -266,6 +266,14 @@ Description: RO. Fan 3 speed in RPM.
Only supported for particular Intel Xe graphics platforms.
+What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/fan[1-3]_max
+Date: July 2026
+KernelVersion: 7.2
+Contact: intel-xe@lists.freedesktop.org
+Description: RO. Maximum fan speed in RPM for each fan channel.
+
+ Only supported for particular Intel Xe graphics platforms.
+
What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/power1_cap
Date: May 2025
KernelVersion: 6.15
diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c
index 0a416e3e7b8c..769f4d1da83e 100644
--- a/drivers/gpu/drm/xe/xe_hwmon.c
+++ b/drivers/gpu/drm/xe/xe_hwmon.c
@@ -165,6 +165,8 @@ struct xe_hwmon_fan_info {
u8 speed;
} fcp[MAX_FAN_CONTROL_POINTS];
} fan_table[FAN_TABLE_MAX];
+ /** @max_rps: maximum fan RPS */
+ u32 max_rps;
/** @min_pwm: minimum fan PWM */
u32 min_pwm;
};
@@ -799,7 +801,8 @@ static const struct hwmon_channel_info * const hwmon_info[] = {
HWMON_CHANNEL_INFO(curr, HWMON_C_LABEL, HWMON_C_CRIT | HWMON_C_LABEL),
HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL),
HWMON_CHANNEL_INFO(energy, HWMON_E_INPUT | HWMON_E_LABEL, HWMON_E_INPUT | HWMON_E_LABEL),
- HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT, HWMON_F_INPUT, HWMON_F_INPUT),
+ HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_MAX, HWMON_F_INPUT | HWMON_F_MAX,
+ HWMON_F_INPUT | HWMON_F_MAX),
NULL
};
@@ -987,6 +990,14 @@ static int xe_hwmon_read_fan_control_info(struct xe_hwmon *hwmon)
}
xe_dbg(hwmon->xe, "fan %d min PWM %u\n", fan, fi->min_pwm);
+
+ /* Read max fan RPS*/
+ ret = xe_hwmon_pcode_read_fan_control(hwmon, FSC_READ_MAX_FAN_RPS, fan,
+ &fi->max_rps);
+ if (ret)
+ xe_warn(hwmon->xe, "failed to read fan %d max RPS, ret=%d\n", fan, ret);
+ else
+ xe_dbg(hwmon->xe, "fan %d max RPS %u\n", fan, fi->max_rps);
}
return 0;
}
@@ -1406,6 +1417,7 @@ xe_hwmon_fan_is_visible(struct xe_hwmon *hwmon, u32 attr, int channel)
switch (attr) {
case hwmon_fan_input:
+ case hwmon_fan_max:
return channel < hwmon->num_fans ? 0444 : 0;
default:
return 0;
@@ -1457,6 +1469,10 @@ xe_hwmon_fan_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *val)
switch (attr) {
case hwmon_fan_input:
return xe_hwmon_fan_input_read(hwmon, channel, val);
+ case hwmon_fan_max:
+ /* Convert RPS to RPM */
+ *val = (long)hwmon->fi[channel].max_rps * 60;
+ return 0;
default:
return -EOPNOTSUPP;
}
diff --git a/drivers/gpu/drm/xe/xe_pcode_api.h b/drivers/gpu/drm/xe/xe_pcode_api.h
index 669010f1e2d0..419ab4f416fa 100644
--- a/drivers/gpu/drm/xe/xe_pcode_api.h
+++ b/drivers/gpu/drm/xe/xe_pcode_api.h
@@ -84,6 +84,7 @@
#define PCODE_MBOX_DOMAIN_HBM 0x2
#define FAN_SPEED_CONTROL 0x7D
+#define FSC_READ_MAX_FAN_RPS 0x3
#define FSC_READ_NUM_FANS 0x4
#define FSC_READ_STOCK_FAN_CONTROL_POINTS 0x5
#define FSC_READ_FAN_TABLE 0x7
--
2.25.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 4/9] drm/xe/hwmon: expose pwm[1-3]
2026-07-17 4:17 [PATCH v2 0/9] Add fan control support Karthik Poosa
` (3 preceding siblings ...)
2026-07-17 4:17 ` [PATCH v2 3/9] drm/xe/hwmon: expose fanN_max Karthik Poosa
@ 2026-07-17 4:17 ` Karthik Poosa
2026-07-24 7:02 ` Purkait, Soham
2026-07-17 4:17 ` [PATCH v2 5/9] drm/xe/hwmon: expose pwm[1-3]_enable Karthik Poosa
` (6 subsequent siblings)
11 siblings, 1 reply; 21+ messages in thread
From: Karthik Poosa @ 2026-07-17 4:17 UTC (permalink / raw)
To: intel-xe
Cc: rodrigo.vivi, anshuman.gupta, badal.nilawar, raag.jadav,
riana.tauro, sk.anirban, mallesh.koujalagi, soham.purkait,
Karthik Poosa
Expose pwm[1-3] fan duty attributes through hwmon.
This enables reading and writing user PWM values (0..255) for each
available fan channel.
Update Xe hwmon ABI documentation for pwm[1-3].
This sysfs node can be used for manual control of fan speed,
irrespective of fan curve.
v2:
- Use xe helpers for dmesg logs.
- Avoid use hwmon->num_fans to get fan count in xe_hwmon_pwm_is_visible().
Signed-off-by: Karthik Poosa <karthik.poosa@intel.com>
Assisted-by: Codex:gpt-5-4
---
.../ABI/testing/sysfs-driver-intel-xe-hwmon | 9 +
drivers/gpu/drm/xe/xe_hwmon.c | 230 ++++++++++++++++++
drivers/gpu/drm/xe/xe_pcode_api.h | 3 +
3 files changed, 242 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon b/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon
index ec0b94d76e22..7383898890aa 100644
--- a/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon
+++ b/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon
@@ -321,3 +321,12 @@ Description: RW. Package burst power limit interval (Tau in PL2/Tau) in
milliseconds over which sustained power is averaged.
Only supported for particular Intel Xe graphics platforms.
+
+What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/pwm[1-3]
+Date: July 2026
+KernelVersion: 7.2
+Contact: intel-xe@lists.freedesktop.org
+Description: RW. Target fan PWM duty value in the range 0..255. 0 is zero
+ fan speed and 255 is 100% fan speed.
+
+ Only supported for particular Intel Xe graphics platforms.
diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c
index 769f4d1da83e..d3379727b1a9 100644
--- a/drivers/gpu/drm/xe/xe_hwmon.c
+++ b/drivers/gpu/drm/xe/xe_hwmon.c
@@ -803,6 +803,7 @@ static const struct hwmon_channel_info * const hwmon_info[] = {
HWMON_CHANNEL_INFO(energy, HWMON_E_INPUT | HWMON_E_LABEL, HWMON_E_INPUT | HWMON_E_LABEL),
HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_MAX, HWMON_F_INPUT | HWMON_F_MAX,
HWMON_F_INPUT | HWMON_F_MAX),
+ HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT, HWMON_PWM_INPUT, HWMON_PWM_INPUT),
NULL
};
@@ -928,6 +929,158 @@ static int xe_hwmon_get_num_fans(const struct xe_hwmon *hwmon, u32 *num_fans)
return 0;
}
+static int xe_hwmon_get_fan_point_count(struct xe_hwmon *hwmon, u8 fan, u32 *point_count,
+ int table_type)
+{
+ int ret;
+
+ ret = xe_hwmon_pcode_read_fan_control(hwmon,
+ (table_type == USER_FAN_TABLE) ?
+ FSC_READ_USER_FAN_CONTROL_POINTS :
+ FSC_READ_STOCK_FAN_CONTROL_POINTS,
+ fan, point_count);
+ if (ret) {
+ xe_err(hwmon->xe, "failed to read fan %d %s point count, ret=%d\n", fan,
+ (table_type == USER_FAN_TABLE) ? "user" : "stock", ret);
+ return ret;
+ }
+
+ xe_dbg(hwmon->xe, "fan %d %s point count read as %u\n", fan,
+ (table_type == USER_FAN_TABLE) ? "user" : "stock", *point_count);
+
+ return 0;
+}
+
+static int xe_hwmon_write_user_fan_point(struct xe_hwmon *hwmon, u8 fan, u8 point, u8 temp,
+ u8 speed)
+{
+ struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
+ u16 user_fcp_raw = FIELD_PREP(FAN_CONTROL_POINT_TEMP_MASK, temp) |
+ FIELD_PREP(FAN_CONTROL_POINT_SPEED_MASK, speed);
+ int ret;
+
+ ret = xe_pcode_write_timeout(root_tile,
+ PCODE_MBOX(FAN_SPEED_CONTROL, FSC_WRITE_FAN_TABLE, fan),
+ (u32)user_fcp_raw, XE_PCODE_FAN_CONTROL_TIMEOUT_MS);
+ if (ret)
+ xe_dbg(hwmon->xe,
+ "failed to write fan %d user point %d temp %u, speed %u %%, ret=%d\n",
+ fan, point, temp, speed, ret);
+
+ return ret;
+}
+
+static int xe_hwmon_activate_user_fan_table(struct xe_hwmon *hwmon, u8 fan,
+ bool is_full_speed, enum fan_table_type table_source)
+{
+ struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
+ struct xe_hwmon_fan_info *fi = &hwmon->fi[fan];
+ u32 point_count;
+ int ret;
+ int point;
+ struct fan_table *source_table = &fi->fan_table[table_source];
+
+ xe_dbg(hwmon->xe, "activating fan %d user table from %s table\n", fan,
+ (table_source == USER_FAN_TABLE) ? "user" : "stock");
+
+ point_count = source_table->fan_control_point_count;
+
+ if (!point_count) {
+ xe_err(hwmon->xe,
+ "fan %d %s table point count is 0, cannot activate user table\n", fan,
+ (table_source == USER_FAN_TABLE) ? "user" : "stock");
+ return -ENODATA;
+ }
+
+ for (point = 0; point < point_count; point++) {
+ u8 temp = source_table->fcp[point].temp;
+ u8 speed = is_full_speed ? 100 : max_t(u8, source_table->fcp[point].speed,
+ min_t(u32, fi->min_pwm, U8_MAX));
+
+ ret = xe_hwmon_write_user_fan_point(hwmon, fan, point, temp, speed);
+ if (ret)
+ return ret;
+
+ if (table_source == STOCK_FAN_TABLE) {
+ /* Update user table cache with stock table values */
+ fi->fan_table[USER_FAN_TABLE].fcp[point].temp = temp;
+ fi->fan_table[USER_FAN_TABLE].fcp[point].speed = speed;
+ }
+ if (is_full_speed)
+ fi->fan_table[USER_FAN_TABLE].fcp[point].speed = speed;
+ }
+
+ ret = xe_pcode_write_timeout(root_tile,
+ PCODE_MBOX(FAN_SPEED_CONTROL,
+ FSC_WRITE_NUM_FAN_CONTROL_POINTS, fan),
+ point_count, XE_PCODE_FAN_CONTROL_TIMEOUT_MS);
+ if (ret) {
+ xe_dbg(hwmon->xe, "failed to write fan %d user table, ret=%d\n", fan, ret);
+ return ret;
+ }
+
+ /* Verify if all the user points are set */
+ ret = xe_hwmon_get_fan_point_count(hwmon, fan, &point_count, USER_FAN_TABLE);
+ if (ret) {
+ xe_err(hwmon->xe,
+ "failed to read fan %d user table point count, ret=%d\n", fan, ret);
+ return ret;
+ }
+ if (point_count != source_table->fan_control_point_count) {
+ xe_err(hwmon->xe, "fan %d user table point count mismatch, expected %u, got %u\n",
+ fan, source_table->fan_control_point_count, point_count);
+ return -EIO;
+ }
+
+ fi->fan_table[USER_FAN_TABLE].fan_control_point_count = point_count;
+
+ return 0;
+}
+
+static int xe_hwmon_set_user_fan_pwm(struct xe_hwmon *hwmon, u8 fan, u8 pwm)
+{
+ struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
+ struct xe_hwmon_fan_info *fi = &hwmon->fi[fan];
+ u32 point_count = fi->fan_table[USER_FAN_TABLE].fan_control_point_count;
+ u8 clamped_pwm;
+ int ret;
+ int point;
+
+ /* Read user table fan point count, if it is not set, activate the user table */
+ ret = xe_hwmon_get_fan_point_count(hwmon, fan, &point_count, USER_FAN_TABLE);
+ if (ret)
+ return ret;
+
+ if (!point_count) {
+ xe_dbg(hwmon->xe, "fan %d user table not set, activating it\n", fan);
+ ret = xe_hwmon_activate_user_fan_table(hwmon, fan, false, STOCK_FAN_TABLE);
+ if (ret)
+ return ret;
+ }
+
+ pwm = DIV_ROUND_CLOSEST(pwm * 100, U8_MAX);
+ clamped_pwm = max_t(u8, pwm, min_t(u32, fi->min_pwm, U8_MAX));
+
+ for (point = 0; point < point_count; point++) {
+ u8 temp = fi->fan_table[USER_FAN_TABLE].fcp[point].temp;
+
+ ret = xe_hwmon_write_user_fan_point(hwmon, fan, point, temp, clamped_pwm);
+ if (ret)
+ return ret;
+
+ fi->fan_table[USER_FAN_TABLE].fcp[point].speed = clamped_pwm;
+ }
+
+ ret = xe_pcode_write(root_tile,
+ PCODE_MBOX(FAN_SPEED_CONTROL, FSC_WRITE_NUM_FAN_CONTROL_POINTS, fan),
+ point_count);
+ if (ret) {
+ xe_dbg(hwmon->xe, "failed to update fan %d user table count, ret=%d\n", fan, ret);
+ return ret;
+ }
+ return 0;
+}
+
static int xe_hwmon_read_fan_control_info(struct xe_hwmon *hwmon)
{
struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
@@ -1424,6 +1577,20 @@ xe_hwmon_fan_is_visible(struct xe_hwmon *hwmon, u32 attr, int channel)
}
}
+static umode_t
+xe_hwmon_pwm_is_visible(struct xe_hwmon *hwmon, u32 attr, int channel)
+{
+ if (!hwmon->xe->info.has_fan_control || channel >= hwmon->num_fans)
+ return 0;
+
+ switch (attr) {
+ case hwmon_pwm_input:
+ return 0644;
+ default:
+ return 0;
+ }
+}
+
static int
xe_hwmon_fan_input_read(struct xe_hwmon *hwmon, int channel, long *val)
{
@@ -1478,6 +1645,62 @@ xe_hwmon_fan_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *val)
}
}
+static int
+xe_hwmon_pwm_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *val)
+{
+ struct xe_hwmon_fan_info *fi;
+ int ret = 0;
+
+ if (channel < 0 || channel >= FAN_MAX)
+ return -EINVAL;
+
+ fi = &hwmon->fi[channel];
+
+ switch (attr) {
+ case hwmon_pwm_input:
+ /* Check if user fan table is set else activate it.*/
+ if (!fi->fan_table[USER_FAN_TABLE].fan_control_point_count) {
+ xe_dbg(hwmon->xe, "fan %d user table not set, activating it\n", channel);
+ ret = xe_hwmon_activate_user_fan_table(hwmon, channel, false,
+ STOCK_FAN_TABLE);
+ if (ret)
+ return ret;
+ }
+ *val = DIV_ROUND_CLOSEST(fi->fan_table[USER_FAN_TABLE].fcp[0].speed *
+ U8_MAX, 100);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int
+xe_hwmon_pwm_write(struct xe_hwmon *hwmon, u32 attr, int channel, long val)
+{
+ int ret = 0;
+
+ if (channel < 0 || channel >= FAN_MAX)
+ return -EINVAL;
+
+ mutex_lock(&hwmon->hwmon_lock);
+
+ switch (attr) {
+ case hwmon_pwm_input:
+ if (val < 0 || val > U8_MAX) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = xe_hwmon_set_user_fan_pwm(hwmon, channel, (u8)val);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+ mutex_unlock(&hwmon->hwmon_lock);
+
+ return ret;
+}
+
static umode_t
xe_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
u32 attr, int channel)
@@ -1504,6 +1727,9 @@ xe_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
case hwmon_fan:
ret = xe_hwmon_fan_is_visible(hwmon, attr, channel);
break;
+ case hwmon_pwm:
+ ret = xe_hwmon_pwm_is_visible(hwmon, attr, channel);
+ break;
default:
ret = 0;
break;
@@ -1533,6 +1759,8 @@ xe_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
return xe_hwmon_energy_read(hwmon, attr, channel, val);
case hwmon_fan:
return xe_hwmon_fan_read(hwmon, attr, channel, val);
+ case hwmon_pwm:
+ return xe_hwmon_pwm_read(hwmon, attr, channel, val);
default:
return -EOPNOTSUPP;
}
@@ -1551,6 +1779,8 @@ xe_hwmon_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
return xe_hwmon_power_write(hwmon, attr, channel, val);
case hwmon_curr:
return xe_hwmon_curr_write(hwmon, attr, channel, val);
+ case hwmon_pwm:
+ return xe_hwmon_pwm_write(hwmon, attr, channel, val);
default:
return -EOPNOTSUPP;
}
diff --git a/drivers/gpu/drm/xe/xe_pcode_api.h b/drivers/gpu/drm/xe/xe_pcode_api.h
index 419ab4f416fa..5c5fdc650a97 100644
--- a/drivers/gpu/drm/xe/xe_pcode_api.h
+++ b/drivers/gpu/drm/xe/xe_pcode_api.h
@@ -84,9 +84,12 @@
#define PCODE_MBOX_DOMAIN_HBM 0x2
#define FAN_SPEED_CONTROL 0x7D
+#define FSC_WRITE_NUM_FAN_CONTROL_POINTS 0x0
+#define FSC_WRITE_FAN_TABLE 0x1
#define FSC_READ_MAX_FAN_RPS 0x3
#define FSC_READ_NUM_FANS 0x4
#define FSC_READ_STOCK_FAN_CONTROL_POINTS 0x5
+#define FSC_READ_USER_FAN_CONTROL_POINTS 0x6
#define FSC_READ_FAN_TABLE 0x7
#define FAN_CONTROL_POINT_TEMP_MASK REG_GENMASK(7, 0)
#define FAN_CONTROL_POINT_SPEED_MASK REG_GENMASK(15, 8)
--
2.25.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 5/9] drm/xe/hwmon: expose pwm[1-3]_enable
2026-07-17 4:17 [PATCH v2 0/9] Add fan control support Karthik Poosa
` (4 preceding siblings ...)
2026-07-17 4:17 ` [PATCH v2 4/9] drm/xe/hwmon: expose pwm[1-3] Karthik Poosa
@ 2026-07-17 4:17 ` Karthik Poosa
2026-07-17 4:17 ` [PATCH v2 6/9] drm/xe/hwmon: Enable fan curve control support Karthik Poosa
` (5 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Karthik Poosa @ 2026-07-17 4:17 UTC (permalink / raw)
To: intel-xe
Cc: rodrigo.vivi, anshuman.gupta, badal.nilawar, raag.jadav,
riana.tauro, sk.anirban, mallesh.koujalagi, soham.purkait,
Karthik Poosa
Expose pwm[1-3]_enable for fan control mode selection.
Value of 0 runs fan at full speed.
A value of 1 keeps user fan-table control active, while value 2
switches back to firmware control.
Update Xe hwmon ABI documentation for pwm[1-3]_enable.
v2:
- Use xe helpers for dmesg logs.
- Update kernel version in Xe hwmon documentation.
- Correct level of some dmesg logs.
Signed-off-by: Karthik Poosa <karthik.poosa@intel.com>
Assisted-by: Codex:gpt-5-4
---
.../ABI/testing/sysfs-driver-intel-xe-hwmon | 11 +++
drivers/gpu/drm/xe/xe_hwmon.c | 91 ++++++++++++++++++-
2 files changed, 100 insertions(+), 2 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon b/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon
index 7383898890aa..effc7d38ce1f 100644
--- a/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon
+++ b/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon
@@ -330,3 +330,14 @@ Description: RW. Target fan PWM duty value in the range 0..255. 0 is zero
fan speed and 255 is 100% fan speed.
Only supported for particular Intel Xe graphics platforms.
+
+What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/pwm[1-3]_enable
+Date: July 2026
+KernelVersion: 7.2
+Contact: intel-xe@lists.freedesktop.org
+Description: RW. Fan control mode selector.
+
+ Use value 0 to run fans at full speed. Use value 1 to enable
+ user table control and value 2 to use firmware default behavior.
+
+ Only supported for particular Intel Xe graphics platforms.
diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c
index d3379727b1a9..fa9dbadd5fd6 100644
--- a/drivers/gpu/drm/xe/xe_hwmon.c
+++ b/drivers/gpu/drm/xe/xe_hwmon.c
@@ -123,6 +123,15 @@ struct xe_hwmon_energy_info {
long accum_energy;
};
+enum xe_fan_pwm_enable_mode {
+ /** @XE_FAN_PWM_FULL_SPEED: force all user table points to full speed */
+ XE_FAN_PWM_FULL_SPEED = 0,
+ /** @XE_FAN_PWM_MANUAL_USER_TABLE: use the driver-managed user table */
+ XE_FAN_PWM_MANUAL_USER_TABLE = 1,
+ /** @XE_FAN_PWM_AUTO_STOCK_TABLE: use the firmware stock table */
+ XE_FAN_PWM_AUTO_STOCK_TABLE = 2,
+};
+
enum fan_table_type {
/** @STOCK_FAN_TABLE: firmware-provided fan table */
STOCK_FAN_TABLE,
@@ -169,6 +178,10 @@ struct xe_hwmon_fan_info {
u32 max_rps;
/** @min_pwm: minimum fan PWM */
u32 min_pwm;
+ /** @pwm_enable_mode: current xe_fan_pwm_enable_mode */
+ u8 pwm_enable_mode;
+ /** @is_full_speed: flag indicating if fan is in full speed */
+ bool is_full_speed;
};
/**
@@ -803,7 +816,9 @@ static const struct hwmon_channel_info * const hwmon_info[] = {
HWMON_CHANNEL_INFO(energy, HWMON_E_INPUT | HWMON_E_LABEL, HWMON_E_INPUT | HWMON_E_LABEL),
HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_MAX, HWMON_F_INPUT | HWMON_F_MAX,
HWMON_F_INPUT | HWMON_F_MAX),
- HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT, HWMON_PWM_INPUT, HWMON_PWM_INPUT),
+ HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
NULL
};
@@ -1033,7 +1048,8 @@ static int xe_hwmon_activate_user_fan_table(struct xe_hwmon *hwmon, u8 fan,
}
fi->fan_table[USER_FAN_TABLE].fan_control_point_count = point_count;
-
+ fi->pwm_enable_mode = XE_FAN_PWM_MANUAL_USER_TABLE;
+ fi->is_full_speed = is_full_speed;
return 0;
}
@@ -1046,6 +1062,11 @@ static int xe_hwmon_set_user_fan_pwm(struct xe_hwmon *hwmon, u8 fan, u8 pwm)
int ret;
int point;
+ if (fi->pwm_enable_mode == XE_FAN_PWM_AUTO_STOCK_TABLE) {
+ xe_dbg(hwmon->xe, "fan %d manual table is not active, cannot set pwm\n", fan);
+ return -EINVAL;
+ }
+
/* Read user table fan point count, if it is not set, activate the user table */
ret = xe_hwmon_get_fan_point_count(hwmon, fan, &point_count, USER_FAN_TABLE);
if (ret)
@@ -1078,6 +1099,9 @@ static int xe_hwmon_set_user_fan_pwm(struct xe_hwmon *hwmon, u8 fan, u8 pwm)
xe_dbg(hwmon->xe, "failed to update fan %d user table count, ret=%d\n", fan, ret);
return ret;
}
+
+ fi->pwm_enable_mode = XE_FAN_PWM_MANUAL_USER_TABLE;
+ fi->is_full_speed = false;
return 0;
}
@@ -1134,6 +1158,9 @@ static int xe_hwmon_read_fan_control_info(struct xe_hwmon *hwmon)
fi->fan_table[STOCK_FAN_TABLE].fcp[point].speed);
}
+ /* Set PWM enable mode to automatic i.e stock table in use. */
+ fi->pwm_enable_mode = XE_FAN_PWM_AUTO_STOCK_TABLE;
+
/* Read minimum fan PWM */
ret = xe_hwmon_pcode_read_fan_control(hwmon, FSC_READ_FAN_MIN_PWM, fan,
&fi->min_pwm);
@@ -1585,6 +1612,7 @@ xe_hwmon_pwm_is_visible(struct xe_hwmon *hwmon, u32 attr, int channel)
switch (attr) {
case hwmon_pwm_input:
+ case hwmon_pwm_enable:
return 0644;
default:
return 0;
@@ -1669,6 +1697,9 @@ xe_hwmon_pwm_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *val)
*val = DIV_ROUND_CLOSEST(fi->fan_table[USER_FAN_TABLE].fcp[0].speed *
U8_MAX, 100);
return 0;
+ case hwmon_pwm_enable:
+ *val = fi->is_full_speed ? XE_FAN_PWM_FULL_SPEED : fi->pwm_enable_mode;
+ return 0;
default:
return -EOPNOTSUPP;
}
@@ -1677,11 +1708,15 @@ xe_hwmon_pwm_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *val)
static int
xe_hwmon_pwm_write(struct xe_hwmon *hwmon, u32 attr, int channel, long val)
{
+ struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
+ struct xe_hwmon_fan_info *fi;
int ret = 0;
if (channel < 0 || channel >= FAN_MAX)
return -EINVAL;
+ fi = &hwmon->fi[channel];
+
mutex_lock(&hwmon->hwmon_lock);
switch (attr) {
@@ -1692,10 +1727,62 @@ xe_hwmon_pwm_write(struct xe_hwmon *hwmon, u32 attr, int channel, long val)
}
ret = xe_hwmon_set_user_fan_pwm(hwmon, channel, (u8)val);
break;
+ case hwmon_pwm_enable:
+ if (val == XE_FAN_PWM_FULL_SPEED) {
+ /* Set fans at full speed. */
+ ret = xe_hwmon_activate_user_fan_table(hwmon, channel, true,
+ STOCK_FAN_TABLE);
+ if (ret) {
+ xe_warn(hwmon->xe,
+ "full speed enable for fan %d failed, ret=%d\n",
+ channel, ret);
+ }
+ } else if (val == XE_FAN_PWM_MANUAL_USER_TABLE) {
+ /*
+ * Manual fan speed control using user fan table.
+ * If user fan table is not programmed, it would be initialized with
+ * clamped stock points which allows manual control with some
+ * default points.
+ */
+ ret = xe_hwmon_activate_user_fan_table(hwmon, channel, false,
+ USER_FAN_TABLE);
+ if (ret) {
+ xe_warn(hwmon->xe,
+ "failed to activate user fan table for fan %d, ret=%d\n",
+ channel, ret);
+ }
+ } else if (val == XE_FAN_PWM_AUTO_STOCK_TABLE) {
+ /* Switch to stock table specifying the user points to 0. */
+ ret = xe_pcode_write_timeout(root_tile,
+ PCODE_MBOX(FAN_SPEED_CONTROL,
+ FSC_WRITE_NUM_FAN_CONTROL_POINTS,
+ channel),
+ 0, XE_PCODE_FAN_CONTROL_TIMEOUT_MS);
+ if (ret) {
+ xe_warn(hwmon->xe,
+ "failed to disable user fan table for fan %d, ret=%d\n",
+ channel, ret);
+ break;
+ }
+ xe_dbg(hwmon->xe,
+ "automatic control enabled for fan %d using stock table\n",
+ channel);
+ ret = 0;
+ } else {
+ ret = -EINVAL;
+ }
+ break;
default:
ret = -EOPNOTSUPP;
break;
}
+
+ /* Update PWM enable mode and full speed status if no error occurred */
+ if (!ret && attr == hwmon_pwm_enable) {
+ fi->pwm_enable_mode = val;
+ fi->is_full_speed = (val == XE_FAN_PWM_FULL_SPEED);
+ }
+
mutex_unlock(&hwmon->hwmon_lock);
return ret;
--
2.25.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 6/9] drm/xe/hwmon: Enable fan curve control support
2026-07-17 4:17 [PATCH v2 0/9] Add fan control support Karthik Poosa
` (5 preceding siblings ...)
2026-07-17 4:17 ` [PATCH v2 5/9] drm/xe/hwmon: expose pwm[1-3]_enable Karthik Poosa
@ 2026-07-17 4:17 ` Karthik Poosa
2026-07-22 18:16 ` Purkait, Soham
2026-07-17 4:17 ` [PATCH v2 7/9] drm/xe/hwmon: Add kernel-doc for fan control Karthik Poosa
` (4 subsequent siblings)
11 siblings, 1 reply; 21+ messages in thread
From: Karthik Poosa @ 2026-07-17 4:17 UTC (permalink / raw)
To: intel-xe
Cc: rodrigo.vivi, anshuman.gupta, badal.nilawar, raag.jadav,
riana.tauro, sk.anirban, mallesh.koujalagi, soham.purkait,
Karthik Poosa
Expose curve-point temperature and pwm controls via
pwmF_auto_point[P]_temp and pwmF_auto_point[P]_pwm, where
F is the fan number and P is the fan curve point number.
Each fan curve point has temperature and pwm sysfs.
Update Xe hwmon ABI documentation for pwm auto point temperature
attributes.
v2:
- Use xe helpers for dmesg logs.
- Update kernel version in Xe hwmon documentation.
- Correct debug level of some dmesg logs.
Signed-off-by: Karthik Poosa <karthik.poosa@intel.com>
Assisted-by: Codex:gpt-5-4
---
.../ABI/testing/sysfs-driver-intel-xe-hwmon | 16 +
drivers/gpu/drm/xe/xe_hwmon.c | 700 +++++++++++++++++-
2 files changed, 715 insertions(+), 1 deletion(-)
diff --git a/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon b/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon
index effc7d38ce1f..68cc6b99e8a5 100644
--- a/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon
+++ b/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon
@@ -341,3 +341,19 @@ Description: RW. Fan control mode selector.
user table control and value 2 to use firmware default behavior.
Only supported for particular Intel Xe graphics platforms.
+
+What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/pwm[1-3]_auto_point[1-10]_temp
+Date: June 2026
+KernelVersion: 7.2
+Contact: intel-xe@lists.freedesktop.org
+Description: RW. Temperature in millidegree Celsius for each fan curve point.
+
+ Only supported for particular Intel Xe graphics platforms.
+
+What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/pwm[1-3]_auto_point[1-10]_pwm
+Date: June 2026
+KernelVersion: 7.2
+Contact: intel-xe@lists.freedesktop.org
+Description: RW. PWM duty value (0..255) for each fan curve point.
+
+ Only supported for particular Intel Xe graphics platforms.
diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c
index fa9dbadd5fd6..a3ed139ad446 100644
--- a/drivers/gpu/drm/xe/xe_hwmon.c
+++ b/drivers/gpu/drm/xe/xe_hwmon.c
@@ -173,6 +173,10 @@ struct xe_hwmon_fan_info {
/** @speed: fan speed in percentage */
u8 speed;
} fcp[MAX_FAN_CONTROL_POINTS];
+ /** @last_point_temp_valid: last user temperature point was written */
+ bool last_point_temp_valid;
+ /** @last_point_speed_valid: last user PWM point was written */
+ bool last_point_speed_valid;
} fan_table[FAN_TABLE_MAX];
/** @max_rps: maximum fan RPS */
u32 max_rps;
@@ -779,8 +783,11 @@ static const struct attribute_group hwmon_attrgroup = {
.is_visible = xe_hwmon_attributes_visible,
};
+static const struct attribute_group hwmon_curve_attrgroup;
+
static const struct attribute_group *hwmon_groups[] = {
&hwmon_attrgroup,
+ &hwmon_curve_attrgroup,
NULL
};
@@ -945,7 +952,7 @@ static int xe_hwmon_get_num_fans(const struct xe_hwmon *hwmon, u32 *num_fans)
}
static int xe_hwmon_get_fan_point_count(struct xe_hwmon *hwmon, u8 fan, u32 *point_count,
- int table_type)
+ enum fan_table_type table_type)
{
int ret;
@@ -960,6 +967,16 @@ static int xe_hwmon_get_fan_point_count(struct xe_hwmon *hwmon, u8 fan, u32 *poi
return ret;
}
+ xe_dbg(hwmon->xe, "fan %d %s point count read as %u\n", fan,
+ (table_type == USER_FAN_TABLE) ? "user" : "stock", *point_count);
+
+ if (*point_count > MAX_FAN_CONTROL_POINTS) {
+ xe_err(hwmon->xe, "fan %d %s point count %u exceeds max %u\n", fan,
+ (table_type == USER_FAN_TABLE) ? "user" : "stock", *point_count,
+ MAX_FAN_CONTROL_POINTS);
+ return -EINVAL;
+ }
+
xe_dbg(hwmon->xe, "fan %d %s point count read as %u\n", fan,
(table_type == USER_FAN_TABLE) ? "user" : "stock", *point_count);
@@ -985,6 +1002,60 @@ static int xe_hwmon_write_user_fan_point(struct xe_hwmon *hwmon, u8 fan, u8 poin
return ret;
}
+static int xe_hwmon_commit_user_fan_table(struct xe_hwmon *hwmon, u8 fan)
+{
+ struct xe_hwmon_fan_info *fi = &hwmon->fi[fan];
+ struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
+ u32 point_count = fi->fan_table[USER_FAN_TABLE].fan_control_point_count;
+ int point;
+ int ret;
+
+ if (!point_count)
+ return -ENODATA;
+
+ for (point = 0; point < point_count; point++) {
+ u8 temp = fi->fan_table[USER_FAN_TABLE].fcp[point].temp;
+ u8 speed = fi->fan_table[USER_FAN_TABLE].fcp[point].speed;
+
+ /* Ensure fan control points are strictly increasing order */
+ if (point > 0) {
+ u8 prev_temp = fi->fan_table[USER_FAN_TABLE].fcp[point - 1].temp;
+ u8 prev_speed = fi->fan_table[USER_FAN_TABLE].fcp[point - 1].speed;
+
+ if (temp < prev_temp || speed < prev_speed) {
+ drm_dbg(&hwmon->xe->drm,
+ "fan %d invalid user point previous value > current %d: %u/%u prev=%u/%u\n",
+ fan, point, temp, speed, prev_temp, prev_speed);
+ return -EINVAL;
+ }
+ }
+
+ drm_dbg(&hwmon->xe->drm,
+ "committing fan %d user point %d temp %u, speed %u %%\n",
+ fan, point, temp, speed);
+ ret = xe_hwmon_write_user_fan_point(hwmon, fan, point, temp, speed);
+ if (ret)
+ return ret;
+ }
+
+ drm_dbg(&hwmon->xe->drm,
+ "committing fan %d user table with %d points\n", fan, point_count);
+ ret = xe_pcode_write_timeout(root_tile, PCODE_MBOX(FAN_SPEED_CONTROL,
+ FSC_WRITE_NUM_FAN_CONTROL_POINTS,
+ fan),
+ point_count, XE_PCODE_FAN_CONTROL_TIMEOUT_MS);
+ if (ret) {
+ drm_dbg(&hwmon->xe->drm,
+ "failed to update fan %d user table count, ret=%d\n", fan, ret);
+ return ret;
+ }
+
+ fi->pwm_enable_mode = XE_FAN_PWM_MANUAL_USER_TABLE;
+ fi->is_full_speed = false;
+
+ return 0;
+}
+
static int xe_hwmon_activate_user_fan_table(struct xe_hwmon *hwmon, u8 fan,
bool is_full_speed, enum fan_table_type table_source)
{
@@ -1105,6 +1176,633 @@ static int xe_hwmon_set_user_fan_pwm(struct xe_hwmon *hwmon, u8 fan, u8 pwm)
return 0;
}
+#define XE_CURVE_IDX(fan, point) (((fan) << 8) | (point))
+#define XE_CURVE_FAN(idx) ((u8)(((idx) >> 8) & 0xff))
+#define XE_CURVE_POINT(idx) ((u8)((idx) & 0xff))
+
+static int
+xe_hwmon_decode_curve_index(u32 index, u8 *fan, u8 *point)
+{
+ u8 local_fan = XE_CURVE_FAN(index);
+ u8 local_point = XE_CURVE_POINT(index);
+
+ if (local_fan >= FAN_MAX || local_point >= MAX_FAN_CONTROL_POINTS)
+ return -EINVAL;
+
+ *fan = local_fan;
+ *point = local_point;
+
+ return 0;
+}
+
+static inline enum fan_table_type xe_hwmon_get_active_fan_table(struct xe_hwmon *hwmon, u8 fan)
+{
+ if (hwmon->fi[fan].pwm_enable_mode == XE_FAN_PWM_AUTO_STOCK_TABLE)
+ return STOCK_FAN_TABLE;
+ else
+ return USER_FAN_TABLE;
+}
+
+static ssize_t
+xe_hwmon_pwm_auto_point_temp_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ struct xe_hwmon *hwmon = dev_get_drvdata(dev);
+ struct xe_hwmon_fan_info *fi;
+ u32 point_count;
+ u8 fan;
+ u8 point;
+ u8 temp;
+ int ret;
+ enum fan_table_type active_table;
+
+ ret = xe_hwmon_decode_curve_index(sattr->index, &fan, &point);
+ if (ret)
+ return ret;
+
+ fi = &hwmon->fi[fan];
+ active_table = xe_hwmon_get_active_fan_table(hwmon, fan);
+
+ guard(xe_pm_runtime)(hwmon->xe);
+
+ mutex_lock(&hwmon->hwmon_lock);
+
+ fi = &hwmon->fi[fan];
+
+ ret = xe_hwmon_get_fan_point_count(hwmon, fan, &point_count, active_table);
+ if (ret)
+ goto unlock;
+
+ if (point >= point_count) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ temp = fi->fan_table[active_table].fcp[point].temp;
+ ret = sysfs_emit(buf, "%u\n", temp * MILLIDEGREE_PER_DEGREE);
+
+unlock:
+ mutex_unlock(&hwmon->hwmon_lock);
+
+ return ret;
+}
+
+static int xe_hwmon_update_user_fan_point(struct xe_hwmon *hwmon, u8 fan, u32 point,
+ u32 point_count, u8 temp, u8 pwm)
+{
+ struct xe_hwmon_fan_info *fi = &hwmon->fi[fan];
+ int ret;
+
+ if (temp) {
+ fi->fan_table[USER_FAN_TABLE].fcp[point].temp = temp;
+ if (point == (point_count - 1))
+ fi->fan_table[USER_FAN_TABLE].last_point_temp_valid = true;
+ } else {
+ fi->fan_table[USER_FAN_TABLE].fcp[point].speed = pwm;
+ if (point == (point_count - 1))
+ fi->fan_table[USER_FAN_TABLE].last_point_speed_valid = true;
+ }
+
+ /* Cache intermediate points and commit once the last point arrives. */
+ if (point == (point_count - 1) &&
+ fi->fan_table[USER_FAN_TABLE].last_point_temp_valid &&
+ fi->fan_table[USER_FAN_TABLE].last_point_speed_valid) {
+ ret = xe_hwmon_commit_user_fan_table(hwmon, fan);
+ if (ret)
+ return ret;
+ fi->fan_table[USER_FAN_TABLE].last_point_temp_valid = false;
+ fi->fan_table[USER_FAN_TABLE].last_point_speed_valid = false;
+ }
+ return 0;
+}
+
+static int xe_hwmon_get_u8_from_sysfs(long val, bool is_temp, u8 *out)
+{
+ long hw_val;
+
+ if (is_temp)
+ hw_val = DIV_ROUND_CLOSEST(val, MILLIDEGREE_PER_DEGREE);
+ else
+ hw_val = DIV_ROUND_CLOSEST(val * 100, U8_MAX);
+
+ if (hw_val < 0 || hw_val > U8_MAX)
+ return -EINVAL;
+
+ *out = hw_val;
+
+ return 0;
+}
+
+static ssize_t
+xe_hwmon_pwm_auto_point_temp_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ struct xe_hwmon *hwmon = dev_get_drvdata(dev);
+ struct xe_hwmon_fan_info *fi;
+ u8 fan;
+ u8 point;
+ u32 point_count;
+ u8 temp;
+ long val;
+ int ret;
+
+ ret = xe_hwmon_decode_curve_index(sattr->index, &fan, &point);
+ if (ret)
+ return ret;
+
+ fi = &hwmon->fi[fan];
+
+ if (fi->pwm_enable_mode != XE_FAN_PWM_MANUAL_USER_TABLE) {
+ drm_err(&hwmon->xe->drm,
+ "pwm%d_enable not in manual mode; cannot update temp point %d\n",
+ fan, point);
+ return -EINVAL;
+ }
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ ret = xe_hwmon_get_u8_from_sysfs(val, true, &temp);
+ if (ret)
+ return ret;
+
+ guard(xe_pm_runtime)(hwmon->xe);
+
+ mutex_lock(&hwmon->hwmon_lock);
+
+ ret = xe_hwmon_get_fan_point_count(hwmon, fan, &point_count, USER_FAN_TABLE);
+ if (ret)
+ goto unlock;
+
+ if (point >= point_count) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ drm_dbg(&hwmon->xe->drm,
+ "updating fan %d point %d temp to %u, pwm %u\n", fan, point,
+ temp, fi->fan_table[USER_FAN_TABLE].fcp[point].speed);
+ ret = xe_hwmon_update_user_fan_point(hwmon, fan, point, point_count, temp, 0);
+
+unlock:
+ mutex_unlock(&hwmon->hwmon_lock);
+
+ return ret ? ret : count;
+}
+
+static ssize_t
+xe_hwmon_pwm_auto_point_pwm_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ struct xe_hwmon *hwmon = dev_get_drvdata(dev);
+ struct xe_hwmon_fan_info *fi;
+ u8 fan;
+ u8 point;
+ u32 point_count;
+ int ret;
+ enum fan_table_type active_table;
+
+ ret = xe_hwmon_decode_curve_index(sattr->index, &fan, &point);
+ if (ret)
+ return ret;
+
+ fi = &hwmon->fi[fan];
+ active_table = xe_hwmon_get_active_fan_table(hwmon, fan);
+
+ guard(xe_pm_runtime)(hwmon->xe);
+
+ mutex_lock(&hwmon->hwmon_lock);
+
+ ret = xe_hwmon_get_fan_point_count(hwmon, fan, &point_count, active_table);
+ if (ret)
+ goto unlock;
+
+ if (point >= point_count) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ ret = sysfs_emit(buf, "%u\n",
+ DIV_ROUND_CLOSEST(fi->fan_table[active_table].fcp[point].speed * U8_MAX,
+ 100));
+
+unlock:
+ mutex_unlock(&hwmon->hwmon_lock);
+
+ return ret;
+}
+
+static ssize_t
+xe_hwmon_pwm_auto_point_pwm_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ struct xe_hwmon *hwmon = dev_get_drvdata(dev);
+ struct xe_hwmon_fan_info *fi;
+ u32 point_count;
+ long val;
+ u8 point;
+ u8 fan;
+ u8 pwm;
+ int ret;
+
+ ret = xe_hwmon_decode_curve_index(sattr->index, &fan, &point);
+ if (ret)
+ return ret;
+
+ fi = &hwmon->fi[fan];
+
+ if (fi->pwm_enable_mode != XE_FAN_PWM_MANUAL_USER_TABLE) {
+ drm_err(&hwmon->xe->drm,
+ "pwm%d_enable is not in manual user mode, cannot update pwm point %d\n",
+ fan, point);
+ return -EINVAL;
+ }
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ ret = xe_hwmon_get_u8_from_sysfs(val, false, &pwm);
+ if (ret)
+ return ret;
+
+ guard(xe_pm_runtime)(hwmon->xe);
+
+ mutex_lock(&hwmon->hwmon_lock);
+
+ ret = xe_hwmon_get_fan_point_count(hwmon, fan, &point_count, USER_FAN_TABLE);
+ if (ret)
+ goto unlock;
+
+ if (point >= point_count) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ pwm = max_t(u8, pwm, fi->min_pwm);
+
+ drm_dbg(&hwmon->xe->drm, "updating fan %d point %d pwm to %u\n",
+ fan, point, pwm);
+ ret = xe_hwmon_update_user_fan_point(hwmon, fan, point, point_count, 0, pwm);
+
+unlock:
+ mutex_unlock(&hwmon->hwmon_lock);
+
+ return ret ? ret : count;
+}
+
+static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(0, 0));
+static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(0, 0));
+static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(0, 1));
+static SENSOR_DEVICE_ATTR(pwm1_auto_point2_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(0, 1));
+static SENSOR_DEVICE_ATTR(pwm1_auto_point3_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(0, 2));
+static SENSOR_DEVICE_ATTR(pwm1_auto_point3_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(0, 2));
+static SENSOR_DEVICE_ATTR(pwm1_auto_point4_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(0, 3));
+static SENSOR_DEVICE_ATTR(pwm1_auto_point4_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(0, 3));
+static SENSOR_DEVICE_ATTR(pwm1_auto_point5_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(0, 4));
+static SENSOR_DEVICE_ATTR(pwm1_auto_point5_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(0, 4));
+static SENSOR_DEVICE_ATTR(pwm1_auto_point6_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(0, 5));
+static SENSOR_DEVICE_ATTR(pwm1_auto_point6_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(0, 5));
+static SENSOR_DEVICE_ATTR(pwm1_auto_point7_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(0, 6));
+static SENSOR_DEVICE_ATTR(pwm1_auto_point7_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(0, 6));
+static SENSOR_DEVICE_ATTR(pwm1_auto_point8_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(0, 7));
+static SENSOR_DEVICE_ATTR(pwm1_auto_point8_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(0, 7));
+static SENSOR_DEVICE_ATTR(pwm1_auto_point9_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(0, 8));
+static SENSOR_DEVICE_ATTR(pwm1_auto_point9_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(0, 8));
+static SENSOR_DEVICE_ATTR(pwm1_auto_point10_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(0, 9));
+static SENSOR_DEVICE_ATTR(pwm1_auto_point10_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(0, 9));
+static SENSOR_DEVICE_ATTR(pwm2_auto_point1_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(1, 0));
+static SENSOR_DEVICE_ATTR(pwm2_auto_point1_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(1, 0));
+static SENSOR_DEVICE_ATTR(pwm2_auto_point2_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(1, 1));
+static SENSOR_DEVICE_ATTR(pwm2_auto_point2_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(1, 1));
+static SENSOR_DEVICE_ATTR(pwm2_auto_point3_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(1, 2));
+static SENSOR_DEVICE_ATTR(pwm2_auto_point3_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(1, 2));
+static SENSOR_DEVICE_ATTR(pwm2_auto_point4_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(1, 3));
+static SENSOR_DEVICE_ATTR(pwm2_auto_point4_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(1, 3));
+static SENSOR_DEVICE_ATTR(pwm2_auto_point5_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(1, 4));
+static SENSOR_DEVICE_ATTR(pwm2_auto_point5_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(1, 4));
+static SENSOR_DEVICE_ATTR(pwm2_auto_point6_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(1, 5));
+static SENSOR_DEVICE_ATTR(pwm2_auto_point6_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(1, 5));
+static SENSOR_DEVICE_ATTR(pwm2_auto_point7_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(1, 6));
+static SENSOR_DEVICE_ATTR(pwm2_auto_point7_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(1, 6));
+static SENSOR_DEVICE_ATTR(pwm2_auto_point8_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(1, 7));
+static SENSOR_DEVICE_ATTR(pwm2_auto_point8_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(1, 7));
+static SENSOR_DEVICE_ATTR(pwm2_auto_point9_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(1, 8));
+static SENSOR_DEVICE_ATTR(pwm2_auto_point9_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(1, 8));
+static SENSOR_DEVICE_ATTR(pwm2_auto_point10_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(1, 9));
+static SENSOR_DEVICE_ATTR(pwm2_auto_point10_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(1, 9));
+static SENSOR_DEVICE_ATTR(pwm3_auto_point1_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(2, 0));
+static SENSOR_DEVICE_ATTR(pwm3_auto_point1_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(2, 0));
+static SENSOR_DEVICE_ATTR(pwm3_auto_point2_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(2, 1));
+static SENSOR_DEVICE_ATTR(pwm3_auto_point2_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(2, 1));
+static SENSOR_DEVICE_ATTR(pwm3_auto_point3_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(2, 2));
+static SENSOR_DEVICE_ATTR(pwm3_auto_point3_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(2, 2));
+static SENSOR_DEVICE_ATTR(pwm3_auto_point4_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(2, 3));
+static SENSOR_DEVICE_ATTR(pwm3_auto_point4_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(2, 3));
+static SENSOR_DEVICE_ATTR(pwm3_auto_point5_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(2, 4));
+static SENSOR_DEVICE_ATTR(pwm3_auto_point5_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(2, 4));
+static SENSOR_DEVICE_ATTR(pwm3_auto_point6_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(2, 5));
+static SENSOR_DEVICE_ATTR(pwm3_auto_point6_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(2, 5));
+static SENSOR_DEVICE_ATTR(pwm3_auto_point7_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(2, 6));
+static SENSOR_DEVICE_ATTR(pwm3_auto_point7_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(2, 6));
+static SENSOR_DEVICE_ATTR(pwm3_auto_point8_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(2, 7));
+static SENSOR_DEVICE_ATTR(pwm3_auto_point8_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(2, 7));
+static SENSOR_DEVICE_ATTR(pwm3_auto_point9_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(2, 8));
+static SENSOR_DEVICE_ATTR(pwm3_auto_point9_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(2, 8));
+static SENSOR_DEVICE_ATTR(pwm3_auto_point10_temp, 0644,
+ xe_hwmon_pwm_auto_point_temp_show,
+ xe_hwmon_pwm_auto_point_temp_store,
+ XE_CURVE_IDX(2, 9));
+static SENSOR_DEVICE_ATTR(pwm3_auto_point10_pwm, 0644,
+ xe_hwmon_pwm_auto_point_pwm_show,
+ xe_hwmon_pwm_auto_point_pwm_store,
+ XE_CURVE_IDX(2, 9));
+
+static struct attribute *hwmon_curve_attributes[] = {
+ &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point4_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point5_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point6_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point7_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point8_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point9_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point10_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point4_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point5_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point6_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point7_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point8_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point9_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point10_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point3_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point4_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point5_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point6_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point7_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point8_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point9_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point10_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point3_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point4_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point5_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point6_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point7_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point8_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point9_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point10_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point3_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point4_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point5_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point6_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point7_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point8_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point9_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point10_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point3_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point4_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point5_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point6_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point7_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point8_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point9_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point10_pwm.dev_attr.attr,
+ NULL
+};
+
+static umode_t xe_hwmon_curve_attributes_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct xe_hwmon *hwmon = dev_get_drvdata(dev);
+
+ /* Temp/PWM pairs for each point. */
+ u32 attrs_per_fan = 2 * MAX_FAN_CONTROL_POINTS;
+ u32 fan = index / attrs_per_fan;
+ u32 local = index % attrs_per_fan;
+ u32 point_count;
+
+ if (!hwmon->xe->info.has_fan_control)
+ return 0;
+
+ if (fan >= hwmon->num_fans)
+ return 0;
+
+ point_count = max(hwmon->fi[fan].fan_table[USER_FAN_TABLE].fan_control_point_count,
+ hwmon->fi[fan].fan_table[STOCK_FAN_TABLE].fan_control_point_count);
+
+ if (!point_count)
+ return 0;
+
+ if (local < MAX_FAN_CONTROL_POINTS)
+ return (local + 1) <= point_count ? attr->mode : 0;
+
+ if (local < (2 * MAX_FAN_CONTROL_POINTS))
+ return (local - MAX_FAN_CONTROL_POINTS + 1) <= point_count ?
+ attr->mode : 0;
+
+ return 0;
+}
+
+static const struct attribute_group hwmon_curve_attrgroup = {
+ .attrs = hwmon_curve_attributes,
+ .is_visible = xe_hwmon_curve_attributes_visible,
+};
+
static int xe_hwmon_read_fan_control_info(struct xe_hwmon *hwmon)
{
struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
--
2.25.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 7/9] drm/xe/hwmon: Add kernel-doc for fan control
2026-07-17 4:17 [PATCH v2 0/9] Add fan control support Karthik Poosa
` (6 preceding siblings ...)
2026-07-17 4:17 ` [PATCH v2 6/9] drm/xe/hwmon: Enable fan curve control support Karthik Poosa
@ 2026-07-17 4:17 ` Karthik Poosa
2026-07-17 4:17 ` [PATCH v2 8/9] drm/xe/hwmon: preserve fan user table across suspend resume Karthik Poosa
` (3 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Karthik Poosa @ 2026-07-17 4:17 UTC (permalink / raw)
To: intel-xe
Cc: rodrigo.vivi, anshuman.gupta, badal.nilawar, raag.jadav,
riana.tauro, sk.anirban, mallesh.koujalagi, soham.purkait,
Karthik Poosa
Add kernel-doc for xe hwmon fan control and describe the fan control
model built around firmware stock tables and driver-managed user tables.
Document the exposed sysfs files for fan speed, maximum speed, pwm
control, pwm mode selection, and per-point fan curve temperature/PWM
programming.
This makes the hwmon fan control userspace interface easier to discover
and understand.
Signed-off-by: Karthik Poosa <karthik.poosa@intel.com>
Assisted-by: Codex:gpt-5-4
---
drivers/gpu/drm/xe/xe_hwmon.c | 43 +++++++++++++++++++++++++++++++++++
1 file changed, 43 insertions(+)
diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c
index a3ed139ad446..770f07fb6511 100644
--- a/drivers/gpu/drm/xe/xe_hwmon.c
+++ b/drivers/gpu/drm/xe/xe_hwmon.c
@@ -123,6 +123,49 @@ struct xe_hwmon_energy_info {
long accum_energy;
};
+/**
+ * DOC: Xe fan control
+ *
+ * Xe fan control is exposed through the hwmon pwm and fan interfaces when the
+ * platform advertises PCODE-backed fan management support.
+ *
+ * The implementation tracks two fan tables per fan:
+ *
+ * - A stock table read from firmware through PCODE.
+ * - A user table cached in the driver and programmed back through PCODE.
+ *
+ * The pwmN_enable sysfs mode selects how those tables are used:
+ *
+ * - ``XE_FAN_PWM_AUTO_STOCK_TABLE`` restores automatic control using the stock
+ * firmware table.
+ * - ``XE_FAN_PWM_MANUAL_USER_TABLE`` enables the user table for manual control.
+ * - ``XE_FAN_PWM_FULL_SPEED`` reuses the user table path but programs every
+ * point to 100 percent duty cycle as a full-speed override.
+ *
+ * The fan-control sysfs files are:
+ *
+ * - ``fanN_input`` reports the measured fan speed in RPM.
+ * - ``fanN_max`` reports the maximum supported fan speed in RPM.
+ * - ``pwmN`` reads or sets the manual PWM duty cycle in the hwmon 0..255
+ * range. Manual writes are accepted only while ``pwmN_enable`` is set to
+ * ``XE_FAN_PWM_MANUAL_USER_TABLE``.
+ * - ``pwmN_enable`` selects stock automatic mode, manual user-table mode, or
+ * full-speed override.
+ * - ``pwmN_auto_pointM_temp`` exposes the temperature threshold for user-table
+ * point ``M`` in millidegrees Celsius.
+ * - ``pwmN_auto_pointM_pwm`` exposes the PWM value for user-table point ``M``
+ * in the hwmon 0..255 range.
+ *
+ * Writes to pwmN_auto_pointM_temp and pwmN_auto_pointM_pwm update the cached
+ * user table entries. The driver commits the full user table once the last
+ * point has both temperature and PWM values, which keeps intermediate sysfs
+ * writes from pushing partially updated curves to firmware.
+ *
+ * Writes to pwmN provide a constant manual duty cycle by rewriting every point
+ * in the active user table to the same PWM value, clamped against the minimum
+ * duty cycle reported by firmware for that fan.
+ */
+
enum xe_fan_pwm_enable_mode {
/** @XE_FAN_PWM_FULL_SPEED: force all user table points to full speed */
XE_FAN_PWM_FULL_SPEED = 0,
--
2.25.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 8/9] drm/xe/hwmon: preserve fan user table across suspend resume
2026-07-17 4:17 [PATCH v2 0/9] Add fan control support Karthik Poosa
` (7 preceding siblings ...)
2026-07-17 4:17 ` [PATCH v2 7/9] drm/xe/hwmon: Add kernel-doc for fan control Karthik Poosa
@ 2026-07-17 4:17 ` Karthik Poosa
2026-08-10 5:53 ` Nilawar, Badal
2026-07-17 4:17 ` [PATCH v2 9/9] drm/xe/hwmon: Update fan info after late binding Karthik Poosa
` (2 subsequent siblings)
11 siblings, 1 reply; 21+ messages in thread
From: Karthik Poosa @ 2026-07-17 4:17 UTC (permalink / raw)
To: intel-xe
Cc: rodrigo.vivi, anshuman.gupta, badal.nilawar, raag.jadav,
riana.tauro, sk.anirban, mallesh.koujalagi, soham.purkait,
Karthik Poosa
Save and restore active fan user tables over suspend/resume via new
xe_hwmon PM hooks, and run restore, only when d3cold is allowed.
Signed-off-by: Karthik Poosa <karthik.poosa@intel.com>
Assisted-by: Codex:gpt-5-3
---
drivers/gpu/drm/xe/xe_hwmon.c | 133 ++++++++++++++++++++++++++++++++++
drivers/gpu/drm/xe/xe_hwmon.h | 4 +
drivers/gpu/drm/xe/xe_pm.c | 9 +++
3 files changed, 146 insertions(+)
diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c
index 770f07fb6511..65f356c741f5 100644
--- a/drivers/gpu/drm/xe/xe_hwmon.c
+++ b/drivers/gpu/drm/xe/xe_hwmon.c
@@ -229,6 +229,8 @@ struct xe_hwmon_fan_info {
u8 pwm_enable_mode;
/** @is_full_speed: flag indicating if fan is in full speed */
bool is_full_speed;
+ /** @restore_user_table: restore user fan table on next resume */
+ bool restore_user_table;
};
/**
@@ -2785,4 +2787,135 @@ int xe_hwmon_register(struct xe_device *xe)
return 0;
}
+
+/**
+ * xe_hwmon_suspend - Save hwmon state before suspend
+ * @xe: xe device instance
+ *
+ * Save any hwmon runtime state that must survive PM suspend transitions.
+ */
+void xe_hwmon_suspend(struct xe_device *xe)
+{
+ struct xe_hwmon *hwmon = xe->hwmon;
+ struct xe_tile *root_tile;
+ int fan;
+
+ if (!hwmon || !xe->info.has_fan_control)
+ return;
+
+ /*
+ * User fan-table restore is limited to d3cold flows, so skip snapshot
+ * work when d3cold is not allowed for this device.
+ */
+ if (!xe->d3cold.allowed) {
+ xe_dbg(xe, "d3cold not allowed, skipping fan user-table snapshot\n");
+ return;
+ }
+
+ root_tile = xe_device_get_root_tile(hwmon->xe);
+
+ mutex_lock(&hwmon->hwmon_lock);
+
+ for (fan = 0; fan < hwmon->num_fans; fan++) {
+ struct xe_hwmon_fan_info *fi = &hwmon->fi[fan];
+ u32 point_count;
+ int ret;
+
+ /*
+ * Record whether this fan was using the user table
+ * so resume can restore it if firmware state is reset.
+ */
+ fi->restore_user_table = (fi->pwm_enable_mode != XE_FAN_PWM_AUTO_STOCK_TABLE);
+
+ xe_dbg(hwmon->xe, "fan %d user table restore flag set to %d\n", fan,
+ fi->restore_user_table);
+
+ if (!fi->restore_user_table)
+ continue;
+
+ ret = xe_hwmon_get_fan_point_count(hwmon, fan, &point_count, USER_FAN_TABLE);
+ if (ret || !point_count) {
+ xe_warn(hwmon->xe,
+ "fan %d point count read before suspend failed, ret=%d, val =%u\n",
+ fan, ret, point_count);
+ continue;
+ }
+
+ fi->fan_table[USER_FAN_TABLE].fan_control_point_count =
+ min_t(u32, point_count, MAX_FAN_CONTROL_POINTS);
+
+ for (int point = 0; point < fi->fan_table[USER_FAN_TABLE].fan_control_point_count;
+ point++) {
+ u32 fcp = FCP_INDEX(USER_FAN_TABLE, point);
+
+ ret = xe_pcode_read_timeout(root_tile,
+ PCODE_MBOX(FAN_SPEED_CONTROL,
+ FSC_READ_FAN_TABLE, fan),
+ &fcp, NULL,
+ XE_PCODE_FAN_CONTROL_TIMEOUT_MS);
+ if (ret) {
+ xe_warn(hwmon->xe,
+ "fan %d user point %d before suspend failed, ret=%d\n",
+ fan, point, ret);
+ continue;
+ }
+
+ fi->fan_table[USER_FAN_TABLE].fcp[point].temp =
+ REG_FIELD_GET(FAN_CONTROL_POINT_TEMP_MASK, fcp);
+ fi->fan_table[USER_FAN_TABLE].fcp[point].speed =
+ REG_FIELD_GET(FAN_CONTROL_POINT_SPEED_MASK, fcp);
+ }
+ xe_dbg(hwmon->xe,
+ "fan %d user table snapshot taken before suspend, point_count=%u\n", fan,
+ fi->fan_table[USER_FAN_TABLE].fan_control_point_count);
+ }
+
+ mutex_unlock(&hwmon->hwmon_lock);
+}
+
+/**
+ * xe_hwmon_resume - Restore hwmon state after resume
+ * @xe: xe device instance
+ *
+ * Restore any hwmon runtime state that was saved across suspend.
+ */
+void xe_hwmon_resume(struct xe_device *xe)
+{
+ struct xe_hwmon *hwmon = xe->hwmon;
+ int fan;
+
+ if (!hwmon || !xe->info.has_fan_control)
+ return;
+
+ if (!xe->d3cold.allowed) {
+ xe_dbg(xe, "d3cold not allowed, skipping fan user-table restore\n");
+ return;
+ }
+
+ mutex_lock(&hwmon->hwmon_lock);
+
+ for (fan = 0; fan < hwmon->num_fans; fan++) {
+ struct xe_hwmon_fan_info *fi = &hwmon->fi[fan];
+ int ret;
+
+ if (!fi->restore_user_table) {
+ xe_dbg(xe, "fan %d user table restore not required\n", fan);
+ continue;
+ }
+
+ /* Re-program cached user-table settings for fans active before suspend. */
+ ret = xe_hwmon_activate_user_fan_table(hwmon, fan,
+ fi->is_full_speed,
+ USER_FAN_TABLE);
+ if (ret)
+ xe_warn(xe, "failed to restore fan %d user table after resume, ret=%d\n",
+ fan, ret);
+
+ xe_dbg(xe, "fan %d user table restored\n", fan);
+ fi->restore_user_table = false;
+ }
+
+ mutex_unlock(&hwmon->hwmon_lock);
+}
+
MODULE_IMPORT_NS("INTEL_PMT_TELEMETRY");
diff --git a/drivers/gpu/drm/xe/xe_hwmon.h b/drivers/gpu/drm/xe/xe_hwmon.h
index d02c1bfe8c0a..6c6f30208508 100644
--- a/drivers/gpu/drm/xe/xe_hwmon.h
+++ b/drivers/gpu/drm/xe/xe_hwmon.h
@@ -12,8 +12,12 @@ struct xe_device;
#if IS_REACHABLE(CONFIG_HWMON)
int xe_hwmon_register(struct xe_device *xe);
+void xe_hwmon_suspend(struct xe_device *xe);
+void xe_hwmon_resume(struct xe_device *xe);
#else
static inline int xe_hwmon_register(struct xe_device *xe) { return 0; };
+static inline void xe_hwmon_suspend(struct xe_device *xe) {}
+static inline void xe_hwmon_resume(struct xe_device *xe) {}
#endif
#endif /* _XE_HWMON_H_ */
diff --git a/drivers/gpu/drm/xe/xe_pm.c b/drivers/gpu/drm/xe/xe_pm.c
index 99562f691080..23a78fa4a71e 100644
--- a/drivers/gpu/drm/xe/xe_pm.c
+++ b/drivers/gpu/drm/xe/xe_pm.c
@@ -23,6 +23,7 @@
#include "xe_i2c.h"
#include "xe_irq.h"
#include "xe_late_bind_fw.h"
+#include "xe_hwmon.h"
#include "xe_pcode.h"
#include "xe_printk.h"
#include "xe_pxp.h"
@@ -189,6 +190,8 @@ int xe_pm_suspend(struct xe_device *xe)
for_each_gt(gt, xe, id)
xe_gt_suspend_prepare(gt);
+ xe_hwmon_suspend(xe);
+
xe_display_pm_suspend(xe);
/* FIXME: Super racey... */
@@ -290,6 +293,8 @@ int xe_pm_resume(struct xe_device *xe)
xe_late_bind_fw_load(&xe->late_bind);
+ xe_hwmon_resume(xe);
+
drm_dbg(&xe->drm, "Device resumed\n");
xe_pm_block_end_signalling();
return 0;
@@ -615,6 +620,8 @@ int xe_pm_runtime_suspend(struct xe_device *xe)
if (err)
goto out;
+ xe_hwmon_suspend(xe);
+
/*
* Applying lock for entire list op as xe_ttm_bo_destroy and xe_bo_move_notify
* also checks and deletes bo entry from user fault list.
@@ -724,6 +731,8 @@ int xe_pm_runtime_resume(struct xe_device *xe)
xe_pxp_pm_resume(xe->pxp);
+ xe_hwmon_resume(xe);
+
if (IS_VF_CCS_READY(xe))
xe_sriov_vf_ccs_register_context(xe);
--
2.25.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 9/9] drm/xe/hwmon: Update fan info after late binding
2026-07-17 4:17 [PATCH v2 0/9] Add fan control support Karthik Poosa
` (8 preceding siblings ...)
2026-07-17 4:17 ` [PATCH v2 8/9] drm/xe/hwmon: preserve fan user table across suspend resume Karthik Poosa
@ 2026-07-17 4:17 ` Karthik Poosa
2026-07-20 6:20 ` Purkait, Soham
2026-07-17 5:00 ` ✓ Xe.CI.BAT: success for Add fan control support (rev2) Patchwork
2026-07-17 8:18 ` ✗ Xe.CI.FULL: failure " Patchwork
11 siblings, 1 reply; 21+ messages in thread
From: Karthik Poosa @ 2026-07-17 4:17 UTC (permalink / raw)
To: intel-xe
Cc: rodrigo.vivi, anshuman.gupta, badal.nilawar, raag.jadav,
riana.tauro, sk.anirban, mallesh.koujalagi, soham.purkait,
Karthik Poosa
Add xe_hwmon_fan_update_post_lb() to update fan info after late binding.
Add param is_init into xe_hwmon_read_fan_control_info() to avoid
resetting user pwm mode post-LB.
Call init path with is_init=true from preregistration.
Signed-off-by: Karthik Poosa <karthik.poosa@intel.com>
Assisted-by: Codex:gpt-5-3
---
drivers/gpu/drm/xe/xe_hwmon.c | 50 +++++++++++++++++++++++++---
drivers/gpu/drm/xe/xe_hwmon.h | 2 ++
drivers/gpu/drm/xe/xe_late_bind_fw.c | 3 ++
3 files changed, 51 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c
index 65f356c741f5..518ba4438de4 100644
--- a/drivers/gpu/drm/xe/xe_hwmon.c
+++ b/drivers/gpu/drm/xe/xe_hwmon.c
@@ -1848,7 +1848,7 @@ static const struct attribute_group hwmon_curve_attrgroup = {
.is_visible = xe_hwmon_curve_attributes_visible,
};
-static int xe_hwmon_read_fan_control_info(struct xe_hwmon *hwmon)
+static int xe_hwmon_read_fan_control_info(struct xe_hwmon *hwmon, bool is_init)
{
struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
int point;
@@ -1901,8 +1901,13 @@ static int xe_hwmon_read_fan_control_info(struct xe_hwmon *hwmon)
fi->fan_table[STOCK_FAN_TABLE].fcp[point].speed);
}
- /* Set PWM enable mode to automatic i.e stock table in use. */
- fi->pwm_enable_mode = XE_FAN_PWM_AUTO_STOCK_TABLE;
+ /*
+ * Set PWM enable mode to automatic i.e stock table in use, only during init.
+ * For late binding, the mode is already set by the user and should not be
+ * overridden.
+ */
+ if (is_init)
+ fi->pwm_enable_mode = XE_FAN_PWM_AUTO_STOCK_TABLE;
/* Read minimum fan PWM */
ret = xe_hwmon_pcode_read_fan_control(hwmon, FSC_READ_FAN_MIN_PWM, fan,
@@ -2737,7 +2742,7 @@ xe_hwmon_get_preregistration_info(struct xe_hwmon *hwmon)
xe_hwmon_fan_input_read(hwmon, channel, &fan_speed);
/* Fan control tables initialization */
- if (xe_hwmon_read_fan_control_info(hwmon))
+ if (xe_hwmon_read_fan_control_info(hwmon, true))
xe_warn(hwmon->xe, "Fan control tables are not available\n");
}
@@ -2918,4 +2923,41 @@ void xe_hwmon_resume(struct xe_device *xe)
mutex_unlock(&hwmon->hwmon_lock);
}
+/**
+ * xe_hwmon_fan_update_post_lb - Update fan info after late binding
+ * @xe: xe device instance
+ *
+ * This function reads the number of fans and their stock table after late binding.
+ */
+void xe_hwmon_fan_update_post_lb(struct xe_device *xe)
+{
+ struct xe_hwmon *hwmon = xe->hwmon;
+ int fan;
+ int num_fans;
+
+ if (!hwmon || !xe->info.has_fan_control)
+ return;
+
+ mutex_lock(&hwmon->hwmon_lock);
+
+ /* Read number of fans */
+ if (xe_hwmon_get_num_fans(hwmon, &num_fans)) {
+ xe_warn(hwmon->xe, "Failed to read number of fans after LB\n");
+ goto unlock;
+ }
+ hwmon->num_fans = num_fans;
+ xe_dbg(hwmon->xe, "Number of fans detected after LB: %u\n", hwmon->num_fans);
+
+ /* Read stock table */
+ for (fan = 0; fan < hwmon->num_fans; fan++) {
+ if (xe_hwmon_read_fan_control_info(hwmon, false)) {
+ xe_warn(hwmon->xe, "Failed to read fan %d stock table after LB\n", fan);
+ continue;
+ }
+ }
+
+unlock:
+ mutex_unlock(&hwmon->hwmon_lock);
+}
+
MODULE_IMPORT_NS("INTEL_PMT_TELEMETRY");
diff --git a/drivers/gpu/drm/xe/xe_hwmon.h b/drivers/gpu/drm/xe/xe_hwmon.h
index 6c6f30208508..7198d7a4a1be 100644
--- a/drivers/gpu/drm/xe/xe_hwmon.h
+++ b/drivers/gpu/drm/xe/xe_hwmon.h
@@ -14,10 +14,12 @@ struct xe_device;
int xe_hwmon_register(struct xe_device *xe);
void xe_hwmon_suspend(struct xe_device *xe);
void xe_hwmon_resume(struct xe_device *xe);
+void xe_hwmon_fan_update_post_lb(struct xe_device *xe);
#else
static inline int xe_hwmon_register(struct xe_device *xe) { return 0; };
static inline void xe_hwmon_suspend(struct xe_device *xe) {}
static inline void xe_hwmon_resume(struct xe_device *xe) {}
+static inline void xe_hwmon_fan_update_post_lb(struct xe_device *xe) {}
#endif
#endif /* _XE_HWMON_H_ */
diff --git a/drivers/gpu/drm/xe/xe_late_bind_fw.c b/drivers/gpu/drm/xe/xe_late_bind_fw.c
index 768442ca7da6..34295418ef50 100644
--- a/drivers/gpu/drm/xe/xe_late_bind_fw.c
+++ b/drivers/gpu/drm/xe/xe_late_bind_fw.c
@@ -13,6 +13,7 @@
#include <drm/drm_print.h>
#include "xe_device.h"
+#include "xe_hwmon.h"
#include "xe_late_bind_fw.h"
#include "xe_pcode.h"
#include "xe_pcode_api.h"
@@ -252,6 +253,8 @@ static void xe_late_bind_work(struct work_struct *work)
if (!ret) {
drm_dbg(&xe->drm, "Load %s firmware successful\n",
fw_id_to_name[lbfw->id]);
+ if (fw_id_to_name[lbfw->id] == XE_LB_FW_FAN_CONTROL)
+ xe_hwmon_fan_update_post_lb(late_bind_to_xe(late_bind));
goto out;
}
--
2.25.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* ✓ Xe.CI.BAT: success for Add fan control support (rev2)
2026-07-17 4:17 [PATCH v2 0/9] Add fan control support Karthik Poosa
` (9 preceding siblings ...)
2026-07-17 4:17 ` [PATCH v2 9/9] drm/xe/hwmon: Update fan info after late binding Karthik Poosa
@ 2026-07-17 5:00 ` Patchwork
2026-07-17 8:18 ` ✗ Xe.CI.FULL: failure " Patchwork
11 siblings, 0 replies; 21+ messages in thread
From: Patchwork @ 2026-07-17 5:00 UTC (permalink / raw)
To: Poosa, Karthik; +Cc: intel-xe
[-- Attachment #1: Type: text/plain, Size: 853 bytes --]
== Series Details ==
Series: Add fan control support (rev2)
URL : https://patchwork.freedesktop.org/series/168027/
State : success
== Summary ==
CI Bug Log - changes from xe-5421-e3083db0ad8088e82975008863679579fc7e9dc0_BAT -> xe-pw-168027v2_BAT
====================================================
Summary
-------
**SUCCESS**
No regressions found.
Participating hosts (13 -> 13)
------------------------------
No changes in participating hosts
Changes
-------
No changes found
Build changes
-------------
* Linux: xe-5421-e3083db0ad8088e82975008863679579fc7e9dc0 -> xe-pw-168027v2
IGT_9011: 9011
xe-5421-e3083db0ad8088e82975008863679579fc7e9dc0: e3083db0ad8088e82975008863679579fc7e9dc0
xe-pw-168027v2: 168027v2
== Logs ==
For more details see: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/index.html
[-- Attachment #2: Type: text/html, Size: 1401 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
* ✗ Xe.CI.FULL: failure for Add fan control support (rev2)
2026-07-17 4:17 [PATCH v2 0/9] Add fan control support Karthik Poosa
` (10 preceding siblings ...)
2026-07-17 5:00 ` ✓ Xe.CI.BAT: success for Add fan control support (rev2) Patchwork
@ 2026-07-17 8:18 ` Patchwork
11 siblings, 0 replies; 21+ messages in thread
From: Patchwork @ 2026-07-17 8:18 UTC (permalink / raw)
To: Poosa, Karthik; +Cc: intel-xe
[-- Attachment #1: Type: text/plain, Size: 23543 bytes --]
== Series Details ==
Series: Add fan control support (rev2)
URL : https://patchwork.freedesktop.org/series/168027/
State : failure
== Summary ==
CI Bug Log - changes from xe-5421-e3083db0ad8088e82975008863679579fc7e9dc0_FULL -> xe-pw-168027v2_FULL
====================================================
Summary
-------
**FAILURE**
Serious unknown changes coming with xe-pw-168027v2_FULL absolutely need to be
verified manually.
If you think the reported changes have nothing to do with the changes
introduced in xe-pw-168027v2_FULL, please notify your bug team (I915-ci-infra@lists.freedesktop.org) to allow them
to document this new failure mode, which will reduce false positives in CI.
Participating hosts (2 -> 2)
------------------------------
No changes in participating hosts
Possible new issues
-------------------
Here are the unknown changes that may have been introduced in xe-pw-168027v2_FULL:
### IGT changes ###
#### Possible regressions ####
* igt@intel_hwmon@hwmon-write:
- shard-bmg: [PASS][1] -> [DMESG-FAIL][2]
[1]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-5421-e3083db0ad8088e82975008863679579fc7e9dc0/shard-bmg-5/igt@intel_hwmon@hwmon-write.html
[2]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-10/igt@intel_hwmon@hwmon-write.html
* igt@kms_flip@2x-flip-vs-expired-vblank-interruptible@bd-dp2-hdmi-a3:
- shard-bmg: [PASS][3] -> [DMESG-WARN][4] +304 other tests dmesg-warn
[3]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-5421-e3083db0ad8088e82975008863679579fc7e9dc0/shard-bmg-7/igt@kms_flip@2x-flip-vs-expired-vblank-interruptible@bd-dp2-hdmi-a3.html
[4]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-9/igt@kms_flip@2x-flip-vs-expired-vblank-interruptible@bd-dp2-hdmi-a3.html
* igt@xe_oa@buffer-size@oag-0-64m:
- shard-bmg: NOTRUN -> [DMESG-WARN][5]
[5]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@xe_oa@buffer-size@oag-0-64m.html
#### Warnings ####
* igt@kms_content_protection@legacy:
- shard-bmg: [FAIL][6] ([Intel XE#1178] / [Intel XE#3304] / [Intel XE#7374]) -> [DMESG-FAIL][7] +1 other test dmesg-fail
[6]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-5421-e3083db0ad8088e82975008863679579fc7e9dc0/shard-bmg-6/igt@kms_content_protection@legacy.html
[7]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-2/igt@kms_content_protection@legacy.html
* igt@kms_content_protection@uevent-hdcp14:
- shard-bmg: [FAIL][8] ([Intel XE#6707] / [Intel XE#7439]) -> [DMESG-FAIL][9] +1 other test dmesg-fail
[8]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-5421-e3083db0ad8088e82975008863679579fc7e9dc0/shard-bmg-9/igt@kms_content_protection@uevent-hdcp14.html
[9]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-1/igt@kms_content_protection@uevent-hdcp14.html
* igt@xe_fault_injection@exec-queue-create-fail-xe_pxp_exec_queue_add:
- shard-bmg: [SKIP][10] ([Intel XE#6281] / [Intel XE#7426]) -> [ABORT][11]
[10]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-5421-e3083db0ad8088e82975008863679579fc7e9dc0/shard-bmg-8/igt@xe_fault_injection@exec-queue-create-fail-xe_pxp_exec_queue_add.html
[11]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-4/igt@xe_fault_injection@exec-queue-create-fail-xe_pxp_exec_queue_add.html
* igt@xe_pmu@engine-activity-accuracy-90:
- shard-bmg: [FAIL][12] -> [DMESG-WARN][13] +1 other test dmesg-warn
[12]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-5421-e3083db0ad8088e82975008863679579fc7e9dc0/shard-bmg-1/igt@xe_pmu@engine-activity-accuracy-90.html
[13]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@xe_pmu@engine-activity-accuracy-90.html
* igt@xe_sriov_flr@flr-vfs-parallel:
- shard-bmg: [FAIL][14] ([Intel XE#6569]) -> [DMESG-FAIL][15] +1 other test dmesg-fail
[14]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-5421-e3083db0ad8088e82975008863679579fc7e9dc0/shard-bmg-2/igt@xe_sriov_flr@flr-vfs-parallel.html
[15]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-8/igt@xe_sriov_flr@flr-vfs-parallel.html
Known issues
------------
Here are the changes found in xe-pw-168027v2_FULL that come from known issues:
### IGT changes ###
#### Issues hit ####
* igt@kms_big_fb@linear-max-hw-stride-32bpp-rotate-180-hflip:
- shard-bmg: NOTRUN -> [SKIP][16] ([Intel XE#7059] / [Intel XE#7085])
[16]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@kms_big_fb@linear-max-hw-stride-32bpp-rotate-180-hflip.html
* igt@kms_big_fb@x-tiled-64bpp-rotate-270:
- shard-bmg: NOTRUN -> [SKIP][17] ([Intel XE#2327]) +1 other test skip
[17]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@kms_big_fb@x-tiled-64bpp-rotate-270.html
* igt@kms_big_fb@x-tiled-max-hw-stride-64bpp-rotate-180-hflip-async-flip:
- shard-lnl: [PASS][18] -> [FAIL][19] ([Intel XE#1231])
[18]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-5421-e3083db0ad8088e82975008863679579fc7e9dc0/shard-lnl-4/igt@kms_big_fb@x-tiled-max-hw-stride-64bpp-rotate-180-hflip-async-flip.html
[19]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-lnl-3/igt@kms_big_fb@x-tiled-max-hw-stride-64bpp-rotate-180-hflip-async-flip.html
* igt@kms_big_fb@yf-tiled-max-hw-stride-64bpp-rotate-180:
- shard-bmg: NOTRUN -> [SKIP][20] ([Intel XE#1124]) +1 other test skip
[20]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@kms_big_fb@yf-tiled-max-hw-stride-64bpp-rotate-180.html
* igt@kms_ccs@crc-primary-basic-4-tiled-dg2-rc-ccs:
- shard-bmg: NOTRUN -> [SKIP][21] ([Intel XE#2887]) +1 other test skip
[21]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@kms_ccs@crc-primary-basic-4-tiled-dg2-rc-ccs.html
* igt@kms_ccs@crc-primary-suspend-4-tiled-bmg-ccs:
- shard-bmg: [PASS][22] -> [INCOMPLETE][23] ([Intel XE#7084] / [Intel XE#8150]) +1 other test incomplete
[22]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-5421-e3083db0ad8088e82975008863679579fc7e9dc0/shard-bmg-5/igt@kms_ccs@crc-primary-suspend-4-tiled-bmg-ccs.html
[23]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-7/igt@kms_ccs@crc-primary-suspend-4-tiled-bmg-ccs.html
* igt@kms_ccs@crc-primary-suspend-4-tiled-mtl-rc-ccs-cc:
- shard-bmg: NOTRUN -> [SKIP][24] ([Intel XE#3432])
[24]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@kms_ccs@crc-primary-suspend-4-tiled-mtl-rc-ccs-cc.html
* igt@kms_chamelium_color@ctm-negative:
- shard-bmg: NOTRUN -> [SKIP][25] ([Intel XE#2325] / [Intel XE#7358])
[25]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@kms_chamelium_color@ctm-negative.html
* igt@kms_chamelium_edid@dp-edid-stress-resolution-non-4k:
- shard-bmg: NOTRUN -> [SKIP][26] ([Intel XE#2252]) +2 other tests skip
[26]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@kms_chamelium_edid@dp-edid-stress-resolution-non-4k.html
* igt@kms_cursor_crc@cursor-offscreen-32x32:
- shard-bmg: NOTRUN -> [SKIP][27] ([Intel XE#2320]) +1 other test skip
[27]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@kms_cursor_crc@cursor-offscreen-32x32.html
* igt@kms_dp_link_training@non-uhbr-mst:
- shard-bmg: NOTRUN -> [SKIP][28] ([Intel XE#4354] / [Intel XE#5882])
[28]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@kms_dp_link_training@non-uhbr-mst.html
* igt@kms_dsc@dsc-fractional-bpp-bigjoiner:
- shard-bmg: NOTRUN -> [SKIP][29] ([Intel XE#8265]) +1 other test skip
[29]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-7/igt@kms_dsc@dsc-fractional-bpp-bigjoiner.html
* igt@kms_flip@2x-flip-vs-absolute-wf_vblank-interruptible@ab-dp2-hdmi-a3:
- shard-bmg: [PASS][30] -> [FAIL][31] ([Intel XE#3098]) +1 other test fail
[30]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-5421-e3083db0ad8088e82975008863679579fc7e9dc0/shard-bmg-4/igt@kms_flip@2x-flip-vs-absolute-wf_vblank-interruptible@ab-dp2-hdmi-a3.html
[31]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@kms_flip@2x-flip-vs-absolute-wf_vblank-interruptible@ab-dp2-hdmi-a3.html
* igt@kms_flip@flip-vs-expired-vblank@b-edp1:
- shard-lnl: [PASS][32] -> [FAIL][33] ([Intel XE#301])
[32]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-5421-e3083db0ad8088e82975008863679579fc7e9dc0/shard-lnl-6/igt@kms_flip@flip-vs-expired-vblank@b-edp1.html
[33]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-lnl-1/igt@kms_flip@flip-vs-expired-vblank@b-edp1.html
* igt@kms_flip@wf_vblank-ts-check-interruptible@a-dp2:
- shard-bmg: [PASS][34] -> [FAIL][35] ([Intel XE#5408] / [Intel XE#6266]) +1 other test fail
[34]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-5421-e3083db0ad8088e82975008863679579fc7e9dc0/shard-bmg-4/igt@kms_flip@wf_vblank-ts-check-interruptible@a-dp2.html
[35]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-5/igt@kms_flip@wf_vblank-ts-check-interruptible@a-dp2.html
* igt@kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-downscaling:
- shard-bmg: NOTRUN -> [SKIP][36] ([Intel XE#7178] / [Intel XE#7351]) +1 other test skip
[36]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-downscaling.html
* igt@kms_frontbuffer_tracking@fbc-2p-scndscrn-spr-indfb-draw-mmap-wc:
- shard-bmg: NOTRUN -> [SKIP][37] ([Intel XE#4141]) +4 other tests skip
[37]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@kms_frontbuffer_tracking@fbc-2p-scndscrn-spr-indfb-draw-mmap-wc.html
* igt@kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-pri-indfb-draw-render:
- shard-bmg: NOTRUN -> [SKIP][38] ([Intel XE#2311]) +12 other tests skip
[38]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-pri-indfb-draw-render.html
* igt@kms_frontbuffer_tracking@fbcdrrs-argb161616f-draw-render:
- shard-bmg: NOTRUN -> [SKIP][39] ([Intel XE#7061] / [Intel XE#7356]) +1 other test skip
[39]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-7/igt@kms_frontbuffer_tracking@fbcdrrs-argb161616f-draw-render.html
* igt@kms_frontbuffer_tracking@fbcdrrs-tiling-y:
- shard-bmg: NOTRUN -> [SKIP][40] ([Intel XE#2352] / [Intel XE#7399])
[40]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@kms_frontbuffer_tracking@fbcdrrs-tiling-y.html
* igt@kms_frontbuffer_tracking@fbcpsr-2p-primscrn-cur-indfb-move:
- shard-bmg: NOTRUN -> [SKIP][41] ([Intel XE#2313]) +14 other tests skip
[41]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@kms_frontbuffer_tracking@fbcpsr-2p-primscrn-cur-indfb-move.html
* igt@kms_frontbuffer_tracking@hdr-abgr161616f-draw-mmap-wc:
- shard-bmg: NOTRUN -> [SKIP][42] ([Intel XE#7061])
[42]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@kms_frontbuffer_tracking@hdr-abgr161616f-draw-mmap-wc.html
* igt@kms_plane@pixel-format-y-tiled-modifier:
- shard-bmg: NOTRUN -> [SKIP][43] ([Intel XE#7283]) +1 other test skip
[43]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@kms_plane@pixel-format-y-tiled-modifier.html
* igt@kms_psr2_sf@pr-cursor-plane-move-continuous-exceed-fully-sf:
- shard-bmg: NOTRUN -> [SKIP][44] ([Intel XE#1489]) +1 other test skip
[44]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@kms_psr2_sf@pr-cursor-plane-move-continuous-exceed-fully-sf.html
* igt@kms_psr@psr-sprite-plane-move:
- shard-bmg: NOTRUN -> [SKIP][45] ([Intel XE#2234] / [Intel XE#2850]) +3 other tests skip
[45]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@kms_psr@psr-sprite-plane-move.html
* igt@kms_psr@psr2-primary-render:
- shard-bmg: NOTRUN -> [SKIP][46] ([Intel XE#2234])
[46]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@kms_psr@psr2-primary-render.html
* igt@kms_rotation_crc@sprite-rotation-90-pos-100-0:
- shard-bmg: NOTRUN -> [SKIP][47] ([Intel XE#3904] / [Intel XE#7342])
[47]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@kms_rotation_crc@sprite-rotation-90-pos-100-0.html
* igt@kms_scaling_modes@scaling-mode-full:
- shard-bmg: NOTRUN -> [SKIP][48] ([Intel XE#2413])
[48]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@kms_scaling_modes@scaling-mode-full.html
* igt@kms_setmode@invalid-clone-exclusive-crtc:
- shard-bmg: NOTRUN -> [SKIP][49] ([Intel XE#1435])
[49]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@kms_setmode@invalid-clone-exclusive-crtc.html
* igt@xe_evict@evict-threads-small-multi-queue:
- shard-bmg: NOTRUN -> [SKIP][50] ([Intel XE#8370])
[50]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-9/igt@xe_evict@evict-threads-small-multi-queue.html
* igt@xe_exec_basic@multigpu-no-exec-rebind:
- shard-bmg: NOTRUN -> [SKIP][51] ([Intel XE#2322] / [Intel XE#7372]) +1 other test skip
[51]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@xe_exec_basic@multigpu-no-exec-rebind.html
* igt@xe_exec_fault_mode@twice-multi-queue-rebind:
- shard-bmg: NOTRUN -> [SKIP][52] ([Intel XE#8374]) +2 other tests skip
[52]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@xe_exec_fault_mode@twice-multi-queue-rebind.html
* igt@xe_exec_multi_queue@two-queues-priority:
- shard-bmg: NOTRUN -> [SKIP][53] ([Intel XE#8364]) +7 other tests skip
[53]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@xe_exec_multi_queue@two-queues-priority.html
* igt@xe_exec_threads@threads-multi-queue-fd-userptr-rebind:
- shard-bmg: NOTRUN -> [SKIP][54] ([Intel XE#8378]) +3 other tests skip
[54]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@xe_exec_threads@threads-multi-queue-fd-userptr-rebind.html
* igt@xe_gpgpu_fill@offset-4x4:
- shard-bmg: NOTRUN -> [SKIP][55] ([Intel XE#7954])
[55]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@xe_gpgpu_fill@offset-4x4.html
* igt@xe_multigpu_svm@mgpu-xgpu-access-prefetch:
- shard-bmg: NOTRUN -> [SKIP][56] ([Intel XE#6964])
[56]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@xe_multigpu_svm@mgpu-xgpu-access-prefetch.html
* igt@xe_pm@d3cold-basic:
- shard-bmg: NOTRUN -> [SKIP][57] ([Intel XE#2284] / [Intel XE#7370])
[57]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@xe_pm@d3cold-basic.html
* igt@xe_wedged@wedged-at-any-timeout:
- shard-bmg: NOTRUN -> [DMESG-WARN][58] ([Intel XE#5545])
[58]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@xe_wedged@wedged-at-any-timeout.html
#### Possible fixes ####
* igt@kms_flip@flip-vs-expired-vblank-interruptible@b-edp1:
- shard-lnl: [FAIL][59] ([Intel XE#301]) -> [PASS][60]
[59]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-5421-e3083db0ad8088e82975008863679579fc7e9dc0/shard-lnl-5/igt@kms_flip@flip-vs-expired-vblank-interruptible@b-edp1.html
[60]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-lnl-3/igt@kms_flip@flip-vs-expired-vblank-interruptible@b-edp1.html
* igt@kms_flip@flip-vs-expired-vblank-interruptible@c-edp1:
- shard-lnl: [FAIL][61] ([Intel XE#301] / [Intel XE#3149]) -> [PASS][62]
[61]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-5421-e3083db0ad8088e82975008863679579fc7e9dc0/shard-lnl-5/igt@kms_flip@flip-vs-expired-vblank-interruptible@c-edp1.html
[62]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-lnl-3/igt@kms_flip@flip-vs-expired-vblank-interruptible@c-edp1.html
* igt@xe_fault_injection@inject-fault-probe-function-xe_pcode_probe_early:
- shard-bmg: [ABORT][63] ([Intel XE#8007]) -> [PASS][64] +1 other test pass
[63]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-5421-e3083db0ad8088e82975008863679579fc7e9dc0/shard-bmg-8/igt@xe_fault_injection@inject-fault-probe-function-xe_pcode_probe_early.html
[64]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@xe_fault_injection@inject-fault-probe-function-xe_pcode_probe_early.html
* igt@xe_pmu@engine-activity-accuracy-90@engine-drm_xe_engine_class_video_enhance0:
- shard-bmg: [FAIL][65] -> [PASS][66] +2 other tests pass
[65]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-5421-e3083db0ad8088e82975008863679579fc7e9dc0/shard-bmg-1/igt@xe_pmu@engine-activity-accuracy-90@engine-drm_xe_engine_class_video_enhance0.html
[66]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-6/igt@xe_pmu@engine-activity-accuracy-90@engine-drm_xe_engine_class_video_enhance0.html
#### Warnings ####
* igt@kms_flip@flip-vs-expired-vblank-interruptible:
- shard-lnl: [FAIL][67] ([Intel XE#301] / [Intel XE#3149]) -> [FAIL][68] ([Intel XE#301])
[67]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-5421-e3083db0ad8088e82975008863679579fc7e9dc0/shard-lnl-5/igt@kms_flip@flip-vs-expired-vblank-interruptible.html
[68]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-lnl-3/igt@kms_flip@flip-vs-expired-vblank-interruptible.html
* igt@kms_tiled_display@basic-test-pattern-with-chamelium:
- shard-bmg: [SKIP][69] ([Intel XE#2426] / [Intel XE#5848]) -> [SKIP][70] ([Intel XE#2509] / [Intel XE#7437])
[69]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-5421-e3083db0ad8088e82975008863679579fc7e9dc0/shard-bmg-9/igt@kms_tiled_display@basic-test-pattern-with-chamelium.html
[70]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/shard-bmg-1/igt@kms_tiled_display@basic-test-pattern-with-chamelium.html
[Intel XE#1124]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/1124
[Intel XE#1178]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/1178
[Intel XE#1231]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/1231
[Intel XE#1435]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/1435
[Intel XE#1489]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/1489
[Intel XE#2234]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2234
[Intel XE#2252]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2252
[Intel XE#2284]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2284
[Intel XE#2311]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2311
[Intel XE#2313]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2313
[Intel XE#2320]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2320
[Intel XE#2322]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2322
[Intel XE#2325]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2325
[Intel XE#2327]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2327
[Intel XE#2352]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2352
[Intel XE#2413]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2413
[Intel XE#2426]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2426
[Intel XE#2509]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2509
[Intel XE#2850]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2850
[Intel XE#2887]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2887
[Intel XE#301]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/301
[Intel XE#3098]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/3098
[Intel XE#3149]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/3149
[Intel XE#3304]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/3304
[Intel XE#3432]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/3432
[Intel XE#3904]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/3904
[Intel XE#4141]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/4141
[Intel XE#4354]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/4354
[Intel XE#5408]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/5408
[Intel XE#5545]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/5545
[Intel XE#5848]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/5848
[Intel XE#5882]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/5882
[Intel XE#6266]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6266
[Intel XE#6281]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6281
[Intel XE#6569]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6569
[Intel XE#6707]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6707
[Intel XE#6964]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6964
[Intel XE#7059]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7059
[Intel XE#7061]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7061
[Intel XE#7084]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7084
[Intel XE#7085]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7085
[Intel XE#7178]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7178
[Intel XE#7283]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7283
[Intel XE#7342]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7342
[Intel XE#7351]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7351
[Intel XE#7356]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7356
[Intel XE#7358]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7358
[Intel XE#7370]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7370
[Intel XE#7372]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7372
[Intel XE#7374]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7374
[Intel XE#7399]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7399
[Intel XE#7426]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7426
[Intel XE#7437]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7437
[Intel XE#7439]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7439
[Intel XE#7954]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7954
[Intel XE#8007]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/8007
[Intel XE#8150]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/8150
[Intel XE#8265]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/8265
[Intel XE#8364]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/8364
[Intel XE#8370]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/8370
[Intel XE#8374]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/8374
[Intel XE#8378]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/8378
Build changes
-------------
* Linux: xe-5421-e3083db0ad8088e82975008863679579fc7e9dc0 -> xe-pw-168027v2
IGT_9011: 9011
xe-5421-e3083db0ad8088e82975008863679579fc7e9dc0: e3083db0ad8088e82975008863679579fc7e9dc0
xe-pw-168027v2: 168027v2
== Logs ==
For more details see: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-168027v2/index.html
[-- Attachment #2: Type: text/html, Size: 25682 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 9/9] drm/xe/hwmon: Update fan info after late binding
2026-07-17 4:17 ` [PATCH v2 9/9] drm/xe/hwmon: Update fan info after late binding Karthik Poosa
@ 2026-07-20 6:20 ` Purkait, Soham
0 siblings, 0 replies; 21+ messages in thread
From: Purkait, Soham @ 2026-07-20 6:20 UTC (permalink / raw)
To: Karthik Poosa, intel-xe
Cc: rodrigo.vivi, anshuman.gupta, badal.nilawar, raag.jadav,
riana.tauro, sk.anirban, mallesh.koujalagi
Hi Karthik,
On 17-07-2026 09:47, Karthik Poosa wrote:
> Add xe_hwmon_fan_update_post_lb() to update fan info after late binding.
> Add param is_init into xe_hwmon_read_fan_control_info() to avoid
> resetting user pwm mode post-LB.
> Call init path with is_init=true from preregistration.
>
> Signed-off-by: Karthik Poosa <karthik.poosa@intel.com>
> Assisted-by: Codex:gpt-5-3
> ---
> drivers/gpu/drm/xe/xe_hwmon.c | 50 +++++++++++++++++++++++++---
> drivers/gpu/drm/xe/xe_hwmon.h | 2 ++
> drivers/gpu/drm/xe/xe_late_bind_fw.c | 3 ++
> 3 files changed, 51 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c
> index 65f356c741f5..518ba4438de4 100644
> --- a/drivers/gpu/drm/xe/xe_hwmon.c
> +++ b/drivers/gpu/drm/xe/xe_hwmon.c
> @@ -1848,7 +1848,7 @@ static const struct attribute_group hwmon_curve_attrgroup = {
> .is_visible = xe_hwmon_curve_attributes_visible,
> };
>
> -static int xe_hwmon_read_fan_control_info(struct xe_hwmon *hwmon)
> +static int xe_hwmon_read_fan_control_info(struct xe_hwmon *hwmon, bool is_init)
> {
> struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
> int point;
> @@ -1901,8 +1901,13 @@ static int xe_hwmon_read_fan_control_info(struct xe_hwmon *hwmon)
> fi->fan_table[STOCK_FAN_TABLE].fcp[point].speed);
> }
>
> - /* Set PWM enable mode to automatic i.e stock table in use. */
> - fi->pwm_enable_mode = XE_FAN_PWM_AUTO_STOCK_TABLE;
> + /*
> + * Set PWM enable mode to automatic i.e stock table in use, only during init.
> + * For late binding, the mode is already set by the user and should not be
> + * overridden.
> + */
> + if (is_init)
> + fi->pwm_enable_mode = XE_FAN_PWM_AUTO_STOCK_TABLE;
>
> /* Read minimum fan PWM */
> ret = xe_hwmon_pcode_read_fan_control(hwmon, FSC_READ_FAN_MIN_PWM, fan,
> @@ -2737,7 +2742,7 @@ xe_hwmon_get_preregistration_info(struct xe_hwmon *hwmon)
> xe_hwmon_fan_input_read(hwmon, channel, &fan_speed);
>
> /* Fan control tables initialization */
> - if (xe_hwmon_read_fan_control_info(hwmon))
> + if (xe_hwmon_read_fan_control_info(hwmon, true))
> xe_warn(hwmon->xe, "Fan control tables are not available\n");
> }
>
> @@ -2918,4 +2923,41 @@ void xe_hwmon_resume(struct xe_device *xe)
> mutex_unlock(&hwmon->hwmon_lock);
> }
>
> +/**
> + * xe_hwmon_fan_update_post_lb - Update fan info after late binding
> + * @xe: xe device instance
> + *
> + * This function reads the number of fans and their stock table after late binding.
> + */
> +void xe_hwmon_fan_update_post_lb(struct xe_device *xe)
> +{
> + struct xe_hwmon *hwmon = xe->hwmon;
> + int fan;
> + int num_fans;
Should this be u32 / u8?
> +
> + if (!hwmon || !xe->info.has_fan_control)
> + return;
> +
> + mutex_lock(&hwmon->hwmon_lock);
> +
> + /* Read number of fans */
> + if (xe_hwmon_get_num_fans(hwmon, &num_fans)) {
> + xe_warn(hwmon->xe, "Failed to read number of fans after LB\n");
> + goto unlock;
> + }
> + hwmon->num_fans = num_fans;
Check the types to avoid type mismatch.
> + xe_dbg(hwmon->xe, "Number of fans detected after LB: %u\n", hwmon->num_fans);
> +
> + /* Read stock table */
> + for (fan = 0; fan < hwmon->num_fans; fan++) {
This loop seems redundant. xe_hwmon_read_fan_control_info() already has
this.
> + if (xe_hwmon_read_fan_control_info(hwmon, false)) {
> + xe_warn(hwmon->xe, "Failed to read fan %d stock table after LB\n", fan);
> + continue;
> + }
> + }
> +
> +unlock:
> + mutex_unlock(&hwmon->hwmon_lock);
> +}
> +
> MODULE_IMPORT_NS("INTEL_PMT_TELEMETRY");
> diff --git a/drivers/gpu/drm/xe/xe_hwmon.h b/drivers/gpu/drm/xe/xe_hwmon.h
> index 6c6f30208508..7198d7a4a1be 100644
> --- a/drivers/gpu/drm/xe/xe_hwmon.h
> +++ b/drivers/gpu/drm/xe/xe_hwmon.h
> @@ -14,10 +14,12 @@ struct xe_device;
> int xe_hwmon_register(struct xe_device *xe);
> void xe_hwmon_suspend(struct xe_device *xe);
> void xe_hwmon_resume(struct xe_device *xe);
> +void xe_hwmon_fan_update_post_lb(struct xe_device *xe);
> #else
> static inline int xe_hwmon_register(struct xe_device *xe) { return 0; };
> static inline void xe_hwmon_suspend(struct xe_device *xe) {}
> static inline void xe_hwmon_resume(struct xe_device *xe) {}
> +static inline void xe_hwmon_fan_update_post_lb(struct xe_device *xe) {}
> #endif
>
> #endif /* _XE_HWMON_H_ */
> diff --git a/drivers/gpu/drm/xe/xe_late_bind_fw.c b/drivers/gpu/drm/xe/xe_late_bind_fw.c
> index 768442ca7da6..34295418ef50 100644
> --- a/drivers/gpu/drm/xe/xe_late_bind_fw.c
> +++ b/drivers/gpu/drm/xe/xe_late_bind_fw.c
> @@ -13,6 +13,7 @@
> #include <drm/drm_print.h>
>
> #include "xe_device.h"
> +#include "xe_hwmon.h"
> #include "xe_late_bind_fw.h"
> #include "xe_pcode.h"
> #include "xe_pcode_api.h"
> @@ -252,6 +253,8 @@ static void xe_late_bind_work(struct work_struct *work)
> if (!ret) {
> drm_dbg(&xe->drm, "Load %s firmware successful\n",
> fw_id_to_name[lbfw->id]);
> + if (fw_id_to_name[lbfw->id] == XE_LB_FW_FAN_CONTROL)
Comparing a 'const char *' to an enum (0). Should this be : if (lbfw->id
== XE_LB_FW_FAN_CONTROL) ?
Thanks,
soham
> + xe_hwmon_fan_update_post_lb(late_bind_to_xe(late_bind));
> goto out;
> }
>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 6/9] drm/xe/hwmon: Enable fan curve control support
2026-07-17 4:17 ` [PATCH v2 6/9] drm/xe/hwmon: Enable fan curve control support Karthik Poosa
@ 2026-07-22 18:16 ` Purkait, Soham
0 siblings, 0 replies; 21+ messages in thread
From: Purkait, Soham @ 2026-07-22 18:16 UTC (permalink / raw)
To: Karthik Poosa, intel-xe
Cc: rodrigo.vivi, anshuman.gupta, badal.nilawar, raag.jadav,
riana.tauro, sk.anirban, mallesh.koujalagi
Hi Karthik,
On 17-07-2026 09:47, Karthik Poosa wrote:
> Expose curve-point temperature and pwm controls via
> pwmF_auto_point[P]_temp and pwmF_auto_point[P]_pwm, where
> F is the fan number and P is the fan curve point number.
> Each fan curve point has temperature and pwm sysfs.
>
> Update Xe hwmon ABI documentation for pwm auto point temperature
> attributes.
>
> v2:
> - Use xe helpers for dmesg logs.
> - Update kernel version in Xe hwmon documentation.
> - Correct debug level of some dmesg logs.
>
> Signed-off-by: Karthik Poosa <karthik.poosa@intel.com>
> Assisted-by: Codex:gpt-5-4
> ---
> .../ABI/testing/sysfs-driver-intel-xe-hwmon | 16 +
> drivers/gpu/drm/xe/xe_hwmon.c | 700 +++++++++++++++++-
> 2 files changed, 715 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon b/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon
> index effc7d38ce1f..68cc6b99e8a5 100644
> --- a/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon
> +++ b/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon
> @@ -341,3 +341,19 @@ Description: RW. Fan control mode selector.
> user table control and value 2 to use firmware default behavior.
>
> Only supported for particular Intel Xe graphics platforms.
> +
> +What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/pwm[1-3]_auto_point[1-10]_temp
> +Date: June 2026
> +KernelVersion: 7.2
> +Contact: intel-xe@lists.freedesktop.org
> +Description: RW. Temperature in millidegree Celsius for each fan curve point.
> +
> + Only supported for particular Intel Xe graphics platforms.
> +
> +What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/pwm[1-3]_auto_point[1-10]_pwm
> +Date: June 2026
> +KernelVersion: 7.2
> +Contact: intel-xe@lists.freedesktop.org
> +Description: RW. PWM duty value (0..255) for each fan curve point.
> +
> + Only supported for particular Intel Xe graphics platforms.
> diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c
> index fa9dbadd5fd6..a3ed139ad446 100644
> --- a/drivers/gpu/drm/xe/xe_hwmon.c
> +++ b/drivers/gpu/drm/xe/xe_hwmon.c
> @@ -173,6 +173,10 @@ struct xe_hwmon_fan_info {
> /** @speed: fan speed in percentage */
> u8 speed;
> } fcp[MAX_FAN_CONTROL_POINTS];
> + /** @last_point_temp_valid: last user temperature point was written */
> + bool last_point_temp_valid;
> + /** @last_point_speed_valid: last user PWM point was written */
> + bool last_point_speed_valid;
> } fan_table[FAN_TABLE_MAX];
> /** @max_rps: maximum fan RPS */
> u32 max_rps;
> @@ -779,8 +783,11 @@ static const struct attribute_group hwmon_attrgroup = {
> .is_visible = xe_hwmon_attributes_visible,
> };
>
> +static const struct attribute_group hwmon_curve_attrgroup;
> +
> static const struct attribute_group *hwmon_groups[] = {
> &hwmon_attrgroup,
> + &hwmon_curve_attrgroup,
> NULL
> };
>
> @@ -945,7 +952,7 @@ static int xe_hwmon_get_num_fans(const struct xe_hwmon *hwmon, u32 *num_fans)
> }
>
> static int xe_hwmon_get_fan_point_count(struct xe_hwmon *hwmon, u8 fan, u32 *point_count,
> - int table_type)
> + enum fan_table_type table_type)
> {
> int ret;
>
> @@ -960,6 +967,16 @@ static int xe_hwmon_get_fan_point_count(struct xe_hwmon *hwmon, u8 fan, u32 *poi
> return ret;
> }
>
> + xe_dbg(hwmon->xe, "fan %d %s point count read as %u\n", fan,
> + (table_type == USER_FAN_TABLE) ? "user" : "stock", *point_count);
Remove duplicate log.
> +
> + if (*point_count > MAX_FAN_CONTROL_POINTS) {
> + xe_err(hwmon->xe, "fan %d %s point count %u exceeds max %u\n", fan,
> + (table_type == USER_FAN_TABLE) ? "user" : "stock", *point_count,
> + MAX_FAN_CONTROL_POINTS);
> + return -EINVAL;
> + }
> +
> xe_dbg(hwmon->xe, "fan %d %s point count read as %u\n", fan,
> (table_type == USER_FAN_TABLE) ? "user" : "stock", *point_count);
>
> @@ -985,6 +1002,60 @@ static int xe_hwmon_write_user_fan_point(struct xe_hwmon *hwmon, u8 fan, u8 poin
> return ret;
> }
>
> +static int xe_hwmon_commit_user_fan_table(struct xe_hwmon *hwmon, u8 fan)
> +{
> + struct xe_hwmon_fan_info *fi = &hwmon->fi[fan];
> + struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
> + u32 point_count = fi->fan_table[USER_FAN_TABLE].fan_control_point_count;
> + int point;
> + int ret;
> +
> + if (!point_count)
> + return -ENODATA;
> +
> + for (point = 0; point < point_count; point++) {
> + u8 temp = fi->fan_table[USER_FAN_TABLE].fcp[point].temp;
> + u8 speed = fi->fan_table[USER_FAN_TABLE].fcp[point].speed;
> +
> + /* Ensure fan control points are strictly increasing order */
> + if (point > 0) {
> + u8 prev_temp = fi->fan_table[USER_FAN_TABLE].fcp[point - 1].temp;
> + u8 prev_speed = fi->fan_table[USER_FAN_TABLE].fcp[point - 1].speed;
> +
> + if (temp < prev_temp || speed < prev_speed) {
> + drm_dbg(&hwmon->xe->drm,
> + "fan %d invalid user point previous value > current %d: %u/%u prev=%u/%u\n",
> + fan, point, temp, speed, prev_temp, prev_speed);
> + return -EINVAL;
> + }
> + }
> +
> + drm_dbg(&hwmon->xe->drm,
> + "committing fan %d user point %d temp %u, speed %u %%\n",
> + fan, point, temp, speed);
> + ret = xe_hwmon_write_user_fan_point(hwmon, fan, point, temp, speed);
> + if (ret)
> + return ret;
> + }
> +
> + drm_dbg(&hwmon->xe->drm,
> + "committing fan %d user table with %d points\n", fan, point_count);
> + ret = xe_pcode_write_timeout(root_tile, PCODE_MBOX(FAN_SPEED_CONTROL,
> + FSC_WRITE_NUM_FAN_CONTROL_POINTS,
> + fan),
> + point_count, XE_PCODE_FAN_CONTROL_TIMEOUT_MS);
> + if (ret) {
> + drm_dbg(&hwmon->xe->drm,
> + "failed to update fan %d user table count, ret=%d\n", fan, ret);
> + return ret;
> + }
> +
> + fi->pwm_enable_mode = XE_FAN_PWM_MANUAL_USER_TABLE;
> + fi->is_full_speed = false;
> +
> + return 0;
> +}
> +
> static int xe_hwmon_activate_user_fan_table(struct xe_hwmon *hwmon, u8 fan,
> bool is_full_speed, enum fan_table_type table_source)
> {
> @@ -1105,6 +1176,633 @@ static int xe_hwmon_set_user_fan_pwm(struct xe_hwmon *hwmon, u8 fan, u8 pwm)
> return 0;
> }
>
> +#define XE_CURVE_IDX(fan, point) (((fan) << 8) | (point))
> +#define XE_CURVE_FAN(idx) ((u8)(((idx) >> 8) & 0xff))
> +#define XE_CURVE_POINT(idx) ((u8)((idx) & 0xff))
> +
> +static int
> +xe_hwmon_decode_curve_index(u32 index, u8 *fan, u8 *point)
> +{
> + u8 local_fan = XE_CURVE_FAN(index);
> + u8 local_point = XE_CURVE_POINT(index);
> +
> + if (local_fan >= FAN_MAX || local_point >= MAX_FAN_CONTROL_POINTS)
> + return -EINVAL;
> +
> + *fan = local_fan;
> + *point = local_point;
> +
> + return 0;
> +}
> +
> +static inline enum fan_table_type xe_hwmon_get_active_fan_table(struct xe_hwmon *hwmon, u8 fan)
> +{
> + if (hwmon->fi[fan].pwm_enable_mode == XE_FAN_PWM_AUTO_STOCK_TABLE)
> + return STOCK_FAN_TABLE;
> + else
> + return USER_FAN_TABLE;
> +}
> +
> +static ssize_t
> +xe_hwmon_pwm_auto_point_temp_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
> + struct xe_hwmon *hwmon = dev_get_drvdata(dev);
> + struct xe_hwmon_fan_info *fi;
> + u32 point_count;
> + u8 fan;
> + u8 point;
> + u8 temp;
> + int ret;
> + enum fan_table_type active_table;
> +
> + ret = xe_hwmon_decode_curve_index(sattr->index, &fan, &point);
> + if (ret)
> + return ret;
> +
> + fi = &hwmon->fi[fan];
> + active_table = xe_hwmon_get_active_fan_table(hwmon, fan);
> +
> + guard(xe_pm_runtime)(hwmon->xe);
> +
> + mutex_lock(&hwmon->hwmon_lock);
> +
> + fi = &hwmon->fi[fan];
> +
> + ret = xe_hwmon_get_fan_point_count(hwmon, fan, &point_count, active_table);
> + if (ret)
> + goto unlock;
> +
> + if (point >= point_count) {
> + ret = -EINVAL;
> + goto unlock;
> + }
> +
> + temp = fi->fan_table[active_table].fcp[point].temp;
> + ret = sysfs_emit(buf, "%u\n", temp * MILLIDEGREE_PER_DEGREE);
> +
> +unlock:
> + mutex_unlock(&hwmon->hwmon_lock);
> +
> + return ret;
> +}
> +
> +static int xe_hwmon_update_user_fan_point(struct xe_hwmon *hwmon, u8 fan, u32 point,
> + u32 point_count, u8 temp, u8 pwm)
> +{
> + struct xe_hwmon_fan_info *fi = &hwmon->fi[fan];
> + int ret;
> +
> + if (temp) {
But as per the xe_hwmon_get_u8_from_sysfs() "0" is a acceptable curve
point then, why is it being over loaded here ?
Thanks,
Soham
> + fi->fan_table[USER_FAN_TABLE].fcp[point].temp = temp;
> + if (point == (point_count - 1))
> + fi->fan_table[USER_FAN_TABLE].last_point_temp_valid = true;
> + } else {
> + fi->fan_table[USER_FAN_TABLE].fcp[point].speed = pwm;
> + if (point == (point_count - 1))
> + fi->fan_table[USER_FAN_TABLE].last_point_speed_valid = true;
> + }
> +
> + /* Cache intermediate points and commit once the last point arrives. */
> + if (point == (point_count - 1) &&
> + fi->fan_table[USER_FAN_TABLE].last_point_temp_valid &&
> + fi->fan_table[USER_FAN_TABLE].last_point_speed_valid) {
> + ret = xe_hwmon_commit_user_fan_table(hwmon, fan);
> + if (ret)
> + return ret;
> + fi->fan_table[USER_FAN_TABLE].last_point_temp_valid = false;
> + fi->fan_table[USER_FAN_TABLE].last_point_speed_valid = false;
> + }
> + return 0;
> +}
> +
> +static int xe_hwmon_get_u8_from_sysfs(long val, bool is_temp, u8 *out)
> +{
> + long hw_val;
> +
> + if (is_temp)
> + hw_val = DIV_ROUND_CLOSEST(val, MILLIDEGREE_PER_DEGREE);
> + else
> + hw_val = DIV_ROUND_CLOSEST(val * 100, U8_MAX);
> +
> + if (hw_val < 0 || hw_val > U8_MAX)
> + return -EINVAL;
> +
> + *out = hw_val;
> +
> + return 0;
> +}
> +
> +static ssize_t
> +xe_hwmon_pwm_auto_point_temp_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
> + struct xe_hwmon *hwmon = dev_get_drvdata(dev);
> + struct xe_hwmon_fan_info *fi;
> + u8 fan;
> + u8 point;
> + u32 point_count;
> + u8 temp;
> + long val;
> + int ret;
> +
> + ret = xe_hwmon_decode_curve_index(sattr->index, &fan, &point);
> + if (ret)
> + return ret;
> +
> + fi = &hwmon->fi[fan];
> +
> + if (fi->pwm_enable_mode != XE_FAN_PWM_MANUAL_USER_TABLE) {
> + drm_err(&hwmon->xe->drm,
> + "pwm%d_enable not in manual mode; cannot update temp point %d\n",
> + fan, point);
> + return -EINVAL;
> + }
> +
> + ret = kstrtol(buf, 10, &val);
> + if (ret)
> + return ret;
> +
> + ret = xe_hwmon_get_u8_from_sysfs(val, true, &temp);
> + if (ret)
> + return ret;
> +
> + guard(xe_pm_runtime)(hwmon->xe);
> +
> + mutex_lock(&hwmon->hwmon_lock);
> +
> + ret = xe_hwmon_get_fan_point_count(hwmon, fan, &point_count, USER_FAN_TABLE);
> + if (ret)
> + goto unlock;
> +
> + if (point >= point_count) {
> + ret = -EINVAL;
> + goto unlock;
> + }
> +
> + drm_dbg(&hwmon->xe->drm,
> + "updating fan %d point %d temp to %u, pwm %u\n", fan, point,
> + temp, fi->fan_table[USER_FAN_TABLE].fcp[point].speed);
> + ret = xe_hwmon_update_user_fan_point(hwmon, fan, point, point_count, temp, 0);
> +
> +unlock:
> + mutex_unlock(&hwmon->hwmon_lock);
> +
> + return ret ? ret : count;
> +}
> +
> +static ssize_t
> +xe_hwmon_pwm_auto_point_pwm_show(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
> + struct xe_hwmon *hwmon = dev_get_drvdata(dev);
> + struct xe_hwmon_fan_info *fi;
> + u8 fan;
> + u8 point;
> + u32 point_count;
> + int ret;
> + enum fan_table_type active_table;
> +
> + ret = xe_hwmon_decode_curve_index(sattr->index, &fan, &point);
> + if (ret)
> + return ret;
> +
> + fi = &hwmon->fi[fan];
> + active_table = xe_hwmon_get_active_fan_table(hwmon, fan);
> +
> + guard(xe_pm_runtime)(hwmon->xe);
> +
> + mutex_lock(&hwmon->hwmon_lock);
> +
> + ret = xe_hwmon_get_fan_point_count(hwmon, fan, &point_count, active_table);
> + if (ret)
> + goto unlock;
> +
> + if (point >= point_count) {
> + ret = -EINVAL;
> + goto unlock;
> + }
> +
> + ret = sysfs_emit(buf, "%u\n",
> + DIV_ROUND_CLOSEST(fi->fan_table[active_table].fcp[point].speed * U8_MAX,
> + 100));
> +
> +unlock:
> + mutex_unlock(&hwmon->hwmon_lock);
> +
> + return ret;
> +}
> +
> +static ssize_t
> +xe_hwmon_pwm_auto_point_pwm_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
> + struct xe_hwmon *hwmon = dev_get_drvdata(dev);
> + struct xe_hwmon_fan_info *fi;
> + u32 point_count;
> + long val;
> + u8 point;
> + u8 fan;
> + u8 pwm;
> + int ret;
> +
> + ret = xe_hwmon_decode_curve_index(sattr->index, &fan, &point);
> + if (ret)
> + return ret;
> +
> + fi = &hwmon->fi[fan];
> +
> + if (fi->pwm_enable_mode != XE_FAN_PWM_MANUAL_USER_TABLE) {
> + drm_err(&hwmon->xe->drm,
> + "pwm%d_enable is not in manual user mode, cannot update pwm point %d\n",
> + fan, point);
> + return -EINVAL;
> + }
> +
> + ret = kstrtol(buf, 10, &val);
> + if (ret)
> + return ret;
> +
> + ret = xe_hwmon_get_u8_from_sysfs(val, false, &pwm);
> + if (ret)
> + return ret;
> +
> + guard(xe_pm_runtime)(hwmon->xe);
> +
> + mutex_lock(&hwmon->hwmon_lock);
> +
> + ret = xe_hwmon_get_fan_point_count(hwmon, fan, &point_count, USER_FAN_TABLE);
> + if (ret)
> + goto unlock;
> +
> + if (point >= point_count) {
> + ret = -EINVAL;
> + goto unlock;
> + }
> +
> + pwm = max_t(u8, pwm, fi->min_pwm);
> +
> + drm_dbg(&hwmon->xe->drm, "updating fan %d point %d pwm to %u\n",
> + fan, point, pwm);
> + ret = xe_hwmon_update_user_fan_point(hwmon, fan, point, point_count, 0, pwm);
> +
> +unlock:
> + mutex_unlock(&hwmon->hwmon_lock);
> +
> + return ret ? ret : count;
> +}
> +
> +static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(0, 0));
> +static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(0, 0));
> +static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(0, 1));
> +static SENSOR_DEVICE_ATTR(pwm1_auto_point2_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(0, 1));
> +static SENSOR_DEVICE_ATTR(pwm1_auto_point3_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(0, 2));
> +static SENSOR_DEVICE_ATTR(pwm1_auto_point3_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(0, 2));
> +static SENSOR_DEVICE_ATTR(pwm1_auto_point4_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(0, 3));
> +static SENSOR_DEVICE_ATTR(pwm1_auto_point4_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(0, 3));
> +static SENSOR_DEVICE_ATTR(pwm1_auto_point5_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(0, 4));
> +static SENSOR_DEVICE_ATTR(pwm1_auto_point5_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(0, 4));
> +static SENSOR_DEVICE_ATTR(pwm1_auto_point6_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(0, 5));
> +static SENSOR_DEVICE_ATTR(pwm1_auto_point6_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(0, 5));
> +static SENSOR_DEVICE_ATTR(pwm1_auto_point7_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(0, 6));
> +static SENSOR_DEVICE_ATTR(pwm1_auto_point7_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(0, 6));
> +static SENSOR_DEVICE_ATTR(pwm1_auto_point8_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(0, 7));
> +static SENSOR_DEVICE_ATTR(pwm1_auto_point8_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(0, 7));
> +static SENSOR_DEVICE_ATTR(pwm1_auto_point9_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(0, 8));
> +static SENSOR_DEVICE_ATTR(pwm1_auto_point9_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(0, 8));
> +static SENSOR_DEVICE_ATTR(pwm1_auto_point10_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(0, 9));
> +static SENSOR_DEVICE_ATTR(pwm1_auto_point10_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(0, 9));
> +static SENSOR_DEVICE_ATTR(pwm2_auto_point1_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(1, 0));
> +static SENSOR_DEVICE_ATTR(pwm2_auto_point1_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(1, 0));
> +static SENSOR_DEVICE_ATTR(pwm2_auto_point2_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(1, 1));
> +static SENSOR_DEVICE_ATTR(pwm2_auto_point2_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(1, 1));
> +static SENSOR_DEVICE_ATTR(pwm2_auto_point3_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(1, 2));
> +static SENSOR_DEVICE_ATTR(pwm2_auto_point3_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(1, 2));
> +static SENSOR_DEVICE_ATTR(pwm2_auto_point4_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(1, 3));
> +static SENSOR_DEVICE_ATTR(pwm2_auto_point4_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(1, 3));
> +static SENSOR_DEVICE_ATTR(pwm2_auto_point5_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(1, 4));
> +static SENSOR_DEVICE_ATTR(pwm2_auto_point5_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(1, 4));
> +static SENSOR_DEVICE_ATTR(pwm2_auto_point6_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(1, 5));
> +static SENSOR_DEVICE_ATTR(pwm2_auto_point6_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(1, 5));
> +static SENSOR_DEVICE_ATTR(pwm2_auto_point7_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(1, 6));
> +static SENSOR_DEVICE_ATTR(pwm2_auto_point7_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(1, 6));
> +static SENSOR_DEVICE_ATTR(pwm2_auto_point8_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(1, 7));
> +static SENSOR_DEVICE_ATTR(pwm2_auto_point8_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(1, 7));
> +static SENSOR_DEVICE_ATTR(pwm2_auto_point9_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(1, 8));
> +static SENSOR_DEVICE_ATTR(pwm2_auto_point9_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(1, 8));
> +static SENSOR_DEVICE_ATTR(pwm2_auto_point10_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(1, 9));
> +static SENSOR_DEVICE_ATTR(pwm2_auto_point10_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(1, 9));
> +static SENSOR_DEVICE_ATTR(pwm3_auto_point1_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(2, 0));
> +static SENSOR_DEVICE_ATTR(pwm3_auto_point1_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(2, 0));
> +static SENSOR_DEVICE_ATTR(pwm3_auto_point2_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(2, 1));
> +static SENSOR_DEVICE_ATTR(pwm3_auto_point2_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(2, 1));
> +static SENSOR_DEVICE_ATTR(pwm3_auto_point3_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(2, 2));
> +static SENSOR_DEVICE_ATTR(pwm3_auto_point3_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(2, 2));
> +static SENSOR_DEVICE_ATTR(pwm3_auto_point4_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(2, 3));
> +static SENSOR_DEVICE_ATTR(pwm3_auto_point4_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(2, 3));
> +static SENSOR_DEVICE_ATTR(pwm3_auto_point5_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(2, 4));
> +static SENSOR_DEVICE_ATTR(pwm3_auto_point5_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(2, 4));
> +static SENSOR_DEVICE_ATTR(pwm3_auto_point6_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(2, 5));
> +static SENSOR_DEVICE_ATTR(pwm3_auto_point6_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(2, 5));
> +static SENSOR_DEVICE_ATTR(pwm3_auto_point7_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(2, 6));
> +static SENSOR_DEVICE_ATTR(pwm3_auto_point7_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(2, 6));
> +static SENSOR_DEVICE_ATTR(pwm3_auto_point8_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(2, 7));
> +static SENSOR_DEVICE_ATTR(pwm3_auto_point8_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(2, 7));
> +static SENSOR_DEVICE_ATTR(pwm3_auto_point9_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(2, 8));
> +static SENSOR_DEVICE_ATTR(pwm3_auto_point9_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(2, 8));
> +static SENSOR_DEVICE_ATTR(pwm3_auto_point10_temp, 0644,
> + xe_hwmon_pwm_auto_point_temp_show,
> + xe_hwmon_pwm_auto_point_temp_store,
> + XE_CURVE_IDX(2, 9));
> +static SENSOR_DEVICE_ATTR(pwm3_auto_point10_pwm, 0644,
> + xe_hwmon_pwm_auto_point_pwm_show,
> + xe_hwmon_pwm_auto_point_pwm_store,
> + XE_CURVE_IDX(2, 9));
> +
> +static struct attribute *hwmon_curve_attributes[] = {
> + &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm1_auto_point4_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm1_auto_point5_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm1_auto_point6_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm1_auto_point7_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm1_auto_point8_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm1_auto_point9_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm1_auto_point10_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
> + &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr,
> + &sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr,
> + &sensor_dev_attr_pwm1_auto_point4_pwm.dev_attr.attr,
> + &sensor_dev_attr_pwm1_auto_point5_pwm.dev_attr.attr,
> + &sensor_dev_attr_pwm1_auto_point6_pwm.dev_attr.attr,
> + &sensor_dev_attr_pwm1_auto_point7_pwm.dev_attr.attr,
> + &sensor_dev_attr_pwm1_auto_point8_pwm.dev_attr.attr,
> + &sensor_dev_attr_pwm1_auto_point9_pwm.dev_attr.attr,
> + &sensor_dev_attr_pwm1_auto_point10_pwm.dev_attr.attr,
> + &sensor_dev_attr_pwm2_auto_point1_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm2_auto_point2_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm2_auto_point3_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm2_auto_point4_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm2_auto_point5_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm2_auto_point6_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm2_auto_point7_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm2_auto_point8_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm2_auto_point9_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm2_auto_point10_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr,
> + &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr,
> + &sensor_dev_attr_pwm2_auto_point3_pwm.dev_attr.attr,
> + &sensor_dev_attr_pwm2_auto_point4_pwm.dev_attr.attr,
> + &sensor_dev_attr_pwm2_auto_point5_pwm.dev_attr.attr,
> + &sensor_dev_attr_pwm2_auto_point6_pwm.dev_attr.attr,
> + &sensor_dev_attr_pwm2_auto_point7_pwm.dev_attr.attr,
> + &sensor_dev_attr_pwm2_auto_point8_pwm.dev_attr.attr,
> + &sensor_dev_attr_pwm2_auto_point9_pwm.dev_attr.attr,
> + &sensor_dev_attr_pwm2_auto_point10_pwm.dev_attr.attr,
> + &sensor_dev_attr_pwm3_auto_point1_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm3_auto_point2_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm3_auto_point3_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm3_auto_point4_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm3_auto_point5_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm3_auto_point6_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm3_auto_point7_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm3_auto_point8_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm3_auto_point9_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm3_auto_point10_temp.dev_attr.attr,
> + &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr,
> + &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr,
> + &sensor_dev_attr_pwm3_auto_point3_pwm.dev_attr.attr,
> + &sensor_dev_attr_pwm3_auto_point4_pwm.dev_attr.attr,
> + &sensor_dev_attr_pwm3_auto_point5_pwm.dev_attr.attr,
> + &sensor_dev_attr_pwm3_auto_point6_pwm.dev_attr.attr,
> + &sensor_dev_attr_pwm3_auto_point7_pwm.dev_attr.attr,
> + &sensor_dev_attr_pwm3_auto_point8_pwm.dev_attr.attr,
> + &sensor_dev_attr_pwm3_auto_point9_pwm.dev_attr.attr,
> + &sensor_dev_attr_pwm3_auto_point10_pwm.dev_attr.attr,
> + NULL
> +};
> +
> +static umode_t xe_hwmon_curve_attributes_visible(struct kobject *kobj,
> + struct attribute *attr, int index)
> +{
> + struct device *dev = kobj_to_dev(kobj);
> + struct xe_hwmon *hwmon = dev_get_drvdata(dev);
> +
> + /* Temp/PWM pairs for each point. */
> + u32 attrs_per_fan = 2 * MAX_FAN_CONTROL_POINTS;
> + u32 fan = index / attrs_per_fan;
> + u32 local = index % attrs_per_fan;
> + u32 point_count;
> +
> + if (!hwmon->xe->info.has_fan_control)
> + return 0;
> +
> + if (fan >= hwmon->num_fans)
> + return 0;
> +
> + point_count = max(hwmon->fi[fan].fan_table[USER_FAN_TABLE].fan_control_point_count,
> + hwmon->fi[fan].fan_table[STOCK_FAN_TABLE].fan_control_point_count);
> +
> + if (!point_count)
> + return 0;
> +
> + if (local < MAX_FAN_CONTROL_POINTS)
> + return (local + 1) <= point_count ? attr->mode : 0;
> +
> + if (local < (2 * MAX_FAN_CONTROL_POINTS))
> + return (local - MAX_FAN_CONTROL_POINTS + 1) <= point_count ?
> + attr->mode : 0;
> +
> + return 0;
> +}
> +
> +static const struct attribute_group hwmon_curve_attrgroup = {
> + .attrs = hwmon_curve_attributes,
> + .is_visible = xe_hwmon_curve_attributes_visible,
> +};
> +
> static int xe_hwmon_read_fan_control_info(struct xe_hwmon *hwmon)
> {
> struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 3/9] drm/xe/hwmon: expose fanN_max
2026-07-17 4:17 ` [PATCH v2 3/9] drm/xe/hwmon: expose fanN_max Karthik Poosa
@ 2026-07-23 5:34 ` Purkait, Soham
0 siblings, 0 replies; 21+ messages in thread
From: Purkait, Soham @ 2026-07-23 5:34 UTC (permalink / raw)
To: Karthik Poosa, intel-xe
Cc: rodrigo.vivi, anshuman.gupta, badal.nilawar, raag.jadav,
riana.tauro, sk.anirban, mallesh.koujalagi
Hi Karthik,
On 17-07-2026 09:47, Karthik Poosa wrote:
> Expose fan maximum RPM via fanN_max.
>
> The value is derived from the firmware-reported maximum fan RPS and
> converted to RPM in the fan hwmon read path.
>
> Update Xe hwmon ABI documentation for fanN_max.
>
> v2: Use xe helper for dmesg logs.
>
> Signed-off-by: Karthik Poosa <karthik.poosa@intel.com>
> Assisted-by: Codex:gpt-5-4
> ---
> .../ABI/testing/sysfs-driver-intel-xe-hwmon | 8 ++++++++
> drivers/gpu/drm/xe/xe_hwmon.c | 18 +++++++++++++++++-
> drivers/gpu/drm/xe/xe_pcode_api.h | 1 +
> 3 files changed, 26 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon b/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon
> index 0da739d9a816..ec0b94d76e22 100644
> --- a/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon
> +++ b/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon
> @@ -266,6 +266,14 @@ Description: RO. Fan 3 speed in RPM.
>
> Only supported for particular Intel Xe graphics platforms.
>
> +What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/fan[1-3]_max
> +Date: July 2026
> +KernelVersion: 7.2
> +Contact: intel-xe@lists.freedesktop.org
> +Description: RO. Maximum fan speed in RPM for each fan channel.
> +
> + Only supported for particular Intel Xe graphics platforms.
> +
> What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/power1_cap
> Date: May 2025
> KernelVersion: 6.15
> diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c
> index 0a416e3e7b8c..769f4d1da83e 100644
> --- a/drivers/gpu/drm/xe/xe_hwmon.c
> +++ b/drivers/gpu/drm/xe/xe_hwmon.c
> @@ -165,6 +165,8 @@ struct xe_hwmon_fan_info {
> u8 speed;
> } fcp[MAX_FAN_CONTROL_POINTS];
> } fan_table[FAN_TABLE_MAX];
> + /** @max_rps: maximum fan RPS */
> + u32 max_rps;
nit: fix indentation.
Thanks,
Soham
> /** @min_pwm: minimum fan PWM */
> u32 min_pwm;
> };
> @@ -799,7 +801,8 @@ static const struct hwmon_channel_info * const hwmon_info[] = {
> HWMON_CHANNEL_INFO(curr, HWMON_C_LABEL, HWMON_C_CRIT | HWMON_C_LABEL),
> HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL),
> HWMON_CHANNEL_INFO(energy, HWMON_E_INPUT | HWMON_E_LABEL, HWMON_E_INPUT | HWMON_E_LABEL),
> - HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT, HWMON_F_INPUT, HWMON_F_INPUT),
> + HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_MAX, HWMON_F_INPUT | HWMON_F_MAX,
> + HWMON_F_INPUT | HWMON_F_MAX),
> NULL
> };
>
> @@ -987,6 +990,14 @@ static int xe_hwmon_read_fan_control_info(struct xe_hwmon *hwmon)
> }
>
> xe_dbg(hwmon->xe, "fan %d min PWM %u\n", fan, fi->min_pwm);
> +
> + /* Read max fan RPS*/
> + ret = xe_hwmon_pcode_read_fan_control(hwmon, FSC_READ_MAX_FAN_RPS, fan,
> + &fi->max_rps);
> + if (ret)
> + xe_warn(hwmon->xe, "failed to read fan %d max RPS, ret=%d\n", fan, ret);
> + else
> + xe_dbg(hwmon->xe, "fan %d max RPS %u\n", fan, fi->max_rps);
> }
> return 0;
> }
> @@ -1406,6 +1417,7 @@ xe_hwmon_fan_is_visible(struct xe_hwmon *hwmon, u32 attr, int channel)
>
> switch (attr) {
> case hwmon_fan_input:
> + case hwmon_fan_max:
> return channel < hwmon->num_fans ? 0444 : 0;
> default:
> return 0;
> @@ -1457,6 +1469,10 @@ xe_hwmon_fan_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *val)
> switch (attr) {
> case hwmon_fan_input:
> return xe_hwmon_fan_input_read(hwmon, channel, val);
> + case hwmon_fan_max:
> + /* Convert RPS to RPM */
> + *val = (long)hwmon->fi[channel].max_rps * 60;
> + return 0;
> default:
> return -EOPNOTSUPP;
> }
> diff --git a/drivers/gpu/drm/xe/xe_pcode_api.h b/drivers/gpu/drm/xe/xe_pcode_api.h
> index 669010f1e2d0..419ab4f416fa 100644
> --- a/drivers/gpu/drm/xe/xe_pcode_api.h
> +++ b/drivers/gpu/drm/xe/xe_pcode_api.h
> @@ -84,6 +84,7 @@
> #define PCODE_MBOX_DOMAIN_HBM 0x2
>
> #define FAN_SPEED_CONTROL 0x7D
> +#define FSC_READ_MAX_FAN_RPS 0x3
> #define FSC_READ_NUM_FANS 0x4
> #define FSC_READ_STOCK_FAN_CONTROL_POINTS 0x5
> #define FSC_READ_FAN_TABLE 0x7
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 4/9] drm/xe/hwmon: expose pwm[1-3]
2026-07-17 4:17 ` [PATCH v2 4/9] drm/xe/hwmon: expose pwm[1-3] Karthik Poosa
@ 2026-07-24 7:02 ` Purkait, Soham
2026-08-11 13:58 ` Poosa, Karthik
0 siblings, 1 reply; 21+ messages in thread
From: Purkait, Soham @ 2026-07-24 7:02 UTC (permalink / raw)
To: Karthik Poosa, intel-xe
Cc: rodrigo.vivi, anshuman.gupta, badal.nilawar, raag.jadav,
riana.tauro, sk.anirban, mallesh.koujalagi
Hi Karthik,
On 17-07-2026 09:47, Karthik Poosa wrote:
> Expose pwm[1-3] fan duty attributes through hwmon.
>
> This enables reading and writing user PWM values (0..255) for each
> available fan channel.
>
> Update Xe hwmon ABI documentation for pwm[1-3].
>
> This sysfs node can be used for manual control of fan speed,
> irrespective of fan curve.
>
> v2:
> - Use xe helpers for dmesg logs.
> - Avoid use hwmon->num_fans to get fan count in xe_hwmon_pwm_is_visible().
>
> Signed-off-by: Karthik Poosa <karthik.poosa@intel.com>
> Assisted-by: Codex:gpt-5-4
> ---
> .../ABI/testing/sysfs-driver-intel-xe-hwmon | 9 +
> drivers/gpu/drm/xe/xe_hwmon.c | 230 ++++++++++++++++++
> drivers/gpu/drm/xe/xe_pcode_api.h | 3 +
> 3 files changed, 242 insertions(+)
>
> diff --git a/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon b/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon
> index ec0b94d76e22..7383898890aa 100644
> --- a/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon
> +++ b/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon
> @@ -321,3 +321,12 @@ Description: RW. Package burst power limit interval (Tau in PL2/Tau) in
> milliseconds over which sustained power is averaged.
>
> Only supported for particular Intel Xe graphics platforms.
> +
> +What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/pwm[1-3]
> +Date: July 2026
> +KernelVersion: 7.2
> +Contact: intel-xe@lists.freedesktop.org
> +Description: RW. Target fan PWM duty value in the range 0..255. 0 is zero
> + fan speed and 255 is 100% fan speed.
> +
> + Only supported for particular Intel Xe graphics platforms.
> diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c
> index 769f4d1da83e..d3379727b1a9 100644
> --- a/drivers/gpu/drm/xe/xe_hwmon.c
> +++ b/drivers/gpu/drm/xe/xe_hwmon.c
> @@ -803,6 +803,7 @@ static const struct hwmon_channel_info * const hwmon_info[] = {
> HWMON_CHANNEL_INFO(energy, HWMON_E_INPUT | HWMON_E_LABEL, HWMON_E_INPUT | HWMON_E_LABEL),
> HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_MAX, HWMON_F_INPUT | HWMON_F_MAX,
> HWMON_F_INPUT | HWMON_F_MAX),
> + HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT, HWMON_PWM_INPUT, HWMON_PWM_INPUT),
> NULL
> };
>
> @@ -928,6 +929,158 @@ static int xe_hwmon_get_num_fans(const struct xe_hwmon *hwmon, u32 *num_fans)
> return 0;
> }
>
> +static int xe_hwmon_get_fan_point_count(struct xe_hwmon *hwmon, u8 fan, u32 *point_count,
> + int table_type)
> +{
> + int ret;
> +
> + ret = xe_hwmon_pcode_read_fan_control(hwmon,
> + (table_type == USER_FAN_TABLE) ?
> + FSC_READ_USER_FAN_CONTROL_POINTS :
> + FSC_READ_STOCK_FAN_CONTROL_POINTS,
> + fan, point_count);
> + if (ret) {
> + xe_err(hwmon->xe, "failed to read fan %d %s point count, ret=%d\n", fan,
> + (table_type == USER_FAN_TABLE) ? "user" : "stock", ret);
> + return ret;
> + }
> +
> + xe_dbg(hwmon->xe, "fan %d %s point count read as %u\n", fan,
> + (table_type == USER_FAN_TABLE) ? "user" : "stock", *point_count);
> +
> + return 0;
> +}
> +
> +static int xe_hwmon_write_user_fan_point(struct xe_hwmon *hwmon, u8 fan, u8 point, u8 temp,
> + u8 speed)
> +{
> + struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
> + u16 user_fcp_raw = FIELD_PREP(FAN_CONTROL_POINT_TEMP_MASK, temp) |
> + FIELD_PREP(FAN_CONTROL_POINT_SPEED_MASK, speed);
> + int ret;
> +
> + ret = xe_pcode_write_timeout(root_tile,
> + PCODE_MBOX(FAN_SPEED_CONTROL, FSC_WRITE_FAN_TABLE, fan),
> + (u32)user_fcp_raw, XE_PCODE_FAN_CONTROL_TIMEOUT_MS);
> + if (ret)
> + xe_dbg(hwmon->xe,
> + "failed to write fan %d user point %d temp %u, speed %u %%, ret=%d\n",
> + fan, point, temp, speed, ret);
> +
> + return ret;
> +}
> +
> +static int xe_hwmon_activate_user_fan_table(struct xe_hwmon *hwmon, u8 fan,
> + bool is_full_speed, enum fan_table_type table_source)
> +{
> + struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
> + struct xe_hwmon_fan_info *fi = &hwmon->fi[fan];
> + u32 point_count;
> + int ret;
> + int point;
> + struct fan_table *source_table = &fi->fan_table[table_source];
> +
> + xe_dbg(hwmon->xe, "activating fan %d user table from %s table\n", fan,
> + (table_source == USER_FAN_TABLE) ? "user" : "stock");
> +
> + point_count = source_table->fan_control_point_count;
> +
> + if (!point_count) {
> + xe_err(hwmon->xe,
> + "fan %d %s table point count is 0, cannot activate user table\n", fan,
> + (table_source == USER_FAN_TABLE) ? "user" : "stock");
> + return -ENODATA;
> + }
> +
> + for (point = 0; point < point_count; point++) {
> + u8 temp = source_table->fcp[point].temp;
> + u8 speed = is_full_speed ? 100 : max_t(u8, source_table->fcp[point].speed,
> + min_t(u32, fi->min_pwm, U8_MAX));
> +
> + ret = xe_hwmon_write_user_fan_point(hwmon, fan, point, temp, speed);
> + if (ret)
> + return ret;
> +
> + if (table_source == STOCK_FAN_TABLE) {
> + /* Update user table cache with stock table values */
> + fi->fan_table[USER_FAN_TABLE].fcp[point].temp = temp;
> + fi->fan_table[USER_FAN_TABLE].fcp[point].speed = speed;
> + }
> + if (is_full_speed)
> + fi->fan_table[USER_FAN_TABLE].fcp[point].speed = speed;
> + }
> +
> + ret = xe_pcode_write_timeout(root_tile,
> + PCODE_MBOX(FAN_SPEED_CONTROL,
> + FSC_WRITE_NUM_FAN_CONTROL_POINTS, fan),
> + point_count, XE_PCODE_FAN_CONTROL_TIMEOUT_MS);
> + if (ret) {
> + xe_dbg(hwmon->xe, "failed to write fan %d user table, ret=%d\n", fan, ret);
> + return ret;
> + }
> +
> + /* Verify if all the user points are set */
> + ret = xe_hwmon_get_fan_point_count(hwmon, fan, &point_count, USER_FAN_TABLE);
> + if (ret) {
> + xe_err(hwmon->xe,
> + "failed to read fan %d user table point count, ret=%d\n", fan, ret);
> + return ret;
> + }
> + if (point_count != source_table->fan_control_point_count) {
> + xe_err(hwmon->xe, "fan %d user table point count mismatch, expected %u, got %u\n",
> + fan, source_table->fan_control_point_count, point_count);
> + return -EIO;
> + }
> +
> + fi->fan_table[USER_FAN_TABLE].fan_control_point_count = point_count;
> +
> + return 0;
> +}
> +
> +static int xe_hwmon_set_user_fan_pwm(struct xe_hwmon *hwmon, u8 fan, u8 pwm)
> +{
> + struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
> + struct xe_hwmon_fan_info *fi = &hwmon->fi[fan];
> + u32 point_count = fi->fan_table[USER_FAN_TABLE].fan_control_point_count;
> + u8 clamped_pwm;
> + int ret;
> + int point;
> +
> + /* Read user table fan point count, if it is not set, activate the user table */
> + ret = xe_hwmon_get_fan_point_count(hwmon, fan, &point_count, USER_FAN_TABLE);
> + if (ret)
> + return ret;
> +
> + if (!point_count) {
> + xe_dbg(hwmon->xe, "fan %d user table not set, activating it\n", fan);
> + ret = xe_hwmon_activate_user_fan_table(hwmon, fan, false, STOCK_FAN_TABLE);
> + if (ret)
> + return ret;
> + }
> +
> + pwm = DIV_ROUND_CLOSEST(pwm * 100, U8_MAX);
> + clamped_pwm = max_t(u8, pwm, min_t(u32, fi->min_pwm, U8_MAX));
What is the use of this clamping logic? as the pwm is in percentage
already.
> +
> + for (point = 0; point < point_count; point++) {
> + u8 temp = fi->fan_table[USER_FAN_TABLE].fcp[point].temp;
> +
> + ret = xe_hwmon_write_user_fan_point(hwmon, fan, point, temp, clamped_pwm);
> + if (ret)
> + return ret;
> +
> + fi->fan_table[USER_FAN_TABLE].fcp[point].speed = clamped_pwm;
> + }
> +
> + ret = xe_pcode_write(root_tile,
> + PCODE_MBOX(FAN_SPEED_CONTROL, FSC_WRITE_NUM_FAN_CONTROL_POINTS, fan),
> + point_count);
> + if (ret) {
> + xe_dbg(hwmon->xe, "failed to update fan %d user table count, ret=%d\n", fan, ret);
> + return ret;
> + }
> + return 0;
> +}
> +
> static int xe_hwmon_read_fan_control_info(struct xe_hwmon *hwmon)
> {
> struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
> @@ -1424,6 +1577,20 @@ xe_hwmon_fan_is_visible(struct xe_hwmon *hwmon, u32 attr, int channel)
> }
> }
>
> +static umode_t
> +xe_hwmon_pwm_is_visible(struct xe_hwmon *hwmon, u32 attr, int channel)
> +{
> + if (!hwmon->xe->info.has_fan_control || channel >= hwmon->num_fans)
> + return 0;
> +
> + switch (attr) {
> + case hwmon_pwm_input:
> + return 0644;
> + default:
> + return 0;
> + }
> +}
> +
> static int
> xe_hwmon_fan_input_read(struct xe_hwmon *hwmon, int channel, long *val)
> {
> @@ -1478,6 +1645,62 @@ xe_hwmon_fan_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *val)
> }
> }
>
> +static int
> +xe_hwmon_pwm_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *val)
> +{
> + struct xe_hwmon_fan_info *fi;
> + int ret = 0;
> +
> + if (channel < 0 || channel >= FAN_MAX)
> + return -EINVAL;
> +
> + fi = &hwmon->fi[channel];
> +
> + switch (attr) {
> + case hwmon_pwm_input:
> + /* Check if user fan table is set else activate it.*/
> + if (!fi->fan_table[USER_FAN_TABLE].fan_control_point_count) {
> + xe_dbg(hwmon->xe, "fan %d user table not set, activating it\n", channel);
> + ret = xe_hwmon_activate_user_fan_table(hwmon, channel, false,
> + STOCK_FAN_TABLE);
> + if (ret)
> + return ret;
> + }
> + *val = DIV_ROUND_CLOSEST(fi->fan_table[USER_FAN_TABLE].fcp[0].speed *
> + U8_MAX, 100);
> + return 0;
> + default:
> + return -EOPNOTSUPP;
> + }
> +}
> +
> +static int
> +xe_hwmon_pwm_write(struct xe_hwmon *hwmon, u32 attr, int channel, long val)
> +{
> + int ret = 0;
> +
> + if (channel < 0 || channel >= FAN_MAX)
> + return -EINVAL;
> +
> + mutex_lock(&hwmon->hwmon_lock);
> +
> + switch (attr) {
> + case hwmon_pwm_input:
> + if (val < 0 || val > U8_MAX) {
> + ret = -EINVAL;
> + break;
> + }
> + ret = xe_hwmon_set_user_fan_pwm(hwmon, channel, (u8)val);
> + break;
> + default:
> + ret = -EOPNOTSUPP;
> + break;
> + }
> + mutex_unlock(&hwmon->hwmon_lock);
> +
> + return ret;
> +}
> +
> static umode_t
> xe_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
> u32 attr, int channel)
> @@ -1504,6 +1727,9 @@ xe_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
> case hwmon_fan:
> ret = xe_hwmon_fan_is_visible(hwmon, attr, channel);
> break;
> + case hwmon_pwm:
> + ret = xe_hwmon_pwm_is_visible(hwmon, attr, channel);
> + break;
> default:
> ret = 0;
> break;
> @@ -1533,6 +1759,8 @@ xe_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
> return xe_hwmon_energy_read(hwmon, attr, channel, val);
> case hwmon_fan:
> return xe_hwmon_fan_read(hwmon, attr, channel, val);
> + case hwmon_pwm:
> + return xe_hwmon_pwm_read(hwmon, attr, channel, val);
> default:
> return -EOPNOTSUPP;
> }
> @@ -1551,6 +1779,8 @@ xe_hwmon_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
> return xe_hwmon_power_write(hwmon, attr, channel, val);
> case hwmon_curr:
> return xe_hwmon_curr_write(hwmon, attr, channel, val);
> + case hwmon_pwm:
> + return xe_hwmon_pwm_write(hwmon, attr, channel, val);
> default:
> return -EOPNOTSUPP;
> }
> diff --git a/drivers/gpu/drm/xe/xe_pcode_api.h b/drivers/gpu/drm/xe/xe_pcode_api.h
> index 419ab4f416fa..5c5fdc650a97 100644
> --- a/drivers/gpu/drm/xe/xe_pcode_api.h
> +++ b/drivers/gpu/drm/xe/xe_pcode_api.h
> @@ -84,9 +84,12 @@
> #define PCODE_MBOX_DOMAIN_HBM 0x2
>
> #define FAN_SPEED_CONTROL 0x7D
> +#define FSC_WRITE_NUM_FAN_CONTROL_POINTS 0x0
> +#define FSC_WRITE_FAN_TABLE 0x1
> #define FSC_READ_MAX_FAN_RPS 0x3
> #define FSC_READ_NUM_FANS 0x4
> #define FSC_READ_STOCK_FAN_CONTROL_POINTS 0x5
> +#define FSC_READ_USER_FAN_CONTROL_POINTS 0x6
> #define FSC_READ_FAN_TABLE 0x7
> #define FAN_CONTROL_POINT_TEMP_MASK REG_GENMASK(7, 0)
> #define FAN_CONTROL_POINT_SPEED_MASK REG_GENMASK(15, 8)
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 8/9] drm/xe/hwmon: preserve fan user table across suspend resume
2026-07-17 4:17 ` [PATCH v2 8/9] drm/xe/hwmon: preserve fan user table across suspend resume Karthik Poosa
@ 2026-08-10 5:53 ` Nilawar, Badal
0 siblings, 0 replies; 21+ messages in thread
From: Nilawar, Badal @ 2026-08-10 5:53 UTC (permalink / raw)
To: Karthik Poosa, intel-xe
Cc: rodrigo.vivi, anshuman.gupta, raag.jadav, riana.tauro, sk.anirban,
mallesh.koujalagi, soham.purkait
On 17-07-2026 09:47, Karthik Poosa wrote:
> Save and restore active fan user tables over suspend/resume via new
> xe_hwmon PM hooks, and run restore, only when d3cold is allowed.
>
> Signed-off-by: Karthik Poosa <karthik.poosa@intel.com>
> Assisted-by: Codex:gpt-5-3
> ---
> drivers/gpu/drm/xe/xe_hwmon.c | 133 ++++++++++++++++++++++++++++++++++
> drivers/gpu/drm/xe/xe_hwmon.h | 4 +
> drivers/gpu/drm/xe/xe_pm.c | 9 +++
> 3 files changed, 146 insertions(+)
>
> diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c
> index 770f07fb6511..65f356c741f5 100644
> --- a/drivers/gpu/drm/xe/xe_hwmon.c
> +++ b/drivers/gpu/drm/xe/xe_hwmon.c
> @@ -229,6 +229,8 @@ struct xe_hwmon_fan_info {
> u8 pwm_enable_mode;
> /** @is_full_speed: flag indicating if fan is in full speed */
> bool is_full_speed;
> + /** @restore_user_table: restore user fan table on next resume */
> + bool restore_user_table;
> };
>
> /**
> @@ -2785,4 +2787,135 @@ int xe_hwmon_register(struct xe_device *xe)
>
> return 0;
> }
> +
> +/**
> + * xe_hwmon_suspend - Save hwmon state before suspend
> + * @xe: xe device instance
> + *
> + * Save any hwmon runtime state that must survive PM suspend transitions.
> + */
> +void xe_hwmon_suspend(struct xe_device *xe)
> +{
> + struct xe_hwmon *hwmon = xe->hwmon;
> + struct xe_tile *root_tile;
> + int fan;
> +
> + if (!hwmon || !xe->info.has_fan_control)
> + return;
> +
> + /*
> + * User fan-table restore is limited to d3cold flows, so skip snapshot
> + * work when d3cold is not allowed for this device.
> + */
> + if (!xe->d3cold.allowed) {
> + xe_dbg(xe, "d3cold not allowed, skipping fan user-table snapshot\n");
> + return;
> + }
> +
> + root_tile = xe_device_get_root_tile(hwmon->xe);
> +
> + mutex_lock(&hwmon->hwmon_lock);
> +
> + for (fan = 0; fan < hwmon->num_fans; fan++) {
> + struct xe_hwmon_fan_info *fi = &hwmon->fi[fan];
> + u32 point_count;
> + int ret;
> +
> + /*
> + * Record whether this fan was using the user table
> + * so resume can restore it if firmware state is reset.
> + */
> + fi->restore_user_table = (fi->pwm_enable_mode != XE_FAN_PWM_AUTO_STOCK_TABLE);
> +
> + xe_dbg(hwmon->xe, "fan %d user table restore flag set to %d\n", fan,
> + fi->restore_user_table);
> +
> + if (!fi->restore_user_table)
> + continue;
> +
> + ret = xe_hwmon_get_fan_point_count(hwmon, fan, &point_count, USER_FAN_TABLE);
> + if (ret || !point_count) {
> + xe_warn(hwmon->xe,
> + "fan %d point count read before suspend failed, ret=%d, val =%u\n",
> + fan, ret, point_count);
> + continue;
> + }
> +
> + fi->fan_table[USER_FAN_TABLE].fan_control_point_count =
> + min_t(u32, point_count, MAX_FAN_CONTROL_POINTS);
> +
> + for (int point = 0; point < fi->fan_table[USER_FAN_TABLE].fan_control_point_count;
> + point++) {
> + u32 fcp = FCP_INDEX(USER_FAN_TABLE, point);
> +
> + ret = xe_pcode_read_timeout(root_tile,
> + PCODE_MBOX(FAN_SPEED_CONTROL,
> + FSC_READ_FAN_TABLE, fan),
> + &fcp, NULL,
> + XE_PCODE_FAN_CONTROL_TIMEOUT_MS);
> + if (ret) {
> + xe_warn(hwmon->xe,
> + "fan %d user point %d before suspend failed, ret=%d\n",
> + fan, point, ret);
> + continue;
> + }
> +
> + fi->fan_table[USER_FAN_TABLE].fcp[point].temp =
> + REG_FIELD_GET(FAN_CONTROL_POINT_TEMP_MASK, fcp);
> + fi->fan_table[USER_FAN_TABLE].fcp[point].speed =
> + REG_FIELD_GET(FAN_CONTROL_POINT_SPEED_MASK, fcp);
> + }
> + xe_dbg(hwmon->xe,
> + "fan %d user table snapshot taken before suspend, point_count=%u\n", fan,
> + fi->fan_table[USER_FAN_TABLE].fan_control_point_count);
> + }
The above loop appears to save the fan table when restore_user_table is
set. Is this required? It seems restoring the table during resume should
be sufficient, as the fan points are already cached.
Thanks,
Badal
> +
> + mutex_unlock(&hwmon->hwmon_lock);
> +}
> +
> +/**
> + * xe_hwmon_resume - Restore hwmon state after resume
> + * @xe: xe device instance
> + *
> + * Restore any hwmon runtime state that was saved across suspend.
> + */
> +void xe_hwmon_resume(struct xe_device *xe)
> +{
> + struct xe_hwmon *hwmon = xe->hwmon;
> + int fan;
> +
> + if (!hwmon || !xe->info.has_fan_control)
> + return;
> +
> + if (!xe->d3cold.allowed) {
> + xe_dbg(xe, "d3cold not allowed, skipping fan user-table restore\n");
> + return;
> + }
> +
> + mutex_lock(&hwmon->hwmon_lock);
> +
> + for (fan = 0; fan < hwmon->num_fans; fan++) {
> + struct xe_hwmon_fan_info *fi = &hwmon->fi[fan];
> + int ret;
> +
> + if (!fi->restore_user_table) {
> + xe_dbg(xe, "fan %d user table restore not required\n", fan);
> + continue;
> + }
> +
> + /* Re-program cached user-table settings for fans active before suspend. */
> + ret = xe_hwmon_activate_user_fan_table(hwmon, fan,
> + fi->is_full_speed,
> + USER_FAN_TABLE);
> + if (ret)
> + xe_warn(xe, "failed to restore fan %d user table after resume, ret=%d\n",
> + fan, ret);
> +
> + xe_dbg(xe, "fan %d user table restored\n", fan);
> + fi->restore_user_table = false;
> + }
> +
> + mutex_unlock(&hwmon->hwmon_lock);
> +}
> +
> MODULE_IMPORT_NS("INTEL_PMT_TELEMETRY");
> diff --git a/drivers/gpu/drm/xe/xe_hwmon.h b/drivers/gpu/drm/xe/xe_hwmon.h
> index d02c1bfe8c0a..6c6f30208508 100644
> --- a/drivers/gpu/drm/xe/xe_hwmon.h
> +++ b/drivers/gpu/drm/xe/xe_hwmon.h
> @@ -12,8 +12,12 @@ struct xe_device;
>
> #if IS_REACHABLE(CONFIG_HWMON)
> int xe_hwmon_register(struct xe_device *xe);
> +void xe_hwmon_suspend(struct xe_device *xe);
> +void xe_hwmon_resume(struct xe_device *xe);
> #else
> static inline int xe_hwmon_register(struct xe_device *xe) { return 0; };
> +static inline void xe_hwmon_suspend(struct xe_device *xe) {}
> +static inline void xe_hwmon_resume(struct xe_device *xe) {}
> #endif
>
> #endif /* _XE_HWMON_H_ */
> diff --git a/drivers/gpu/drm/xe/xe_pm.c b/drivers/gpu/drm/xe/xe_pm.c
> index 99562f691080..23a78fa4a71e 100644
> --- a/drivers/gpu/drm/xe/xe_pm.c
> +++ b/drivers/gpu/drm/xe/xe_pm.c
> @@ -23,6 +23,7 @@
> #include "xe_i2c.h"
> #include "xe_irq.h"
> #include "xe_late_bind_fw.h"
> +#include "xe_hwmon.h"
> #include "xe_pcode.h"
> #include "xe_printk.h"
> #include "xe_pxp.h"
> @@ -189,6 +190,8 @@ int xe_pm_suspend(struct xe_device *xe)
> for_each_gt(gt, xe, id)
> xe_gt_suspend_prepare(gt);
>
> + xe_hwmon_suspend(xe);
> +
> xe_display_pm_suspend(xe);
>
> /* FIXME: Super racey... */
> @@ -290,6 +293,8 @@ int xe_pm_resume(struct xe_device *xe)
>
> xe_late_bind_fw_load(&xe->late_bind);
>
> + xe_hwmon_resume(xe);
> +
> drm_dbg(&xe->drm, "Device resumed\n");
> xe_pm_block_end_signalling();
> return 0;
> @@ -615,6 +620,8 @@ int xe_pm_runtime_suspend(struct xe_device *xe)
> if (err)
> goto out;
>
> + xe_hwmon_suspend(xe);
> +
> /*
> * Applying lock for entire list op as xe_ttm_bo_destroy and xe_bo_move_notify
> * also checks and deletes bo entry from user fault list.
> @@ -724,6 +731,8 @@ int xe_pm_runtime_resume(struct xe_device *xe)
>
> xe_pxp_pm_resume(xe->pxp);
>
> + xe_hwmon_resume(xe);
> +
> if (IS_VF_CCS_READY(xe))
> xe_sriov_vf_ccs_register_context(xe);
>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 2/9] drm/xe/hwmon: initialize fan-control backend and table cache
2026-07-17 4:17 ` [PATCH v2 2/9] drm/xe/hwmon: initialize fan-control backend and table cache Karthik Poosa
@ 2026-08-10 6:03 ` Nilawar, Badal
2026-08-11 13:11 ` Nilawar, Badal
1 sibling, 0 replies; 21+ messages in thread
From: Nilawar, Badal @ 2026-08-10 6:03 UTC (permalink / raw)
To: Karthik Poosa, intel-xe
Cc: rodrigo.vivi, anshuman.gupta, raag.jadav, riana.tauro, sk.anirban,
mallesh.koujalagi, soham.purkait
[-- Attachment #1: Type: text/plain, Size: 9965 bytes --]
On 17-07-2026 09:47, Karthik Poosa wrote:
> Initialize Xe hwmon fan-control support by detecting fan count,
> reading stock fan control points and min PWM.
>
> v2:
> - Avoid user table initialization during probe. (Badal)
> - Move unused code to appropriate patches.
> - Use xe helpers for dmesg logs.
>
> Signed-off-by: Karthik Poosa<karthik.poosa@intel.com>
> Assisted-by: Codex:gpt-5-4
> ---
> drivers/gpu/drm/xe/xe_hwmon.c | 159 +++++++++++++++++++++++++++---
> drivers/gpu/drm/xe/xe_pcode_api.h | 5 +
> 2 files changed, 149 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c
> index de3f2aeffc3f..0a416e3e7b8c 100644
> --- a/drivers/gpu/drm/xe/xe_hwmon.c
> +++ b/drivers/gpu/drm/xe/xe_hwmon.c
> @@ -20,6 +20,7 @@
> #include "xe_pcode_api.h"
> #include "xe_sriov.h"
> #include "xe_pm.h"
> +#include "xe_printk.h"
> #include "xe_vsec.h"
> #include "regs/xe_pmt.h"
>
> @@ -122,14 +123,50 @@ struct xe_hwmon_energy_info {
> long accum_energy;
> };
>
> +enum fan_table_type {
> + /** @STOCK_FAN_TABLE: firmware-provided fan table */
> + STOCK_FAN_TABLE,
> + /** @USER_FAN_TABLE: user fan table written through sysfs */
> + USER_FAN_TABLE,
> + /** @FAN_TABLE_MAX: number of fan table slots tracked per fan */
> + FAN_TABLE_MAX,
> +};
> +
> +/* Maximum number of fan control points supported by each fan */
> +#define MAX_FAN_CONTROL_POINTS (10)
> +
> +/* Fan control point index: bit31 selects fan table type; bits30:0 control point index */
> +#define FCP_INDEX(FAN_TABLE_TYPE, POINT_NUM) (((FAN_TABLE_TYPE) << 31) | \
> + ((POINT_NUM) & REG_GENMASK(30, 0)))
> +
> +/* PCODE operations timeout for fan control commands */
> +#define XE_PCODE_FAN_CONTROL_TIMEOUT_MS (10)
> +
> /**
> - * struct xe_hwmon_fan_info - to cache previous fan reading
> + * struct xe_hwmon_fan_info - cached fan telemetry and control state
> + *
> + * Each fan keeps the latest tachometer sampling state along with two fan
> + * tables: the stock table discovered from firmware and the user table managed
> + * by hwmon sysfs writes.
> */
> struct xe_hwmon_fan_info {
> /** @reg_val_prev: previous fan reg val */
> u32 reg_val_prev;
> /** @time_prev: previous timestamp */
> u64 time_prev;
> + /** @fan_table: fan control tables */
> + struct fan_table {
> + /** @fan_control_point_count: number of supported fan control points */
> + u8 fan_control_point_count;
> + struct fan_control_point {
> + /** @temp: temperature in degree celsius */
> + u8 temp;
> + /** @speed: fan speed in percentage */
> + u8 speed;
> + } fcp[MAX_FAN_CONTROL_POINTS];
> + } fan_table[FAN_TABLE_MAX];
> + /** @min_pwm: minimum fan PWM */
> + u32 min_pwm;
> };
>
> /**
> @@ -168,6 +205,8 @@ struct xe_hwmon {
> int scl_shift_time;
> /** @ei: Energy info for energyN_input */
> struct xe_hwmon_energy_info ei[CHANNEL_MAX];
> + /** @num_fans: number of fans available */
> + u8 num_fans;
> /** @fi: Fan info for fanN_input */
> struct xe_hwmon_fan_info fi[FAN_MAX];
> /** @boot_power_limit_read: is boot power limits read */
> @@ -856,17 +895,100 @@ static int xe_hwmon_pcode_write_i1(const struct xe_hwmon *hwmon, u32 uval)
> (uval & POWER_SETUP_I1_DATA_MASK));
> }
>
> -static int xe_hwmon_pcode_read_fan_control(const struct xe_hwmon *hwmon, u32 subcmd, u32 *uval)
> +static int xe_hwmon_pcode_read_fan_control(const struct xe_hwmon *hwmon, u32 subcmd, u8 fan_num,
> + u32 *uval)
> {
> struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
> + return xe_pcode_read_timeout(root_tile, PCODE_MBOX(FAN_SPEED_CONTROL, subcmd, fan_num),
> + uval, NULL, XE_PCODE_FAN_CONTROL_TIMEOUT_MS);
> +}
> +
> +static int xe_hwmon_get_num_fans(const struct xe_hwmon *hwmon, u32 *num_fans)
> +{
> + u32 fan_mask = 0;
> + int ret;
>
> /* Platforms that don't return correct value */
> - if (hwmon->xe->info.platform == XE_DG2 && subcmd == FSC_READ_NUM_FANS) {
> - *uval = 2;
> + if (hwmon->xe->info.platform == XE_DG2) {
> + *num_fans = 2;
> return 0;
> }
>
> - return xe_pcode_read(root_tile, PCODE_MBOX(FAN_SPEED_CONTROL, subcmd, 0), uval, NULL);
> + ret = xe_hwmon_pcode_read_fan_control(hwmon, FSC_READ_NUM_FANS, 0, &fan_mask);
> + if (ret) {
> + xe_warn(hwmon->xe, "failed to read number of fans, ret=%d\n", ret);
> + return ret;
> + }
> +
> + *num_fans = min_t(u32, hweight32(fan_mask), FAN_MAX);
> +
> + return 0;
> +}
> +
> +static int xe_hwmon_read_fan_control_info(struct xe_hwmon *hwmon)
> +{
> + struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
> + int point;
> + int fan;
> + int ret;
> +
> + for (fan = 0; fan < hwmon->num_fans; fan++) {
> + u32 stock_fcp_count = 0;
> +
> + struct xe_hwmon_fan_info *fi = &hwmon->fi[fan];
> +
> + ret = xe_hwmon_pcode_read_fan_control(hwmon,
> + FSC_READ_STOCK_FAN_CONTROL_POINTS,
> + fan, &stock_fcp_count);
> + if (ret) {
> + xe_err(hwmon->xe,
> + "failed to read fan %d stock control point count, ret=%d\n",
> + fan, ret);
> + return ret;
> + }
> + fi->fan_table[STOCK_FAN_TABLE].fan_control_point_count =
> + min_t(u8, stock_fcp_count, MAX_FAN_CONTROL_POINTS);
> +
> + xe_dbg(hwmon->xe, "fan %d stock points %u\n", fan,
> + fi->fan_table[STOCK_FAN_TABLE].fan_control_point_count);
> +
> + /* Dump the stock fan control points for debugging purposes. */
> + for (point = 0; point < fi->fan_table[STOCK_FAN_TABLE].fan_control_point_count;
> + point++) {
> + u32 fcp = 0;
> +
> + fcp = FCP_INDEX(STOCK_FAN_TABLE, point);
> + ret = xe_pcode_read_timeout(root_tile,
> + PCODE_MBOX(FAN_SPEED_CONTROL,
> + FSC_READ_FAN_TABLE, fan),
> + &fcp, NULL, XE_PCODE_FAN_CONTROL_TIMEOUT_MS);
> + if (ret) {
> + xe_err(hwmon->xe, "failed to read fan %d stock point %d, ret=%d\n",
> + fan, point, ret);
> + continue;
> + }
> +
> + /* Cache the stock fan control points in local structure for later use. */
> + fi->fan_table[STOCK_FAN_TABLE].fcp[point].temp =
> + REG_FIELD_GET(FAN_CONTROL_POINT_TEMP_MASK, fcp);
> + fi->fan_table[STOCK_FAN_TABLE].fcp[point].speed =
> + REG_FIELD_GET(FAN_CONTROL_POINT_SPEED_MASK, fcp);
> + xe_dbg(hwmon->xe, "fan %d stock point %d: temp %u C, speed %u %%\n",
> + fan, point, fi->fan_table[STOCK_FAN_TABLE].fcp[point].temp,
> + fi->fan_table[STOCK_FAN_TABLE].fcp[point].speed);
> + }
> +
> + /* Read minimum fan PWM */
> + ret = xe_hwmon_pcode_read_fan_control(hwmon, FSC_READ_FAN_MIN_PWM, fan,
> + &fi->min_pwm);
> + if (ret) {
> + xe_err(hwmon->xe, "failed to read fan %d min PWM, ret=%d\n", fan, ret);
> + continue;
> + }
> +
> + xe_dbg(hwmon->xe, "fan %d min PWM %u\n", fan, fi->min_pwm);
> + }
> + return 0;
> }
>
> static int xe_hwmon_power_curr_crit_read(struct xe_hwmon *hwmon, int channel,
> @@ -1279,17 +1401,12 @@ xe_hwmon_energy_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *val)
> static umode_t
> xe_hwmon_fan_is_visible(struct xe_hwmon *hwmon, u32 attr, int channel)
> {
> - u32 uval = 0;
> -
> if (!hwmon->xe->info.has_fan_control)
> return 0;
>
> switch (attr) {
> case hwmon_fan_input:
> - if (xe_hwmon_pcode_read_fan_control(hwmon, FSC_READ_NUM_FANS, &uval))
> - return 0;
> -
> - return channel < uval ? 0444 : 0;
> + return channel < hwmon->num_fans ? 0444 : 0;
> default:
> return 0;
> }
> @@ -1476,6 +1593,7 @@ xe_hwmon_get_preregistration_info(struct xe_hwmon *hwmon)
> u64 val_sku_unit = 0;
> int channel;
> struct xe_reg pkg_power_sku_unit;
> + u32 num_fans = 0;
>
> if (hwmon->xe->info.has_mbx_power_limits) {
> /* Check if GPU firmware support mailbox power limits commands. */
> @@ -1531,10 +1649,21 @@ xe_hwmon_get_preregistration_info(struct xe_hwmon *hwmon)
> if (xe_hwmon_is_visible(hwmon, hwmon_energy, hwmon_energy_input, channel))
> xe_hwmon_energy_get(hwmon, channel, &energy);
>
> - /* Initialize 'struct xe_hwmon_fan_info' with initial fan register reading. */
> - for (channel = 0; channel < FAN_MAX; channel++)
> - if (xe_hwmon_is_visible(hwmon, hwmon_fan, hwmon_fan_input, channel))
> - xe_hwmon_fan_input_read(hwmon, channel, &fan_speed);
> + if (hwmon->xe->info.has_fan_control) {
> + xe_hwmon_get_num_fans(hwmon, &num_fans);
> +
> + xe_info(hwmon->xe, "Number of fans detected: %u\n", num_fans);
> + hwmon->num_fans = num_fans;
> +
> + /* Initialize 'struct xe_hwmon_fan_info' with initial fan register reading. */
> + for (channel = 0; channel < hwmon->num_fans; channel++)
> + if (xe_hwmon_is_visible(hwmon, hwmon_fan, hwmon_fan_input, channel))
> + xe_hwmon_fan_input_read(hwmon, channel, &fan_speed);
> +
> + /* Fan control tables initialization */
> + if (xe_hwmon_read_fan_control_info(hwmon))
> + xe_warn(hwmon->xe, "Fan control tables are not available\n");
The table appears to be captured before late binding. Do we need
separate pre-/post-LB tables, or should the table be updated again after
late binding done?
Thanks,
Badal
> + }
>
> if (hwmon->xe->info.has_mbx_thermal_info && xe_hwmon_pcode_read_thermal_info(hwmon))
> drm_warn(&hwmon->xe->drm, "Thermal mailbox not supported by card firmware\n");
> diff --git a/drivers/gpu/drm/xe/xe_pcode_api.h b/drivers/gpu/drm/xe/xe_pcode_api.h
> index 94575c476e3d..669010f1e2d0 100644
> --- a/drivers/gpu/drm/xe/xe_pcode_api.h
> +++ b/drivers/gpu/drm/xe/xe_pcode_api.h
> @@ -85,6 +85,11 @@
>
> #define FAN_SPEED_CONTROL 0x7D
> #define FSC_READ_NUM_FANS 0x4
> +#define FSC_READ_STOCK_FAN_CONTROL_POINTS 0x5
> +#define FSC_READ_FAN_TABLE 0x7
> +#define FAN_CONTROL_POINT_TEMP_MASK REG_GENMASK(7, 0)
> +#define FAN_CONTROL_POINT_SPEED_MASK REG_GENMASK(15, 8)
> +#define FSC_READ_FAN_MIN_PWM 0x8
>
> #define PCODE_SCRATCH(x) XE_REG(0x138320 + ((x) * 4))
> /* PCODE_SCRATCH0 */
[-- Attachment #2: Type: text/html, Size: 10618 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 2/9] drm/xe/hwmon: initialize fan-control backend and table cache
2026-07-17 4:17 ` [PATCH v2 2/9] drm/xe/hwmon: initialize fan-control backend and table cache Karthik Poosa
2026-08-10 6:03 ` Nilawar, Badal
@ 2026-08-11 13:11 ` Nilawar, Badal
1 sibling, 0 replies; 21+ messages in thread
From: Nilawar, Badal @ 2026-08-11 13:11 UTC (permalink / raw)
To: Karthik Poosa, intel-xe
Cc: rodrigo.vivi, anshuman.gupta, raag.jadav, riana.tauro, sk.anirban,
mallesh.koujalagi, soham.purkait
On 17-07-2026 09:47, Karthik Poosa wrote:
> Initialize Xe hwmon fan-control support by detecting fan count,
> reading stock fan control points and min PWM.
>
> v2:
> - Avoid user table initialization during probe. (Badal)
> - Move unused code to appropriate patches.
> - Use xe helpers for dmesg logs.
>
> Signed-off-by: Karthik Poosa <karthik.poosa@intel.com>
> Assisted-by: Codex:gpt-5-4
> ---
> drivers/gpu/drm/xe/xe_hwmon.c | 159 +++++++++++++++++++++++++++---
> drivers/gpu/drm/xe/xe_pcode_api.h | 5 +
> 2 files changed, 149 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c
> index de3f2aeffc3f..0a416e3e7b8c 100644
> --- a/drivers/gpu/drm/xe/xe_hwmon.c
> +++ b/drivers/gpu/drm/xe/xe_hwmon.c
> @@ -20,6 +20,7 @@
> #include "xe_pcode_api.h"
> #include "xe_sriov.h"
> #include "xe_pm.h"
> +#include "xe_printk.h"
> #include "xe_vsec.h"
> #include "regs/xe_pmt.h"
>
> @@ -122,14 +123,50 @@ struct xe_hwmon_energy_info {
> long accum_energy;
> };
>
> +enum fan_table_type {
> + /** @STOCK_FAN_TABLE: firmware-provided fan table */
> + STOCK_FAN_TABLE,
> + /** @USER_FAN_TABLE: user fan table written through sysfs */
> + USER_FAN_TABLE,
> + /** @FAN_TABLE_MAX: number of fan table slots tracked per fan */
> + FAN_TABLE_MAX,
> +};
> +
> +/* Maximum number of fan control points supported by each fan */
> +#define MAX_FAN_CONTROL_POINTS (10)
> +
> +/* Fan control point index: bit31 selects fan table type; bits30:0 control point index */
> +#define FCP_INDEX(FAN_TABLE_TYPE, POINT_NUM) (((FAN_TABLE_TYPE) << 31) | \
> + ((POINT_NUM) & REG_GENMASK(30, 0)))
> +
> +/* PCODE operations timeout for fan control commands */
> +#define XE_PCODE_FAN_CONTROL_TIMEOUT_MS (10)
> +
> /**
> - * struct xe_hwmon_fan_info - to cache previous fan reading
> + * struct xe_hwmon_fan_info - cached fan telemetry and control state
> + *
> + * Each fan keeps the latest tachometer sampling state along with two fan
> + * tables: the stock table discovered from firmware and the user table managed
> + * by hwmon sysfs writes.
> */
> struct xe_hwmon_fan_info {
> /** @reg_val_prev: previous fan reg val */
> u32 reg_val_prev;
> /** @time_prev: previous timestamp */
> u64 time_prev;
> + /** @fan_table: fan control tables */
> + struct fan_table {
> + /** @fan_control_point_count: number of supported fan control points */
> + u8 fan_control_point_count;
> + struct fan_control_point {
> + /** @temp: temperature in degree celsius */
> + u8 temp;
> + /** @speed: fan speed in percentage */
> + u8 speed;
> + } fcp[MAX_FAN_CONTROL_POINTS];
> + } fan_table[FAN_TABLE_MAX];
> + /** @min_pwm: minimum fan PWM */
> + u32 min_pwm;
> };
>
> /**
> @@ -168,6 +205,8 @@ struct xe_hwmon {
> int scl_shift_time;
> /** @ei: Energy info for energyN_input */
> struct xe_hwmon_energy_info ei[CHANNEL_MAX];
> + /** @num_fans: number of fans available */
> + u8 num_fans;
> /** @fi: Fan info for fanN_input */
> struct xe_hwmon_fan_info fi[FAN_MAX];
> /** @boot_power_limit_read: is boot power limits read */
> @@ -856,17 +895,100 @@ static int xe_hwmon_pcode_write_i1(const struct xe_hwmon *hwmon, u32 uval)
> (uval & POWER_SETUP_I1_DATA_MASK));
> }
>
> -static int xe_hwmon_pcode_read_fan_control(const struct xe_hwmon *hwmon, u32 subcmd, u32 *uval)
> +static int xe_hwmon_pcode_read_fan_control(const struct xe_hwmon *hwmon, u32 subcmd, u8 fan_num,
> + u32 *uval)
> {
> struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
> + return xe_pcode_read_timeout(root_tile, PCODE_MBOX(FAN_SPEED_CONTROL, subcmd, fan_num),
> + uval, NULL, XE_PCODE_FAN_CONTROL_TIMEOUT_MS);
> +}
> +
> +static int xe_hwmon_get_num_fans(const struct xe_hwmon *hwmon, u32 *num_fans)
> +{
> + u32 fan_mask = 0;
> + int ret;
>
> /* Platforms that don't return correct value */
> - if (hwmon->xe->info.platform == XE_DG2 && subcmd == FSC_READ_NUM_FANS) {
> - *uval = 2;
> + if (hwmon->xe->info.platform == XE_DG2) {
> + *num_fans = 2;
> return 0;
> }
>
> - return xe_pcode_read(root_tile, PCODE_MBOX(FAN_SPEED_CONTROL, subcmd, 0), uval, NULL);
> + ret = xe_hwmon_pcode_read_fan_control(hwmon, FSC_READ_NUM_FANS, 0, &fan_mask);
> + if (ret) {
> + xe_warn(hwmon->xe, "failed to read number of fans, ret=%d\n", ret);
> + return ret;
> + }
> +
> + *num_fans = min_t(u32, hweight32(fan_mask), FAN_MAX);
> +
> + return 0;
> +}
> +
> +static int xe_hwmon_read_fan_control_info(struct xe_hwmon *hwmon)
> +{
> + struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
> + int point;
> + int fan;
> + int ret;
> +
> + for (fan = 0; fan < hwmon->num_fans; fan++) {
> + u32 stock_fcp_count = 0;
> +
> + struct xe_hwmon_fan_info *fi = &hwmon->fi[fan];
> +
> + ret = xe_hwmon_pcode_read_fan_control(hwmon,
> + FSC_READ_STOCK_FAN_CONTROL_POINTS,
> + fan, &stock_fcp_count);
Is stock fan table going to change after late bind? If yes, then I will
suggest to maintain one more table.
Or Update the current one once late binding is done.
Thanks,
Badal
> + if (ret) {
> + xe_err(hwmon->xe,
> + "failed to read fan %d stock control point count, ret=%d\n",
> + fan, ret);
> + return ret;
> + }
> + fi->fan_table[STOCK_FAN_TABLE].fan_control_point_count =
> + min_t(u8, stock_fcp_count, MAX_FAN_CONTROL_POINTS);
> +
> + xe_dbg(hwmon->xe, "fan %d stock points %u\n", fan,
> + fi->fan_table[STOCK_FAN_TABLE].fan_control_point_count);
> +
> + /* Dump the stock fan control points for debugging purposes. */
> + for (point = 0; point < fi->fan_table[STOCK_FAN_TABLE].fan_control_point_count;
> + point++) {
> + u32 fcp = 0;
> +
> + fcp = FCP_INDEX(STOCK_FAN_TABLE, point);
> + ret = xe_pcode_read_timeout(root_tile,
> + PCODE_MBOX(FAN_SPEED_CONTROL,
> + FSC_READ_FAN_TABLE, fan),
> + &fcp, NULL, XE_PCODE_FAN_CONTROL_TIMEOUT_MS);
> + if (ret) {
> + xe_err(hwmon->xe, "failed to read fan %d stock point %d, ret=%d\n",
> + fan, point, ret);
> + continue;
> + }
> +
> + /* Cache the stock fan control points in local structure for later use. */
> + fi->fan_table[STOCK_FAN_TABLE].fcp[point].temp =
> + REG_FIELD_GET(FAN_CONTROL_POINT_TEMP_MASK, fcp);
> + fi->fan_table[STOCK_FAN_TABLE].fcp[point].speed =
> + REG_FIELD_GET(FAN_CONTROL_POINT_SPEED_MASK, fcp);
> + xe_dbg(hwmon->xe, "fan %d stock point %d: temp %u C, speed %u %%\n",
> + fan, point, fi->fan_table[STOCK_FAN_TABLE].fcp[point].temp,
> + fi->fan_table[STOCK_FAN_TABLE].fcp[point].speed);
> + }
> +
> + /* Read minimum fan PWM */
> + ret = xe_hwmon_pcode_read_fan_control(hwmon, FSC_READ_FAN_MIN_PWM, fan,
> + &fi->min_pwm);
> + if (ret) {
> + xe_err(hwmon->xe, "failed to read fan %d min PWM, ret=%d\n", fan, ret);
> + continue;
> + }
> +
> + xe_dbg(hwmon->xe, "fan %d min PWM %u\n", fan, fi->min_pwm);
> + }
> + return 0;
> }
>
> static int xe_hwmon_power_curr_crit_read(struct xe_hwmon *hwmon, int channel,
> @@ -1279,17 +1401,12 @@ xe_hwmon_energy_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *val)
> static umode_t
> xe_hwmon_fan_is_visible(struct xe_hwmon *hwmon, u32 attr, int channel)
> {
> - u32 uval = 0;
> -
> if (!hwmon->xe->info.has_fan_control)
> return 0;
>
> switch (attr) {
> case hwmon_fan_input:
> - if (xe_hwmon_pcode_read_fan_control(hwmon, FSC_READ_NUM_FANS, &uval))
> - return 0;
> -
> - return channel < uval ? 0444 : 0;
> + return channel < hwmon->num_fans ? 0444 : 0;
> default:
> return 0;
> }
> @@ -1476,6 +1593,7 @@ xe_hwmon_get_preregistration_info(struct xe_hwmon *hwmon)
> u64 val_sku_unit = 0;
> int channel;
> struct xe_reg pkg_power_sku_unit;
> + u32 num_fans = 0;
>
> if (hwmon->xe->info.has_mbx_power_limits) {
> /* Check if GPU firmware support mailbox power limits commands. */
> @@ -1531,10 +1649,21 @@ xe_hwmon_get_preregistration_info(struct xe_hwmon *hwmon)
> if (xe_hwmon_is_visible(hwmon, hwmon_energy, hwmon_energy_input, channel))
> xe_hwmon_energy_get(hwmon, channel, &energy);
>
> - /* Initialize 'struct xe_hwmon_fan_info' with initial fan register reading. */
> - for (channel = 0; channel < FAN_MAX; channel++)
> - if (xe_hwmon_is_visible(hwmon, hwmon_fan, hwmon_fan_input, channel))
> - xe_hwmon_fan_input_read(hwmon, channel, &fan_speed);
> + if (hwmon->xe->info.has_fan_control) {
> + xe_hwmon_get_num_fans(hwmon, &num_fans);
> +
> + xe_info(hwmon->xe, "Number of fans detected: %u\n", num_fans);
> + hwmon->num_fans = num_fans;
> +
> + /* Initialize 'struct xe_hwmon_fan_info' with initial fan register reading. */
> + for (channel = 0; channel < hwmon->num_fans; channel++)
> + if (xe_hwmon_is_visible(hwmon, hwmon_fan, hwmon_fan_input, channel))
> + xe_hwmon_fan_input_read(hwmon, channel, &fan_speed);
> +
> + /* Fan control tables initialization */
> + if (xe_hwmon_read_fan_control_info(hwmon))
> + xe_warn(hwmon->xe, "Fan control tables are not available\n");
> + }
>
> if (hwmon->xe->info.has_mbx_thermal_info && xe_hwmon_pcode_read_thermal_info(hwmon))
> drm_warn(&hwmon->xe->drm, "Thermal mailbox not supported by card firmware\n");
> diff --git a/drivers/gpu/drm/xe/xe_pcode_api.h b/drivers/gpu/drm/xe/xe_pcode_api.h
> index 94575c476e3d..669010f1e2d0 100644
> --- a/drivers/gpu/drm/xe/xe_pcode_api.h
> +++ b/drivers/gpu/drm/xe/xe_pcode_api.h
> @@ -85,6 +85,11 @@
>
> #define FAN_SPEED_CONTROL 0x7D
> #define FSC_READ_NUM_FANS 0x4
> +#define FSC_READ_STOCK_FAN_CONTROL_POINTS 0x5
> +#define FSC_READ_FAN_TABLE 0x7
> +#define FAN_CONTROL_POINT_TEMP_MASK REG_GENMASK(7, 0)
> +#define FAN_CONTROL_POINT_SPEED_MASK REG_GENMASK(15, 8)
> +#define FSC_READ_FAN_MIN_PWM 0x8
>
> #define PCODE_SCRATCH(x) XE_REG(0x138320 + ((x) * 4))
> /* PCODE_SCRATCH0 */
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 4/9] drm/xe/hwmon: expose pwm[1-3]
2026-07-24 7:02 ` Purkait, Soham
@ 2026-08-11 13:58 ` Poosa, Karthik
0 siblings, 0 replies; 21+ messages in thread
From: Poosa, Karthik @ 2026-08-11 13:58 UTC (permalink / raw)
To: Purkait, Soham, intel-xe
Cc: rodrigo.vivi, anshuman.gupta, badal.nilawar, raag.jadav,
riana.tauro, sk.anirban, mallesh.koujalagi
[-- Attachment #1: Type: text/plain, Size: 15488 bytes --]
On 24-07-2026 12:32, Purkait, Soham wrote:
> Hi Karthik,
>
> On 17-07-2026 09:47, Karthik Poosa wrote:
>> Expose pwm[1-3] fan duty attributes through hwmon.
>>
>> This enables reading and writing user PWM values (0..255) for each
>> available fan channel.
>>
>> Update Xe hwmon ABI documentation for pwm[1-3].
>>
>> This sysfs node can be used for manual control of fan speed,
>> irrespective of fan curve.
>>
>> v2:
>> - Use xe helpers for dmesg logs.
>> - Avoid use hwmon->num_fans to get fan count in
>> xe_hwmon_pwm_is_visible().
>>
>> Signed-off-by: Karthik Poosa <karthik.poosa@intel.com>
>> Assisted-by: Codex:gpt-5-4
>> ---
>> .../ABI/testing/sysfs-driver-intel-xe-hwmon | 9 +
>> drivers/gpu/drm/xe/xe_hwmon.c | 230 ++++++++++++++++++
>> drivers/gpu/drm/xe/xe_pcode_api.h | 3 +
>> 3 files changed, 242 insertions(+)
>>
>> diff --git a/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon
>> b/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon
>> index ec0b94d76e22..7383898890aa 100644
>> --- a/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon
>> +++ b/Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon
>> @@ -321,3 +321,12 @@ Description: RW. Package burst power limit
>> interval (Tau in PL2/Tau) in
>> milliseconds over which sustained power is averaged.
>> Only supported for particular Intel Xe graphics platforms.
>> +
>> +What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/pwm[1-3]
>> +Date: July 2026
>> +KernelVersion: 7.2
>> +Contact: intel-xe@lists.freedesktop.org
>> +Description: RW. Target fan PWM duty value in the range 0..255. 0
>> is zero
>> + fan speed and 255 is 100% fan speed.
>> +
>> + Only supported for particular Intel Xe graphics
>> platforms.
>> diff --git a/drivers/gpu/drm/xe/xe_hwmon.c
>> b/drivers/gpu/drm/xe/xe_hwmon.c
>> index 769f4d1da83e..d3379727b1a9 100644
>> --- a/drivers/gpu/drm/xe/xe_hwmon.c
>> +++ b/drivers/gpu/drm/xe/xe_hwmon.c
>> @@ -803,6 +803,7 @@ static const struct hwmon_channel_info * const
>> hwmon_info[] = {
>> HWMON_CHANNEL_INFO(energy, HWMON_E_INPUT | HWMON_E_LABEL,
>> HWMON_E_INPUT | HWMON_E_LABEL),
>> HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_MAX,
>> HWMON_F_INPUT | HWMON_F_MAX,
>> HWMON_F_INPUT | HWMON_F_MAX),
>> + HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT, HWMON_PWM_INPUT,
>> HWMON_PWM_INPUT),
>> NULL
>> };
>> @@ -928,6 +929,158 @@ static int xe_hwmon_get_num_fans(const struct
>> xe_hwmon *hwmon, u32 *num_fans)
>> return 0;
>> }
>> +static int xe_hwmon_get_fan_point_count(struct xe_hwmon *hwmon, u8
>> fan, u32 *point_count,
>> + int table_type)
>> +{
>> + int ret;
>> +
>> + ret = xe_hwmon_pcode_read_fan_control(hwmon,
>> + (table_type == USER_FAN_TABLE) ?
>> + FSC_READ_USER_FAN_CONTROL_POINTS :
>> + FSC_READ_STOCK_FAN_CONTROL_POINTS,
>> + fan, point_count);
>> + if (ret) {
>> + xe_err(hwmon->xe, "failed to read fan %d %s point count,
>> ret=%d\n", fan,
>> + (table_type == USER_FAN_TABLE) ? "user" : "stock", ret);
>> + return ret;
>> + }
>> +
>> + xe_dbg(hwmon->xe, "fan %d %s point count read as %u\n", fan,
>> + (table_type == USER_FAN_TABLE) ? "user" : "stock",
>> *point_count);
>> +
>> + return 0;
>> +}
>> +
>> +static int xe_hwmon_write_user_fan_point(struct xe_hwmon *hwmon, u8
>> fan, u8 point, u8 temp,
>> + u8 speed)
>> +{
>> + struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
>> + u16 user_fcp_raw = FIELD_PREP(FAN_CONTROL_POINT_TEMP_MASK, temp) |
>> + FIELD_PREP(FAN_CONTROL_POINT_SPEED_MASK, speed);
>> + int ret;
>> +
>> + ret = xe_pcode_write_timeout(root_tile,
>> + PCODE_MBOX(FAN_SPEED_CONTROL,
>> FSC_WRITE_FAN_TABLE, fan),
>> + (u32)user_fcp_raw,
>> XE_PCODE_FAN_CONTROL_TIMEOUT_MS);
>> + if (ret)
>> + xe_dbg(hwmon->xe,
>> + "failed to write fan %d user point %d temp %u, speed
>> %u %%, ret=%d\n",
>> + fan, point, temp, speed, ret);
>> +
>> + return ret;
>> +}
>> +
>> +static int xe_hwmon_activate_user_fan_table(struct xe_hwmon *hwmon,
>> u8 fan,
>> + bool is_full_speed, enum fan_table_type
>> table_source)
>> +{
>> + struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
>> + struct xe_hwmon_fan_info *fi = &hwmon->fi[fan];
>> + u32 point_count;
>> + int ret;
>> + int point;
>> + struct fan_table *source_table = &fi->fan_table[table_source];
>> +
>> + xe_dbg(hwmon->xe, "activating fan %d user table from %s
>> table\n", fan,
>> + (table_source == USER_FAN_TABLE) ? "user" : "stock");
>> +
>> + point_count = source_table->fan_control_point_count;
>> +
>> + if (!point_count) {
>> + xe_err(hwmon->xe,
>> + "fan %d %s table point count is 0, cannot activate
>> user table\n", fan,
>> + (table_source == USER_FAN_TABLE) ? "user" : "stock");
>> + return -ENODATA;
>> + }
>> +
>> + for (point = 0; point < point_count; point++) {
>> + u8 temp = source_table->fcp[point].temp;
>> + u8 speed = is_full_speed ? 100 : max_t(u8,
>> source_table->fcp[point].speed,
>> + min_t(u32, fi->min_pwm, U8_MAX));
>> +
>> + ret = xe_hwmon_write_user_fan_point(hwmon, fan, point, temp,
>> speed);
>> + if (ret)
>> + return ret;
>> +
>> + if (table_source == STOCK_FAN_TABLE) {
>> + /* Update user table cache with stock table values */
>> + fi->fan_table[USER_FAN_TABLE].fcp[point].temp = temp;
>> + fi->fan_table[USER_FAN_TABLE].fcp[point].speed = speed;
>> + }
>> + if (is_full_speed)
>> + fi->fan_table[USER_FAN_TABLE].fcp[point].speed = speed;
>> + }
>> +
>> + ret = xe_pcode_write_timeout(root_tile,
>> + PCODE_MBOX(FAN_SPEED_CONTROL,
>> + FSC_WRITE_NUM_FAN_CONTROL_POINTS, fan),
>> + point_count, XE_PCODE_FAN_CONTROL_TIMEOUT_MS);
>> + if (ret) {
>> + xe_dbg(hwmon->xe, "failed to write fan %d user table,
>> ret=%d\n", fan, ret);
>> + return ret;
>> + }
>> +
>> + /* Verify if all the user points are set */
>> + ret = xe_hwmon_get_fan_point_count(hwmon, fan, &point_count,
>> USER_FAN_TABLE);
>> + if (ret) {
>> + xe_err(hwmon->xe,
>> + "failed to read fan %d user table point count,
>> ret=%d\n", fan, ret);
>> + return ret;
>> + }
>> + if (point_count != source_table->fan_control_point_count) {
>> + xe_err(hwmon->xe, "fan %d user table point count mismatch,
>> expected %u, got %u\n",
>> + fan, source_table->fan_control_point_count,
>> point_count);
>> + return -EIO;
>> + }
>> +
>> + fi->fan_table[USER_FAN_TABLE].fan_control_point_count =
>> point_count;
>> +
>> + return 0;
>> +}
>> +
>> +static int xe_hwmon_set_user_fan_pwm(struct xe_hwmon *hwmon, u8 fan,
>> u8 pwm)
>> +{
>> + struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
>> + struct xe_hwmon_fan_info *fi = &hwmon->fi[fan];
>> + u32 point_count =
>> fi->fan_table[USER_FAN_TABLE].fan_control_point_count;
>> + u8 clamped_pwm;
>> + int ret;
>> + int point;
>> +
>> + /* Read user table fan point count, if it is not set, activate
>> the user table */
>> + ret = xe_hwmon_get_fan_point_count(hwmon, fan, &point_count,
>> USER_FAN_TABLE);
>> + if (ret)
>> + return ret;
>> +
>> + if (!point_count) {
>> + xe_dbg(hwmon->xe, "fan %d user table not set, activating
>> it\n", fan);
>> + ret = xe_hwmon_activate_user_fan_table(hwmon, fan, false,
>> STOCK_FAN_TABLE);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + pwm = DIV_ROUND_CLOSEST(pwm * 100, U8_MAX);
>> + clamped_pwm = max_t(u8, pwm, min_t(u32, fi->min_pwm, U8_MAX));
> What is the use of this clamping logic? as the pwm is in percentage
> already.
Clamping ensures that the PWM value requested by the user is not lower
than the minimum PWM supported by the hardware, preventing fan buzzing
or stalling.
>> +
>> + for (point = 0; point < point_count; point++) {
>> + u8 temp = fi->fan_table[USER_FAN_TABLE].fcp[point].temp;
>> +
>> + ret = xe_hwmon_write_user_fan_point(hwmon, fan, point, temp,
>> clamped_pwm);
>> + if (ret)
>> + return ret;
>> +
>> + fi->fan_table[USER_FAN_TABLE].fcp[point].speed = clamped_pwm;
>> + }
>> +
>> + ret = xe_pcode_write(root_tile,
>> + PCODE_MBOX(FAN_SPEED_CONTROL,
>> FSC_WRITE_NUM_FAN_CONTROL_POINTS, fan),
>> + point_count);
>> + if (ret) {
>> + xe_dbg(hwmon->xe, "failed to update fan %d user table count,
>> ret=%d\n", fan, ret);
>> + return ret;
>> + }
>> + return 0;
>> +}
>> +
>> static int xe_hwmon_read_fan_control_info(struct xe_hwmon *hwmon)
>> {
>> struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
>> @@ -1424,6 +1577,20 @@ xe_hwmon_fan_is_visible(struct xe_hwmon
>> *hwmon, u32 attr, int channel)
>> }
>> }
>> +static umode_t
>> +xe_hwmon_pwm_is_visible(struct xe_hwmon *hwmon, u32 attr, int channel)
>> +{
>> + if (!hwmon->xe->info.has_fan_control || channel >= hwmon->num_fans)
>> + return 0;
>> +
>> + switch (attr) {
>> + case hwmon_pwm_input:
>> + return 0644;
>> + default:
>> + return 0;
>> + }
>> +}
>> +
>> static int
>> xe_hwmon_fan_input_read(struct xe_hwmon *hwmon, int channel, long
>> *val)
>> {
>> @@ -1478,6 +1645,62 @@ xe_hwmon_fan_read(struct xe_hwmon *hwmon, u32
>> attr, int channel, long *val)
>> }
>> }
>> +static int
>> +xe_hwmon_pwm_read(struct xe_hwmon *hwmon, u32 attr, int channel,
>> long *val)
>> +{
>> + struct xe_hwmon_fan_info *fi;
>> + int ret = 0;
>> +
>> + if (channel < 0 || channel >= FAN_MAX)
>> + return -EINVAL;
>> +
>> + fi = &hwmon->fi[channel];
>> +
>> + switch (attr) {
>> + case hwmon_pwm_input:
>> + /* Check if user fan table is set else activate it.*/
>> + if (!fi->fan_table[USER_FAN_TABLE].fan_control_point_count) {
>> + xe_dbg(hwmon->xe, "fan %d user table not set, activating
>> it\n", channel);
>> + ret = xe_hwmon_activate_user_fan_table(hwmon, channel,
>> false,
>> + STOCK_FAN_TABLE);
>> + if (ret)
>> + return ret;
>> + }
>> + *val =
>> DIV_ROUND_CLOSEST(fi->fan_table[USER_FAN_TABLE].fcp[0].speed *
>> + U8_MAX, 100);
>> + return 0;
>> + default:
>> + return -EOPNOTSUPP;
>> + }
>> +}
>> +
>> +static int
>> +xe_hwmon_pwm_write(struct xe_hwmon *hwmon, u32 attr, int channel,
>> long val)
>> +{
>> + int ret = 0;
>> +
>> + if (channel < 0 || channel >= FAN_MAX)
>> + return -EINVAL;
>> +
>> + mutex_lock(&hwmon->hwmon_lock);
>> +
>> + switch (attr) {
>> + case hwmon_pwm_input:
>> + if (val < 0 || val > U8_MAX) {
>> + ret = -EINVAL;
>> + break;
>> + }
>> + ret = xe_hwmon_set_user_fan_pwm(hwmon, channel, (u8)val);
>> + break;
>> + default:
>> + ret = -EOPNOTSUPP;
>> + break;
>> + }
>> + mutex_unlock(&hwmon->hwmon_lock);
>> +
>> + return ret;
>> +}
>> +
>> static umode_t
>> xe_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
>> u32 attr, int channel)
>> @@ -1504,6 +1727,9 @@ xe_hwmon_is_visible(const void *drvdata, enum
>> hwmon_sensor_types type,
>> case hwmon_fan:
>> ret = xe_hwmon_fan_is_visible(hwmon, attr, channel);
>> break;
>> + case hwmon_pwm:
>> + ret = xe_hwmon_pwm_is_visible(hwmon, attr, channel);
>> + break;
>> default:
>> ret = 0;
>> break;
>> @@ -1533,6 +1759,8 @@ xe_hwmon_read(struct device *dev, enum
>> hwmon_sensor_types type, u32 attr,
>> return xe_hwmon_energy_read(hwmon, attr, channel, val);
>> case hwmon_fan:
>> return xe_hwmon_fan_read(hwmon, attr, channel, val);
>> + case hwmon_pwm:
>> + return xe_hwmon_pwm_read(hwmon, attr, channel, val);
>> default:
>> return -EOPNOTSUPP;
>> }
>> @@ -1551,6 +1779,8 @@ xe_hwmon_write(struct device *dev, enum
>> hwmon_sensor_types type, u32 attr,
>> return xe_hwmon_power_write(hwmon, attr, channel, val);
>> case hwmon_curr:
>> return xe_hwmon_curr_write(hwmon, attr, channel, val);
>> + case hwmon_pwm:
>> + return xe_hwmon_pwm_write(hwmon, attr, channel, val);
>> default:
>> return -EOPNOTSUPP;
>> }
>> diff --git a/drivers/gpu/drm/xe/xe_pcode_api.h
>> b/drivers/gpu/drm/xe/xe_pcode_api.h
>> index 419ab4f416fa..5c5fdc650a97 100644
>> --- a/drivers/gpu/drm/xe/xe_pcode_api.h
>> +++ b/drivers/gpu/drm/xe/xe_pcode_api.h
>> @@ -84,9 +84,12 @@
>> #define PCODE_MBOX_DOMAIN_HBM 0x2
>> #define FAN_SPEED_CONTROL 0x7D
>> +#define FSC_WRITE_NUM_FAN_CONTROL_POINTS 0x0
>> +#define FSC_WRITE_FAN_TABLE 0x1
>> #define FSC_READ_MAX_FAN_RPS 0x3
>> #define FSC_READ_NUM_FANS 0x4
>> #define FSC_READ_STOCK_FAN_CONTROL_POINTS 0x5
>> +#define FSC_READ_USER_FAN_CONTROL_POINTS 0x6
>> #define FSC_READ_FAN_TABLE 0x7
>> #define FAN_CONTROL_POINT_TEMP_MASK REG_GENMASK(7, 0)
>> #define FAN_CONTROL_POINT_SPEED_MASK REG_GENMASK(15, 8)
[-- Attachment #2: Type: text/html, Size: 30162 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
end of thread, other threads:[~2026-08-11 13:58 UTC | newest]
Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-17 4:17 [PATCH v2 0/9] Add fan control support Karthik Poosa
2026-07-17 4:16 ` ✓ CI.KUnit: success for Add fan control support (rev2) Patchwork
2026-07-17 4:17 ` [PATCH v2 1/9] drm/xe/pcode: Introduce xe_pcode_read_timeout() API Karthik Poosa
2026-07-17 4:17 ` [PATCH v2 2/9] drm/xe/hwmon: initialize fan-control backend and table cache Karthik Poosa
2026-08-10 6:03 ` Nilawar, Badal
2026-08-11 13:11 ` Nilawar, Badal
2026-07-17 4:17 ` [PATCH v2 3/9] drm/xe/hwmon: expose fanN_max Karthik Poosa
2026-07-23 5:34 ` Purkait, Soham
2026-07-17 4:17 ` [PATCH v2 4/9] drm/xe/hwmon: expose pwm[1-3] Karthik Poosa
2026-07-24 7:02 ` Purkait, Soham
2026-08-11 13:58 ` Poosa, Karthik
2026-07-17 4:17 ` [PATCH v2 5/9] drm/xe/hwmon: expose pwm[1-3]_enable Karthik Poosa
2026-07-17 4:17 ` [PATCH v2 6/9] drm/xe/hwmon: Enable fan curve control support Karthik Poosa
2026-07-22 18:16 ` Purkait, Soham
2026-07-17 4:17 ` [PATCH v2 7/9] drm/xe/hwmon: Add kernel-doc for fan control Karthik Poosa
2026-07-17 4:17 ` [PATCH v2 8/9] drm/xe/hwmon: preserve fan user table across suspend resume Karthik Poosa
2026-08-10 5:53 ` Nilawar, Badal
2026-07-17 4:17 ` [PATCH v2 9/9] drm/xe/hwmon: Update fan info after late binding Karthik Poosa
2026-07-20 6:20 ` Purkait, Soham
2026-07-17 5:00 ` ✓ Xe.CI.BAT: success for Add fan control support (rev2) Patchwork
2026-07-17 8:18 ` ✗ Xe.CI.FULL: failure " Patchwork
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox