power.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * System Control and Management Interface (SCMI) Power Protocol
  4. *
  5. * Copyright (C) 2018-2022 ARM Ltd.
  6. */
  7. #define pr_fmt(fmt) "SCMI Notifications POWER - " fmt
  8. #include <linux/module.h>
  9. #include <linux/scmi_protocol.h>
  10. #include "protocols.h"
  11. #include "notify.h"
  12. /* Updated only after ALL the mandatory features for that version are merged */
  13. #define SCMI_PROTOCOL_SUPPORTED_VERSION 0x30001
  14. enum scmi_power_protocol_cmd {
  15. POWER_DOMAIN_ATTRIBUTES = 0x3,
  16. POWER_STATE_SET = 0x4,
  17. POWER_STATE_GET = 0x5,
  18. POWER_STATE_NOTIFY = 0x6,
  19. POWER_DOMAIN_NAME_GET = 0x8,
  20. };
  21. struct scmi_msg_resp_power_attributes {
  22. __le16 num_domains;
  23. __le16 reserved;
  24. __le32 stats_addr_low;
  25. __le32 stats_addr_high;
  26. __le32 stats_size;
  27. };
  28. struct scmi_msg_resp_power_domain_attributes {
  29. __le32 flags;
  30. #define SUPPORTS_STATE_SET_NOTIFY(x) ((x) & BIT(31))
  31. #define SUPPORTS_STATE_SET_ASYNC(x) ((x) & BIT(30))
  32. #define SUPPORTS_STATE_SET_SYNC(x) ((x) & BIT(29))
  33. #define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(27))
  34. u8 name[SCMI_SHORT_NAME_MAX_SIZE];
  35. };
  36. struct scmi_power_set_state {
  37. __le32 flags;
  38. #define STATE_SET_ASYNC BIT(0)
  39. __le32 domain;
  40. __le32 state;
  41. };
  42. struct scmi_power_state_notify {
  43. __le32 domain;
  44. __le32 notify_enable;
  45. };
  46. struct scmi_power_state_notify_payld {
  47. __le32 agent_id;
  48. __le32 domain_id;
  49. __le32 power_state;
  50. };
  51. struct power_dom_info {
  52. bool state_set_sync;
  53. bool state_set_async;
  54. bool state_set_notify;
  55. char name[SCMI_MAX_STR_SIZE];
  56. };
  57. struct scmi_power_info {
  58. u32 version;
  59. bool notify_state_change_cmd;
  60. int num_domains;
  61. u64 stats_addr;
  62. u32 stats_size;
  63. struct power_dom_info *dom_info;
  64. };
  65. static int scmi_power_attributes_get(const struct scmi_protocol_handle *ph,
  66. struct scmi_power_info *pi)
  67. {
  68. int ret;
  69. struct scmi_xfer *t;
  70. struct scmi_msg_resp_power_attributes *attr;
  71. ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES,
  72. 0, sizeof(*attr), &t);
  73. if (ret)
  74. return ret;
  75. attr = t->rx.buf;
  76. ret = ph->xops->do_xfer(ph, t);
  77. if (!ret) {
  78. pi->num_domains = le16_to_cpu(attr->num_domains);
  79. pi->stats_addr = le32_to_cpu(attr->stats_addr_low) |
  80. (u64)le32_to_cpu(attr->stats_addr_high) << 32;
  81. pi->stats_size = le32_to_cpu(attr->stats_size);
  82. }
  83. ph->xops->xfer_put(ph, t);
  84. if (!ret)
  85. if (!ph->hops->protocol_msg_check(ph, POWER_STATE_NOTIFY, NULL))
  86. pi->notify_state_change_cmd = true;
  87. return ret;
  88. }
  89. static int
  90. scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph,
  91. u32 domain, struct power_dom_info *dom_info,
  92. u32 version, bool notify_state_change_cmd)
  93. {
  94. int ret;
  95. u32 flags;
  96. struct scmi_xfer *t;
  97. struct scmi_msg_resp_power_domain_attributes *attr;
  98. ret = ph->xops->xfer_get_init(ph, POWER_DOMAIN_ATTRIBUTES,
  99. sizeof(domain), sizeof(*attr), &t);
  100. if (ret)
  101. return ret;
  102. put_unaligned_le32(domain, t->tx.buf);
  103. attr = t->rx.buf;
  104. ret = ph->xops->do_xfer(ph, t);
  105. if (!ret) {
  106. flags = le32_to_cpu(attr->flags);
  107. if (notify_state_change_cmd)
  108. dom_info->state_set_notify =
  109. SUPPORTS_STATE_SET_NOTIFY(flags);
  110. dom_info->state_set_async = SUPPORTS_STATE_SET_ASYNC(flags);
  111. dom_info->state_set_sync = SUPPORTS_STATE_SET_SYNC(flags);
  112. strscpy(dom_info->name, attr->name, SCMI_SHORT_NAME_MAX_SIZE);
  113. }
  114. ph->xops->xfer_put(ph, t);
  115. /*
  116. * If supported overwrite short name with the extended one;
  117. * on error just carry on and use already provided short name.
  118. */
  119. if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x3 &&
  120. SUPPORTS_EXTENDED_NAMES(flags)) {
  121. ph->hops->extended_name_get(ph, POWER_DOMAIN_NAME_GET,
  122. domain, NULL, dom_info->name,
  123. SCMI_MAX_STR_SIZE);
  124. }
  125. return ret;
  126. }
  127. static int scmi_power_state_set(const struct scmi_protocol_handle *ph,
  128. u32 domain, u32 state)
  129. {
  130. int ret;
  131. struct scmi_xfer *t;
  132. struct scmi_power_set_state *st;
  133. ret = ph->xops->xfer_get_init(ph, POWER_STATE_SET, sizeof(*st), 0, &t);
  134. if (ret)
  135. return ret;
  136. st = t->tx.buf;
  137. st->flags = cpu_to_le32(0);
  138. st->domain = cpu_to_le32(domain);
  139. st->state = cpu_to_le32(state);
  140. ret = ph->xops->do_xfer(ph, t);
  141. ph->xops->xfer_put(ph, t);
  142. return ret;
  143. }
  144. static int scmi_power_state_get(const struct scmi_protocol_handle *ph,
  145. u32 domain, u32 *state)
  146. {
  147. int ret;
  148. struct scmi_xfer *t;
  149. ret = ph->xops->xfer_get_init(ph, POWER_STATE_GET, sizeof(u32), sizeof(u32), &t);
  150. if (ret)
  151. return ret;
  152. put_unaligned_le32(domain, t->tx.buf);
  153. ret = ph->xops->do_xfer(ph, t);
  154. if (!ret)
  155. *state = get_unaligned_le32(t->rx.buf);
  156. ph->xops->xfer_put(ph, t);
  157. return ret;
  158. }
  159. static int scmi_power_num_domains_get(const struct scmi_protocol_handle *ph)
  160. {
  161. struct scmi_power_info *pi = ph->get_priv(ph);
  162. return pi->num_domains;
  163. }
  164. static const char *
  165. scmi_power_name_get(const struct scmi_protocol_handle *ph,
  166. u32 domain)
  167. {
  168. struct scmi_power_info *pi = ph->get_priv(ph);
  169. struct power_dom_info *dom = pi->dom_info + domain;
  170. return dom->name;
  171. }
  172. static const struct scmi_power_proto_ops power_proto_ops = {
  173. .num_domains_get = scmi_power_num_domains_get,
  174. .name_get = scmi_power_name_get,
  175. .state_set = scmi_power_state_set,
  176. .state_get = scmi_power_state_get,
  177. };
  178. static int scmi_power_request_notify(const struct scmi_protocol_handle *ph,
  179. u32 domain, bool enable)
  180. {
  181. int ret;
  182. struct scmi_xfer *t;
  183. struct scmi_power_state_notify *notify;
  184. ret = ph->xops->xfer_get_init(ph, POWER_STATE_NOTIFY,
  185. sizeof(*notify), 0, &t);
  186. if (ret)
  187. return ret;
  188. notify = t->tx.buf;
  189. notify->domain = cpu_to_le32(domain);
  190. notify->notify_enable = enable ? cpu_to_le32(BIT(0)) : 0;
  191. ret = ph->xops->do_xfer(ph, t);
  192. ph->xops->xfer_put(ph, t);
  193. return ret;
  194. }
  195. static bool scmi_power_notify_supported(const struct scmi_protocol_handle *ph,
  196. u8 evt_id, u32 src_id)
  197. {
  198. struct power_dom_info *dom;
  199. struct scmi_power_info *pinfo = ph->get_priv(ph);
  200. if (evt_id != SCMI_EVENT_POWER_STATE_CHANGED ||
  201. src_id >= pinfo->num_domains)
  202. return false;
  203. dom = pinfo->dom_info + src_id;
  204. return dom->state_set_notify;
  205. }
  206. static int scmi_power_set_notify_enabled(const struct scmi_protocol_handle *ph,
  207. u8 evt_id, u32 src_id, bool enable)
  208. {
  209. int ret;
  210. ret = scmi_power_request_notify(ph, src_id, enable);
  211. if (ret)
  212. pr_debug("FAIL_ENABLE - evt[%X] dom[%d] - ret:%d\n",
  213. evt_id, src_id, ret);
  214. return ret;
  215. }
  216. static void *
  217. scmi_power_fill_custom_report(const struct scmi_protocol_handle *ph,
  218. u8 evt_id, ktime_t timestamp,
  219. const void *payld, size_t payld_sz,
  220. void *report, u32 *src_id)
  221. {
  222. const struct scmi_power_state_notify_payld *p = payld;
  223. struct scmi_power_state_changed_report *r = report;
  224. if (evt_id != SCMI_EVENT_POWER_STATE_CHANGED || sizeof(*p) != payld_sz)
  225. return NULL;
  226. r->timestamp = timestamp;
  227. r->agent_id = le32_to_cpu(p->agent_id);
  228. r->domain_id = le32_to_cpu(p->domain_id);
  229. r->power_state = le32_to_cpu(p->power_state);
  230. *src_id = r->domain_id;
  231. return r;
  232. }
  233. static int scmi_power_get_num_sources(const struct scmi_protocol_handle *ph)
  234. {
  235. struct scmi_power_info *pinfo = ph->get_priv(ph);
  236. if (!pinfo)
  237. return -EINVAL;
  238. return pinfo->num_domains;
  239. }
  240. static const struct scmi_event power_events[] = {
  241. {
  242. .id = SCMI_EVENT_POWER_STATE_CHANGED,
  243. .max_payld_sz = sizeof(struct scmi_power_state_notify_payld),
  244. .max_report_sz =
  245. sizeof(struct scmi_power_state_changed_report),
  246. },
  247. };
  248. static const struct scmi_event_ops power_event_ops = {
  249. .is_notify_supported = scmi_power_notify_supported,
  250. .get_num_sources = scmi_power_get_num_sources,
  251. .set_notify_enabled = scmi_power_set_notify_enabled,
  252. .fill_custom_report = scmi_power_fill_custom_report,
  253. };
  254. static const struct scmi_protocol_events power_protocol_events = {
  255. .queue_sz = SCMI_PROTO_QUEUE_SZ,
  256. .ops = &power_event_ops,
  257. .evts = power_events,
  258. .num_events = ARRAY_SIZE(power_events),
  259. };
  260. static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph)
  261. {
  262. int domain, ret;
  263. u32 version;
  264. struct scmi_power_info *pinfo;
  265. ret = ph->xops->version_get(ph, &version);
  266. if (ret)
  267. return ret;
  268. dev_dbg(ph->dev, "Power Version %d.%d\n",
  269. PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
  270. pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
  271. if (!pinfo)
  272. return -ENOMEM;
  273. ret = scmi_power_attributes_get(ph, pinfo);
  274. if (ret)
  275. return ret;
  276. pinfo->dom_info = devm_kcalloc(ph->dev, pinfo->num_domains,
  277. sizeof(*pinfo->dom_info), GFP_KERNEL);
  278. if (!pinfo->dom_info)
  279. return -ENOMEM;
  280. for (domain = 0; domain < pinfo->num_domains; domain++) {
  281. struct power_dom_info *dom = pinfo->dom_info + domain;
  282. scmi_power_domain_attributes_get(ph, domain, dom, version,
  283. pinfo->notify_state_change_cmd);
  284. }
  285. pinfo->version = version;
  286. return ph->set_priv(ph, pinfo, version);
  287. }
  288. static const struct scmi_protocol scmi_power = {
  289. .id = SCMI_PROTOCOL_POWER,
  290. .owner = THIS_MODULE,
  291. .instance_init = &scmi_power_protocol_init,
  292. .ops = &power_proto_ops,
  293. .events = &power_protocol_events,
  294. .supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
  295. };
  296. DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(power, scmi_power)