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