192946c1dSCezary Rojewski // SPDX-License-Identifier: GPL-2.0-only
292946c1dSCezary Rojewski //
392946c1dSCezary Rojewski // Copyright(c) 2020 Intel Corporation. All rights reserved.
492946c1dSCezary Rojewski //
592946c1dSCezary Rojewski // Author: Cezary Rojewski <cezary.rojewski@intel.com>
692946c1dSCezary Rojewski //
792946c1dSCezary Rojewski
892946c1dSCezary Rojewski #include <linux/irqreturn.h>
992946c1dSCezary Rojewski #include "core.h"
1092946c1dSCezary Rojewski #include "messages.h"
1192946c1dSCezary Rojewski #include "registers.h"
12*8ba1edb9SCezary Rojewski #include "trace.h"
1392946c1dSCezary Rojewski
1492946c1dSCezary Rojewski #define CATPT_IPC_TIMEOUT_MS 300
1592946c1dSCezary Rojewski
catpt_ipc_init(struct catpt_ipc * ipc,struct device * dev)1692946c1dSCezary Rojewski void catpt_ipc_init(struct catpt_ipc *ipc, struct device *dev)
1792946c1dSCezary Rojewski {
1892946c1dSCezary Rojewski ipc->dev = dev;
1992946c1dSCezary Rojewski ipc->ready = false;
2092946c1dSCezary Rojewski ipc->default_timeout = CATPT_IPC_TIMEOUT_MS;
2192946c1dSCezary Rojewski init_completion(&ipc->done_completion);
2292946c1dSCezary Rojewski init_completion(&ipc->busy_completion);
2392946c1dSCezary Rojewski spin_lock_init(&ipc->lock);
2492946c1dSCezary Rojewski mutex_init(&ipc->mutex);
2592946c1dSCezary Rojewski }
2692946c1dSCezary Rojewski
catpt_ipc_arm(struct catpt_ipc * ipc,struct catpt_fw_ready * config)2792946c1dSCezary Rojewski static int catpt_ipc_arm(struct catpt_ipc *ipc, struct catpt_fw_ready *config)
2892946c1dSCezary Rojewski {
2992946c1dSCezary Rojewski /*
3092946c1dSCezary Rojewski * Both tx and rx are put into and received from outbox. Inbox is
3192946c1dSCezary Rojewski * only used for notifications where payload size is known upfront,
3292946c1dSCezary Rojewski * thus no separate buffer is allocated for it.
3392946c1dSCezary Rojewski */
3492946c1dSCezary Rojewski ipc->rx.data = devm_kzalloc(ipc->dev, config->outbox_size, GFP_KERNEL);
3592946c1dSCezary Rojewski if (!ipc->rx.data)
3692946c1dSCezary Rojewski return -ENOMEM;
3792946c1dSCezary Rojewski
3892946c1dSCezary Rojewski memcpy(&ipc->config, config, sizeof(*config));
3992946c1dSCezary Rojewski ipc->ready = true;
4092946c1dSCezary Rojewski
4192946c1dSCezary Rojewski return 0;
4292946c1dSCezary Rojewski }
4392946c1dSCezary Rojewski
catpt_ipc_msg_init(struct catpt_ipc * ipc,struct catpt_ipc_msg * reply)4492946c1dSCezary Rojewski static void catpt_ipc_msg_init(struct catpt_ipc *ipc,
4592946c1dSCezary Rojewski struct catpt_ipc_msg *reply)
4692946c1dSCezary Rojewski {
4792946c1dSCezary Rojewski lockdep_assert_held(&ipc->lock);
4892946c1dSCezary Rojewski
4992946c1dSCezary Rojewski ipc->rx.header = 0;
5092946c1dSCezary Rojewski ipc->rx.size = reply ? reply->size : 0;
5192946c1dSCezary Rojewski reinit_completion(&ipc->done_completion);
5292946c1dSCezary Rojewski reinit_completion(&ipc->busy_completion);
5392946c1dSCezary Rojewski }
5492946c1dSCezary Rojewski
catpt_dsp_send_tx(struct catpt_dev * cdev,const struct catpt_ipc_msg * tx)5592946c1dSCezary Rojewski static void catpt_dsp_send_tx(struct catpt_dev *cdev,
5692946c1dSCezary Rojewski const struct catpt_ipc_msg *tx)
5792946c1dSCezary Rojewski {
5892946c1dSCezary Rojewski u32 header = tx->header | CATPT_IPCC_BUSY;
5992946c1dSCezary Rojewski
60*8ba1edb9SCezary Rojewski trace_catpt_ipc_request(header);
61*8ba1edb9SCezary Rojewski trace_catpt_ipc_payload(tx->data, tx->size);
62*8ba1edb9SCezary Rojewski
6392946c1dSCezary Rojewski memcpy_toio(catpt_outbox_addr(cdev), tx->data, tx->size);
6492946c1dSCezary Rojewski catpt_writel_shim(cdev, IPCC, header);
6592946c1dSCezary Rojewski }
6692946c1dSCezary Rojewski
catpt_wait_msg_completion(struct catpt_dev * cdev,int timeout)6792946c1dSCezary Rojewski static int catpt_wait_msg_completion(struct catpt_dev *cdev, int timeout)
6892946c1dSCezary Rojewski {
6992946c1dSCezary Rojewski struct catpt_ipc *ipc = &cdev->ipc;
7092946c1dSCezary Rojewski int ret;
7192946c1dSCezary Rojewski
7292946c1dSCezary Rojewski ret = wait_for_completion_timeout(&ipc->done_completion,
7392946c1dSCezary Rojewski msecs_to_jiffies(timeout));
7492946c1dSCezary Rojewski if (!ret)
7592946c1dSCezary Rojewski return -ETIMEDOUT;
7692946c1dSCezary Rojewski if (ipc->rx.rsp.status != CATPT_REPLY_PENDING)
7792946c1dSCezary Rojewski return 0;
7892946c1dSCezary Rojewski
7992946c1dSCezary Rojewski /* wait for delayed reply */
8092946c1dSCezary Rojewski ret = wait_for_completion_timeout(&ipc->busy_completion,
8192946c1dSCezary Rojewski msecs_to_jiffies(timeout));
8292946c1dSCezary Rojewski return ret ? 0 : -ETIMEDOUT;
8392946c1dSCezary Rojewski }
8492946c1dSCezary Rojewski
catpt_dsp_do_send_msg(struct catpt_dev * cdev,struct catpt_ipc_msg request,struct catpt_ipc_msg * reply,int timeout)8592946c1dSCezary Rojewski static int catpt_dsp_do_send_msg(struct catpt_dev *cdev,
8692946c1dSCezary Rojewski struct catpt_ipc_msg request,
8792946c1dSCezary Rojewski struct catpt_ipc_msg *reply, int timeout)
8892946c1dSCezary Rojewski {
8992946c1dSCezary Rojewski struct catpt_ipc *ipc = &cdev->ipc;
9092946c1dSCezary Rojewski unsigned long flags;
9192946c1dSCezary Rojewski int ret;
9292946c1dSCezary Rojewski
9392946c1dSCezary Rojewski if (!ipc->ready)
9492946c1dSCezary Rojewski return -EPERM;
9592946c1dSCezary Rojewski if (request.size > ipc->config.outbox_size ||
9692946c1dSCezary Rojewski (reply && reply->size > ipc->config.outbox_size))
9792946c1dSCezary Rojewski return -EINVAL;
9892946c1dSCezary Rojewski
9992946c1dSCezary Rojewski spin_lock_irqsave(&ipc->lock, flags);
10092946c1dSCezary Rojewski catpt_ipc_msg_init(ipc, reply);
10192946c1dSCezary Rojewski catpt_dsp_send_tx(cdev, &request);
10292946c1dSCezary Rojewski spin_unlock_irqrestore(&ipc->lock, flags);
10392946c1dSCezary Rojewski
10492946c1dSCezary Rojewski ret = catpt_wait_msg_completion(cdev, timeout);
10592946c1dSCezary Rojewski if (ret) {
10692946c1dSCezary Rojewski dev_crit(cdev->dev, "communication severed: %d, rebooting dsp..\n",
10792946c1dSCezary Rojewski ret);
10892946c1dSCezary Rojewski ipc->ready = false;
10992946c1dSCezary Rojewski /* TODO: attempt recovery */
11092946c1dSCezary Rojewski return ret;
11192946c1dSCezary Rojewski }
11292946c1dSCezary Rojewski
11392946c1dSCezary Rojewski ret = ipc->rx.rsp.status;
11492946c1dSCezary Rojewski if (reply) {
11592946c1dSCezary Rojewski reply->header = ipc->rx.header;
11692946c1dSCezary Rojewski
11792946c1dSCezary Rojewski if (!ret && reply->data)
11892946c1dSCezary Rojewski memcpy(reply->data, ipc->rx.data, reply->size);
11992946c1dSCezary Rojewski }
12092946c1dSCezary Rojewski
12192946c1dSCezary Rojewski return ret;
12292946c1dSCezary Rojewski }
12392946c1dSCezary Rojewski
catpt_dsp_send_msg_timeout(struct catpt_dev * cdev,struct catpt_ipc_msg request,struct catpt_ipc_msg * reply,int timeout)12492946c1dSCezary Rojewski int catpt_dsp_send_msg_timeout(struct catpt_dev *cdev,
12592946c1dSCezary Rojewski struct catpt_ipc_msg request,
12692946c1dSCezary Rojewski struct catpt_ipc_msg *reply, int timeout)
12792946c1dSCezary Rojewski {
12892946c1dSCezary Rojewski struct catpt_ipc *ipc = &cdev->ipc;
12992946c1dSCezary Rojewski int ret;
13092946c1dSCezary Rojewski
13192946c1dSCezary Rojewski mutex_lock(&ipc->mutex);
13292946c1dSCezary Rojewski ret = catpt_dsp_do_send_msg(cdev, request, reply, timeout);
13392946c1dSCezary Rojewski mutex_unlock(&ipc->mutex);
13492946c1dSCezary Rojewski
13592946c1dSCezary Rojewski return ret;
13692946c1dSCezary Rojewski }
13792946c1dSCezary Rojewski
catpt_dsp_send_msg(struct catpt_dev * cdev,struct catpt_ipc_msg request,struct catpt_ipc_msg * reply)13892946c1dSCezary Rojewski int catpt_dsp_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request,
13992946c1dSCezary Rojewski struct catpt_ipc_msg *reply)
14092946c1dSCezary Rojewski {
14192946c1dSCezary Rojewski return catpt_dsp_send_msg_timeout(cdev, request, reply,
14292946c1dSCezary Rojewski cdev->ipc.default_timeout);
14392946c1dSCezary Rojewski }
14492946c1dSCezary Rojewski
145a126750fSCezary Rojewski static void
catpt_dsp_notify_stream(struct catpt_dev * cdev,union catpt_notify_msg msg)146a126750fSCezary Rojewski catpt_dsp_notify_stream(struct catpt_dev *cdev, union catpt_notify_msg msg)
147a126750fSCezary Rojewski {
148a126750fSCezary Rojewski struct catpt_stream_runtime *stream;
149a126750fSCezary Rojewski struct catpt_notify_position pos;
150a126750fSCezary Rojewski struct catpt_notify_glitch glitch;
151a126750fSCezary Rojewski
152a126750fSCezary Rojewski stream = catpt_stream_find(cdev, msg.stream_hw_id);
153a126750fSCezary Rojewski if (!stream) {
154a126750fSCezary Rojewski dev_warn(cdev->dev, "notify %d for non-existent stream %d\n",
155a126750fSCezary Rojewski msg.notify_reason, msg.stream_hw_id);
156a126750fSCezary Rojewski return;
157a126750fSCezary Rojewski }
158a126750fSCezary Rojewski
159a126750fSCezary Rojewski switch (msg.notify_reason) {
160a126750fSCezary Rojewski case CATPT_NOTIFY_POSITION_CHANGED:
161a126750fSCezary Rojewski memcpy_fromio(&pos, catpt_inbox_addr(cdev), sizeof(pos));
162*8ba1edb9SCezary Rojewski trace_catpt_ipc_payload((u8 *)&pos, sizeof(pos));
163a126750fSCezary Rojewski
164a126750fSCezary Rojewski catpt_stream_update_position(cdev, stream, &pos);
165a126750fSCezary Rojewski break;
166a126750fSCezary Rojewski
167a126750fSCezary Rojewski case CATPT_NOTIFY_GLITCH_OCCURRED:
168a126750fSCezary Rojewski memcpy_fromio(&glitch, catpt_inbox_addr(cdev), sizeof(glitch));
169*8ba1edb9SCezary Rojewski trace_catpt_ipc_payload((u8 *)&glitch, sizeof(glitch));
170a126750fSCezary Rojewski
171a126750fSCezary Rojewski dev_warn(cdev->dev, "glitch %d at pos: 0x%08llx, wp: 0x%08x\n",
172a126750fSCezary Rojewski glitch.type, glitch.presentation_pos,
173a126750fSCezary Rojewski glitch.write_pos);
174a126750fSCezary Rojewski break;
175a126750fSCezary Rojewski
176a126750fSCezary Rojewski default:
177a126750fSCezary Rojewski dev_warn(cdev->dev, "unknown notification: %d received\n",
178a126750fSCezary Rojewski msg.notify_reason);
179a126750fSCezary Rojewski break;
180a126750fSCezary Rojewski }
181a126750fSCezary Rojewski }
182a126750fSCezary Rojewski
catpt_dsp_copy_rx(struct catpt_dev * cdev,u32 header)18392946c1dSCezary Rojewski static void catpt_dsp_copy_rx(struct catpt_dev *cdev, u32 header)
18492946c1dSCezary Rojewski {
18592946c1dSCezary Rojewski struct catpt_ipc *ipc = &cdev->ipc;
18692946c1dSCezary Rojewski
18792946c1dSCezary Rojewski ipc->rx.header = header;
18892946c1dSCezary Rojewski if (ipc->rx.rsp.status != CATPT_REPLY_SUCCESS)
18992946c1dSCezary Rojewski return;
19092946c1dSCezary Rojewski
19192946c1dSCezary Rojewski memcpy_fromio(ipc->rx.data, catpt_outbox_addr(cdev), ipc->rx.size);
192*8ba1edb9SCezary Rojewski trace_catpt_ipc_payload(ipc->rx.data, ipc->rx.size);
19392946c1dSCezary Rojewski }
19492946c1dSCezary Rojewski
catpt_dsp_process_response(struct catpt_dev * cdev,u32 header)19592946c1dSCezary Rojewski static void catpt_dsp_process_response(struct catpt_dev *cdev, u32 header)
19692946c1dSCezary Rojewski {
19792946c1dSCezary Rojewski union catpt_notify_msg msg = CATPT_MSG(header);
19892946c1dSCezary Rojewski struct catpt_ipc *ipc = &cdev->ipc;
19992946c1dSCezary Rojewski
20092946c1dSCezary Rojewski if (msg.fw_ready) {
20192946c1dSCezary Rojewski struct catpt_fw_ready config;
20292946c1dSCezary Rojewski /* to fit 32b header original address is shifted right by 3 */
20392946c1dSCezary Rojewski u32 off = msg.mailbox_address << 3;
20492946c1dSCezary Rojewski
20592946c1dSCezary Rojewski memcpy_fromio(&config, cdev->lpe_ba + off, sizeof(config));
206*8ba1edb9SCezary Rojewski trace_catpt_ipc_payload((u8 *)&config, sizeof(config));
20792946c1dSCezary Rojewski
20892946c1dSCezary Rojewski catpt_ipc_arm(ipc, &config);
20992946c1dSCezary Rojewski complete(&cdev->fw_ready);
21092946c1dSCezary Rojewski return;
21192946c1dSCezary Rojewski }
21292946c1dSCezary Rojewski
21392946c1dSCezary Rojewski switch (msg.global_msg_type) {
21492946c1dSCezary Rojewski case CATPT_GLB_REQUEST_CORE_DUMP:
21564b9b1b0SCezary Rojewski dev_err(cdev->dev, "ADSP device coredump received\n");
21664b9b1b0SCezary Rojewski ipc->ready = false;
21764b9b1b0SCezary Rojewski catpt_coredump(cdev);
21864b9b1b0SCezary Rojewski /* TODO: attempt recovery */
21992946c1dSCezary Rojewski break;
22092946c1dSCezary Rojewski
22192946c1dSCezary Rojewski case CATPT_GLB_STREAM_MESSAGE:
22292946c1dSCezary Rojewski switch (msg.stream_msg_type) {
22392946c1dSCezary Rojewski case CATPT_STRM_NOTIFICATION:
224a126750fSCezary Rojewski catpt_dsp_notify_stream(cdev, msg);
22592946c1dSCezary Rojewski break;
22692946c1dSCezary Rojewski default:
22792946c1dSCezary Rojewski catpt_dsp_copy_rx(cdev, header);
22892946c1dSCezary Rojewski /* signal completion of delayed reply */
22992946c1dSCezary Rojewski complete(&ipc->busy_completion);
23092946c1dSCezary Rojewski break;
23192946c1dSCezary Rojewski }
23292946c1dSCezary Rojewski break;
23392946c1dSCezary Rojewski
23492946c1dSCezary Rojewski default:
23592946c1dSCezary Rojewski dev_warn(cdev->dev, "unknown response: %d received\n",
23692946c1dSCezary Rojewski msg.global_msg_type);
23792946c1dSCezary Rojewski break;
23892946c1dSCezary Rojewski }
23992946c1dSCezary Rojewski }
24092946c1dSCezary Rojewski
catpt_dsp_irq_thread(int irq,void * dev_id)24192946c1dSCezary Rojewski irqreturn_t catpt_dsp_irq_thread(int irq, void *dev_id)
24292946c1dSCezary Rojewski {
24392946c1dSCezary Rojewski struct catpt_dev *cdev = dev_id;
24492946c1dSCezary Rojewski u32 ipcd;
24592946c1dSCezary Rojewski
24692946c1dSCezary Rojewski ipcd = catpt_readl_shim(cdev, IPCD);
247*8ba1edb9SCezary Rojewski trace_catpt_ipc_notify(ipcd);
24892946c1dSCezary Rojewski
24992946c1dSCezary Rojewski /* ensure there is delayed reply or notification to process */
25092946c1dSCezary Rojewski if (!(ipcd & CATPT_IPCD_BUSY))
25192946c1dSCezary Rojewski return IRQ_NONE;
25292946c1dSCezary Rojewski
25392946c1dSCezary Rojewski catpt_dsp_process_response(cdev, ipcd);
25492946c1dSCezary Rojewski
25592946c1dSCezary Rojewski /* tell DSP processing is completed */
25692946c1dSCezary Rojewski catpt_updatel_shim(cdev, IPCD, CATPT_IPCD_BUSY | CATPT_IPCD_DONE,
25792946c1dSCezary Rojewski CATPT_IPCD_DONE);
25892946c1dSCezary Rojewski /* unmask dsp BUSY interrupt */
25992946c1dSCezary Rojewski catpt_updatel_shim(cdev, IMC, CATPT_IMC_IPCDB, 0);
26092946c1dSCezary Rojewski
26192946c1dSCezary Rojewski return IRQ_HANDLED;
26292946c1dSCezary Rojewski }
26392946c1dSCezary Rojewski
catpt_dsp_irq_handler(int irq,void * dev_id)26492946c1dSCezary Rojewski irqreturn_t catpt_dsp_irq_handler(int irq, void *dev_id)
26592946c1dSCezary Rojewski {
26692946c1dSCezary Rojewski struct catpt_dev *cdev = dev_id;
26792946c1dSCezary Rojewski irqreturn_t ret = IRQ_NONE;
26892946c1dSCezary Rojewski u32 isc, ipcc;
26992946c1dSCezary Rojewski
27092946c1dSCezary Rojewski isc = catpt_readl_shim(cdev, ISC);
271*8ba1edb9SCezary Rojewski trace_catpt_irq(isc);
27292946c1dSCezary Rojewski
27392946c1dSCezary Rojewski /* immediate reply */
27492946c1dSCezary Rojewski if (isc & CATPT_ISC_IPCCD) {
27592946c1dSCezary Rojewski /* mask host DONE interrupt */
27692946c1dSCezary Rojewski catpt_updatel_shim(cdev, IMC, CATPT_IMC_IPCCD, CATPT_IMC_IPCCD);
27792946c1dSCezary Rojewski
27892946c1dSCezary Rojewski ipcc = catpt_readl_shim(cdev, IPCC);
279*8ba1edb9SCezary Rojewski trace_catpt_ipc_reply(ipcc);
28092946c1dSCezary Rojewski catpt_dsp_copy_rx(cdev, ipcc);
28192946c1dSCezary Rojewski complete(&cdev->ipc.done_completion);
28292946c1dSCezary Rojewski
28392946c1dSCezary Rojewski /* tell DSP processing is completed */
28492946c1dSCezary Rojewski catpt_updatel_shim(cdev, IPCC, CATPT_IPCC_DONE, 0);
28592946c1dSCezary Rojewski /* unmask host DONE interrupt */
28692946c1dSCezary Rojewski catpt_updatel_shim(cdev, IMC, CATPT_IMC_IPCCD, 0);
28792946c1dSCezary Rojewski ret = IRQ_HANDLED;
28892946c1dSCezary Rojewski }
28992946c1dSCezary Rojewski
29092946c1dSCezary Rojewski /* delayed reply or notification */
29192946c1dSCezary Rojewski if (isc & CATPT_ISC_IPCDB) {
29292946c1dSCezary Rojewski /* mask dsp BUSY interrupt */
29392946c1dSCezary Rojewski catpt_updatel_shim(cdev, IMC, CATPT_IMC_IPCDB, CATPT_IMC_IPCDB);
29492946c1dSCezary Rojewski ret = IRQ_WAKE_THREAD;
29592946c1dSCezary Rojewski }
29692946c1dSCezary Rojewski
29792946c1dSCezary Rojewski return ret;
29892946c1dSCezary Rojewski }
299