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