|
7 | 7 |
|
8 | 8 | #include <linux/cleanup.h>
|
9 | 9 | #include <linux/delay.h>
|
| 10 | +#include <linux/jiffies.h> |
10 | 11 | #include <linux/ktime.h>
|
| 12 | +#include <linux/wait_bit.h> |
11 | 13 |
|
12 | 14 | #include <drm/drm_managed.h>
|
13 | 15 | #include <drm/drm_print.h>
|
|
53 | 55 | #define LNL_MERT_FREQ_CAP 800
|
54 | 56 | #define BMG_MERT_FREQ_CAP 2133
|
55 | 57 | #define BMG_MIN_FREQ 1200
|
| 58 | +#define BMG_MERT_FLUSH_FREQ_CAP 2600 |
56 | 59 |
|
57 | 60 | #define SLPC_RESET_TIMEOUT_MS 5 /* roughly 5ms, but no need for precision */
|
58 | 61 | #define SLPC_RESET_EXTENDED_TIMEOUT_MS 1000 /* To be used only at pc_start */
|
| 62 | +#define SLPC_ACT_FREQ_TIMEOUT_MS 100 |
59 | 63 |
|
60 | 64 | /**
|
61 | 65 | * DOC: GuC Power Conservation (PC)
|
@@ -143,6 +147,36 @@ static int wait_for_pc_state(struct xe_guc_pc *pc,
|
143 | 147 | return -ETIMEDOUT;
|
144 | 148 | }
|
145 | 149 |
|
| 150 | +static int wait_for_flush_complete(struct xe_guc_pc *pc) |
| 151 | +{ |
| 152 | + const unsigned long timeout = msecs_to_jiffies(30); |
| 153 | + |
| 154 | + if (!wait_var_event_timeout(&pc->flush_freq_limit, |
| 155 | + !atomic_read(&pc->flush_freq_limit), |
| 156 | + timeout)) |
| 157 | + return -ETIMEDOUT; |
| 158 | + |
| 159 | + return 0; |
| 160 | +} |
| 161 | + |
| 162 | +static int wait_for_act_freq_limit(struct xe_guc_pc *pc, u32 freq) |
| 163 | +{ |
| 164 | + int timeout_us = SLPC_ACT_FREQ_TIMEOUT_MS * USEC_PER_MSEC; |
| 165 | + int slept, wait = 10; |
| 166 | + |
| 167 | + for (slept = 0; slept < timeout_us;) { |
| 168 | + if (xe_guc_pc_get_act_freq(pc) <= freq) |
| 169 | + return 0; |
| 170 | + |
| 171 | + usleep_range(wait, wait << 1); |
| 172 | + slept += wait; |
| 173 | + wait <<= 1; |
| 174 | + if (slept + wait > timeout_us) |
| 175 | + wait = timeout_us - slept; |
| 176 | + } |
| 177 | + |
| 178 | + return -ETIMEDOUT; |
| 179 | +} |
146 | 180 | static int pc_action_reset(struct xe_guc_pc *pc)
|
147 | 181 | {
|
148 | 182 | struct xe_guc_ct *ct = pc_to_ct(pc);
|
@@ -688,6 +722,11 @@ static int xe_guc_pc_set_max_freq_locked(struct xe_guc_pc *pc, u32 freq)
|
688 | 722 | */
|
689 | 723 | int xe_guc_pc_set_max_freq(struct xe_guc_pc *pc, u32 freq)
|
690 | 724 | {
|
| 725 | + if (XE_WA(pc_to_gt(pc), 22019338487)) { |
| 726 | + if (wait_for_flush_complete(pc) != 0) |
| 727 | + return -EAGAIN; |
| 728 | + } |
| 729 | + |
691 | 730 | guard(mutex)(&pc->freq_lock);
|
692 | 731 |
|
693 | 732 | return xe_guc_pc_set_max_freq_locked(pc, freq);
|
@@ -888,6 +927,92 @@ static int pc_adjust_requested_freq(struct xe_guc_pc *pc)
|
888 | 927 | return ret;
|
889 | 928 | }
|
890 | 929 |
|
| 930 | +static bool needs_flush_freq_limit(struct xe_guc_pc *pc) |
| 931 | +{ |
| 932 | + struct xe_gt *gt = pc_to_gt(pc); |
| 933 | + |
| 934 | + return XE_WA(gt, 22019338487) && |
| 935 | + pc->rp0_freq > BMG_MERT_FLUSH_FREQ_CAP; |
| 936 | +} |
| 937 | + |
| 938 | +/** |
| 939 | + * xe_guc_pc_apply_flush_freq_limit() - Limit max GT freq during L2 flush |
| 940 | + * @pc: the xe_guc_pc object |
| 941 | + * |
| 942 | + * As per the WA, reduce max GT frequency during L2 cache flush |
| 943 | + */ |
| 944 | +void xe_guc_pc_apply_flush_freq_limit(struct xe_guc_pc *pc) |
| 945 | +{ |
| 946 | + struct xe_gt *gt = pc_to_gt(pc); |
| 947 | + u32 max_freq; |
| 948 | + int ret; |
| 949 | + |
| 950 | + if (!needs_flush_freq_limit(pc)) |
| 951 | + return; |
| 952 | + |
| 953 | + guard(mutex)(&pc->freq_lock); |
| 954 | + |
| 955 | + ret = xe_guc_pc_get_max_freq_locked(pc, &max_freq); |
| 956 | + if (!ret && max_freq > BMG_MERT_FLUSH_FREQ_CAP) { |
| 957 | + ret = pc_set_max_freq(pc, BMG_MERT_FLUSH_FREQ_CAP); |
| 958 | + if (ret) { |
| 959 | + xe_gt_err_once(gt, "Failed to cap max freq on flush to %u, %pe\n", |
| 960 | + BMG_MERT_FLUSH_FREQ_CAP, ERR_PTR(ret)); |
| 961 | + return; |
| 962 | + } |
| 963 | + |
| 964 | + atomic_set(&pc->flush_freq_limit, 1); |
| 965 | + |
| 966 | + /* |
| 967 | + * If user has previously changed max freq, stash that value to |
| 968 | + * restore later, otherwise use the current max. New user |
| 969 | + * requests wait on flush. |
| 970 | + */ |
| 971 | + if (pc->user_requested_max != 0) |
| 972 | + pc->stashed_max_freq = pc->user_requested_max; |
| 973 | + else |
| 974 | + pc->stashed_max_freq = max_freq; |
| 975 | + } |
| 976 | + |
| 977 | + /* |
| 978 | + * Wait for actual freq to go below the flush cap: even if the previous |
| 979 | + * max was below cap, the current one might still be above it |
| 980 | + */ |
| 981 | + ret = wait_for_act_freq_limit(pc, BMG_MERT_FLUSH_FREQ_CAP); |
| 982 | + if (ret) |
| 983 | + xe_gt_err_once(gt, "Actual freq did not reduce to %u, %pe\n", |
| 984 | + BMG_MERT_FLUSH_FREQ_CAP, ERR_PTR(ret)); |
| 985 | +} |
| 986 | + |
| 987 | +/** |
| 988 | + * xe_guc_pc_remove_flush_freq_limit() - Remove max GT freq limit after L2 flush completes. |
| 989 | + * @pc: the xe_guc_pc object |
| 990 | + * |
| 991 | + * Retrieve the previous GT max frequency value. |
| 992 | + */ |
| 993 | +void xe_guc_pc_remove_flush_freq_limit(struct xe_guc_pc *pc) |
| 994 | +{ |
| 995 | + struct xe_gt *gt = pc_to_gt(pc); |
| 996 | + int ret = 0; |
| 997 | + |
| 998 | + if (!needs_flush_freq_limit(pc)) |
| 999 | + return; |
| 1000 | + |
| 1001 | + if (!atomic_read(&pc->flush_freq_limit)) |
| 1002 | + return; |
| 1003 | + |
| 1004 | + mutex_lock(&pc->freq_lock); |
| 1005 | + |
| 1006 | + ret = pc_set_max_freq(>->uc.guc.pc, pc->stashed_max_freq); |
| 1007 | + if (ret) |
| 1008 | + xe_gt_err_once(gt, "Failed to restore max freq %u:%d", |
| 1009 | + pc->stashed_max_freq, ret); |
| 1010 | + |
| 1011 | + atomic_set(&pc->flush_freq_limit, 0); |
| 1012 | + mutex_unlock(&pc->freq_lock); |
| 1013 | + wake_up_var(&pc->flush_freq_limit); |
| 1014 | +} |
| 1015 | + |
891 | 1016 | static int pc_set_mert_freq_cap(struct xe_guc_pc *pc)
|
892 | 1017 | {
|
893 | 1018 | int ret;
|
|
0 commit comments