1 // SPDX-License-Identifier: BSD-3-Clause
2 //
3 //Copyright(c) 2021 AMD. All rights reserved.
4 //
5 //Author: Basavaraj Hiregoudar <basavaraj.hiregoudar@amd.com>
6 // Anup Kulkarni <anup.kulkarni@amd.com>
7 // Bala Kishore <balakishore.pati@amd.com>
8
9 #include <rtos/panic.h>
10 #include <xtensa/core-macros.h>
11 #include <platform/chip_offset_byte.h>
12 #include <platform/chip_registers.h>
13 #include <rtos/interrupt.h>
14 #include <sof/ipc/driver.h>
15 #include <sof/ipc/msg.h>
16 #include <sof/ipc/schedule.h>
17 #include <rtos/alloc.h>
18 #include <sof/lib/dma.h>
19 #include <sof/lib/mailbox.h>
20 #include <sof/lib/memory.h>
21 #include <sof/lib/uuid.h>
22 #include <rtos/wait.h>
23 #include <sof/list.h>
24 #include <sof/platform.h>
25 #include <sof/schedule/edf_schedule.h>
26 #include <sof/schedule/schedule.h>
27 #include <rtos/task.h>
28 #include <rtos/spinlock.h>
29 #include <ipc/header.h>
30 #include <ipc/topology.h>
31 #include <errno.h>
32 #include <stddef.h>
33 #include <stdint.h>
34 #include <platform/platform.h>
35
36 /* 49be8ff3-71a3-4456-bb7e-4723f2e5730c */
37 DECLARE_SOF_UUID("renoir-ipc-task", ipc_task_uuid, 0x49be8ff3, 0x71a3, 0x4456,
38 0xbb, 0x7e, 0x47, 0x23, 0xf2, 0xe5, 0x73, 0x0c);
39
40 volatile acp_scratch_mem_config_t *pscratch_mem_cfg = (volatile acp_scratch_mem_config_t *)
41 (PU_REGISTER_BASE + SCRATCH_REG_OFFSET);
sof_ipc_host_status(void)42 static inline uint32_t sof_ipc_host_status(void)
43 {
44 return (pscratch_mem_cfg->acp_host_ack_write | pscratch_mem_cfg->acp_host_msg_write);
45 }
46
sof_ipc_host_msg_flag(void)47 static inline uint32_t sof_ipc_host_msg_flag(void)
48 {
49 return pscratch_mem_cfg->acp_host_msg_write;
50 }
51
sof_ipc_host_ack_flag(void)52 static inline uint32_t sof_ipc_host_ack_flag(void)
53 {
54 return pscratch_mem_cfg->acp_host_ack_write;
55 }
56
sof_ipc_dsp_status(void)57 static inline uint32_t sof_ipc_dsp_status(void)
58 {
59 return (pscratch_mem_cfg->acp_dsp_msg_write | pscratch_mem_cfg->acp_dsp_ack_write);
60 }
61
sof_ipc_host_ack_clear(void)62 static inline void sof_ipc_host_ack_clear(void)
63 {
64 pscratch_mem_cfg->acp_host_ack_write = 0;
65 }
66
sof_ipc_host_msg_clear(void)67 static inline void sof_ipc_host_msg_clear(void)
68 {
69 pscratch_mem_cfg->acp_host_msg_write = 0;
70 }
71
sof_ipc_dsp_ack_set(void)72 static inline void sof_ipc_dsp_ack_set(void)
73 {
74 pscratch_mem_cfg->acp_dsp_ack_write = 1;
75 }
76
sof_ipc_dsp_msg_set(void)77 static inline void sof_ipc_dsp_msg_set(void)
78 {
79 pscratch_mem_cfg->acp_dsp_msg_write = 1;
80 }
81
irq_handler(void * arg)82 static void irq_handler(void *arg)
83 {
84 struct ipc *ipc = arg;
85 uint32_t status;
86 uint32_t lock;
87 uint32_t delay_cnt = 10000;
88 bool lock_fail = false;
89 acp_dsp_sw_intr_stat_t swintrstat;
90 acp_sw_intr_trig_t swintrtrig;
91 acp_future_reg_aclk_0_t acp_reg_aclk;
92
93 swintrstat = (acp_dsp_sw_intr_stat_t)io_reg_read(PU_REGISTER_BASE + ACP_DSP_SW_INTR_STAT);
94 status = swintrstat.u32all & HOST_TO_DSP_INTR;
95 acp_reg_aclk = (acp_future_reg_aclk_0_t)
96 io_reg_read(PU_REGISTER_BASE + ACP_FUTURE_REG_ACLK_0);
97 if (status) {
98 /* Interrupt source */
99 if (sof_ipc_host_status()) {
100 lock = io_reg_read(PU_REGISTER_BASE + ACP_AXI2DAGB_SEM_0);
101 while (lock) {
102 lock = io_reg_read(PU_REGISTER_BASE + ACP_AXI2DAGB_SEM_0);
103 if (!delay_cnt) {
104 lock_fail = true;
105 break;
106 }
107 delay_cnt--;
108 }
109 if (lock_fail) {
110 tr_err(&ipc_tr, "ACP fail to acquire the lock");
111 return;
112 }
113 /* Check if it is response from host */
114 if (sof_ipc_host_ack_flag()) {
115 /* Clear the ACK from host */
116 sof_ipc_host_ack_clear();
117 acp_reg_aclk.bits.host_aclk = 0;
118 io_reg_write((PU_REGISTER_BASE + ACP_FUTURE_REG_ACLK_0),
119 acp_reg_aclk.u32all);
120 /* Clear the Host to DSP Status Register */
121 acp_ack_intr_from_host();
122 /* Configures the trigger bit in ACP_DSP_SW_INTR_TRIG register */
123 swintrtrig = (acp_sw_intr_trig_t)
124 io_reg_read(PU_REGISTER_BASE + ACP_SW_INTR_TRIG);
125 swintrtrig.bits.trig_host_to_dsp0_intr1 = INTERRUPT_DISABLE;
126 swintrtrig.bits.trig_dsp0_to_host_intr = INTERRUPT_DISABLE;
127 io_reg_write((PU_REGISTER_BASE + ACP_SW_INTR_TRIG),
128 swintrtrig.u32all);
129 }
130 /* Check if new message from host */
131 if (sof_ipc_host_msg_flag()) {
132 /* Clear the msg bit from host */
133 sof_ipc_host_msg_clear();
134 acp_reg_aclk.bits.host_msg = 0;
135 io_reg_write((PU_REGISTER_BASE + ACP_FUTURE_REG_ACLK_0),
136 acp_reg_aclk.u32all);
137 /* Clear the Host to DSP Status Register */
138 acp_ack_intr_from_host();
139 ipc_schedule_process(ipc);
140 }
141 io_reg_write((PU_REGISTER_BASE + ACP_AXI2DAGB_SEM_0), lock);
142 } else {
143 tr_err(&ipc_tr, "IPC:interrupt without setting flags host status 0x%x",
144 sof_ipc_host_status());
145 }
146 }
147 }
148
ipc_platform_do_cmd(struct ipc * ipc)149 enum task_state ipc_platform_do_cmd(struct ipc *ipc)
150 {
151 struct ipc_cmd_hdr *hdr;
152
153 hdr = mailbox_validate();
154 ipc_cmd(hdr);
155 return SOF_TASK_STATE_COMPLETED;
156 }
157
ipc_platform_complete_cmd(struct ipc * ipc)158 void ipc_platform_complete_cmd(struct ipc *ipc)
159 {
160 acp_sw_intr_trig_t sw_intr_trig;
161 acp_future_reg_aclk_0_t acp_reg_aclk;
162
163 /* Set Dsp Ack for msg from host */
164 sof_ipc_dsp_ack_set();
165 acp_reg_aclk = (acp_future_reg_aclk_0_t)
166 io_reg_read(PU_REGISTER_BASE + ACP_FUTURE_REG_ACLK_0);
167 acp_reg_aclk.bits.dsp_aclk = 1;
168 io_reg_write((PU_REGISTER_BASE + ACP_FUTURE_REG_ACLK_0), acp_reg_aclk.u32all);
169 /* Configures the trigger bit in ACP_DSP_SW_INTR_TRIG register */
170 sw_intr_trig = (acp_sw_intr_trig_t)
171 io_reg_read(PU_REGISTER_BASE + ACP_SW_INTR_TRIG);
172 sw_intr_trig.bits.trig_host_to_dsp0_intr1 = INTERRUPT_DISABLE;
173 sw_intr_trig.bits.trig_dsp0_to_host_intr = INTERRUPT_DISABLE;
174 io_reg_write((PU_REGISTER_BASE + ACP_SW_INTR_TRIG), sw_intr_trig.u32all);
175 /* now interrupt host to tell it we have sent a message */
176 acp_dsp_to_host_Intr_trig();
177 if (ipc->pm_prepare_D3) {
178 while (1)
179 wait_for_interrupt(0);
180 }
181 }
182
ipc_platform_send_msg(const struct ipc_msg * msg)183 int ipc_platform_send_msg(const struct ipc_msg *msg)
184 {
185 acp_sw_intr_trig_t sw_intr_trig;
186 acp_dsp_sw_intr_stat_t sw_intr_stat;
187 acp_future_reg_aclk_0_t acp_reg_aclk;
188 uint32_t status;
189 uint32_t lock;
190 uint32_t delay_cnt = 10000;
191 /* Check if host cleared the status for previous messages */
192 sw_intr_stat = (acp_dsp_sw_intr_stat_t)
193 io_reg_read(PU_REGISTER_BASE + ACP_DSP_SW_INTR_STAT);
194 status = sw_intr_stat.bits.dsp0_to_host_intr_stat;
195 if (sof_ipc_dsp_status() || status) {
196 sw_intr_stat = (acp_dsp_sw_intr_stat_t)
197 io_reg_read(PU_REGISTER_BASE + ACP_DSP_SW_INTR_STAT);
198 status = sw_intr_stat.bits.dsp0_to_host_intr_stat;
199 return -EBUSY;
200 }
201 lock = io_reg_read(PU_REGISTER_BASE + ACP_AXI2DAGB_SEM_0);
202 while (lock) {
203 lock = io_reg_read(PU_REGISTER_BASE + ACP_AXI2DAGB_SEM_0);
204 if (!delay_cnt)
205 return -EBUSY;
206
207 delay_cnt--;
208 }
209
210 /* Write new message in the mailbox */
211 mailbox_dspbox_write(0, msg->tx_data, msg->tx_size);
212
213 /* Need to set DSP message flag */
214 sof_ipc_dsp_msg_set();
215 acp_reg_aclk = (acp_future_reg_aclk_0_t)
216 io_reg_read(PU_REGISTER_BASE + ACP_FUTURE_REG_ACLK_0);
217 acp_reg_aclk.bits.dsp_msg = 1;
218 io_reg_write((PU_REGISTER_BASE + ACP_FUTURE_REG_ACLK_0), acp_reg_aclk.u32all);
219 /* now interrupt host to tell it we have sent a message */
220 acp_dsp_to_host_Intr_trig();
221 /* Disable the trigger bit in ACP_DSP_SW_INTR_TRIG register */
222 sw_intr_trig = (acp_sw_intr_trig_t)io_reg_read(PU_REGISTER_BASE + ACP_SW_INTR_TRIG);
223 sw_intr_trig.bits.trig_dsp0_to_host_intr = INTERRUPT_DISABLE;
224 io_reg_write((PU_REGISTER_BASE + ACP_SW_INTR_TRIG), sw_intr_trig.u32all);
225 io_reg_write((PU_REGISTER_BASE + ACP_AXI2DAGB_SEM_0), lock);
226 return 0;
227 }
228
platform_ipc_init(struct ipc * ipc)229 int platform_ipc_init(struct ipc *ipc)
230 {
231 ipc_set_drvdata(ipc, NULL);
232 /* schedule */
233 schedule_task_init_edf(&ipc->ipc_task, SOF_UUID(ipc_task_uuid),
234 &ipc_task_ops, ipc, 0, 0);
235 arch_interrupt_clear(IRQ_NUM_EXT_LEVEL3);
236 interrupt_register(IRQ_NUM_EXT_LEVEL3, irq_handler, ipc);
237 /* Enabling software interuppts */
238 interrupt_enable(IRQ_NUM_EXT_LEVEL3, ipc);
239 return 0;
240 }
241