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