access.c 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * AMD Address Translation Library
  4. *
  5. * access.c : DF Indirect Access functions
  6. *
  7. * Copyright (c) 2023, Advanced Micro Devices, Inc.
  8. * All Rights Reserved.
  9. *
  10. * Author: Yazen Ghannam <Yazen.Ghannam@amd.com>
  11. */
  12. #include "internal.h"
  13. /* Protect the PCI config register pairs used for DF indirect access. */
  14. static DEFINE_MUTEX(df_indirect_mutex);
  15. /*
  16. * Data Fabric Indirect Access uses FICAA/FICAD.
  17. *
  18. * Fabric Indirect Configuration Access Address (FICAA): constructed based
  19. * on the device's Instance Id and the PCI function and register offset of
  20. * the desired register.
  21. *
  22. * Fabric Indirect Configuration Access Data (FICAD): there are FICAD
  23. * low and high registers but so far only the low register is needed.
  24. *
  25. * Use Instance Id 0xFF to indicate a broadcast read.
  26. */
  27. #define DF_BROADCAST 0xFF
  28. #define DF_FICAA_INST_EN BIT(0)
  29. #define DF_FICAA_REG_NUM GENMASK(10, 1)
  30. #define DF_FICAA_FUNC_NUM GENMASK(13, 11)
  31. #define DF_FICAA_INST_ID GENMASK(23, 16)
  32. #define DF_FICAA_REG_NUM_LEGACY GENMASK(10, 2)
  33. static u16 get_accessible_node(u16 node)
  34. {
  35. /*
  36. * On heterogeneous systems, not all AMD Nodes are accessible
  37. * through software-visible registers. The Node ID needs to be
  38. * adjusted for register accesses. But its value should not be
  39. * changed for the translation methods.
  40. */
  41. if (df_cfg.flags.heterogeneous) {
  42. /* Only Node 0 is accessible on DF3.5 systems. */
  43. if (df_cfg.rev == DF3p5)
  44. node = 0;
  45. /*
  46. * Only the first Node in each Socket is accessible on
  47. * DF4.5 systems, and this is visible to software as one
  48. * Fabric per Socket. The Socket ID can be derived from
  49. * the Node ID and global shift values.
  50. */
  51. if (df_cfg.rev == DF4p5)
  52. node >>= df_cfg.socket_id_shift - df_cfg.node_id_shift;
  53. }
  54. return node;
  55. }
  56. static int __df_indirect_read(u16 node, u8 func, u16 reg, u8 instance_id, u32 *lo)
  57. {
  58. u32 ficaa_addr = 0x8C, ficad_addr = 0xB8;
  59. struct pci_dev *F4;
  60. int err = -ENODEV;
  61. u32 ficaa = 0;
  62. node = get_accessible_node(node);
  63. if (node >= amd_nb_num()) {
  64. pr_debug("Node %u is out of bounds\n", node);
  65. goto out;
  66. }
  67. F4 = node_to_amd_nb(node)->link;
  68. if (!F4) {
  69. pr_debug("DF function 4 not found\n");
  70. goto out;
  71. }
  72. /* Enable instance-specific access. */
  73. if (instance_id != DF_BROADCAST) {
  74. ficaa |= FIELD_PREP(DF_FICAA_INST_EN, 1);
  75. ficaa |= FIELD_PREP(DF_FICAA_INST_ID, instance_id);
  76. }
  77. /*
  78. * The two least-significant bits are masked when inputing the
  79. * register offset to FICAA.
  80. */
  81. reg >>= 2;
  82. if (df_cfg.flags.legacy_ficaa) {
  83. ficaa_addr = 0x5C;
  84. ficad_addr = 0x98;
  85. ficaa |= FIELD_PREP(DF_FICAA_REG_NUM_LEGACY, reg);
  86. } else {
  87. ficaa |= FIELD_PREP(DF_FICAA_REG_NUM, reg);
  88. }
  89. ficaa |= FIELD_PREP(DF_FICAA_FUNC_NUM, func);
  90. mutex_lock(&df_indirect_mutex);
  91. err = pci_write_config_dword(F4, ficaa_addr, ficaa);
  92. if (err) {
  93. pr_warn("Error writing DF Indirect FICAA, FICAA=0x%x\n", ficaa);
  94. goto out_unlock;
  95. }
  96. err = pci_read_config_dword(F4, ficad_addr, lo);
  97. if (err)
  98. pr_warn("Error reading DF Indirect FICAD LO, FICAA=0x%x.\n", ficaa);
  99. pr_debug("node=%u inst=0x%x func=0x%x reg=0x%x val=0x%x",
  100. node, instance_id, func, reg << 2, *lo);
  101. out_unlock:
  102. mutex_unlock(&df_indirect_mutex);
  103. out:
  104. return err;
  105. }
  106. int df_indirect_read_instance(u16 node, u8 func, u16 reg, u8 instance_id, u32 *lo)
  107. {
  108. return __df_indirect_read(node, func, reg, instance_id, lo);
  109. }
  110. int df_indirect_read_broadcast(u16 node, u8 func, u16 reg, u32 *lo)
  111. {
  112. return __df_indirect_read(node, func, reg, DF_BROADCAST, lo);
  113. }