system.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * System Control and Management Interface (SCMI) System Power Protocol
  4. *
  5. * Copyright (C) 2020-2022 ARM Ltd.
  6. */
  7. #define pr_fmt(fmt) "SCMI Notifications SYSTEM - " fmt
  8. #include <linux/module.h>
  9. #include <linux/scmi_protocol.h>
  10. #include "protocols.h"
  11. #include "notify.h"
  12. #define SCMI_SYSTEM_NUM_SOURCES 1
  13. enum scmi_system_protocol_cmd {
  14. SYSTEM_POWER_STATE_NOTIFY = 0x5,
  15. };
  16. struct scmi_system_power_state_notify {
  17. __le32 notify_enable;
  18. };
  19. struct scmi_system_power_state_notifier_payld {
  20. __le32 agent_id;
  21. __le32 flags;
  22. __le32 system_state;
  23. __le32 timeout;
  24. };
  25. struct scmi_system_info {
  26. u32 version;
  27. bool graceful_timeout_supported;
  28. };
  29. static int scmi_system_request_notify(const struct scmi_protocol_handle *ph,
  30. bool enable)
  31. {
  32. int ret;
  33. struct scmi_xfer *t;
  34. struct scmi_system_power_state_notify *notify;
  35. ret = ph->xops->xfer_get_init(ph, SYSTEM_POWER_STATE_NOTIFY,
  36. sizeof(*notify), 0, &t);
  37. if (ret)
  38. return ret;
  39. notify = t->tx.buf;
  40. notify->notify_enable = enable ? cpu_to_le32(BIT(0)) : 0;
  41. ret = ph->xops->do_xfer(ph, t);
  42. ph->xops->xfer_put(ph, t);
  43. return ret;
  44. }
  45. static int scmi_system_set_notify_enabled(const struct scmi_protocol_handle *ph,
  46. u8 evt_id, u32 src_id, bool enable)
  47. {
  48. int ret;
  49. ret = scmi_system_request_notify(ph, enable);
  50. if (ret)
  51. pr_debug("FAIL_ENABLE - evt[%X] - ret:%d\n", evt_id, ret);
  52. return ret;
  53. }
  54. static void *
  55. scmi_system_fill_custom_report(const struct scmi_protocol_handle *ph,
  56. u8 evt_id, ktime_t timestamp,
  57. const void *payld, size_t payld_sz,
  58. void *report, u32 *src_id)
  59. {
  60. size_t expected_sz;
  61. const struct scmi_system_power_state_notifier_payld *p = payld;
  62. struct scmi_system_power_state_notifier_report *r = report;
  63. struct scmi_system_info *pinfo = ph->get_priv(ph);
  64. expected_sz = pinfo->graceful_timeout_supported ?
  65. sizeof(*p) : sizeof(*p) - sizeof(__le32);
  66. if (evt_id != SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER ||
  67. payld_sz != expected_sz)
  68. return NULL;
  69. r->timestamp = timestamp;
  70. r->agent_id = le32_to_cpu(p->agent_id);
  71. r->flags = le32_to_cpu(p->flags);
  72. r->system_state = le32_to_cpu(p->system_state);
  73. if (pinfo->graceful_timeout_supported &&
  74. r->system_state == SCMI_SYSTEM_SHUTDOWN &&
  75. SCMI_SYSPOWER_IS_REQUEST_GRACEFUL(r->flags))
  76. r->timeout = le32_to_cpu(p->timeout);
  77. else
  78. r->timeout = 0x00;
  79. *src_id = 0;
  80. return r;
  81. }
  82. static const struct scmi_event system_events[] = {
  83. {
  84. .id = SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER,
  85. .max_payld_sz =
  86. sizeof(struct scmi_system_power_state_notifier_payld),
  87. .max_report_sz =
  88. sizeof(struct scmi_system_power_state_notifier_report),
  89. },
  90. };
  91. static const struct scmi_event_ops system_event_ops = {
  92. .set_notify_enabled = scmi_system_set_notify_enabled,
  93. .fill_custom_report = scmi_system_fill_custom_report,
  94. };
  95. static const struct scmi_protocol_events system_protocol_events = {
  96. .queue_sz = SCMI_PROTO_QUEUE_SZ,
  97. .ops = &system_event_ops,
  98. .evts = system_events,
  99. .num_events = ARRAY_SIZE(system_events),
  100. .num_sources = SCMI_SYSTEM_NUM_SOURCES,
  101. };
  102. static int scmi_system_protocol_init(const struct scmi_protocol_handle *ph)
  103. {
  104. int ret;
  105. u32 version;
  106. struct scmi_system_info *pinfo;
  107. ret = ph->xops->version_get(ph, &version);
  108. if (ret)
  109. return ret;
  110. dev_dbg(ph->dev, "System Power Version %d.%d\n",
  111. PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
  112. pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
  113. if (!pinfo)
  114. return -ENOMEM;
  115. pinfo->version = version;
  116. if (PROTOCOL_REV_MAJOR(pinfo->version) >= 0x2)
  117. pinfo->graceful_timeout_supported = true;
  118. return ph->set_priv(ph, pinfo);
  119. }
  120. static const struct scmi_protocol scmi_system = {
  121. .id = SCMI_PROTOCOL_SYSTEM,
  122. .owner = THIS_MODULE,
  123. .instance_init = &scmi_system_protocol_init,
  124. .ops = NULL,
  125. .events = &system_protocol_events,
  126. };
  127. DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(system, scmi_system)