/* * tiomap_io.c * * DSP-BIOS Bridge driver support functions for TI OMAP processors. * * Implementation for the io read/write routines. * * Copyright (C) 2005-2006 Texas Instruments, Inc. * * This package is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include /* ----------------------------------- DSP/BIOS Bridge */ #include /* ----------------------------------- Trace & Debug */ #include /* ----------------------------------- Platform Manager */ #include #include /* ----------------------------------- OS Adaptation Layer */ #include /* ----------------------------------- specific to this file */ #include "_tiomap.h" #include "_tiomap_pwr.h" #include "tiomap_io.h" static u32 ul_ext_base; static u32 ul_ext_end; static u32 shm0_end; static u32 ul_dyn_ext_base; static u32 ul_trace_sec_beg; static u32 ul_trace_sec_end; static u32 ul_shm_base_virt; bool symbols_reloaded = true; /* * ======== read_ext_dsp_data ======== * Copies DSP external memory buffers to the host side buffers. */ int read_ext_dsp_data(struct bridge_dev_context *dev_ctxt, u8 *host_buff, u32 dsp_addr, u32 ul_num_bytes, u32 mem_type) { int status = 0; struct bridge_dev_context *dev_context = dev_ctxt; u32 offset; u32 ul_tlb_base_virt = 0; u32 ul_shm_offset_virt = 0; u32 dw_ext_prog_virt_mem; u32 dw_base_addr = dev_context->dsp_ext_base_addr; bool trace_read = false; if (!ul_shm_base_virt) { status = dev_get_symbol(dev_context->dev_obj, SHMBASENAME, &ul_shm_base_virt); } DBC_ASSERT(ul_shm_base_virt != 0); /* Check if it is a read of Trace section */ if (!status && !ul_trace_sec_beg) { status = dev_get_symbol(dev_context->dev_obj, DSP_TRACESEC_BEG, &ul_trace_sec_beg); } DBC_ASSERT(ul_trace_sec_beg != 0); if (!status && !ul_trace_sec_end) { status = dev_get_symbol(dev_context->dev_obj, DSP_TRACESEC_END, &ul_trace_sec_end); } DBC_ASSERT(ul_trace_sec_end != 0); if (!status) { if ((dsp_addr <= ul_trace_sec_end) && (dsp_addr >= ul_trace_sec_beg)) trace_read = true; } /* If reading from TRACE, force remap/unmap */ if (trace_read && dw_base_addr) { dw_base_addr = 0; dev_context->dsp_ext_base_addr = 0; } if (!dw_base_addr) { /* Initialize ul_ext_base and ul_ext_end */ ul_ext_base = 0; ul_ext_end = 0; /* Get DYNEXT_BEG, EXT_BEG and EXT_END. */ if (!status && !ul_dyn_ext_base) { status = dev_get_symbol(dev_context->dev_obj, DYNEXTBASE, &ul_dyn_ext_base); } DBC_ASSERT(ul_dyn_ext_base != 0); if (!status) { status = dev_get_symbol(dev_context->dev_obj, EXTBASE, &ul_ext_base); } DBC_ASSERT(ul_ext_base != 0); if (!status) { status = dev_get_symbol(dev_context->dev_obj, EXTEND, &ul_ext_end); } DBC_ASSERT(ul_ext_end != 0); /* Trace buffer is right after the shm SEG0, * so set the base address to SHMBASE */ if (trace_read) { ul_ext_base = ul_shm_base_virt; ul_ext_end = ul_trace_sec_end; } DBC_ASSERT(ul_ext_end != 0); DBC_ASSERT(ul_ext_end > ul_ext_base); if (ul_ext_end < ul_ext_base) status = -EPERM; if (!status) { ul_tlb_base_virt = dev_context->atlb_entry[0].dsp_va * DSPWORDSIZE; DBC_ASSERT(ul_tlb_base_virt <= ul_shm_base_virt); dw_ext_prog_virt_mem = dev_context->atlb_entry[0].gpp_va; if (!trace_read) { ul_shm_offset_virt = ul_shm_base_virt - ul_tlb_base_virt; ul_shm_offset_virt += PG_ALIGN_HIGH(ul_ext_end - ul_dyn_ext_base + 1, HW_PAGE_SIZE64KB); dw_ext_prog_virt_mem -= ul_shm_offset_virt; dw_ext_prog_virt_mem += (ul_ext_base - ul_dyn_ext_base); dev_context->dsp_ext_base_addr = dw_ext_prog_virt_mem; /* * This dsp_ext_base_addr will get cleared * only when the board is stopped. */ if (!dev_context->dsp_ext_base_addr) status = -EPERM; } dw_base_addr = dw_ext_prog_virt_mem; } } if (!dw_base_addr || !ul_ext_base || !ul_ext_end) status = -EPERM; offset = dsp_addr - ul_ext_base; if (!status) memcpy(host_buff, (u8 *) dw_base_addr + offset, ul_num_bytes); return status; } /* * ======== write_dsp_data ======== * purpose: * Copies buffers to the DSP internal/external memory. */ int write_dsp_data(struct bridge_dev_context *dev_context, u8 *host_buff, u32 dsp_addr, u32 ul_num_bytes, u32 mem_type) { u32 offset; u32 dw_base_addr = dev_context->dsp_base_addr; struct cfg_hostres *resources = dev_context->resources; int status = 0; u32 base1, base2, base3; base1 = OMAP_DSP_MEM1_SIZE; base2 = OMAP_DSP_MEM2_BASE - OMAP_DSP_MEM1_BASE; base3 = OMAP_DSP_MEM3_BASE - OMAP_DSP_MEM1_BASE; if (!resources) return -EPERM; offset = dsp_addr - dev_context->dsp_start_add; if (offset < base1) { dw_base_addr = MEM_LINEAR_ADDRESS(resources->mem_base[2], resources->mem_length[2]); } else if (offset > base1 && offset < base2 + OMAP_DSP_MEM2_SIZE) { dw_base_addr = MEM_LINEAR_ADDRESS(resources->mem_base[3], resources->mem_length[3]); offset = offset - base2; } else if (offset >= base2 + OMAP_DSP_MEM2_SIZE && offset < base3 + OMAP_DSP_MEM3_SIZE) { dw_base_addr = MEM_LINEAR_ADDRESS(resources->mem_base[4], resources->mem_length[4]); offset = offset - base3; } else { return -EPERM; } if (ul_num_bytes) memcpy((u8 *) (dw_base_addr + offset), host_buff, ul_num_bytes); else *((u32 *) host_buff) = dw_base_addr + offset; return status; } /* * ======== write_ext_dsp_data ======== * purpose: * Copies buffers to the external memory. * */ int 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) { u32 dw_base_addr = dev_context->dsp_ext_base_addr; u32 dw_offset = 0; u8 temp_byte1, temp_byte2; u8 remain_byte[4]; s32 i; int ret = 0; u32 dw_ext_prog_virt_mem; u32 ul_tlb_base_virt = 0; u32 ul_shm_offset_virt = 0; struct cfg_hostres *host_res = dev_context->resources; bool trace_load = false; temp_byte1 = 0x0; temp_byte2 = 0x0; if (symbols_reloaded) { /* Check if it is a load to Trace section */ ret = dev_get_symbol(dev_context->dev_obj, DSP_TRACESEC_BEG, &ul_trace_sec_beg); if (!ret) ret = dev_get_symbol(dev_context->dev_obj, DSP_TRACESEC_END, &ul_trace_sec_end); } if (!ret) { if ((dsp_addr <= ul_trace_sec_end) && (dsp_addr >= ul_trace_sec_beg)) trace_load = true; } /* If dynamic, force remap/unmap */ if ((dynamic_load || trace_load) && dw_base_addr) { dw_base_addr = 0; MEM_UNMAP_LINEAR_ADDRESS((void *) dev_context->dsp_ext_base_addr); dev_context->dsp_ext_base_addr = 0x0; } if (!dw_base_addr) { if (symbols_reloaded) /* Get SHM_BEG EXT_BEG and EXT_END. */ ret = dev_get_symbol(dev_context->dev_obj, SHMBASENAME, &ul_shm_base_virt); DBC_ASSERT(ul_shm_base_virt != 0); if (dynamic_load) { if (!ret) { if (symbols_reloaded) ret = dev_get_symbol (dev_context->dev_obj, DYNEXTBASE, &ul_ext_base); } DBC_ASSERT(ul_ext_base != 0); if (!ret) { /* DR OMAPS00013235 : DLModules array may be * in EXTMEM. It is expected that DYNEXTMEM and * EXTMEM are contiguous, so checking for the * upper bound at EXTEND should be Ok. */ if (symbols_reloaded) ret = dev_get_symbol (dev_context->dev_obj, EXTEND, &ul_ext_end); } } else { if (symbols_reloaded) { if (!ret) ret = dev_get_symbol (dev_context->dev_obj, EXTBASE, &ul_ext_base); DBC_ASSERT(ul_ext_base != 0); if (!ret) ret = dev_get_symbol (dev_context->dev_obj, EXTEND, &ul_ext_end); } } /* Trace buffer it right after the shm SEG0, so set the * base address to SHMBASE */ if (trace_load) ul_ext_base = ul_shm_base_virt; DBC_ASSERT(ul_ext_end != 0); DBC_ASSERT(ul_ext_end > ul_ext_base); if (ul_ext_end < ul_ext_base) ret = -EPERM; if (!ret) { ul_tlb_base_virt = dev_context->atlb_entry[0].dsp_va * DSPWORDSIZE; DBC_ASSERT(ul_tlb_base_virt <= ul_shm_base_virt); if (symbols_reloaded) { ret = dev_get_symbol (dev_context->dev_obj, DSP_TRACESEC_END, &shm0_end); if (!ret) { ret = dev_get_symbol (dev_context->dev_obj, DYNEXTBASE, &ul_dyn_ext_base); } } ul_shm_offset_virt = ul_shm_base_virt - ul_tlb_base_virt; if (trace_load) { dw_ext_prog_virt_mem = dev_context->atlb_entry[0].gpp_va; } else { dw_ext_prog_virt_mem = host_res->mem_base[1]; dw_ext_prog_virt_mem += (ul_ext_base - ul_dyn_ext_base); } dev_context->dsp_ext_base_addr = (u32) MEM_LINEAR_ADDRESS((void *) dw_ext_prog_virt_mem, ul_ext_end - ul_ext_base); dw_base_addr += dev_context->dsp_ext_base_addr; /* This dsp_ext_base_addr will get cleared only when * the board is stopped. */ if (!dev_context->dsp_ext_base_addr) ret = -EPERM; } } if (!dw_base_addr || !ul_ext_base || !ul_ext_end) ret = -EPERM; if (!ret) { for (i = 0; i < 4; i++) remain_byte[i] = 0x0; dw_offset = dsp_addr - ul_ext_base; /* Also make sure the dsp_addr is < ul_ext_end */ if (dsp_addr > ul_ext_end || dw_offset > dsp_addr) ret = -EPERM; } if (!ret) { if (ul_num_bytes) memcpy((u8 *) dw_base_addr + dw_offset, host_buff, ul_num_bytes); else *((u32 *) host_buff) = dw_base_addr + dw_offset; } /* Unmap here to force remap for other Ext loads */ if ((dynamic_load || trace_load) && dev_context->dsp_ext_base_addr) { MEM_UNMAP_LINEAR_ADDRESS((void *) dev_context->dsp_ext_base_addr); dev_context->dsp_ext_base_addr = 0x0; } symbols_reloaded = false; return ret; } int sm_interrupt_dsp(struct bridge_dev_context *dev_context, u16 mb_val) { #ifdef CONFIG_TIDSPBRIDGE_DVFS u32 opplevel = 0; #endif struct omap_dsp_platform_data *pdata = omap_dspbridge_dev->dev.platform_data; struct cfg_hostres *resources = dev_context->resources; int status = 0; u32 temp; if (!dev_context->mbox) return 0; if (!resources) return -EPERM; if (dev_context->brd_state == BRD_DSP_HIBERNATION || dev_context->brd_state == BRD_HIBERNATION) { #ifdef CONFIG_TIDSPBRIDGE_DVFS if (pdata->dsp_get_opp) opplevel = (*pdata->dsp_get_opp) (); if (opplevel == VDD1_OPP1) { if (pdata->dsp_set_min_opp) (*pdata->dsp_set_min_opp) (VDD1_OPP2); } #endif /* Restart the peripheral clocks */ dsp_clock_enable_all(dev_context->dsp_per_clks); dsp_wdt_enable(true); /* * 2:0 AUTO_IVA2_DPLL - Enabling IVA2 DPLL auto control * in CM_AUTOIDLE_PLL_IVA2 register */ (*pdata->dsp_cm_write)(1 << OMAP3430_AUTO_IVA2_DPLL_SHIFT, OMAP3430_IVA2_MOD, OMAP3430_CM_AUTOIDLE_PLL); /* * 7:4 IVA2_DPLL_FREQSEL - IVA2 internal frq set to * 0.75 MHz - 1.0 MHz * 2:0 EN_IVA2_DPLL - Enable IVA2 DPLL in lock mode */ (*pdata->dsp_cm_rmw_bits)(OMAP3430_IVA2_DPLL_FREQSEL_MASK | OMAP3430_EN_IVA2_DPLL_MASK, 0x3 << OMAP3430_IVA2_DPLL_FREQSEL_SHIFT | 0x7 << OMAP3430_EN_IVA2_DPLL_SHIFT, OMAP3430_IVA2_MOD, OMAP3430_CM_CLKEN_PLL); /* Restore mailbox settings */ omap_mbox_restore_ctx(dev_context->mbox); /* Access MMU SYS CONFIG register to generate a short wakeup */ temp = readl(resources->dmmu_base + 0x10); dev_context->brd_state = BRD_RUNNING; } else if (dev_context->brd_state == BRD_RETENTION) { /* Restart the peripheral clocks */ dsp_clock_enable_all(dev_context->dsp_per_clks); } status = omap_mbox_msg_send(dev_context->mbox, mb_val); if (status) { pr_err("omap_mbox_msg_send Fail and status = %d\n", status); status = -EPERM; } return 0; }