| @@ -34,62 +34,6 @@ struct pci_dev *amd_node_get_func(u16 node, u8 func) · 33 unmodified lines |
| 34 | return pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(AMD_NODE0_PCI_SLOT + node, func)); | 34 | return pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(AMD_NODE0_PCI_SLOT + node, func)); |
| 35 | } | 35 | } |
| 36 | | 36 | |
| 37 | #define DF_BLK_INST_CNT 0x040 | | |
| 38 | #define DF_CFG_ADDR_CNTL_LEGACY 0x084 | | |
| 39 | #define DF_CFG_ADDR_CNTL_DF4 0xC04 | | |
| 40 | | | |
| 41 | #define DF_MAJOR_REVISION GENMASK(27, 24) | | |
| 42 | | | |
| 43 | static u16 get_cfg_addr_cntl_offset(struct pci_dev *df_f0) | | |
| 44 | { | | |
| 45 | u32 reg; | | |
| 46 | | | |
| 47 | /* | | |
| 48 | * Revision fields added for DF4 and later. | | |
| 49 | * | | |
| 50 | * Major revision of '0' is found pre-DF4. Field is Read-as-Zero. | | |
| 51 | */ | | |
| 52 | if (pci_read_config_dword(df_f0, DF_BLK_INST_CNT, ®)) | | |
| 53 | return 0; | | |
| 54 | | | |
| 55 | if (reg & DF_MAJOR_REVISION) | | |
| 56 | return DF_CFG_ADDR_CNTL_DF4; | | |
| 57 | | | |
| 58 | return DF_CFG_ADDR_CNTL_LEGACY; | | |
| 59 | } | | |
| 60 | | | |
| 61 | struct pci_dev *amd_node_get_root(u16 node) | | |
| 62 | { | | |
| 63 | struct pci_dev *root; | | |
| 64 | u16 cntl_off; | | |
| 65 | u8 bus; | | |
| 66 | | | |
| 67 | if (!cpu_feature_enabled(X86_FEATURE_ZEN)) | | |
| 68 | return NULL; | | |
| 69 | | | |
| 70 | /* | | |
| 71 | * D18F0xXXX [Config Address Control] (DF::CfgAddressCntl) | | |
| 72 | * Bits [7:0] (SecBusNum) holds the bus number of the root device for | | |
| 73 | * this Data Fabric instance. The segment, device, and function will be 0. | | |
| 74 | */ | | |
| 75 | struct pci_dev *df_f0 __free(pci_dev_put) = amd_node_get_func(node, 0); | | |
| 76 | if (!df_f0) | | |
| 77 | return NULL; | | |
| 78 | | | |
| 79 | cntl_off = get_cfg_addr_cntl_offset(df_f0); | | |
| 80 | if (!cntl_off) | | |
| 81 | return NULL; | | |
| 82 | | | |
| 83 | if (pci_read_config_byte(df_f0, cntl_off, &bus)) | | |
| 84 | return NULL; | | |
| 85 | | | |
| 86 | /* Grab the pointer for the actual root device instance. */ | | |
| 87 | root = pci_get_domain_bus_and_slot(0, bus, 0); | | |
| 88 | | | |
| 89 | pci_dbg(root, "is root for AMD node %u\n", node); | | |
| 90 | return root; | | |
| 91 | } | | |
| 92 | | | |
| 93 | static struct pci_dev **amd_roots; | 37 | static struct pci_dev **amd_roots; |
| 94 | | 38 | |
| 95 | /* Protect the PCI config register pairs used for SMN. */ | 39 | /* Protect the PCI config register pairs used for SMN. */ |
| @@ -274,51 +218,21 @@ DEFINE_SHOW_STORE_ATTRIBUTE(smn_node); · 178 unmodified lines |
| 274 | DEFINE_SHOW_STORE_ATTRIBUTE(smn_address); | 218 | DEFINE_SHOW_STORE_ATTRIBUTE(smn_address); |
| 275 | DEFINE_SHOW_STORE_ATTRIBUTE(smn_value); | 219 | DEFINE_SHOW_STORE_ATTRIBUTE(smn_value); |
| 276 | | 220 | |
| 277 | static int amd_cache_roots(void) | 221 | static struct pci_dev *get_next_root(struct pci_dev *root) |
| 278 | { | | |
| 279 | u16 node, num_nodes = amd_num_nodes(); | | |
| 280 | | | |
| 281 | amd_roots = kcalloc(num_nodes, sizeof(*amd_roots), GFP_KERNEL); | | |
| 282 | if (!amd_roots) | | |
| 283 | return -ENOMEM; | | |
| 284 | | | |
| 285 | for (node = 0; node < num_nodes; node++) | | |
| 286 | amd_roots[node] = amd_node_get_root(node); | | |
| 287 | | | |
| 288 | return 0; | | |
| 289 | } | | |
| 290 | | | |
| 291 | static int reserve_root_config_spaces(void) | | |
| 292 | { | 222 | { |
| 293 | struct pci_dev *root = NULL; | 223 | while ((root = pci_get_class(PCI_CLASS_BRIDGE_HOST << 8, root))) { |
| 294 | struct pci_bus *bus = NULL; | 224 | /* Root device is Device 0 Function 0. */ |
| 295 | | 225 | if (root->devfn) |
| 296 | while ((bus = pci_find_next_bus(bus))) { | | |
| 297 | /* Root device is Device 0 Function 0 on each Primary Bus. */ | | |
| 298 | root = pci_get_slot(bus, 0); | | |
| 299 | if (!root) | | |
| 300 | continue; | 226 | continue; |
| 301 | | 227 | |
| 302 | if (root->vendor != PCI_VENDOR_ID_AMD && | 228 | if (root->vendor != PCI_VENDOR_ID_AMD && |
| 303 | root->vendor != PCI_VENDOR_ID_HYGON) | 229 | root->vendor != PCI_VENDOR_ID_HYGON) |
| 304 | continue; | 230 | continue; |
| 305 | | 231 | |
| 306 | pci_dbg(root, "Reserving PCI config space\n"); | 232 | break; |
| 307 | | | |
| 308 | /* | | |
| 309 | * There are a few SMN index/data pairs and other registers | | |
| 310 | * that shouldn't be accessed by user space. | | |
| 311 | * So reserve the entire PCI config space for simplicity rather | | |
| 312 | * than covering specific registers piecemeal. | | |
| 313 | */ | | |
| 314 | if (!pci_request_config_region_exclusive(root, 0, PCI_CFG_SPACE_SIZE, NULL)) { | | |
| 315 | pci_err(root, "Failed to reserve config space\n"); | | |
| 316 | return -EEXIST; | | |
| 317 | } | | |
| 318 | } | 233 | } |
| 319 | | 234 | |
| 320 | smn_exclusive = true; | 235 | return root; |
| 321 | return 0; | | |
| 322 | } | 236 | } |
| 323 | | 237 | |
| 324 | static bool enable_dfs; | 238 | static bool enable_dfs; |
| @@ -332,7 +246,8 @@ __setup("amd_smn_debugfs_enable", amd_smn_enable_dfs); · 7 unmodified lines |
| 332 | | 246 | |
| 333 | static int __init amd_smn_init(void) | 247 | static int __init amd_smn_init(void) |
| 334 | { | 248 | { |
| 335 | int err; | 249 | u16 count, num_roots, roots_per_node, node, num_nodes; |
| | 250 | struct pci_dev *root; |
| 336 | | 251 | |
| 337 | if (!cpu_feature_enabled(X86_FEATURE_ZEN)) | 252 | if (!cpu_feature_enabled(X86_FEATURE_ZEN)) |
| 338 | return 0; | 253 | return 0; |
| @@ -342,13 +257,48 @@ static int __init amd_smn_init(void) · 3 unmodified lines |
| 342 | if (amd_roots) | 257 | if (amd_roots) |
| 343 | return 0; | 258 | return 0; |
| 344 | | 259 | |
| 345 | err = amd_cache_roots(); | 260 | num_roots = 0; |
| 346 | if (err) | 261 | root = NULL; |
| 347 | return err; | 262 | while ((root = get_next_root(root))) { |
| | 263 | pci_dbg(root, "Reserving PCI config space\n"); |
| 348 | | 264 | |
| 349 | err = reserve_root_config_spaces(); | 265 | /* |
| 350 | if (err) | 266 | * There are a few SMN index/data pairs and other registers |
| 351 | return err; | 267 | * that shouldn't be accessed by user space. So reserve the |
| | 268 | * entire PCI config space for simplicity rather than covering |
| | 269 | * specific registers piecemeal. |
| | 270 | */ |
| | 271 | if (!pci_request_config_region_exclusive(root, 0, PCI_CFG_SPACE_SIZE, NULL)) { |
| | 272 | pci_err(root, "Failed to reserve config space\n"); |
| | 273 | return -EEXIST; |
| | 274 | } |
| | 275 | |
| | 276 | num_roots++; |
| | 277 | } |
| | 278 | |
| | 279 | pr_debug("Found %d AMD root devices\n", num_roots); |
| | 280 | |
| | 281 | if (!num_roots) |
| | 282 | return -ENODEV; |
| | 283 | |
| | 284 | num_nodes = amd_num_nodes(); |
| | 285 | amd_roots = kcalloc(num_nodes, sizeof(*amd_roots), GFP_KERNEL); |
| | 286 | if (!amd_roots) |
| | 287 | return -ENOMEM; |
| | 288 | |
| | 289 | roots_per_node = num_roots / num_nodes; |
| | 290 | |
| | 291 | count = 0; |
| | 292 | node = 0; |
| | 293 | root = NULL; |
| | 294 | while (node < num_nodes && (root = get_next_root(root))) { |
| | 295 | /* Use one root for each node and skip the rest. */ |
| | 296 | if (count++ % roots_per_node) |
| | 297 | continue; |
| | 298 | |
| | 299 | pci_dbg(root, "is root for AMD node %u\n", node); |
| | 300 | amd_roots[node++] = root; |
| | 301 | } |
| 352 | | 302 | |
| 353 | if (enable_dfs) { | 303 | if (enable_dfs) { |
| 354 | debugfs_dir = debugfs_create_dir("amd_smn", arch_debugfs_dir); | 304 | debugfs_dir = debugfs_create_dir("amd_smn", arch_debugfs_dir); |
| @@ -358,6 +308,8 @@ static int __init amd_smn_init(void) · 3 unmodified lines |
| 358 | debugfs_create_file("value", 0600, debugfs_dir, NULL, &smn_value_fops); | 308 | debugfs_create_file("value", 0600, debugfs_dir, NULL, &smn_value_fops); |
| 359 | } | 309 | } |
| 360 | | 310 | |
| | 311 | smn_exclusive = true; |
| | 312 | |
| 361 | return 0; | 313 | return 0; |
| 362 | } | 314 | } |
| 363 | | 315 | |