ARM SCMI/SCPI updates for v5.8 1. Addition of ARM SMC/HVC as SCMI transport type with required abstraction already in place 2. Initial infrastructure support to add SCMI notifications from platform to agents 3. Miscellaneous fix adding header include guards * tag 'scmi-updates-5.8' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux: firmware: arm_scmi: fix psci dependency firmware: arm_scmi: Fix return error code in smc_send_message firmware: arm_scmi: Fix handling of unexpected delayed responses firmware: arm_scmi: Clear channel for delayed responses firmware: arm_scmi: Clear channel on reception of unexpected responses firmware: arm_scmi: Rename .clear_notification() transport_ops firmware: arm_scmi: Add support for notifications message processing firmware: arm_scmi: Add notifications support in transport layer firmware: arm_scmi: Update protocol commands and notification list firmware: arm_scmi: Add receive buffer support for notifications firmware: arm_scpi: Add include guard to linux/scpi_protocol.h firmware: arm_scmi: Add include guard to linux/scmi_protocol.h firmware: arm_scmi: Drop checking for shmem property in parent node firmware: arm_scmi: Check shmem property for channel availablity firmware: arm_scmi: Drop empty stub for smc_mark_txdone firmware: arm_scmi: Make mutex channel specific firmware: arm_scmi: Add smc/hvc transport dt-bindings: arm: Add smc/hvc transport for SCMI
Unified · Split
| @@ -14,7 +14,7 @@ Required properties: · 13 unmodified lines | ||
| 14 | 14 | |
| 15 | 15 | The scmi node with the following properties shall be under the /firmware/ node. |
| 16 | 16 | |
| 17 | −- compatible : shall be "arm,scmi" | |
| 17 | +- compatible : shall be "arm,scmi" or "arm,scmi-smc" for smc/hvc transports | |
| 18 | 18 | - mboxes: List of phandle and mailbox channel specifiers. It should contain |
| 19 | 19 | exactly one or two mailboxes, one for transmitting messages("tx") |
| 20 | 20 | and another optional for receiving the notifications("rx") if |
| @@ -25,6 +25,7 @@ The scmi node with the following properties shall be under the /firmware/ node. · 4 unmodified lines | ||
| 25 | 25 | protocol identifier for a given sub-node. |
| 26 | 26 | - #size-cells : should be '0' as 'reg' property doesn't have any size |
| 27 | 27 | associated with it. |
| 28 | +- arm,smc-id : SMC id required when using smc or hvc transports | |
| 28 | 29 | |
| 29 | 30 | Optional properties: |
| 30 | 31 | |
| @@ -2,6 +2,8 @@ · 1 unmodified line | ||
| 2 | 2 | obj-y = scmi-bus.o scmi-driver.o scmi-protocols.o scmi-transport.o |
| 3 | 3 | scmi-bus-y = bus.o |
| 4 | 4 | scmi-driver-y = driver.o |
| 5 | −scmi-transport-y = mailbox.o shmem.o | |
| 5 | +scmi-transport-y = shmem.o | |
| 6 | +scmi-transport-$(CONFIG_MAILBOX) += mailbox.o | |
| 7 | +scmi-transport-$(CONFIG_ARM_PSCI_FW) += smc.o | |
| 6 | 8 | scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o |
| 7 | 9 | obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o |
| @@ -14,6 +14,13 @@ enum scmi_base_protocol_cmd { · 13 unmodified lines | ||
| 14 | 14 | BASE_DISCOVER_LIST_PROTOCOLS = 0x6, |
| 15 | 15 | BASE_DISCOVER_AGENT = 0x7, |
| 16 | 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, | |
| 17 | 24 | }; |
| 18 | 25 | |
| 19 | 26 | struct scmi_msg_resp_base_attributes { |
| @@ -178,6 +178,8 @@ struct scmi_chan_info { · 177 unmodified lines | ||
| 178 | 178 | * @send_message: Callback to send a message |
| 179 | 179 | * @mark_txdone: Callback to mark tx as done |
| 180 | 180 | * @fetch_response: Callback to fetch response |
| 181 | + * @fetch_notification: Callback to fetch notification | |
| 182 | + * @clear_channel: Callback to clear a channel | |
| 181 | 183 | * @poll_done: Callback to poll transfer status |
| 182 | 184 | */ |
| 183 | 185 | struct scmi_transport_ops { |
| @@ -190,6 +192,9 @@ struct scmi_transport_ops { · 6 unmodified lines | ||
| 190 | 192 | void (*mark_txdone)(struct scmi_chan_info *cinfo, int ret); |
| 191 | 193 | void (*fetch_response)(struct scmi_chan_info *cinfo, |
| 192 | 194 | struct scmi_xfer *xfer); |
| 195 | + void (*fetch_notification)(struct scmi_chan_info *cinfo, | |
| 196 | + size_t max_len, struct scmi_xfer *xfer); | |
| 197 | + void (*clear_channel)(struct scmi_chan_info *cinfo); | |
| 193 | 198 | bool (*poll_done)(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer); |
| 194 | 199 | }; |
| 195 | 200 | |
| @@ -210,6 +215,9 @@ struct scmi_desc { · 14 unmodified lines | ||
| 210 | 215 | }; |
| 211 | 216 | |
| 212 | 217 | extern const struct scmi_desc scmi_mailbox_desc; |
| 218 | +#ifdef CONFIG_HAVE_ARM_SMCCC | |
| 219 | +extern const struct scmi_desc scmi_smc_desc; | |
| 220 | +#endif | |
| 213 | 221 | |
| 214 | 222 | void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr); |
| 215 | 223 | void scmi_free_channel(struct scmi_chan_info *cinfo, struct idr *idr, int id); |
| @@ -222,5 +230,8 @@ void shmem_tx_prepare(struct scmi_shared_mem __iomem *shmem, · 6 unmodified lines | ||
| 222 | 230 | u32 shmem_read_header(struct scmi_shared_mem __iomem *shmem); |
| 223 | 231 | void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem, |
| 224 | 232 | struct scmi_xfer *xfer); |
| 233 | +void shmem_fetch_notification(struct scmi_shared_mem __iomem *shmem, | |
| 234 | + size_t max_len, struct scmi_xfer *xfer); | |
| 235 | +void shmem_clear_channel(struct scmi_shared_mem __iomem *shmem); | |
| 225 | 236 | bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem, |
| 226 | 237 | struct scmi_xfer *xfer); |
| @@ -76,6 +76,7 @@ struct scmi_xfers_info { · 75 unmodified lines | ||
| 76 | 76 | * implementation version and (sub-)vendor identification. |
| 77 | 77 | * @handle: Instance of SCMI handle to send to clients |
| 78 | 78 | * @tx_minfo: Universal Transmit Message management info |
| 79 | + * @rx_minfo: Universal Receive Message management info | |
| 79 | 80 | * @tx_idr: IDR object to map protocol id to Tx channel info pointer |
| 80 | 81 | * @rx_idr: IDR object to map protocol id to Rx channel info pointer |
| 81 | 82 | * @protocols_imp: List of protocols implemented, currently maximum of |
| @@ -89,6 +90,7 @@ struct scmi_info { · 7 unmodified lines | ||
| 89 | 90 | struct scmi_revision_info version; |
| 90 | 91 | struct scmi_handle handle; |
| 91 | 92 | struct scmi_xfers_info tx_minfo; |
| 93 | + struct scmi_xfers_info rx_minfo; | |
| 92 | 94 | struct idr tx_idr; |
| 93 | 95 | struct idr rx_idr; |
| 94 | 96 | u8 *protocols_imp; |
| @@ -200,37 +202,66 @@ __scmi_xfer_put(struct scmi_xfers_info *minfo, struct scmi_xfer *xfer) · 105 unmodified lines | ||
| 200 | 202 | spin_unlock_irqrestore(&minfo->xfer_lock, flags); |
| 201 | 203 | } |
| 202 | 204 | |
| 203 | −/** | |
| 204 | − * scmi_rx_callback() - callback for receiving messages | |
| 205 | − * | |
| 206 | − * @cinfo: SCMI channel info | |
| 207 | − * @msg_hdr: Message header | |
| 208 | − * | |
| 209 | − * Processes one received message to appropriate transfer information and | |
| 210 | − * signals completion of the transfer. | |
| 211 | − * | |
| 212 | − * NOTE: This function will be invoked in IRQ context, hence should be | |
| 213 | − * as optimal as possible. | |
| 214 | − */ | |
| 215 | −void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr) | |
| 205 | +static void scmi_handle_notification(struct scmi_chan_info *cinfo, u32 msg_hdr) | |
| 216 | 206 | { |
| 217 | − struct scmi_info *info = handle_to_scmi_info(cinfo->handle); | |
| 218 | − struct scmi_xfers_info *minfo = &info->tx_minfo; | |
| 219 | − u16 xfer_id = MSG_XTRACT_TOKEN(msg_hdr); | |
| 220 | − u8 msg_type = MSG_XTRACT_TYPE(msg_hdr); | |
| 221 | − struct device *dev = cinfo->dev; | |
| 222 | 207 | struct scmi_xfer *xfer; |
| 208 | + struct device *dev = cinfo->dev; | |
| 209 | + struct scmi_info *info = handle_to_scmi_info(cinfo->handle); | |
| 210 | + struct scmi_xfers_info *minfo = &info->rx_minfo; | |
| 223 | 211 | |
| 224 | − if (msg_type == MSG_TYPE_NOTIFICATION) | |
| 225 | − return; /* Notifications not yet supported */ | |
| 212 | + xfer = scmi_xfer_get(cinfo->handle, minfo); | |
| 213 | + if (IS_ERR(xfer)) { | |
| 214 | + dev_err(dev, "failed to get free message slot (%ld)\n", | |
| 215 | + PTR_ERR(xfer)); | |
| 216 | + info->desc->ops->clear_channel(cinfo); | |
| 217 | + return; | |
| 218 | + } | |
| 219 | + | |
| 220 | + unpack_scmi_header(msg_hdr, &xfer->hdr); | |
| 221 | + scmi_dump_header_dbg(dev, &xfer->hdr); | |
| 222 | + info->desc->ops->fetch_notification(cinfo, info->desc->max_msg_size, | |
| 223 | + xfer); | |
| 224 | + | |
| 225 | + trace_scmi_rx_done(xfer->transfer_id, xfer->hdr.id, | |
| 226 | + xfer->hdr.protocol_id, xfer->hdr.seq, | |
| 227 | + MSG_TYPE_NOTIFICATION); | |
| 228 | + | |
| 229 | + __scmi_xfer_put(minfo, xfer); | |
| 230 | + | |
| 231 | + info->desc->ops->clear_channel(cinfo); | |
| 232 | +} | |
| 233 | + | |
| 234 | +static void scmi_handle_response(struct scmi_chan_info *cinfo, | |
| 235 | + u16 xfer_id, u8 msg_type) | |
| 236 | +{ | |
| 237 | + struct scmi_xfer *xfer; | |
| 238 | + struct device *dev = cinfo->dev; | |
| 239 | + struct scmi_info *info = handle_to_scmi_info(cinfo->handle); | |
| 240 | + struct scmi_xfers_info *minfo = &info->tx_minfo; | |
| 226 | 241 | |
| 227 | 242 | /* Are we even expecting this? */ |
| 228 | 243 | if (!test_bit(xfer_id, minfo->xfer_alloc_table)) { |
| 229 | 244 | dev_err(dev, "message for %d is not expected!\n", xfer_id); |
| 245 | + info->desc->ops->clear_channel(cinfo); | |
| 230 | 246 | return; |
| 231 | 247 | } |
| 232 | 248 | |
| 233 | 249 | xfer = &minfo->xfer_block[xfer_id]; |
| 250 | + /* | |
| 251 | + * Even if a response was indeed expected on this slot at this point, | |
| 252 | + * a buggy platform could wrongly reply feeding us an unexpected | |
| 253 | + * delayed response we're not prepared to handle: bail-out safely | |
| 254 | + * blaming firmware. | |
| 255 | + */ | |
| 256 | + if (unlikely(msg_type == MSG_TYPE_DELAYED_RESP && !xfer->async_done)) { | |
| 257 | + dev_err(dev, | |
| 258 | + "Delayed Response for %d not expected! Buggy F/W ?\n", | |
| 259 | + xfer_id); | |
| 260 | + info->desc->ops->clear_channel(cinfo); | |
| 261 | + /* It was unexpected, so nobody will clear the xfer if not us */ | |
| 262 | + __scmi_xfer_put(minfo, xfer); | |
| 263 | + return; | |
| 264 | + } | |
| 234 | 265 | |
| 235 | 266 | scmi_dump_header_dbg(dev, &xfer->hdr); |
| 236 | 267 | |
| @@ -240,10 +271,43 @@ void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr) · 3 unmodified lines | ||
| 240 | 271 | xfer->hdr.protocol_id, xfer->hdr.seq, |
| 241 | 272 | msg_type); |
| 242 | 273 | |
| 243 | − if (msg_type == MSG_TYPE_DELAYED_RESP) | |
| 274 | + if (msg_type == MSG_TYPE_DELAYED_RESP) { | |
| 275 | + info->desc->ops->clear_channel(cinfo); | |
| 244 | 276 | complete(xfer->async_done); |
| 245 | − else | |
| 277 | + } else { | |
| 246 | 278 | complete(&xfer->done); |
| 279 | + } | |
| 280 | +} | |
| 281 | + | |
| 282 | +/** | |
| 283 | + * scmi_rx_callback() - callback for receiving messages | |
| 284 | + * | |
| 285 | + * @cinfo: SCMI channel info | |
| 286 | + * @msg_hdr: Message header | |
| 287 | + * | |
| 288 | + * Processes one received message to appropriate transfer information and | |
| 289 | + * signals completion of the transfer. | |
| 290 | + * | |
| 291 | + * NOTE: This function will be invoked in IRQ context, hence should be | |
| 292 | + * as optimal as possible. | |
| 293 | + */ | |
| 294 | +void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr) | |
| 295 | +{ | |
| 296 | + u16 xfer_id = MSG_XTRACT_TOKEN(msg_hdr); | |
| 297 | + u8 msg_type = MSG_XTRACT_TYPE(msg_hdr); | |
| 298 | + | |
| 299 | + switch (msg_type) { | |
| 300 | + case MSG_TYPE_NOTIFICATION: | |
| 301 | + scmi_handle_notification(cinfo, msg_hdr); | |
| 302 | + break; | |
| 303 | + case MSG_TYPE_COMMAND: | |
| 304 | + case MSG_TYPE_DELAYED_RESP: | |
| 305 | + scmi_handle_response(cinfo, xfer_id, msg_type); | |
| 306 | + break; | |
| 307 | + default: | |
| 308 | + WARN_ONCE(1, "received unknown msg_type:%d\n", msg_type); | |
| 309 | + break; | |
| 310 | + } | |
| 247 | 311 | } |
| 248 | 312 | |
| 249 | 313 | /** |
| @@ -525,13 +589,13 @@ int scmi_handle_put(const struct scmi_handle *handle) · 275 unmodified lines | ||
| 525 | 589 | return 0; |
| 526 | 590 | } |
| 527 | 591 | |
| 528 | −static int scmi_xfer_info_init(struct scmi_info *sinfo) | |
| 592 | +static int __scmi_xfer_info_init(struct scmi_info *sinfo, | |
| 593 | + struct scmi_xfers_info *info) | |
| 529 | 594 | { |
| 530 | 595 | int i; |
| 531 | 596 | struct scmi_xfer *xfer; |
| 532 | 597 | struct device *dev = sinfo->dev; |
| 533 | 598 | const struct scmi_desc *desc = sinfo->desc; |
| 534 | − struct scmi_xfers_info *info = &sinfo->tx_minfo; | |
| 535 | 599 | |
| 536 | 600 | /* Pre-allocated messages, no more than what hdr.seq can support */ |
| 537 | 601 | if (WARN_ON(desc->max_msg >= MSG_TOKEN_MAX)) { |
| @@ -566,6 +630,16 @@ static int scmi_xfer_info_init(struct scmi_info *sinfo) · 28 unmodified lines | ||
| 566 | 630 | return 0; |
| 567 | 631 | } |
| 568 | 632 | |
| 633 | +static int scmi_xfer_info_init(struct scmi_info *sinfo) | |
| 634 | +{ | |
| 635 | + int ret = __scmi_xfer_info_init(sinfo, &sinfo->tx_minfo); | |
| 636 | + | |
| 637 | + if (!ret && idr_find(&sinfo->rx_idr, SCMI_PROTOCOL_BASE)) | |
| 638 | + ret = __scmi_xfer_info_init(sinfo, &sinfo->rx_minfo); | |
| 639 | + | |
| 640 | + return ret; | |
| 641 | +} | |
| 642 | + | |
| 569 | 643 | static int scmi_chan_setup(struct scmi_info *info, struct device *dev, |
| 570 | 644 | int prot_id, bool tx) |
| 571 | 645 | { |
| @@ -699,10 +773,6 @@ static int scmi_probe(struct platform_device *pdev) · 127 unmodified lines | ||
| 699 | 773 | info->desc = desc; |
| 700 | 774 | INIT_LIST_HEAD(&info->node); |
| 701 | 775 | |
| 702 | − ret = scmi_xfer_info_init(info); | |
| 703 | − if (ret) | |
| 704 | − return ret; | |
| 705 | − | |
| 706 | 776 | platform_set_drvdata(pdev, info); |
| 707 | 777 | idr_init(&info->tx_idr); |
| 708 | 778 | idr_init(&info->rx_idr); |
| @@ -715,6 +785,10 @@ static int scmi_probe(struct platform_device *pdev) · 6 unmodified lines | ||
| 715 | 785 | if (ret) |
| 716 | 786 | return ret; |
| 717 | 787 | |
| 788 | + ret = scmi_xfer_info_init(info); | |
| 789 | + if (ret) | |
| 790 | + return ret; | |
| 791 | + | |
| 718 | 792 | ret = scmi_base_protocol_init(handle); |
| 719 | 793 | if (ret) { |
| 720 | 794 | dev_err(dev, "unable to communicate with SCMI(%d)\n", ret); |
| @@ -827,6 +901,9 @@ ATTRIBUTE_GROUPS(versions); · 106 unmodified lines | ||
| 827 | 901 | /* Each compatible listed below must have descriptor associated with it */ |
| 828 | 902 | static const struct of_device_id scmi_of_match[] = { |
| 829 | 903 | { .compatible = "arm,scmi", .data = &scmi_mailbox_desc }, |
| 904 | +#ifdef CONFIG_ARM_PSCI_FW | |
| 905 | + { .compatible = "arm,scmi-smc", .data = &scmi_smc_desc}, | |
| 906 | +#endif | |
| 830 | 907 | { /* Sentinel */ }, |
| 831 | 908 | }; |
| 832 | 909 | |
| @@ -158,6 +158,21 @@ static void mailbox_fetch_response(struct scmi_chan_info *cinfo, · 157 unmodified lines | ||
| 158 | 158 | shmem_fetch_response(smbox->shmem, xfer); |
| 159 | 159 | } |
| 160 | 160 | |
| 161 | +static void mailbox_fetch_notification(struct scmi_chan_info *cinfo, | |
| 162 | + size_t max_len, struct scmi_xfer *xfer) | |
| 163 | +{ | |
| 164 | + struct scmi_mailbox *smbox = cinfo->transport_info; | |
| 165 | + | |
| 166 | + shmem_fetch_notification(smbox->shmem, max_len, xfer); | |
| 167 | +} | |
| 168 | + | |
| 169 | +static void mailbox_clear_channel(struct scmi_chan_info *cinfo) | |
| 170 | +{ | |
| 171 | + struct scmi_mailbox *smbox = cinfo->transport_info; | |
| 172 | + | |
| 173 | + shmem_clear_channel(smbox->shmem); | |
| 174 | +} | |
| 175 | + | |
| 161 | 176 | static bool |
| 162 | 177 | mailbox_poll_done(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer) |
| 163 | 178 | { |
| @@ -173,6 +188,8 @@ static struct scmi_transport_ops scmi_mailbox_ops = { · 9 unmodified lines | ||
| 173 | 188 | .send_message = mailbox_send_message, |
| 174 | 189 | .mark_txdone = mailbox_mark_txdone, |
| 175 | 190 | .fetch_response = mailbox_fetch_response, |
| 191 | + .fetch_notification = mailbox_fetch_notification, | |
| 192 | + .clear_channel = mailbox_clear_channel, | |
| 176 | 193 | .poll_done = mailbox_poll_done, |
| 177 | 194 | }; |
| 178 | 195 | |
| @@ -27,6 +27,11 @@ enum scmi_performance_protocol_cmd { · 26 unmodified lines | ||
| 27 | 27 | PERF_DESCRIBE_FASTCHANNEL = 0xb, |
| 28 | 28 | }; |
| 29 | 29 | |
| 30 | +enum scmi_performance_protocol_notify { | |
| 31 | + PERFORMANCE_LIMITS_CHANGED = 0x0, | |
| 32 | + PERFORMANCE_LEVEL_CHANGED = 0x1, | |
| 33 | +}; | |
| 34 | + | |
| 30 | 35 | struct scmi_opp { |
| 31 | 36 | u32 perf; |
| 32 | 37 | u32 power; |
| @@ -12,6 +12,12 @@ enum scmi_power_protocol_cmd { · 11 unmodified lines | ||
| 12 | 12 | POWER_STATE_SET = 0x4, |
| 13 | 13 | POWER_STATE_GET = 0x5, |
| 14 | 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, | |
| 15 | 21 | }; |
| 16 | 22 | |
| 17 | 23 | struct scmi_msg_resp_power_attributes { |
| @@ -14,6 +14,10 @@ enum scmi_sensor_protocol_cmd { · 13 unmodified lines | ||
| 14 | 14 | SENSOR_READING_GET = 0x6, |
| 15 | 15 | }; |
| 16 | 16 | |
| 17 | +enum scmi_sensor_protocol_notify { | |
| 18 | + SENSOR_TRIP_POINT_EVENT = 0x0, | |
| 19 | +}; | |
| 20 | + | |
| 17 | 21 | struct scmi_msg_resp_sensor_attributes { |
| 18 | 22 | __le16 num_sensors; |
| 19 | 23 | u8 max_requests; |
| @@ -67,6 +67,21 @@ void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem, · 66 unmodified lines | ||
| 67 | 67 | memcpy_fromio(xfer->rx.buf, shmem->msg_payload + 4, xfer->rx.len); |
| 68 | 68 | } |
| 69 | 69 | |
| 70 | +void shmem_fetch_notification(struct scmi_shared_mem __iomem *shmem, | |
| 71 | + size_t max_len, struct scmi_xfer *xfer) | |
| 72 | +{ | |
| 73 | + /* Skip only the length of header in shmem area i.e 4 bytes */ | |
| 74 | + xfer->rx.len = min_t(size_t, max_len, ioread32(&shmem->length) - 4); | |
| 75 | + | |
| 76 | + /* Take a copy to the rx buffer.. */ | |
| 77 | + memcpy_fromio(xfer->rx.buf, shmem->msg_payload, xfer->rx.len); | |
| 78 | +} | |
| 79 | + | |
| 80 | +void shmem_clear_channel(struct scmi_shared_mem __iomem *shmem) | |
| 81 | +{ | |
| 82 | + iowrite32(SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE, &shmem->channel_status); | |
| 83 | +} | |
| 84 | + | |
| 70 | 85 | bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem, |
| 71 | 86 | struct scmi_xfer *xfer) |
| 72 | 87 | { |
| @@ -0,0 +1,153 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* | |
| 3 | + * System Control and Management Interface (SCMI) Message SMC/HVC | |
| 4 | + * Transport driver | |
| 5 | + * | |
| 6 | + * Copyright 2020 NXP | |
| 7 | + */ | |
| 8 | + | |
| 9 | +#include <linux/arm-smccc.h> | |
| 10 | +#include <linux/device.h> | |
| 11 | +#include <linux/err.h> | |
| 12 | +#include <linux/mutex.h> | |
| 13 | +#include <linux/of.h> | |
| 14 | +#include <linux/of_address.h> | |
| 15 | +#include <linux/slab.h> | |
| 16 | + | |
| 17 | +#include "common.h" | |
| 18 | + | |
| 19 | +/** | |
| 20 | + * struct scmi_smc - Structure representing a SCMI smc transport | |
| 21 | + * | |
| 22 | + * @cinfo: SCMI channel info | |
| 23 | + * @shmem: Transmit/Receive shared memory area | |
| 24 | + * @func_id: smc/hvc call function id | |
| 25 | + */ | |
| 26 | + | |
| 27 | +struct scmi_smc { | |
| 28 | + struct scmi_chan_info *cinfo; | |
| 29 | + struct scmi_shared_mem __iomem *shmem; | |
| 30 | + struct mutex shmem_lock; | |
| 31 | + u32 func_id; | |
| 32 | +}; | |
| 33 | + | |
| 34 | +static bool smc_chan_available(struct device *dev, int idx) | |
| 35 | +{ | |
| 36 | + struct device_node *np = of_parse_phandle(dev->of_node, "shmem", 0); | |
| 37 | + if (!np) | |
| 38 | + return false; | |
| 39 | + | |
| 40 | + of_node_put(np); | |
| 41 | + return true; | |
| 42 | +} | |
| 43 | + | |
| 44 | +static int smc_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, | |
| 45 | + bool tx) | |
| 46 | +{ | |
| 47 | + struct device *cdev = cinfo->dev; | |
| 48 | + struct scmi_smc *scmi_info; | |
| 49 | + resource_size_t size; | |
| 50 | + struct resource res; | |
| 51 | + struct device_node *np; | |
| 52 | + u32 func_id; | |
| 53 | + int ret; | |
| 54 | + | |
| 55 | + if (!tx) | |
| 56 | + return -ENODEV; | |
| 57 | + | |
| 58 | + scmi_info = devm_kzalloc(dev, sizeof(*scmi_info), GFP_KERNEL); | |
| 59 | + if (!scmi_info) | |
| 60 | + return -ENOMEM; | |
| 61 | + | |
| 62 | + np = of_parse_phandle(cdev->of_node, "shmem", 0); | |
| 63 | + ret = of_address_to_resource(np, 0, &res); | |
| 64 | + of_node_put(np); | |
| 65 | + if (ret) { | |
| 66 | + dev_err(cdev, "failed to get SCMI Tx shared memory\n"); | |
| 67 | + return ret; | |
| 68 | + } | |
| 69 | + | |
| 70 | + size = resource_size(&res); | |
| 71 | + scmi_info->shmem = devm_ioremap(dev, res.start, size); | |
| 72 | + if (!scmi_info->shmem) { | |
| 73 | + dev_err(dev, "failed to ioremap SCMI Tx shared memory\n"); | |
| 74 | + return -EADDRNOTAVAIL; | |
| 75 | + } | |
| 76 | + | |
| 77 | + ret = of_property_read_u32(dev->of_node, "arm,smc-id", &func_id); | |
| 78 | + if (ret < 0) | |
| 79 | + return ret; | |
| 80 | + | |
| 81 | + scmi_info->func_id = func_id; | |
| 82 | + scmi_info->cinfo = cinfo; | |
| 83 | + mutex_init(&scmi_info->shmem_lock); | |
| 84 | + cinfo->transport_info = scmi_info; | |
| 85 | + | |
| 86 | + return 0; | |
| 87 | +} | |
| 88 | + | |
| 89 | +static int smc_chan_free(int id, void *p, void *data) | |
| 90 | +{ | |
| 91 | + struct scmi_chan_info *cinfo = p; | |
| 92 | + struct scmi_smc *scmi_info = cinfo->transport_info; | |
| 93 | + | |
| 94 | + cinfo->transport_info = NULL; | |
| 95 | + scmi_info->cinfo = NULL; | |
| 96 | + | |
| 97 | + scmi_free_channel(cinfo, data, id); | |
| 98 | + | |
| 99 | + return 0; | |
| 100 | +} | |
| 101 | + | |
| 102 | +static int smc_send_message(struct scmi_chan_info *cinfo, | |
| 103 | + struct scmi_xfer *xfer) | |
| 104 | +{ | |
| 105 | + struct scmi_smc *scmi_info = cinfo->transport_info; | |
| 106 | + struct arm_smccc_res res; | |
| 107 | + | |
| 108 | + mutex_lock(&scmi_info->shmem_lock); | |
| 109 | + | |
| 110 | + shmem_tx_prepare(scmi_info->shmem, xfer); | |
| 111 | + | |
| 112 | + arm_smccc_1_1_invoke(scmi_info->func_id, 0, 0, 0, 0, 0, 0, 0, &res); | |
| 113 | + scmi_rx_callback(scmi_info->cinfo, shmem_read_header(scmi_info->shmem)); | |
| 114 | + | |
| 115 | + mutex_unlock(&scmi_info->shmem_lock); | |
| 116 | + | |
| 117 | + /* Only SMCCC_RET_NOT_SUPPORTED is valid error code */ | |
| 118 | + if (res.a0) | |
| 119 | + return -EOPNOTSUPP; | |
| 120 | + return 0; | |
| 121 | +} | |
| 122 | + | |
| 123 | +static void smc_fetch_response(struct scmi_chan_info *cinfo, | |
| 124 | + struct scmi_xfer *xfer) | |
| 125 | +{ | |
| 126 | + struct scmi_smc *scmi_info = cinfo->transport_info; | |
| 127 | + | |
| 128 | + shmem_fetch_response(scmi_info->shmem, xfer); | |
| 129 | +} | |
| 130 | + | |
| 131 | +static bool | |
| 132 | +smc_poll_done(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer) | |
| 133 | +{ | |
| 134 | + struct scmi_smc *scmi_info = cinfo->transport_info; | |
| 135 | + | |
| 136 | + return shmem_poll_done(scmi_info->shmem, xfer); | |
| 137 | +} | |
| 138 | + | |
| 139 | +static struct scmi_transport_ops scmi_smc_ops = { | |
| 140 | + .chan_available = smc_chan_available, | |
| 141 | + .chan_setup = smc_chan_setup, | |
| 142 | + .chan_free = smc_chan_free, | |
| 143 | + .send_message = smc_send_message, | |
| 144 | + .fetch_response = smc_fetch_response, | |
| 145 | + .poll_done = smc_poll_done, | |
| 146 | +}; | |
| 147 | + | |
| 148 | +const struct scmi_desc scmi_smc_desc = { | |
| 149 | + .ops = &scmi_smc_ops, | |
| 150 | + .max_rx_timeout_ms = 30, | |
| 151 | + .max_msg = 1, | |
| 152 | + .max_msg_size = 128, | |
| 153 | +}; | |
| @@ -4,6 +4,10 @@ · 3 unmodified lines | ||
| 4 | 4 | * |
| 5 | 5 | * Copyright (C) 2018 ARM Ltd. |
| 6 | 6 | */ |
| 7 | + | |
| 8 | +#ifndef _LINUX_SCMI_PROTOCOL_H | |
| 9 | +#define _LINUX_SCMI_PROTOCOL_H | |
| 10 | + | |
| 7 | 11 | #include <linux/device.h> |
| 8 | 12 | #include <linux/types.h> |
| 9 | 13 | |
| @@ -319,3 +323,5 @@ static inline void scmi_driver_unregister(struct scmi_driver *driver) {} · 309 unmodified lines | ||
| 319 | 323 | typedef int (*scmi_prot_init_fn_t)(struct scmi_handle *); |
| 320 | 324 | int scmi_protocol_register(int protocol_id, scmi_prot_init_fn_t fn); |
| 321 | 325 | void scmi_protocol_unregister(int protocol_id); |
| 326 | + | |
| 327 | +#endif /* _LINUX_SCMI_PROTOCOL_H */ | |
| @@ -4,6 +4,10 @@ · 3 unmodified lines | ||
| 4 | 4 | * |
| 5 | 5 | * Copyright (C) 2014 ARM Ltd. |
| 6 | 6 | */ |
| 7 | + | |
| 8 | +#ifndef _LINUX_SCPI_PROTOCOL_H | |
| 9 | +#define _LINUX_SCPI_PROTOCOL_H | |
| 10 | + | |
| 7 | 11 | #include <linux/types.h> |
| 8 | 12 | |
| 9 | 13 | struct scpi_opp { |
| @@ -71,3 +75,5 @@ struct scpi_ops *get_scpi_ops(void); · 61 unmodified lines | ||
| 71 | 75 | #else |
| 72 | 76 | static inline struct scpi_ops *get_scpi_ops(void) { return NULL; } |
| 73 | 77 | #endif |
| 78 | + | |
| 79 | +#endif /* _LINUX_SCPI_PROTOCOL_H */ | |