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