1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * System Control and Management Interface (SCMI) Base Protocol 4 * 5 * Copyright (C) 2018 ARM Ltd. 6 */ 7 8 #include "common.h" 9 10 enum scmi_base_protocol_cmd { 11 BASE_DISCOVER_VENDOR = 0x3, 12 BASE_DISCOVER_SUB_VENDOR = 0x4, 13 BASE_DISCOVER_IMPLEMENT_VERSION = 0x5, 14 BASE_DISCOVER_LIST_PROTOCOLS = 0x6, 15 BASE_DISCOVER_AGENT = 0x7, 16 BASE_NOTIFY_ERRORS = 0x8, 17 }; 18 19 struct scmi_msg_resp_base_attributes { 20 u8 num_protocols; 21 u8 num_agents; 22 __le16 reserved; 23 }; 24 25 /** 26 * scmi_base_attributes_get() - gets the implementation details 27 * that are associated with the base protocol. 28 * 29 * @handle - SCMI entity handle 30 * 31 * Return: 0 on success, else appropriate SCMI error. 32 */ 33 static int scmi_base_attributes_get(const struct scmi_handle *handle) 34 { 35 int ret; 36 struct scmi_xfer *t; 37 struct scmi_msg_resp_base_attributes *attr_info; 38 struct scmi_revision_info *rev = handle->version; 39 40 ret = scmi_one_xfer_init(handle, PROTOCOL_ATTRIBUTES, 41 SCMI_PROTOCOL_BASE, 0, sizeof(*attr_info), &t); 42 if (ret) 43 return ret; 44 45 ret = scmi_do_xfer(handle, t); 46 if (!ret) { 47 attr_info = t->rx.buf; 48 rev->num_protocols = attr_info->num_protocols; 49 rev->num_agents = attr_info->num_agents; 50 } 51 52 scmi_one_xfer_put(handle, t); 53 return ret; 54 } 55 56 /** 57 * scmi_base_vendor_id_get() - gets vendor/subvendor identifier ASCII string. 58 * 59 * @handle - SCMI entity handle 60 * @sub_vendor - specify true if sub-vendor ID is needed 61 * 62 * Return: 0 on success, else appropriate SCMI error. 63 */ 64 static int 65 scmi_base_vendor_id_get(const struct scmi_handle *handle, bool sub_vendor) 66 { 67 u8 cmd; 68 int ret, size; 69 char *vendor_id; 70 struct scmi_xfer *t; 71 struct scmi_revision_info *rev = handle->version; 72 73 if (sub_vendor) { 74 cmd = BASE_DISCOVER_SUB_VENDOR; 75 vendor_id = rev->sub_vendor_id; 76 size = ARRAY_SIZE(rev->sub_vendor_id); 77 } else { 78 cmd = BASE_DISCOVER_VENDOR; 79 vendor_id = rev->vendor_id; 80 size = ARRAY_SIZE(rev->vendor_id); 81 } 82 83 ret = scmi_one_xfer_init(handle, cmd, SCMI_PROTOCOL_BASE, 0, size, &t); 84 if (ret) 85 return ret; 86 87 ret = scmi_do_xfer(handle, t); 88 if (!ret) 89 memcpy(vendor_id, t->rx.buf, size); 90 91 scmi_one_xfer_put(handle, t); 92 return ret; 93 } 94 95 /** 96 * scmi_base_implementation_version_get() - gets a vendor-specific 97 * implementation 32-bit version. The format of the version number is 98 * vendor-specific 99 * 100 * @handle - SCMI entity handle 101 * 102 * Return: 0 on success, else appropriate SCMI error. 103 */ 104 static int 105 scmi_base_implementation_version_get(const struct scmi_handle *handle) 106 { 107 int ret; 108 __le32 *impl_ver; 109 struct scmi_xfer *t; 110 struct scmi_revision_info *rev = handle->version; 111 112 ret = scmi_one_xfer_init(handle, BASE_DISCOVER_IMPLEMENT_VERSION, 113 SCMI_PROTOCOL_BASE, 0, sizeof(*impl_ver), &t); 114 if (ret) 115 return ret; 116 117 ret = scmi_do_xfer(handle, t); 118 if (!ret) { 119 impl_ver = t->rx.buf; 120 rev->impl_ver = le32_to_cpu(*impl_ver); 121 } 122 123 scmi_one_xfer_put(handle, t); 124 return ret; 125 } 126 127 /** 128 * scmi_base_implementation_list_get() - gets the list of protocols it is 129 * OSPM is allowed to access 130 * 131 * @handle - SCMI entity handle 132 * @protocols_imp - pointer to hold the list of protocol identifiers 133 * 134 * Return: 0 on success, else appropriate SCMI error. 135 */ 136 static int scmi_base_implementation_list_get(const struct scmi_handle *handle, 137 u8 *protocols_imp) 138 { 139 u8 *list; 140 int ret, loop; 141 struct scmi_xfer *t; 142 __le32 *num_skip, *num_ret; 143 u32 tot_num_ret = 0, loop_num_ret; 144 struct device *dev = handle->dev; 145 146 ret = scmi_one_xfer_init(handle, BASE_DISCOVER_LIST_PROTOCOLS, 147 SCMI_PROTOCOL_BASE, sizeof(*num_skip), 0, &t); 148 if (ret) 149 return ret; 150 151 num_skip = t->tx.buf; 152 num_ret = t->rx.buf; 153 list = t->rx.buf + sizeof(*num_ret); 154 155 do { 156 /* Set the number of protocols to be skipped/already read */ 157 *num_skip = cpu_to_le32(tot_num_ret); 158 159 ret = scmi_do_xfer(handle, t); 160 if (ret) 161 break; 162 163 loop_num_ret = le32_to_cpu(*num_ret); 164 if (tot_num_ret + loop_num_ret > MAX_PROTOCOLS_IMP) { 165 dev_err(dev, "No. of Protocol > MAX_PROTOCOLS_IMP"); 166 break; 167 } 168 169 for (loop = 0; loop < loop_num_ret; loop++) 170 protocols_imp[tot_num_ret + loop] = *(list + loop); 171 172 tot_num_ret += loop_num_ret; 173 } while (loop_num_ret); 174 175 scmi_one_xfer_put(handle, t); 176 return ret; 177 } 178 179 /** 180 * scmi_base_discover_agent_get() - discover the name of an agent 181 * 182 * @handle - SCMI entity handle 183 * @id - Agent identifier 184 * @name - Agent identifier ASCII string 185 * 186 * An agent id of 0 is reserved to identify the platform itself. 187 * Generally operating system is represented as "OSPM" 188 * 189 * Return: 0 on success, else appropriate SCMI error. 190 */ 191 static int scmi_base_discover_agent_get(const struct scmi_handle *handle, 192 int id, char *name) 193 { 194 int ret; 195 struct scmi_xfer *t; 196 197 ret = scmi_one_xfer_init(handle, BASE_DISCOVER_AGENT, 198 SCMI_PROTOCOL_BASE, sizeof(__le32), 199 SCMI_MAX_STR_SIZE, &t); 200 if (ret) 201 return ret; 202 203 *(__le32 *)t->tx.buf = cpu_to_le32(id); 204 205 ret = scmi_do_xfer(handle, t); 206 if (!ret) 207 memcpy(name, t->rx.buf, SCMI_MAX_STR_SIZE); 208 209 scmi_one_xfer_put(handle, t); 210 return ret; 211 } 212 213 int scmi_base_protocol_init(struct scmi_handle *h) 214 { 215 int id, ret; 216 u8 *prot_imp; 217 u32 version; 218 char name[SCMI_MAX_STR_SIZE]; 219 const struct scmi_handle *handle = h; 220 struct device *dev = handle->dev; 221 struct scmi_revision_info *rev = handle->version; 222 223 ret = scmi_version_get(handle, SCMI_PROTOCOL_BASE, &version); 224 if (ret) 225 return ret; 226 227 prot_imp = devm_kcalloc(dev, MAX_PROTOCOLS_IMP, sizeof(u8), GFP_KERNEL); 228 if (!prot_imp) 229 return -ENOMEM; 230 231 rev->major_ver = PROTOCOL_REV_MAJOR(version), 232 rev->minor_ver = PROTOCOL_REV_MINOR(version); 233 234 scmi_base_attributes_get(handle); 235 scmi_base_vendor_id_get(handle, false); 236 scmi_base_vendor_id_get(handle, true); 237 scmi_base_implementation_version_get(handle); 238 scmi_base_implementation_list_get(handle, prot_imp); 239 scmi_setup_protocol_implemented(handle, prot_imp); 240 241 dev_info(dev, "SCMI Protocol v%d.%d '%s:%s' Firmware version 0x%x\n", 242 rev->major_ver, rev->minor_ver, rev->vendor_id, 243 rev->sub_vendor_id, rev->impl_ver); 244 dev_dbg(dev, "Found %d protocol(s) %d agent(s)\n", rev->num_protocols, 245 rev->num_agents); 246 247 for (id = 0; id < rev->num_agents; id++) { 248 scmi_base_discover_agent_get(handle, id, name); 249 dev_dbg(dev, "Agent %d: %s\n", id, name); 250 } 251 252 return 0; 253 } 254