| .. | .. |
|---|
| 9 | 9 | #include <linux/arm-smccc.h> |
|---|
| 10 | 10 | #include <linux/device.h> |
|---|
| 11 | 11 | #include <linux/err.h> |
|---|
| 12 | +#include <linux/interrupt.h> |
|---|
| 12 | 13 | #include <linux/mutex.h> |
|---|
| 13 | 14 | #include <linux/of.h> |
|---|
| 14 | 15 | #include <linux/of_address.h> |
|---|
| 16 | +#include <linux/of_irq.h> |
|---|
| 15 | 17 | #include <linux/slab.h> |
|---|
| 16 | 18 | |
|---|
| 17 | 19 | #include "common.h" |
|---|
| .. | .. |
|---|
| 23 | 25 | * @shmem: Transmit/Receive shared memory area |
|---|
| 24 | 26 | * @shmem_lock: Lock to protect access to Tx/Rx shared memory area |
|---|
| 25 | 27 | * @func_id: smc/hvc call function id |
|---|
| 28 | + * @irq: Optional; employed when platforms indicates msg completion by intr. |
|---|
| 29 | + * @tx_complete: Optional, employed only when irq is valid. |
|---|
| 26 | 30 | */ |
|---|
| 27 | 31 | |
|---|
| 28 | 32 | struct scmi_smc { |
|---|
| .. | .. |
|---|
| 30 | 34 | struct scmi_shared_mem __iomem *shmem; |
|---|
| 31 | 35 | struct mutex shmem_lock; |
|---|
| 32 | 36 | u32 func_id; |
|---|
| 37 | + int irq; |
|---|
| 38 | + struct completion tx_complete; |
|---|
| 33 | 39 | }; |
|---|
| 40 | + |
|---|
| 41 | +static irqreturn_t smc_msg_done_isr(int irq, void *data) |
|---|
| 42 | +{ |
|---|
| 43 | + struct scmi_smc *scmi_info = data; |
|---|
| 44 | + |
|---|
| 45 | + complete(&scmi_info->tx_complete); |
|---|
| 46 | + |
|---|
| 47 | + return IRQ_HANDLED; |
|---|
| 48 | +} |
|---|
| 34 | 49 | |
|---|
| 35 | 50 | static bool smc_chan_available(struct device *dev, int idx) |
|---|
| 36 | 51 | { |
|---|
| .. | .. |
|---|
| 51 | 66 | struct resource res; |
|---|
| 52 | 67 | struct device_node *np; |
|---|
| 53 | 68 | u32 func_id; |
|---|
| 54 | | - int ret; |
|---|
| 69 | + int ret, irq; |
|---|
| 55 | 70 | |
|---|
| 56 | 71 | if (!tx) |
|---|
| 57 | 72 | return -ENODEV; |
|---|
| .. | .. |
|---|
| 78 | 93 | ret = of_property_read_u32(dev->of_node, "arm,smc-id", &func_id); |
|---|
| 79 | 94 | if (ret < 0) |
|---|
| 80 | 95 | return ret; |
|---|
| 96 | + |
|---|
| 97 | + /* |
|---|
| 98 | + * If there is an interrupt named "a2p", then the service and |
|---|
| 99 | + * completion of a message is signaled by an interrupt rather than by |
|---|
| 100 | + * the return of the SMC call. |
|---|
| 101 | + */ |
|---|
| 102 | + irq = of_irq_get_byname(cdev->of_node, "a2p"); |
|---|
| 103 | + if (irq > 0) { |
|---|
| 104 | + ret = devm_request_irq(dev, irq, smc_msg_done_isr, |
|---|
| 105 | + IRQF_NO_SUSPEND, |
|---|
| 106 | + dev_name(dev), scmi_info); |
|---|
| 107 | + if (ret) { |
|---|
| 108 | + dev_err(dev, "failed to setup SCMI smc irq\n"); |
|---|
| 109 | + return ret; |
|---|
| 110 | + } |
|---|
| 111 | + init_completion(&scmi_info->tx_complete); |
|---|
| 112 | + scmi_info->irq = irq; |
|---|
| 113 | + } |
|---|
| 81 | 114 | |
|---|
| 82 | 115 | scmi_info->func_id = func_id; |
|---|
| 83 | 116 | scmi_info->cinfo = cinfo; |
|---|
| .. | .. |
|---|
| 110 | 143 | |
|---|
| 111 | 144 | shmem_tx_prepare(scmi_info->shmem, xfer); |
|---|
| 112 | 145 | |
|---|
| 146 | + if (scmi_info->irq) |
|---|
| 147 | + reinit_completion(&scmi_info->tx_complete); |
|---|
| 148 | + |
|---|
| 113 | 149 | arm_smccc_1_1_invoke(scmi_info->func_id, 0, 0, 0, 0, 0, 0, 0, &res); |
|---|
| 150 | + |
|---|
| 151 | + if (scmi_info->irq) |
|---|
| 152 | + wait_for_completion(&scmi_info->tx_complete); |
|---|
| 153 | + |
|---|
| 114 | 154 | scmi_rx_callback(scmi_info->cinfo, shmem_read_header(scmi_info->shmem)); |
|---|
| 115 | 155 | |
|---|
| 116 | 156 | mutex_unlock(&scmi_info->shmem_lock); |
|---|
| .. | .. |
|---|
| 137 | 177 | return shmem_poll_done(scmi_info->shmem, xfer); |
|---|
| 138 | 178 | } |
|---|
| 139 | 179 | |
|---|
| 140 | | -static struct scmi_transport_ops scmi_smc_ops = { |
|---|
| 180 | +static const struct scmi_transport_ops scmi_smc_ops = { |
|---|
| 141 | 181 | .chan_available = smc_chan_available, |
|---|
| 142 | 182 | .chan_setup = smc_chan_setup, |
|---|
| 143 | 183 | .chan_free = smc_chan_free, |
|---|