apmt.c 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * ARM APMT table support.
  4. * Design document number: ARM DEN0117.
  5. *
  6. * Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES.
  7. *
  8. */
  9. #define pr_fmt(fmt) "ACPI: APMT: " fmt
  10. #include <linux/acpi.h>
  11. #include <linux/init.h>
  12. #include <linux/kernel.h>
  13. #include <linux/platform_device.h>
  14. #include "init.h"
  15. #define DEV_NAME "arm-cs-arch-pmu"
  16. /* There can be up to 3 resources: page 0 and 1 address, and interrupt. */
  17. #define DEV_MAX_RESOURCE_COUNT 3
  18. /* Root pointer to the mapped APMT table */
  19. static struct acpi_table_header *apmt_table;
  20. static int __init apmt_init_resources(struct resource *res,
  21. struct acpi_apmt_node *node)
  22. {
  23. int irq, trigger;
  24. int num_res = 0;
  25. res[num_res].start = node->base_address0;
  26. res[num_res].end = node->base_address0 + SZ_4K - 1;
  27. res[num_res].flags = IORESOURCE_MEM;
  28. num_res++;
  29. if (node->flags & ACPI_APMT_FLAGS_DUAL_PAGE) {
  30. res[num_res].start = node->base_address1;
  31. res[num_res].end = node->base_address1 + SZ_4K - 1;
  32. res[num_res].flags = IORESOURCE_MEM;
  33. num_res++;
  34. }
  35. if (node->ovflw_irq != 0) {
  36. trigger = (node->ovflw_irq_flags & ACPI_APMT_OVFLW_IRQ_FLAGS_MODE);
  37. trigger = (trigger == ACPI_APMT_OVFLW_IRQ_FLAGS_MODE_LEVEL) ?
  38. ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE;
  39. irq = acpi_register_gsi(NULL, node->ovflw_irq, trigger,
  40. ACPI_ACTIVE_HIGH);
  41. if (irq <= 0) {
  42. pr_warn("APMT could not register gsi hwirq %d\n", irq);
  43. return num_res;
  44. }
  45. res[num_res].start = irq;
  46. res[num_res].end = irq;
  47. res[num_res].flags = IORESOURCE_IRQ;
  48. num_res++;
  49. }
  50. return num_res;
  51. }
  52. /**
  53. * apmt_add_platform_device() - Allocate a platform device for APMT node
  54. * @node: Pointer to device ACPI APMT node
  55. * @fwnode: fwnode associated with the APMT node
  56. *
  57. * Returns: 0 on success, <0 failure
  58. */
  59. static int __init apmt_add_platform_device(struct acpi_apmt_node *node,
  60. struct fwnode_handle *fwnode)
  61. {
  62. struct platform_device *pdev;
  63. int ret, count;
  64. struct resource res[DEV_MAX_RESOURCE_COUNT];
  65. pdev = platform_device_alloc(DEV_NAME, PLATFORM_DEVID_AUTO);
  66. if (!pdev)
  67. return -ENOMEM;
  68. memset(res, 0, sizeof(res));
  69. count = apmt_init_resources(res, node);
  70. ret = platform_device_add_resources(pdev, res, count);
  71. if (ret)
  72. goto dev_put;
  73. /*
  74. * Add a copy of APMT node pointer to platform_data to be used to
  75. * retrieve APMT data information.
  76. */
  77. ret = platform_device_add_data(pdev, &node, sizeof(node));
  78. if (ret)
  79. goto dev_put;
  80. pdev->dev.fwnode = fwnode;
  81. ret = platform_device_add(pdev);
  82. if (ret)
  83. goto dev_put;
  84. return 0;
  85. dev_put:
  86. platform_device_put(pdev);
  87. return ret;
  88. }
  89. static int __init apmt_init_platform_devices(void)
  90. {
  91. struct acpi_apmt_node *apmt_node;
  92. struct acpi_table_apmt *apmt;
  93. struct fwnode_handle *fwnode;
  94. u64 offset, end;
  95. int ret;
  96. /*
  97. * apmt_table and apmt both point to the start of APMT table, but
  98. * have different struct types
  99. */
  100. apmt = (struct acpi_table_apmt *)apmt_table;
  101. offset = sizeof(*apmt);
  102. end = apmt->header.length;
  103. while (offset < end) {
  104. apmt_node = ACPI_ADD_PTR(struct acpi_apmt_node, apmt,
  105. offset);
  106. fwnode = acpi_alloc_fwnode_static();
  107. if (!fwnode)
  108. return -ENOMEM;
  109. ret = apmt_add_platform_device(apmt_node, fwnode);
  110. if (ret) {
  111. acpi_free_fwnode_static(fwnode);
  112. return ret;
  113. }
  114. offset += apmt_node->length;
  115. }
  116. return 0;
  117. }
  118. void __init acpi_apmt_init(void)
  119. {
  120. acpi_status status;
  121. int ret;
  122. /**
  123. * APMT table nodes will be used at runtime after the apmt init,
  124. * so we don't need to call acpi_put_table() to release
  125. * the APMT table mapping.
  126. */
  127. status = acpi_get_table(ACPI_SIG_APMT, 0, &apmt_table);
  128. if (ACPI_FAILURE(status)) {
  129. if (status != AE_NOT_FOUND) {
  130. const char *msg = acpi_format_exception(status);
  131. pr_err("Failed to get APMT table, %s\n", msg);
  132. }
  133. return;
  134. }
  135. ret = apmt_init_platform_devices();
  136. if (ret) {
  137. pr_err("Failed to initialize APMT platform devices, ret: %d\n", ret);
  138. acpi_put_table(apmt_table);
  139. }
  140. }