Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 //
0003 // Copyright(c) 2020 Intel Corporation. All rights reserved.
0004 //
0005 // Author: Cezary Rojewski <cezary.rojewski@intel.com>
0006 //
0007 
0008 #include <linux/irqreturn.h>
0009 #include "core.h"
0010 #include "messages.h"
0011 #include "registers.h"
0012 #include "trace.h"
0013 
0014 #define CATPT_IPC_TIMEOUT_MS    300
0015 
0016 void catpt_ipc_init(struct catpt_ipc *ipc, struct device *dev)
0017 {
0018     ipc->dev = dev;
0019     ipc->ready = false;
0020     ipc->default_timeout = CATPT_IPC_TIMEOUT_MS;
0021     init_completion(&ipc->done_completion);
0022     init_completion(&ipc->busy_completion);
0023     spin_lock_init(&ipc->lock);
0024     mutex_init(&ipc->mutex);
0025 }
0026 
0027 static int catpt_ipc_arm(struct catpt_ipc *ipc, struct catpt_fw_ready *config)
0028 {
0029     /*
0030      * Both tx and rx are put into and received from outbox. Inbox is
0031      * only used for notifications where payload size is known upfront,
0032      * thus no separate buffer is allocated for it.
0033      */
0034     ipc->rx.data = devm_kzalloc(ipc->dev, config->outbox_size, GFP_KERNEL);
0035     if (!ipc->rx.data)
0036         return -ENOMEM;
0037 
0038     memcpy(&ipc->config, config, sizeof(*config));
0039     ipc->ready = true;
0040 
0041     return 0;
0042 }
0043 
0044 static void catpt_ipc_msg_init(struct catpt_ipc *ipc,
0045                    struct catpt_ipc_msg *reply)
0046 {
0047     lockdep_assert_held(&ipc->lock);
0048 
0049     ipc->rx.header = 0;
0050     ipc->rx.size = reply ? reply->size : 0;
0051     reinit_completion(&ipc->done_completion);
0052     reinit_completion(&ipc->busy_completion);
0053 }
0054 
0055 static void catpt_dsp_send_tx(struct catpt_dev *cdev,
0056                   const struct catpt_ipc_msg *tx)
0057 {
0058     u32 header = tx->header | CATPT_IPCC_BUSY;
0059 
0060     trace_catpt_ipc_request(header);
0061     trace_catpt_ipc_payload(tx->data, tx->size);
0062 
0063     memcpy_toio(catpt_outbox_addr(cdev), tx->data, tx->size);
0064     catpt_writel_shim(cdev, IPCC, header);
0065 }
0066 
0067 static int catpt_wait_msg_completion(struct catpt_dev *cdev, int timeout)
0068 {
0069     struct catpt_ipc *ipc = &cdev->ipc;
0070     int ret;
0071 
0072     ret = wait_for_completion_timeout(&ipc->done_completion,
0073                       msecs_to_jiffies(timeout));
0074     if (!ret)
0075         return -ETIMEDOUT;
0076     if (ipc->rx.rsp.status != CATPT_REPLY_PENDING)
0077         return 0;
0078 
0079     /* wait for delayed reply */
0080     ret = wait_for_completion_timeout(&ipc->busy_completion,
0081                       msecs_to_jiffies(timeout));
0082     return ret ? 0 : -ETIMEDOUT;
0083 }
0084 
0085 static int catpt_dsp_do_send_msg(struct catpt_dev *cdev,
0086                  struct catpt_ipc_msg request,
0087                  struct catpt_ipc_msg *reply, int timeout)
0088 {
0089     struct catpt_ipc *ipc = &cdev->ipc;
0090     unsigned long flags;
0091     int ret;
0092 
0093     if (!ipc->ready)
0094         return -EPERM;
0095     if (request.size > ipc->config.outbox_size ||
0096         (reply && reply->size > ipc->config.outbox_size))
0097         return -EINVAL;
0098 
0099     spin_lock_irqsave(&ipc->lock, flags);
0100     catpt_ipc_msg_init(ipc, reply);
0101     catpt_dsp_send_tx(cdev, &request);
0102     spin_unlock_irqrestore(&ipc->lock, flags);
0103 
0104     ret = catpt_wait_msg_completion(cdev, timeout);
0105     if (ret) {
0106         dev_crit(cdev->dev, "communication severed: %d, rebooting dsp..\n",
0107              ret);
0108         ipc->ready = false;
0109         /* TODO: attempt recovery */
0110         return ret;
0111     }
0112 
0113     ret = ipc->rx.rsp.status;
0114     if (reply) {
0115         reply->header = ipc->rx.header;
0116 
0117         if (!ret && reply->data)
0118             memcpy(reply->data, ipc->rx.data, reply->size);
0119     }
0120 
0121     return ret;
0122 }
0123 
0124 int catpt_dsp_send_msg_timeout(struct catpt_dev *cdev,
0125                    struct catpt_ipc_msg request,
0126                    struct catpt_ipc_msg *reply, int timeout)
0127 {
0128     struct catpt_ipc *ipc = &cdev->ipc;
0129     int ret;
0130 
0131     mutex_lock(&ipc->mutex);
0132     ret = catpt_dsp_do_send_msg(cdev, request, reply, timeout);
0133     mutex_unlock(&ipc->mutex);
0134 
0135     return ret;
0136 }
0137 
0138 int catpt_dsp_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request,
0139                struct catpt_ipc_msg *reply)
0140 {
0141     return catpt_dsp_send_msg_timeout(cdev, request, reply,
0142                       cdev->ipc.default_timeout);
0143 }
0144 
0145 static void
0146 catpt_dsp_notify_stream(struct catpt_dev *cdev, union catpt_notify_msg msg)
0147 {
0148     struct catpt_stream_runtime *stream;
0149     struct catpt_notify_position pos;
0150     struct catpt_notify_glitch glitch;
0151 
0152     stream = catpt_stream_find(cdev, msg.stream_hw_id);
0153     if (!stream) {
0154         dev_warn(cdev->dev, "notify %d for non-existent stream %d\n",
0155              msg.notify_reason, msg.stream_hw_id);
0156         return;
0157     }
0158 
0159     switch (msg.notify_reason) {
0160     case CATPT_NOTIFY_POSITION_CHANGED:
0161         memcpy_fromio(&pos, catpt_inbox_addr(cdev), sizeof(pos));
0162         trace_catpt_ipc_payload((u8 *)&pos, sizeof(pos));
0163 
0164         catpt_stream_update_position(cdev, stream, &pos);
0165         break;
0166 
0167     case CATPT_NOTIFY_GLITCH_OCCURRED:
0168         memcpy_fromio(&glitch, catpt_inbox_addr(cdev), sizeof(glitch));
0169         trace_catpt_ipc_payload((u8 *)&glitch, sizeof(glitch));
0170 
0171         dev_warn(cdev->dev, "glitch %d at pos: 0x%08llx, wp: 0x%08x\n",
0172              glitch.type, glitch.presentation_pos,
0173              glitch.write_pos);
0174         break;
0175 
0176     default:
0177         dev_warn(cdev->dev, "unknown notification: %d received\n",
0178              msg.notify_reason);
0179         break;
0180     }
0181 }
0182 
0183 static void catpt_dsp_copy_rx(struct catpt_dev *cdev, u32 header)
0184 {
0185     struct catpt_ipc *ipc = &cdev->ipc;
0186 
0187     ipc->rx.header = header;
0188     if (ipc->rx.rsp.status != CATPT_REPLY_SUCCESS)
0189         return;
0190 
0191     memcpy_fromio(ipc->rx.data, catpt_outbox_addr(cdev), ipc->rx.size);
0192     trace_catpt_ipc_payload(ipc->rx.data, ipc->rx.size);
0193 }
0194 
0195 static void catpt_dsp_process_response(struct catpt_dev *cdev, u32 header)
0196 {
0197     union catpt_notify_msg msg = CATPT_MSG(header);
0198     struct catpt_ipc *ipc = &cdev->ipc;
0199 
0200     if (msg.fw_ready) {
0201         struct catpt_fw_ready config;
0202         /* to fit 32b header original address is shifted right by 3 */
0203         u32 off = msg.mailbox_address << 3;
0204 
0205         memcpy_fromio(&config, cdev->lpe_ba + off, sizeof(config));
0206         trace_catpt_ipc_payload((u8 *)&config, sizeof(config));
0207 
0208         catpt_ipc_arm(ipc, &config);
0209         complete(&cdev->fw_ready);
0210         return;
0211     }
0212 
0213     switch (msg.global_msg_type) {
0214     case CATPT_GLB_REQUEST_CORE_DUMP:
0215         dev_err(cdev->dev, "ADSP device coredump received\n");
0216         ipc->ready = false;
0217         catpt_coredump(cdev);
0218         /* TODO: attempt recovery */
0219         break;
0220 
0221     case CATPT_GLB_STREAM_MESSAGE:
0222         switch (msg.stream_msg_type) {
0223         case CATPT_STRM_NOTIFICATION:
0224             catpt_dsp_notify_stream(cdev, msg);
0225             break;
0226         default:
0227             catpt_dsp_copy_rx(cdev, header);
0228             /* signal completion of delayed reply */
0229             complete(&ipc->busy_completion);
0230             break;
0231         }
0232         break;
0233 
0234     default:
0235         dev_warn(cdev->dev, "unknown response: %d received\n",
0236              msg.global_msg_type);
0237         break;
0238     }
0239 }
0240 
0241 irqreturn_t catpt_dsp_irq_thread(int irq, void *dev_id)
0242 {
0243     struct catpt_dev *cdev = dev_id;
0244     u32 ipcd;
0245 
0246     ipcd = catpt_readl_shim(cdev, IPCD);
0247     trace_catpt_ipc_notify(ipcd);
0248 
0249     /* ensure there is delayed reply or notification to process */
0250     if (!(ipcd & CATPT_IPCD_BUSY))
0251         return IRQ_NONE;
0252 
0253     catpt_dsp_process_response(cdev, ipcd);
0254 
0255     /* tell DSP processing is completed */
0256     catpt_updatel_shim(cdev, IPCD, CATPT_IPCD_BUSY | CATPT_IPCD_DONE,
0257                CATPT_IPCD_DONE);
0258     /* unmask dsp BUSY interrupt */
0259     catpt_updatel_shim(cdev, IMC, CATPT_IMC_IPCDB, 0);
0260 
0261     return IRQ_HANDLED;
0262 }
0263 
0264 irqreturn_t catpt_dsp_irq_handler(int irq, void *dev_id)
0265 {
0266     struct catpt_dev *cdev = dev_id;
0267     irqreturn_t ret = IRQ_NONE;
0268     u32 isc, ipcc;
0269 
0270     isc = catpt_readl_shim(cdev, ISC);
0271     trace_catpt_irq(isc);
0272 
0273     /* immediate reply */
0274     if (isc & CATPT_ISC_IPCCD) {
0275         /* mask host DONE interrupt */
0276         catpt_updatel_shim(cdev, IMC, CATPT_IMC_IPCCD, CATPT_IMC_IPCCD);
0277 
0278         ipcc = catpt_readl_shim(cdev, IPCC);
0279         trace_catpt_ipc_reply(ipcc);
0280         catpt_dsp_copy_rx(cdev, ipcc);
0281         complete(&cdev->ipc.done_completion);
0282 
0283         /* tell DSP processing is completed */
0284         catpt_updatel_shim(cdev, IPCC, CATPT_IPCC_DONE, 0);
0285         /* unmask host DONE interrupt */
0286         catpt_updatel_shim(cdev, IMC, CATPT_IMC_IPCCD, 0);
0287         ret = IRQ_HANDLED;
0288     }
0289 
0290     /* delayed reply or notification */
0291     if (isc & CATPT_ISC_IPCDB) {
0292         /* mask dsp BUSY interrupt */
0293         catpt_updatel_shim(cdev, IMC, CATPT_IMC_IPCDB, CATPT_IMC_IPCDB);
0294         ret = IRQ_WAKE_THREAD;
0295     }
0296 
0297     return ret;
0298 }