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 | The scmi node with the following properties shall be under the /firmware/ node. | 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 | - mboxes: List of phandle and mailbox channel specifiers. It should contain | 18 | - mboxes: List of phandle and mailbox channel specifiers. It should contain |
| 19 | exactly one or two mailboxes, one for transmitting messages("tx") | 19 | exactly one or two mailboxes, one for transmitting messages("tx") |
| 20 | and another optional for receiving the notifications("rx") if | 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 | protocol identifier for a given sub-node. | 25 | protocol identifier for a given sub-node. |
| 26 | - #size-cells : should be '0' as 'reg' property doesn't have any size | 26 | - #size-cells : should be '0' as 'reg' property doesn't have any size |
| 27 | associated with it. | 27 | associated with it. |
| 28 | - arm,smc-id : SMC id required when using smc or hvc transports | ||
| 28 | 29 | ||
| 29 | Optional properties: | 30 | Optional properties: |
| 30 | 31 | ||
| @@ -2,6 +2,8 @@ · 1 unmodified line | |||
| 2 | obj-y = scmi-bus.o scmi-driver.o scmi-protocols.o scmi-transport.o | 2 | obj-y = scmi-bus.o scmi-driver.o scmi-protocols.o scmi-transport.o |
| 3 | scmi-bus-y = bus.o | 3 | scmi-bus-y = bus.o |
| 4 | scmi-driver-y = driver.o | 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 | scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o | 8 | scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o |
| 7 | obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o | 9 | obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o |
| @@ -14,6 +14,13 @@ enum scmi_base_protocol_cmd { · 13 unmodified lines | |||
| 14 | BASE_DISCOVER_LIST_PROTOCOLS = 0x6, | 14 | BASE_DISCOVER_LIST_PROTOCOLS = 0x6, |
| 15 | BASE_DISCOVER_AGENT = 0x7, | 15 | BASE_DISCOVER_AGENT = 0x7, |
| 16 | BASE_NOTIFY_ERRORS = 0x8, | 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 | struct scmi_msg_resp_base_attributes { | 26 | struct scmi_msg_resp_base_attributes { |
| @@ -178,6 +178,8 @@ struct scmi_chan_info { · 177 unmodified lines | |||
| 178 | * @send_message: Callback to send a message | 178 | * @send_message: Callback to send a message |
| 179 | * @mark_txdone: Callback to mark tx as done | 179 | * @mark_txdone: Callback to mark tx as done |
| 180 | * @fetch_response: Callback to fetch response | 180 | * @fetch_response: Callback to fetch response |
| 181 | * @fetch_notification: Callback to fetch notification | ||
| 182 | * @clear_channel: Callback to clear a channel | ||
| 181 | * @poll_done: Callback to poll transfer status | 183 | * @poll_done: Callback to poll transfer status |
| 182 | */ | 184 | */ |
| 183 | struct scmi_transport_ops { | 185 | struct scmi_transport_ops { |
| @@ -190,6 +192,9 @@ struct scmi_transport_ops { · 6 unmodified lines | |||
| 190 | void (*mark_txdone)(struct scmi_chan_info *cinfo, int ret); | 192 | void (*mark_txdone)(struct scmi_chan_info *cinfo, int ret); |
| 191 | void (*fetch_response)(struct scmi_chan_info *cinfo, | 193 | void (*fetch_response)(struct scmi_chan_info *cinfo, |
| 192 | struct scmi_xfer *xfer); | 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 | bool (*poll_done)(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer); | 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 | extern const struct scmi_desc scmi_mailbox_desc; | 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 | void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr); | 222 | void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr); |
| 215 | void scmi_free_channel(struct scmi_chan_info *cinfo, struct idr *idr, int id); | 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 | u32 shmem_read_header(struct scmi_shared_mem __iomem *shmem); | 230 | u32 shmem_read_header(struct scmi_shared_mem __iomem *shmem); |
| 223 | void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem, | 231 | void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem, |
| 224 | struct scmi_xfer *xfer); | 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 | bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem, | 236 | bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem, |
| 226 | struct scmi_xfer *xfer); | 237 | struct scmi_xfer *xfer); |
| @@ -76,6 +76,7 @@ struct scmi_xfers_info { · 75 unmodified lines | |||
| 76 | * implementation version and (sub-)vendor identification. | 76 | * implementation version and (sub-)vendor identification. |
| 77 | * @handle: Instance of SCMI handle to send to clients | 77 | * @handle: Instance of SCMI handle to send to clients |
| 78 | * @tx_minfo: Universal Transmit Message management info | 78 | * @tx_minfo: Universal Transmit Message management info |
| 79 | * @rx_minfo: Universal Receive Message management info | ||
| 79 | * @tx_idr: IDR object to map protocol id to Tx channel info pointer | 80 | * @tx_idr: IDR object to map protocol id to Tx channel info pointer |
| 80 | * @rx_idr: IDR object to map protocol id to Rx channel info pointer | 81 | * @rx_idr: IDR object to map protocol id to Rx channel info pointer |
| 81 | * @protocols_imp: List of protocols implemented, currently maximum of | 82 | * @protocols_imp: List of protocols implemented, currently maximum of |
| @@ -89,6 +90,7 @@ struct scmi_info { · 7 unmodified lines | |||
| 89 | struct scmi_revision_info version; | 90 | struct scmi_revision_info version; |
| 90 | struct scmi_handle handle; | 91 | struct scmi_handle handle; |
| 91 | struct scmi_xfers_info tx_minfo; | 92 | struct scmi_xfers_info tx_minfo; |
| 93 | struct scmi_xfers_info rx_minfo; | ||
| 92 | struct idr tx_idr; | 94 | struct idr tx_idr; |
| 93 | struct idr rx_idr; | 95 | struct idr rx_idr; |
| 94 | u8 *protocols_imp; | 96 | u8 *protocols_imp; |
| @@ -200,37 +202,66 @@ __scmi_xfer_put(struct scmi_xfers_info *minfo, struct scmi_xfer *xfer) · 105 unmodified lines | |||
| 200 | spin_unlock_irqrestore(&minfo->xfer_lock, flags); | 202 | spin_unlock_irqrestore(&minfo->xfer_lock, flags); |
| 201 | } | 203 | } |
| 202 | 204 | ||
| 203 | /** | 205 | static void scmi_handle_notification(struct scmi_chan_info *cinfo, u32 msg_hdr) |
| 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) | ||
| 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 | struct scmi_xfer *xfer; | 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) | 212 | xfer = scmi_xfer_get(cinfo->handle, minfo); |
| 225 | return; /* Notifications not yet supported */ | 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 | /* Are we even expecting this? */ | 242 | /* Are we even expecting this? */ |
| 228 | if (!test_bit(xfer_id, minfo->xfer_alloc_table)) { | 243 | if (!test_bit(xfer_id, minfo->xfer_alloc_table)) { |
| 229 | dev_err(dev, "message for %d is not expected!\n", xfer_id); | 244 | dev_err(dev, "message for %d is not expected!\n", xfer_id); |
| 245 | info->desc->ops->clear_channel(cinfo); | ||
| 230 | return; | 246 | return; |
| 231 | } | 247 | } |
| 232 | 248 | ||
| 233 | xfer = &minfo->xfer_block[xfer_id]; | 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 | scmi_dump_header_dbg(dev, &xfer->hdr); | 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 | xfer->hdr.protocol_id, xfer->hdr.seq, | 271 | xfer->hdr.protocol_id, xfer->hdr.seq, |
| 241 | msg_type); | 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 | complete(xfer->async_done); | 276 | complete(xfer->async_done); |
| 245 | else | 277 | } else { |
| 246 | complete(&xfer->done); | 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 | return 0; | 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 | int i; | 595 | int i; |
| 531 | struct scmi_xfer *xfer; | 596 | struct scmi_xfer *xfer; |
| 532 | struct device *dev = sinfo->dev; | 597 | struct device *dev = sinfo->dev; |
| 533 | const struct scmi_desc *desc = sinfo->desc; | 598 | const struct scmi_desc *desc = sinfo->desc; |
| 534 | struct scmi_xfers_info *info = &sinfo->tx_minfo; | ||
| 535 | 599 | ||
| 536 | /* Pre-allocated messages, no more than what hdr.seq can support */ | 600 | /* Pre-allocated messages, no more than what hdr.seq can support */ |
| 537 | if (WARN_ON(desc->max_msg >= MSG_TOKEN_MAX)) { | 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 | return 0; | 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 | static int scmi_chan_setup(struct scmi_info *info, struct device *dev, | 643 | static int scmi_chan_setup(struct scmi_info *info, struct device *dev, |
| 570 | int prot_id, bool tx) | 644 | int prot_id, bool tx) |
| 571 | { | 645 | { |
| @@ -699,10 +773,6 @@ static int scmi_probe(struct platform_device *pdev) · 127 unmodified lines | |||
| 699 | info->desc = desc; | 773 | info->desc = desc; |
| 700 | INIT_LIST_HEAD(&info->node); | 774 | INIT_LIST_HEAD(&info->node); |
| 701 | 775 | ||
| 702 | ret = scmi_xfer_info_init(info); | ||
| 703 | if (ret) | ||
| 704 | return ret; | ||
| 705 | |||
| 706 | platform_set_drvdata(pdev, info); | 776 | platform_set_drvdata(pdev, info); |
| 707 | idr_init(&info->tx_idr); | 777 | idr_init(&info->tx_idr); |
| 708 | idr_init(&info->rx_idr); | 778 | idr_init(&info->rx_idr); |
| @@ -715,6 +785,10 @@ static int scmi_probe(struct platform_device *pdev) · 6 unmodified lines | |||
| 715 | if (ret) | 785 | if (ret) |
| 716 | return ret; | 786 | return ret; |
| 717 | 787 | ||
| 788 | ret = scmi_xfer_info_init(info); | ||
| 789 | if (ret) | ||
| 790 | return ret; | ||
| 791 | |||
| 718 | ret = scmi_base_protocol_init(handle); | 792 | ret = scmi_base_protocol_init(handle); |
| 719 | if (ret) { | 793 | if (ret) { |
| 720 | dev_err(dev, "unable to communicate with SCMI(%d)\n", ret); | 794 | dev_err(dev, "unable to communicate with SCMI(%d)\n", ret); |
| @@ -827,6 +901,9 @@ ATTRIBUTE_GROUPS(versions); · 106 unmodified lines | |||
| 827 | /* Each compatible listed below must have descriptor associated with it */ | 901 | /* Each compatible listed below must have descriptor associated with it */ |
| 828 | static const struct of_device_id scmi_of_match[] = { | 902 | static const struct of_device_id scmi_of_match[] = { |
| 829 | { .compatible = "arm,scmi", .data = &scmi_mailbox_desc }, | 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 | { /* Sentinel */ }, | 907 | { /* Sentinel */ }, |
| 831 | }; | 908 | }; |
| 832 | 909 | ||
| @@ -158,6 +158,21 @@ static void mailbox_fetch_response(struct scmi_chan_info *cinfo, · 157 unmodified lines | |||
| 158 | shmem_fetch_response(smbox->shmem, xfer); | 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 | static bool | 176 | static bool |
| 162 | mailbox_poll_done(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer) | 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 | .send_message = mailbox_send_message, | 188 | .send_message = mailbox_send_message, |
| 174 | .mark_txdone = mailbox_mark_txdone, | 189 | .mark_txdone = mailbox_mark_txdone, |
| 175 | .fetch_response = mailbox_fetch_response, | 190 | .fetch_response = mailbox_fetch_response, |
| 191 | .fetch_notification = mailbox_fetch_notification, | ||
| 192 | .clear_channel = mailbox_clear_channel, | ||
| 176 | .poll_done = mailbox_poll_done, | 193 | .poll_done = mailbox_poll_done, |
| 177 | }; | 194 | }; |
| 178 | 195 | ||
| @@ -27,6 +27,11 @@ enum scmi_performance_protocol_cmd { · 26 unmodified lines | |||
| 27 | PERF_DESCRIBE_FASTCHANNEL = 0xb, | 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 | struct scmi_opp { | 35 | struct scmi_opp { |
| 31 | u32 perf; | 36 | u32 perf; |
| 32 | u32 power; | 37 | u32 power; |
| @@ -12,6 +12,12 @@ enum scmi_power_protocol_cmd { · 11 unmodified lines | |||
| 12 | POWER_STATE_SET = 0x4, | 12 | POWER_STATE_SET = 0x4, |
| 13 | POWER_STATE_GET = 0x5, | 13 | POWER_STATE_GET = 0x5, |
| 14 | POWER_STATE_NOTIFY = 0x6, | 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 | struct scmi_msg_resp_power_attributes { | 23 | struct scmi_msg_resp_power_attributes { |
| @@ -14,6 +14,10 @@ enum scmi_sensor_protocol_cmd { · 13 unmodified lines | |||
| 14 | SENSOR_READING_GET = 0x6, | 14 | SENSOR_READING_GET = 0x6, |
| 15 | }; | 15 | }; |
| 16 | 16 | ||
| 17 | enum scmi_sensor_protocol_notify { | ||
| 18 | SENSOR_TRIP_POINT_EVENT = 0x0, | ||
| 19 | }; | ||
| 20 | |||
| 17 | struct scmi_msg_resp_sensor_attributes { | 21 | struct scmi_msg_resp_sensor_attributes { |
| 18 | __le16 num_sensors; | 22 | __le16 num_sensors; |
| 19 | u8 max_requests; | 23 | u8 max_requests; |
| @@ -67,6 +67,21 @@ void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem, · 66 unmodified lines | |||
| 67 | memcpy_fromio(xfer->rx.buf, shmem->msg_payload + 4, xfer->rx.len); | 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 | bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem, | 85 | bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem, |
| 71 | struct scmi_xfer *xfer) | 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 | * Copyright (C) 2018 ARM Ltd. | 5 | * Copyright (C) 2018 ARM Ltd. |
| 6 | */ | 6 | */ |
| 7 | |||
| 8 | #ifndef _LINUX_SCMI_PROTOCOL_H | ||
| 9 | #define _LINUX_SCMI_PROTOCOL_H | ||
| 10 | |||
| 7 | #include <linux/device.h> | 11 | #include <linux/device.h> |
| 8 | #include <linux/types.h> | 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 | typedef int (*scmi_prot_init_fn_t)(struct scmi_handle *); | 323 | typedef int (*scmi_prot_init_fn_t)(struct scmi_handle *); |
| 320 | int scmi_protocol_register(int protocol_id, scmi_prot_init_fn_t fn); | 324 | int scmi_protocol_register(int protocol_id, scmi_prot_init_fn_t fn); |
| 321 | void scmi_protocol_unregister(int protocol_id); | 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 | * Copyright (C) 2014 ARM Ltd. | 5 | * Copyright (C) 2014 ARM Ltd. |
| 6 | */ | 6 | */ |
| 7 | |||
| 8 | #ifndef _LINUX_SCPI_PROTOCOL_H | ||
| 9 | #define _LINUX_SCPI_PROTOCOL_H | ||
| 10 | |||
| 7 | #include <linux/types.h> | 11 | #include <linux/types.h> |
| 8 | 12 | ||
| 9 | struct scpi_opp { | 13 | struct scpi_opp { |
| @@ -71,3 +75,5 @@ struct scpi_ops *get_scpi_ops(void); · 61 unmodified lines | |||
| 71 | #else | 75 | #else |
| 72 | static inline struct scpi_ops *get_scpi_ops(void) { return NULL; } | 76 | static inline struct scpi_ops *get_scpi_ops(void) { return NULL; } |
| 73 | #endif | 77 | #endif |
| 78 | |||
| 79 | #endif /* _LINUX_SCPI_PROTOCOL_H */ | ||