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_xfer_get_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_xfer_put(handle, t); 53 54 return ret; 55 } 56 57 /** 58 * scmi_base_vendor_id_get() - gets vendor/subvendor identifier ASCII string. 59 * 60 * @handle: SCMI entity handle 61 * @sub_vendor: specify true if sub-vendor ID is needed 62 * 63 * Return: 0 on success, else appropriate SCMI error. 64 */ 65 static int 66 scmi_base_vendor_id_get(const struct scmi_handle *handle, bool sub_vendor) 67 { 68 u8 cmd; 69 int ret, size; 70 char *vendor_id; 71 struct scmi_xfer *t; 72 struct scmi_revision_info *rev = handle->version; 73 74 if (sub_vendor) { 75 cmd = BASE_DISCOVER_SUB_VENDOR; 76 vendor_id = rev->sub_vendor_id; 77 size = ARRAY_SIZE(rev->sub_vendor_id); 78 } else { 79 cmd = BASE_DISCOVER_VENDOR; 80 vendor_id = rev->vendor_id; 81 size = ARRAY_SIZE(rev->vendor_id); 82 } 83 84 ret = scmi_xfer_get_init(handle, cmd, SCMI_PROTOCOL_BASE, 0, size, &t); 85 if (ret) 86 return ret; 87 88 ret = scmi_do_xfer(handle, t); 89 if (!ret) 90 memcpy(vendor_id, t->rx.buf, size); 91 92 scmi_xfer_put(handle, t); 93 94 return ret; 95 } 96 97 /** 98 * scmi_base_implementation_version_get() - gets a vendor-specific 99 * implementation 32-bit version. The format of the version number is 100 * vendor-specific 101 * 102 * @handle: SCMI entity handle 103 * 104 * Return: 0 on success, else appropriate SCMI error. 105 */ 106 static int 107 scmi_base_implementation_version_get(const struct scmi_handle *handle) 108 { 109 int ret; 110 __le32 *impl_ver; 111 struct scmi_xfer *t; 112 struct scmi_revision_info *rev = handle->version; 113 114 ret = scmi_xfer_get_init(handle, BASE_DISCOVER_IMPLEMENT_VERSION, 115 SCMI_PROTOCOL_BASE, 0, sizeof(*impl_ver), &t); 116 if (ret) 117 return ret; 118 119 ret = scmi_do_xfer(handle, t); 120 if (!ret) { 121 impl_ver = t->rx.buf; 122 rev->impl_ver = le32_to_cpu(*impl_ver); 123 } 124 125 scmi_xfer_put(handle, t); 126 127 return ret; 128 } 129 130 /** 131 * scmi_base_implementation_list_get() - gets the list of protocols it is 132 * OSPM is allowed to access 133 * 134 * @handle: SCMI entity handle 135 * @protocols_imp: pointer to hold the list of protocol identifiers 136 * 137 * Return: 0 on success, else appropriate SCMI error. 138 */ 139 static int scmi_base_implementation_list_get(const struct scmi_handle *handle, 140 u8 *protocols_imp) 141 { 142 u8 *list; 143 int ret, loop; 144 struct scmi_xfer *t; 145 __le32 *num_skip, *num_ret; 146 u32 tot_num_ret = 0, loop_num_ret; 147 struct device *dev = handle->dev; 148 149 ret = scmi_xfer_get_init(handle, BASE_DISCOVER_LIST_PROTOCOLS, 150 SCMI_PROTOCOL_BASE, sizeof(*num_skip), 0, &t); 151 if (ret) 152 return ret; 153 154 num_skip = t->tx.buf; 155 num_ret = t->rx.buf; 156 list = t->rx.buf + sizeof(*num_ret); 157 158 do { 159 /* Set the number of protocols to be skipped/already read */ 160 *num_skip = cpu_to_le32(tot_num_ret); 161 162 ret = scmi_do_xfer(handle, t); 163 if (ret) 164 break; 165 166 loop_num_ret = le32_to_cpu(*num_ret); 167 if (tot_num_ret + loop_num_ret > MAX_PROTOCOLS_IMP) { 168 dev_err(dev, "No. of Protocol > MAX_PROTOCOLS_IMP"); 169 break; 170 } 171 172 for (loop = 0; loop < loop_num_ret; loop++) 173 protocols_imp[tot_num_ret + loop] = *(list + loop); 174 175 tot_num_ret += loop_num_ret; 176 } while (loop_num_ret); 177 178 scmi_xfer_put(handle, t); 179 180 return ret; 181 } 182 183 /** 184 * scmi_base_discover_agent_get() - discover the name of an agent 185 * 186 * @handle: SCMI entity handle 187 * @id: Agent identifier 188 * @name: Agent identifier ASCII string 189 * 190 * An agent id of 0 is reserved to identify the platform itself. 191 * Generally operating system is represented as "OSPM" 192 * 193 * Return: 0 on success, else appropriate SCMI error. 194 */ 195 static int scmi_base_discover_agent_get(const struct scmi_handle *handle, 196 int id, char *name) 197 { 198 int ret; 199 struct scmi_xfer *t; 200 201 ret = scmi_xfer_get_init(handle, BASE_DISCOVER_AGENT, 202 SCMI_PROTOCOL_BASE, sizeof(__le32), 203 SCMI_MAX_STR_SIZE, &t); 204 if (ret) 205 return ret; 206 207 put_unaligned_le32(id, t->tx.buf); 208 209 ret = scmi_do_xfer(handle, t); 210 if (!ret) 211 strlcpy(name, t->rx.buf, SCMI_MAX_STR_SIZE); 212 213 scmi_xfer_put(handle, t); 214 215 return ret; 216 } 217 218 int scmi_base_protocol_init(struct scmi_handle *h) 219 { 220 int id, ret; 221 u8 *prot_imp; 222 u32 version; 223 char name[SCMI_MAX_STR_SIZE]; 224 const struct scmi_handle *handle = h; 225 struct device *dev = handle->dev; 226 struct scmi_revision_info *rev = handle->version; 227 228 ret = scmi_version_get(handle, SCMI_PROTOCOL_BASE, &version); 229 if (ret) 230 return ret; 231 232 prot_imp = devm_kcalloc(dev, MAX_PROTOCOLS_IMP, sizeof(u8), GFP_KERNEL); 233 if (!prot_imp) 234 return -ENOMEM; 235 236 rev->major_ver = PROTOCOL_REV_MAJOR(version), 237 rev->minor_ver = PROTOCOL_REV_MINOR(version); 238 239 scmi_base_attributes_get(handle); 240 scmi_base_vendor_id_get(handle, false); 241 scmi_base_vendor_id_get(handle, true); 242 scmi_base_implementation_version_get(handle); 243 scmi_base_implementation_list_get(handle, prot_imp); 244 scmi_setup_protocol_implemented(handle, prot_imp); 245 246 dev_info(dev, "SCMI Protocol v%d.%d '%s:%s' Firmware version 0x%x\n", 247 rev->major_ver, rev->minor_ver, rev->vendor_id, 248 rev->sub_vendor_id, rev->impl_ver); 249 dev_dbg(dev, "Found %d protocol(s) %d agent(s)\n", rev->num_protocols, 250 rev->num_agents); 251 252 for (id = 0; id < rev->num_agents; id++) { 253 scmi_base_discover_agent_get(handle, id, name); 254 dev_dbg(dev, "Agent %d: %s\n", id, name); 255 } 256 257 return 0; 258 } 259