1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * System Control and Management Interface (SCMI) Power Protocol 4 * 5 * Copyright (C) 2018 ARM Ltd. 6 */ 7 8 #include "common.h" 9 10 enum scmi_power_protocol_cmd { 11 POWER_DOMAIN_ATTRIBUTES = 0x3, 12 POWER_STATE_SET = 0x4, 13 POWER_STATE_GET = 0x5, 14 POWER_STATE_NOTIFY = 0x6, 15 }; 16 17 struct scmi_msg_resp_power_attributes { 18 __le16 num_domains; 19 __le16 reserved; 20 __le32 stats_addr_low; 21 __le32 stats_addr_high; 22 __le32 stats_size; 23 }; 24 25 struct scmi_msg_resp_power_domain_attributes { 26 __le32 flags; 27 #define SUPPORTS_STATE_SET_NOTIFY(x) ((x) & BIT(31)) 28 #define SUPPORTS_STATE_SET_ASYNC(x) ((x) & BIT(30)) 29 #define SUPPORTS_STATE_SET_SYNC(x) ((x) & BIT(29)) 30 u8 name[SCMI_MAX_STR_SIZE]; 31 }; 32 33 struct scmi_power_set_state { 34 __le32 flags; 35 #define STATE_SET_ASYNC BIT(0) 36 __le32 domain; 37 __le32 state; 38 }; 39 40 struct scmi_power_state_notify { 41 __le32 domain; 42 __le32 notify_enable; 43 }; 44 45 struct power_dom_info { 46 bool state_set_sync; 47 bool state_set_async; 48 bool state_set_notify; 49 char name[SCMI_MAX_STR_SIZE]; 50 }; 51 52 struct scmi_power_info { 53 int num_domains; 54 u64 stats_addr; 55 u32 stats_size; 56 struct power_dom_info *dom_info; 57 }; 58 59 static int scmi_power_attributes_get(const struct scmi_handle *handle, 60 struct scmi_power_info *pi) 61 { 62 int ret; 63 struct scmi_xfer *t; 64 struct scmi_msg_resp_power_attributes *attr; 65 66 ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES, 67 SCMI_PROTOCOL_POWER, 0, sizeof(*attr), &t); 68 if (ret) 69 return ret; 70 71 attr = t->rx.buf; 72 73 ret = scmi_do_xfer(handle, t); 74 if (!ret) { 75 pi->num_domains = le16_to_cpu(attr->num_domains); 76 pi->stats_addr = le32_to_cpu(attr->stats_addr_low) | 77 (u64)le32_to_cpu(attr->stats_addr_high) << 32; 78 pi->stats_size = le32_to_cpu(attr->stats_size); 79 } 80 81 scmi_xfer_put(handle, t); 82 return ret; 83 } 84 85 static int 86 scmi_power_domain_attributes_get(const struct scmi_handle *handle, u32 domain, 87 struct power_dom_info *dom_info) 88 { 89 int ret; 90 struct scmi_xfer *t; 91 struct scmi_msg_resp_power_domain_attributes *attr; 92 93 ret = scmi_xfer_get_init(handle, POWER_DOMAIN_ATTRIBUTES, 94 SCMI_PROTOCOL_POWER, sizeof(domain), 95 sizeof(*attr), &t); 96 if (ret) 97 return ret; 98 99 *(__le32 *)t->tx.buf = cpu_to_le32(domain); 100 attr = t->rx.buf; 101 102 ret = scmi_do_xfer(handle, t); 103 if (!ret) { 104 u32 flags = le32_to_cpu(attr->flags); 105 106 dom_info->state_set_notify = SUPPORTS_STATE_SET_NOTIFY(flags); 107 dom_info->state_set_async = SUPPORTS_STATE_SET_ASYNC(flags); 108 dom_info->state_set_sync = SUPPORTS_STATE_SET_SYNC(flags); 109 memcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE); 110 } 111 112 scmi_xfer_put(handle, t); 113 return ret; 114 } 115 116 static int 117 scmi_power_state_set(const struct scmi_handle *handle, u32 domain, u32 state) 118 { 119 int ret; 120 struct scmi_xfer *t; 121 struct scmi_power_set_state *st; 122 123 ret = scmi_xfer_get_init(handle, POWER_STATE_SET, SCMI_PROTOCOL_POWER, 124 sizeof(*st), 0, &t); 125 if (ret) 126 return ret; 127 128 st = t->tx.buf; 129 st->flags = cpu_to_le32(0); 130 st->domain = cpu_to_le32(domain); 131 st->state = cpu_to_le32(state); 132 133 ret = scmi_do_xfer(handle, t); 134 135 scmi_xfer_put(handle, t); 136 return ret; 137 } 138 139 static int 140 scmi_power_state_get(const struct scmi_handle *handle, u32 domain, u32 *state) 141 { 142 int ret; 143 struct scmi_xfer *t; 144 145 ret = scmi_xfer_get_init(handle, POWER_STATE_GET, SCMI_PROTOCOL_POWER, 146 sizeof(u32), sizeof(u32), &t); 147 if (ret) 148 return ret; 149 150 *(__le32 *)t->tx.buf = cpu_to_le32(domain); 151 152 ret = scmi_do_xfer(handle, t); 153 if (!ret) 154 *state = le32_to_cpu(*(__le32 *)t->rx.buf); 155 156 scmi_xfer_put(handle, t); 157 return ret; 158 } 159 160 static int scmi_power_num_domains_get(const struct scmi_handle *handle) 161 { 162 struct scmi_power_info *pi = handle->power_priv; 163 164 return pi->num_domains; 165 } 166 167 static char *scmi_power_name_get(const struct scmi_handle *handle, u32 domain) 168 { 169 struct scmi_power_info *pi = handle->power_priv; 170 struct power_dom_info *dom = pi->dom_info + domain; 171 172 return dom->name; 173 } 174 175 static struct scmi_power_ops power_ops = { 176 .num_domains_get = scmi_power_num_domains_get, 177 .name_get = scmi_power_name_get, 178 .state_set = scmi_power_state_set, 179 .state_get = scmi_power_state_get, 180 }; 181 182 static int scmi_power_protocol_init(struct scmi_handle *handle) 183 { 184 int domain; 185 u32 version; 186 struct scmi_power_info *pinfo; 187 188 scmi_version_get(handle, SCMI_PROTOCOL_POWER, &version); 189 190 dev_dbg(handle->dev, "Power Version %d.%d\n", 191 PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); 192 193 pinfo = devm_kzalloc(handle->dev, sizeof(*pinfo), GFP_KERNEL); 194 if (!pinfo) 195 return -ENOMEM; 196 197 scmi_power_attributes_get(handle, pinfo); 198 199 pinfo->dom_info = devm_kcalloc(handle->dev, pinfo->num_domains, 200 sizeof(*pinfo->dom_info), GFP_KERNEL); 201 if (!pinfo->dom_info) 202 return -ENOMEM; 203 204 for (domain = 0; domain < pinfo->num_domains; domain++) { 205 struct power_dom_info *dom = pinfo->dom_info + domain; 206 207 scmi_power_domain_attributes_get(handle, domain, dom); 208 } 209 210 handle->power_ops = &power_ops; 211 handle->power_priv = pinfo; 212 213 return 0; 214 } 215 216 static int __init scmi_power_init(void) 217 { 218 return scmi_protocol_register(SCMI_PROTOCOL_POWER, 219 &scmi_power_protocol_init); 220 } 221 subsys_initcall(scmi_power_init); 222