1 /*
2  * tiomap_io.c
3  *
4  * DSP-BIOS Bridge driver support functions for TI OMAP processors.
5  *
6  * Implementation for the io read/write routines.
7  *
8  * Copyright (C) 2005-2006 Texas Instruments, Inc.
9  *
10  * This package is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License version 2 as
12  * published by the Free Software Foundation.
13  *
14  * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
16  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
17  */
18 
19 #include <plat/dsp.h>
20 
21 /*  ----------------------------------- DSP/BIOS Bridge */
22 #include <dspbridge/dbdefs.h>
23 
24 /*  ----------------------------------- Platform Manager */
25 #include <dspbridge/dev.h>
26 #include <dspbridge/drv.h>
27 
28 /*  ----------------------------------- OS Adaptation Layer */
29 #include <dspbridge/wdt.h>
30 
31 /*  ----------------------------------- specific to this file */
32 #include "_tiomap.h"
33 #include "_tiomap_pwr.h"
34 #include "tiomap_io.h"
35 
36 static u32 ul_ext_base;
37 static u32 ul_ext_end;
38 
39 static u32 shm0_end;
40 static u32 ul_dyn_ext_base;
41 static u32 ul_trace_sec_beg;
42 static u32 ul_trace_sec_end;
43 static u32 ul_shm_base_virt;
44 
45 bool symbols_reloaded = true;
46 
47 /*
48  *  ======== read_ext_dsp_data ========
49  *      Copies DSP external memory buffers to the host side buffers.
50  */
read_ext_dsp_data(struct bridge_dev_context * dev_ctxt,u8 * host_buff,u32 dsp_addr,u32 ul_num_bytes,u32 mem_type)51 int read_ext_dsp_data(struct bridge_dev_context *dev_ctxt,
52 			     u8 *host_buff, u32 dsp_addr,
53 			     u32 ul_num_bytes, u32 mem_type)
54 {
55 	int status = 0;
56 	struct bridge_dev_context *dev_context = dev_ctxt;
57 	u32 offset;
58 	u32 ul_tlb_base_virt = 0;
59 	u32 ul_shm_offset_virt = 0;
60 	u32 dw_ext_prog_virt_mem;
61 	u32 dw_base_addr = dev_context->dsp_ext_base_addr;
62 	bool trace_read = false;
63 
64 	if (!ul_shm_base_virt) {
65 		status = dev_get_symbol(dev_context->dev_obj,
66 					SHMBASENAME, &ul_shm_base_virt);
67 	}
68 
69 	/* Check if it is a read of Trace section */
70 	if (!status && !ul_trace_sec_beg) {
71 		status = dev_get_symbol(dev_context->dev_obj,
72 					DSP_TRACESEC_BEG, &ul_trace_sec_beg);
73 	}
74 
75 	if (!status && !ul_trace_sec_end) {
76 		status = dev_get_symbol(dev_context->dev_obj,
77 					DSP_TRACESEC_END, &ul_trace_sec_end);
78 	}
79 
80 	if (!status) {
81 		if ((dsp_addr <= ul_trace_sec_end) &&
82 		    (dsp_addr >= ul_trace_sec_beg))
83 			trace_read = true;
84 	}
85 
86 	/* If reading from TRACE, force remap/unmap */
87 	if (trace_read && dw_base_addr) {
88 		dw_base_addr = 0;
89 		dev_context->dsp_ext_base_addr = 0;
90 	}
91 
92 	if (!dw_base_addr) {
93 		/* Initialize ul_ext_base and ul_ext_end */
94 		ul_ext_base = 0;
95 		ul_ext_end = 0;
96 
97 		/* Get DYNEXT_BEG, EXT_BEG and EXT_END. */
98 		if (!status && !ul_dyn_ext_base) {
99 			status = dev_get_symbol(dev_context->dev_obj,
100 						DYNEXTBASE, &ul_dyn_ext_base);
101 		}
102 
103 		if (!status) {
104 			status = dev_get_symbol(dev_context->dev_obj,
105 						EXTBASE, &ul_ext_base);
106 		}
107 
108 		if (!status) {
109 			status = dev_get_symbol(dev_context->dev_obj,
110 						EXTEND, &ul_ext_end);
111 		}
112 
113 		/* Trace buffer is right after the shm SEG0,
114 		 *  so set the base address to SHMBASE */
115 		if (trace_read) {
116 			ul_ext_base = ul_shm_base_virt;
117 			ul_ext_end = ul_trace_sec_end;
118 		}
119 
120 
121 		if (ul_ext_end < ul_ext_base)
122 			status = -EPERM;
123 
124 		if (!status) {
125 			ul_tlb_base_virt =
126 			    dev_context->atlb_entry[0].dsp_va * DSPWORDSIZE;
127 			dw_ext_prog_virt_mem =
128 			    dev_context->atlb_entry[0].gpp_va;
129 
130 			if (!trace_read) {
131 				ul_shm_offset_virt =
132 				    ul_shm_base_virt - ul_tlb_base_virt;
133 				ul_shm_offset_virt +=
134 				    PG_ALIGN_HIGH(ul_ext_end - ul_dyn_ext_base +
135 						  1, HW_PAGE_SIZE64KB);
136 				dw_ext_prog_virt_mem -= ul_shm_offset_virt;
137 				dw_ext_prog_virt_mem +=
138 				    (ul_ext_base - ul_dyn_ext_base);
139 				dev_context->dsp_ext_base_addr =
140 				    dw_ext_prog_virt_mem;
141 
142 				/*
143 				 * This dsp_ext_base_addr will get cleared
144 				 * only when the board is stopped.
145 				*/
146 				if (!dev_context->dsp_ext_base_addr)
147 					status = -EPERM;
148 			}
149 
150 			dw_base_addr = dw_ext_prog_virt_mem;
151 		}
152 	}
153 
154 	if (!dw_base_addr || !ul_ext_base || !ul_ext_end)
155 		status = -EPERM;
156 
157 	offset = dsp_addr - ul_ext_base;
158 
159 	if (!status)
160 		memcpy(host_buff, (u8 *) dw_base_addr + offset, ul_num_bytes);
161 
162 	return status;
163 }
164 
165 /*
166  *  ======== write_dsp_data ========
167  *  purpose:
168  *      Copies buffers to the DSP internal/external memory.
169  */
write_dsp_data(struct bridge_dev_context * dev_context,u8 * host_buff,u32 dsp_addr,u32 ul_num_bytes,u32 mem_type)170 int write_dsp_data(struct bridge_dev_context *dev_context,
171 			  u8 *host_buff, u32 dsp_addr, u32 ul_num_bytes,
172 			  u32 mem_type)
173 {
174 	u32 offset;
175 	u32 dw_base_addr = dev_context->dsp_base_addr;
176 	struct cfg_hostres *resources = dev_context->resources;
177 	int status = 0;
178 	u32 base1, base2, base3;
179 	base1 = OMAP_DSP_MEM1_SIZE;
180 	base2 = OMAP_DSP_MEM2_BASE - OMAP_DSP_MEM1_BASE;
181 	base3 = OMAP_DSP_MEM3_BASE - OMAP_DSP_MEM1_BASE;
182 
183 	if (!resources)
184 		return -EPERM;
185 
186 	offset = dsp_addr - dev_context->dsp_start_add;
187 	if (offset < base1) {
188 		dw_base_addr = MEM_LINEAR_ADDRESS(resources->mem_base[2],
189 						  resources->mem_length[2]);
190 	} else if (offset > base1 && offset < base2 + OMAP_DSP_MEM2_SIZE) {
191 		dw_base_addr = MEM_LINEAR_ADDRESS(resources->mem_base[3],
192 						  resources->mem_length[3]);
193 		offset = offset - base2;
194 	} else if (offset >= base2 + OMAP_DSP_MEM2_SIZE &&
195 		   offset < base3 + OMAP_DSP_MEM3_SIZE) {
196 		dw_base_addr = MEM_LINEAR_ADDRESS(resources->mem_base[4],
197 						  resources->mem_length[4]);
198 		offset = offset - base3;
199 	} else {
200 		return -EPERM;
201 	}
202 	if (ul_num_bytes)
203 		memcpy((u8 *) (dw_base_addr + offset), host_buff, ul_num_bytes);
204 	else
205 		*((u32 *) host_buff) = dw_base_addr + offset;
206 
207 	return status;
208 }
209 
210 /*
211  *  ======== write_ext_dsp_data ========
212  *  purpose:
213  *      Copies buffers to the external memory.
214  *
215  */
write_ext_dsp_data(struct bridge_dev_context * dev_context,u8 * host_buff,u32 dsp_addr,u32 ul_num_bytes,u32 mem_type,bool dynamic_load)216 int write_ext_dsp_data(struct bridge_dev_context *dev_context,
217 			      u8 *host_buff, u32 dsp_addr,
218 			      u32 ul_num_bytes, u32 mem_type,
219 			      bool dynamic_load)
220 {
221 	u32 dw_base_addr = dev_context->dsp_ext_base_addr;
222 	u32 dw_offset = 0;
223 	u8 temp_byte1, temp_byte2;
224 	u8 remain_byte[4];
225 	s32 i;
226 	int ret = 0;
227 	u32 dw_ext_prog_virt_mem;
228 	u32 ul_tlb_base_virt = 0;
229 	u32 ul_shm_offset_virt = 0;
230 	struct cfg_hostres *host_res = dev_context->resources;
231 	bool trace_load = false;
232 	temp_byte1 = 0x0;
233 	temp_byte2 = 0x0;
234 
235 	if (symbols_reloaded) {
236 		/* Check if it is a load to Trace section */
237 		ret = dev_get_symbol(dev_context->dev_obj,
238 				     DSP_TRACESEC_BEG, &ul_trace_sec_beg);
239 		if (!ret)
240 			ret = dev_get_symbol(dev_context->dev_obj,
241 					     DSP_TRACESEC_END,
242 					     &ul_trace_sec_end);
243 	}
244 	if (!ret) {
245 		if ((dsp_addr <= ul_trace_sec_end) &&
246 		    (dsp_addr >= ul_trace_sec_beg))
247 			trace_load = true;
248 	}
249 
250 	/* If dynamic, force remap/unmap */
251 	if ((dynamic_load || trace_load) && dw_base_addr) {
252 		dw_base_addr = 0;
253 		MEM_UNMAP_LINEAR_ADDRESS((void *)
254 					 dev_context->dsp_ext_base_addr);
255 		dev_context->dsp_ext_base_addr = 0x0;
256 	}
257 	if (!dw_base_addr) {
258 		if (symbols_reloaded)
259 			/* Get SHM_BEG  EXT_BEG and EXT_END. */
260 			ret = dev_get_symbol(dev_context->dev_obj,
261 					     SHMBASENAME, &ul_shm_base_virt);
262 		if (dynamic_load) {
263 			if (!ret) {
264 				if (symbols_reloaded)
265 					ret =
266 					    dev_get_symbol
267 					    (dev_context->dev_obj, DYNEXTBASE,
268 					     &ul_ext_base);
269 			}
270 			if (!ret) {
271 				/* DR  OMAPS00013235 : DLModules array may be
272 				 * in EXTMEM. It is expected that DYNEXTMEM and
273 				 * EXTMEM are contiguous, so checking for the
274 				 * upper bound at EXTEND should be Ok. */
275 				if (symbols_reloaded)
276 					ret =
277 					    dev_get_symbol
278 					    (dev_context->dev_obj, EXTEND,
279 					     &ul_ext_end);
280 			}
281 		} else {
282 			if (symbols_reloaded) {
283 				if (!ret)
284 					ret =
285 					    dev_get_symbol
286 					    (dev_context->dev_obj, EXTBASE,
287 					     &ul_ext_base);
288 				if (!ret)
289 					ret =
290 					    dev_get_symbol
291 					    (dev_context->dev_obj, EXTEND,
292 					     &ul_ext_end);
293 			}
294 		}
295 		/* Trace buffer it right after the shm SEG0, so set the
296 		 *      base address to SHMBASE */
297 		if (trace_load)
298 			ul_ext_base = ul_shm_base_virt;
299 
300 		if (ul_ext_end < ul_ext_base)
301 			ret = -EPERM;
302 
303 		if (!ret) {
304 			ul_tlb_base_virt =
305 			    dev_context->atlb_entry[0].dsp_va * DSPWORDSIZE;
306 
307 			if (symbols_reloaded) {
308 				ret = dev_get_symbol
309 					    (dev_context->dev_obj,
310 					     DSP_TRACESEC_END, &shm0_end);
311 				if (!ret) {
312 					ret =
313 					    dev_get_symbol
314 					    (dev_context->dev_obj, DYNEXTBASE,
315 					     &ul_dyn_ext_base);
316 				}
317 			}
318 			ul_shm_offset_virt =
319 			    ul_shm_base_virt - ul_tlb_base_virt;
320 			if (trace_load) {
321 				dw_ext_prog_virt_mem =
322 				    dev_context->atlb_entry[0].gpp_va;
323 			} else {
324 				dw_ext_prog_virt_mem = host_res->mem_base[1];
325 				dw_ext_prog_virt_mem +=
326 				    (ul_ext_base - ul_dyn_ext_base);
327 			}
328 
329 			dev_context->dsp_ext_base_addr =
330 			    (u32) MEM_LINEAR_ADDRESS((void *)
331 						     dw_ext_prog_virt_mem,
332 						     ul_ext_end - ul_ext_base);
333 			dw_base_addr += dev_context->dsp_ext_base_addr;
334 			/* This dsp_ext_base_addr will get cleared only when
335 			 * the board is stopped. */
336 			if (!dev_context->dsp_ext_base_addr)
337 				ret = -EPERM;
338 		}
339 	}
340 	if (!dw_base_addr || !ul_ext_base || !ul_ext_end)
341 		ret = -EPERM;
342 
343 	if (!ret) {
344 		for (i = 0; i < 4; i++)
345 			remain_byte[i] = 0x0;
346 
347 		dw_offset = dsp_addr - ul_ext_base;
348 		/* Also make sure the dsp_addr is < ul_ext_end */
349 		if (dsp_addr > ul_ext_end || dw_offset > dsp_addr)
350 			ret = -EPERM;
351 	}
352 	if (!ret) {
353 		if (ul_num_bytes)
354 			memcpy((u8 *) dw_base_addr + dw_offset, host_buff,
355 			       ul_num_bytes);
356 		else
357 			*((u32 *) host_buff) = dw_base_addr + dw_offset;
358 	}
359 	/* Unmap here to force remap for other Ext loads */
360 	if ((dynamic_load || trace_load) && dev_context->dsp_ext_base_addr) {
361 		MEM_UNMAP_LINEAR_ADDRESS((void *)
362 					 dev_context->dsp_ext_base_addr);
363 		dev_context->dsp_ext_base_addr = 0x0;
364 	}
365 	symbols_reloaded = false;
366 	return ret;
367 }
368 
sm_interrupt_dsp(struct bridge_dev_context * dev_context,u16 mb_val)369 int sm_interrupt_dsp(struct bridge_dev_context *dev_context, u16 mb_val)
370 {
371 #ifdef CONFIG_TIDSPBRIDGE_DVFS
372 	u32 opplevel = 0;
373 #endif
374 	struct omap_dsp_platform_data *pdata =
375 		omap_dspbridge_dev->dev.platform_data;
376 	struct cfg_hostres *resources = dev_context->resources;
377 	int status = 0;
378 	u32 temp;
379 
380 	if (!dev_context->mbox)
381 		return 0;
382 
383 	if (!resources)
384 		return -EPERM;
385 
386 	if (dev_context->brd_state == BRD_DSP_HIBERNATION ||
387 	    dev_context->brd_state == BRD_HIBERNATION) {
388 #ifdef CONFIG_TIDSPBRIDGE_DVFS
389 		if (pdata->dsp_get_opp)
390 			opplevel = (*pdata->dsp_get_opp) ();
391 		if (opplevel == VDD1_OPP1) {
392 			if (pdata->dsp_set_min_opp)
393 				(*pdata->dsp_set_min_opp) (VDD1_OPP2);
394 		}
395 #endif
396 		/* Restart the peripheral clocks */
397 		dsp_clock_enable_all(dev_context->dsp_per_clks);
398 		dsp_wdt_enable(true);
399 
400 		/*
401 		 * 2:0 AUTO_IVA2_DPLL - Enabling IVA2 DPLL auto control
402 		 *     in CM_AUTOIDLE_PLL_IVA2 register
403 		 */
404 		(*pdata->dsp_cm_write)(1 << OMAP3430_AUTO_IVA2_DPLL_SHIFT,
405 				OMAP3430_IVA2_MOD, OMAP3430_CM_AUTOIDLE_PLL);
406 
407 		/*
408 		 * 7:4 IVA2_DPLL_FREQSEL - IVA2 internal frq set to
409 		 *     0.75 MHz - 1.0 MHz
410 		 * 2:0 EN_IVA2_DPLL - Enable IVA2 DPLL in lock mode
411 		 */
412 		(*pdata->dsp_cm_rmw_bits)(OMAP3430_IVA2_DPLL_FREQSEL_MASK |
413 				OMAP3430_EN_IVA2_DPLL_MASK,
414 				0x3 << OMAP3430_IVA2_DPLL_FREQSEL_SHIFT |
415 				0x7 << OMAP3430_EN_IVA2_DPLL_SHIFT,
416 				OMAP3430_IVA2_MOD, OMAP3430_CM_CLKEN_PLL);
417 
418 		/* Restore mailbox settings */
419 		omap_mbox_restore_ctx(dev_context->mbox);
420 
421 		/* Access MMU SYS CONFIG register to generate a short wakeup */
422 		temp = readl(resources->dmmu_base + 0x10);
423 
424 		dev_context->brd_state = BRD_RUNNING;
425 	} else if (dev_context->brd_state == BRD_RETENTION) {
426 		/* Restart the peripheral clocks */
427 		dsp_clock_enable_all(dev_context->dsp_per_clks);
428 	}
429 
430 	status = omap_mbox_msg_send(dev_context->mbox, mb_val);
431 
432 	if (status) {
433 		pr_err("omap_mbox_msg_send Fail and status = %d\n", status);
434 		status = -EPERM;
435 	}
436 
437 	return 0;
438 }
439