Merge tag 'trace-printf-v6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/trace...
[drm/drm-misc.git] / drivers / ras / amd / atl / core.c
blob4197e10993acaaa0a5097c7f21796ccde418020c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * AMD Address Translation Library
5 * core.c : Module init and base translation functions
7 * Copyright (c) 2023, Advanced Micro Devices, Inc.
8 * All Rights Reserved.
10 * Author: Yazen Ghannam <Yazen.Ghannam@amd.com>
13 #include <linux/module.h>
14 #include <asm/cpu_device_id.h>
16 #include "internal.h"
18 struct df_config df_cfg __read_mostly;
20 static int addr_over_limit(struct addr_ctx *ctx)
22 u64 dram_limit_addr;
24 if (df_cfg.rev >= DF4)
25 dram_limit_addr = FIELD_GET(DF4_DRAM_LIMIT_ADDR, ctx->map.limit);
26 else
27 dram_limit_addr = FIELD_GET(DF2_DRAM_LIMIT_ADDR, ctx->map.limit);
29 dram_limit_addr <<= DF_DRAM_BASE_LIMIT_LSB;
30 dram_limit_addr |= GENMASK(DF_DRAM_BASE_LIMIT_LSB - 1, 0);
32 /* Is calculated system address above DRAM limit address? */
33 if (ctx->ret_addr > dram_limit_addr) {
34 atl_debug(ctx, "Calculated address (0x%016llx) > DRAM limit (0x%016llx)",
35 ctx->ret_addr, dram_limit_addr);
36 return -EINVAL;
39 return 0;
42 static bool legacy_hole_en(struct addr_ctx *ctx)
44 u32 reg = ctx->map.base;
46 if (df_cfg.rev >= DF4)
47 reg = ctx->map.ctl;
49 return FIELD_GET(DF_LEGACY_MMIO_HOLE_EN, reg);
52 static u64 add_legacy_hole(struct addr_ctx *ctx, u64 addr)
54 if (!legacy_hole_en(ctx))
55 return addr;
57 if (addr >= df_cfg.dram_hole_base)
58 addr += (BIT_ULL(32) - df_cfg.dram_hole_base);
60 return addr;
63 static u64 remove_legacy_hole(struct addr_ctx *ctx, u64 addr)
65 if (!legacy_hole_en(ctx))
66 return addr;
68 if (addr >= df_cfg.dram_hole_base)
69 addr -= (BIT_ULL(32) - df_cfg.dram_hole_base);
71 return addr;
74 static u64 get_base_addr(struct addr_ctx *ctx)
76 u64 base_addr;
78 if (df_cfg.rev >= DF4)
79 base_addr = FIELD_GET(DF4_BASE_ADDR, ctx->map.base);
80 else
81 base_addr = FIELD_GET(DF2_BASE_ADDR, ctx->map.base);
83 return base_addr << DF_DRAM_BASE_LIMIT_LSB;
86 u64 add_base_and_hole(struct addr_ctx *ctx, u64 addr)
88 return add_legacy_hole(ctx, addr + get_base_addr(ctx));
91 u64 remove_base_and_hole(struct addr_ctx *ctx, u64 addr)
93 return remove_legacy_hole(ctx, addr) - get_base_addr(ctx);
96 static bool late_hole_remove(struct addr_ctx *ctx)
98 if (df_cfg.rev == DF3p5)
99 return true;
101 if (df_cfg.rev == DF4)
102 return true;
104 if (ctx->map.intlv_mode == DF3_6CHAN)
105 return true;
107 return false;
110 unsigned long norm_to_sys_addr(u8 socket_id, u8 die_id, u8 coh_st_inst_id, unsigned long addr)
112 struct addr_ctx ctx;
114 if (df_cfg.rev == UNKNOWN)
115 return -EINVAL;
117 memset(&ctx, 0, sizeof(ctx));
119 /* Start from the normalized address */
120 ctx.ret_addr = addr;
121 ctx.inst_id = coh_st_inst_id;
123 ctx.inputs.norm_addr = addr;
124 ctx.inputs.socket_id = socket_id;
125 ctx.inputs.die_id = die_id;
126 ctx.inputs.coh_st_inst_id = coh_st_inst_id;
128 if (legacy_hole_en(&ctx) && !df_cfg.dram_hole_base)
129 return -EINVAL;
131 if (determine_node_id(&ctx, socket_id, die_id))
132 return -EINVAL;
134 if (get_address_map(&ctx))
135 return -EINVAL;
137 if (denormalize_address(&ctx))
138 return -EINVAL;
140 if (!late_hole_remove(&ctx))
141 ctx.ret_addr = add_base_and_hole(&ctx, ctx.ret_addr);
143 if (dehash_address(&ctx))
144 return -EINVAL;
146 if (late_hole_remove(&ctx))
147 ctx.ret_addr = add_base_and_hole(&ctx, ctx.ret_addr);
149 if (addr_over_limit(&ctx))
150 return -EINVAL;
152 return ctx.ret_addr;
155 static void check_for_legacy_df_access(void)
158 * All Zen-based systems before Family 19h use the legacy
159 * DF Indirect Access (FICAA/FICAD) offsets.
161 if (boot_cpu_data.x86 < 0x19) {
162 df_cfg.flags.legacy_ficaa = true;
163 return;
166 /* All systems after Family 19h use the current offsets. */
167 if (boot_cpu_data.x86 > 0x19)
168 return;
170 /* Some Family 19h systems use the legacy offsets. */
171 switch (boot_cpu_data.x86_model) {
172 case 0x00 ... 0x0f:
173 case 0x20 ... 0x5f:
174 df_cfg.flags.legacy_ficaa = true;
179 * This library provides functionality for AMD-based systems with a Data Fabric.
180 * The set of systems with a Data Fabric is equivalent to the set of Zen-based systems
181 * and the set of systems with the Scalable MCA feature at this time. However, these
182 * are technically independent things.
184 * It's possible to match on the PCI IDs of the Data Fabric devices, but this will be
185 * an ever expanding list. Instead, match on the SMCA and Zen features to cover all
186 * relevant systems.
188 static const struct x86_cpu_id amd_atl_cpuids[] = {
189 X86_MATCH_FEATURE(X86_FEATURE_SMCA, NULL),
190 X86_MATCH_FEATURE(X86_FEATURE_ZEN, NULL),
193 MODULE_DEVICE_TABLE(x86cpu, amd_atl_cpuids);
195 static int __init amd_atl_init(void)
197 if (!x86_match_cpu(amd_atl_cpuids))
198 return -ENODEV;
200 if (!amd_nb_num())
201 return -ENODEV;
203 check_for_legacy_df_access();
205 if (get_df_system_info())
206 return -ENODEV;
208 /* Increment this module's recount so that it can't be easily unloaded. */
209 __module_get(THIS_MODULE);
210 amd_atl_register_decoder(convert_umc_mca_addr_to_sys_addr);
212 pr_info("AMD Address Translation Library initialized\n");
213 return 0;
217 * Exit function is only needed for testing and debug. Module unload must be
218 * forced to override refcount check.
220 static void __exit amd_atl_exit(void)
222 amd_atl_unregister_decoder();
225 module_init(amd_atl_init);
226 module_exit(amd_atl_exit);
228 MODULE_DESCRIPTION("AMD Address Translation Library");
229 MODULE_LICENSE("GPL");