diff options
Diffstat (limited to 'drivers/staging/tidspbridge/core/io_sm.c')
-rw-r--r-- | drivers/staging/tidspbridge/core/io_sm.c | 2410 |
1 files changed, 2410 insertions, 0 deletions
diff --git a/drivers/staging/tidspbridge/core/io_sm.c b/drivers/staging/tidspbridge/core/io_sm.c new file mode 100644 index 000000000000..7fb840da627a --- /dev/null +++ b/drivers/staging/tidspbridge/core/io_sm.c @@ -0,0 +1,2410 @@ +/* + * io_sm.c + * + * DSP-BIOS Bridge driver support functions for TI OMAP processors. + * + * IO dispatcher for a shared memory channel driver. + * + * 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. + */ + +/* + * Channel Invariant: + * There is an important invariant condition which must be maintained per + * channel outside of bridge_chnl_get_ioc() and IO_Dispatch(), violation of + * which may cause timeouts and/or failure of the sync_wait_on_event + * function. + */ + +/* Host OS */ +#include <dspbridge/host_os.h> +#include <linux/workqueue.h> + +/* ----------------------------------- DSP/BIOS Bridge */ +#include <dspbridge/std.h> +#include <dspbridge/dbdefs.h> + +/* Trace & Debug */ +#include <dspbridge/dbc.h> + +/* Services Layer */ +#include <dspbridge/cfg.h> +#include <dspbridge/ntfy.h> +#include <dspbridge/sync.h> + +/* Hardware Abstraction Layer */ +#include <hw_defs.h> +#include <hw_mmu.h> + +/* Bridge Driver */ +#include <dspbridge/dspdeh.h> +#include <dspbridge/dspio.h> +#include <dspbridge/dspioctl.h> +#include <dspbridge/wdt.h> +#include <_tiomap.h> +#include <tiomap_io.h> +#include <_tiomap_pwr.h> + +/* Platform Manager */ +#include <dspbridge/cod.h> +#include <dspbridge/node.h> +#include <dspbridge/dev.h> + +/* Others */ +#include <dspbridge/rms_sh.h> +#include <dspbridge/mgr.h> +#include <dspbridge/drv.h> +#include "_cmm.h" +#include "module_list.h" + +/* This */ +#include <dspbridge/io_sm.h> +#include "_msg_sm.h" + +/* Defines, Data Structures, Typedefs */ +#define OUTPUTNOTREADY 0xffff +#define NOTENABLED 0xffff /* Channel(s) not enabled */ + +#define EXTEND "_EXT_END" + +#define SWAP_WORD(x) (x) +#define UL_PAGE_ALIGN_SIZE 0x10000 /* Page Align Size */ + +#define MAX_PM_REQS 32 + +#define MMU_FAULT_HEAD1 0xa5a5a5a5 +#define MMU_FAULT_HEAD2 0x96969696 +#define POLL_MAX 1000 +#define MAX_MMU_DBGBUFF 10240 + +/* IO Manager: only one created per board */ +struct io_mgr { + /* These four fields must be the first fields in a io_mgr_ struct */ + /* Bridge device context */ + struct bridge_dev_context *hbridge_context; + /* Function interface to Bridge driver */ + struct bridge_drv_interface *intf_fxns; + struct dev_object *hdev_obj; /* Device this board represents */ + + /* These fields initialized in bridge_io_create() */ + struct chnl_mgr *hchnl_mgr; + struct shm *shared_mem; /* Shared Memory control */ + u8 *input; /* Address of input channel */ + u8 *output; /* Address of output channel */ + struct msg_mgr *hmsg_mgr; /* Message manager */ + /* Msg control for from DSP messages */ + struct msg_ctrl *msg_input_ctrl; + /* Msg control for to DSP messages */ + struct msg_ctrl *msg_output_ctrl; + u8 *msg_input; /* Address of input messages */ + u8 *msg_output; /* Address of output messages */ + u32 usm_buf_size; /* Size of a shared memory I/O channel */ + bool shared_irq; /* Is this IRQ shared? */ + u32 word_size; /* Size in bytes of DSP word */ + u16 intr_val; /* Interrupt value */ + /* Private extnd proc info; mmu setup */ + struct mgr_processorextinfo ext_proc_info; + struct cmm_object *hcmm_mgr; /* Shared Mem Mngr */ + struct work_struct io_workq; /* workqueue */ +#ifndef DSP_TRACEBUF_DISABLED + u32 ul_trace_buffer_begin; /* Trace message start address */ + u32 ul_trace_buffer_end; /* Trace message end address */ + u32 ul_trace_buffer_current; /* Trace message current address */ + u32 ul_gpp_read_pointer; /* GPP Read pointer to Trace buffer */ + u8 *pmsg; + u32 ul_gpp_va; + u32 ul_dsp_va; +#endif + /* IO Dpc */ + u32 dpc_req; /* Number of requested DPC's. */ + u32 dpc_sched; /* Number of executed DPC's. */ + struct tasklet_struct dpc_tasklet; + spinlock_t dpc_lock; + +}; + +/* Function Prototypes */ +static void io_dispatch_chnl(IN struct io_mgr *pio_mgr, + IN OUT struct chnl_object *pchnl, u8 iMode); +static void io_dispatch_msg(IN struct io_mgr *pio_mgr, + struct msg_mgr *hmsg_mgr); +static void io_dispatch_pm(struct io_mgr *pio_mgr); +static void notify_chnl_complete(struct chnl_object *pchnl, + struct chnl_irp *chnl_packet_obj); +static void input_chnl(struct io_mgr *pio_mgr, struct chnl_object *pchnl, + u8 iMode); +static void output_chnl(struct io_mgr *pio_mgr, struct chnl_object *pchnl, + u8 iMode); +static void input_msg(struct io_mgr *pio_mgr, struct msg_mgr *hmsg_mgr); +static void output_msg(struct io_mgr *pio_mgr, struct msg_mgr *hmsg_mgr); +static u32 find_ready_output(struct chnl_mgr *chnl_mgr_obj, + struct chnl_object *pchnl, u32 dwMask); +static u32 read_data(struct bridge_dev_context *hDevContext, void *dest, + void *pSrc, u32 usize); +static u32 write_data(struct bridge_dev_context *hDevContext, void *dest, + void *pSrc, u32 usize); + +/* Bus Addr (cached kernel) */ +static int register_shm_segs(struct io_mgr *hio_mgr, + struct cod_manager *cod_man, + u32 dw_gpp_base_pa); + +/* + * ======== bridge_io_create ======== + * Create an IO manager object. + */ +int bridge_io_create(OUT struct io_mgr **phIOMgr, + struct dev_object *hdev_obj, + IN CONST struct io_attrs *pMgrAttrs) +{ + int status = 0; + struct io_mgr *pio_mgr = NULL; + struct shm *shared_mem = NULL; + struct bridge_dev_context *hbridge_context = NULL; + struct cfg_devnode *dev_node_obj; + struct chnl_mgr *hchnl_mgr; + u8 dev_type; + + /* Check requirements */ + if (!phIOMgr || !pMgrAttrs || pMgrAttrs->word_size == 0) { + status = -EFAULT; + goto func_end; + } + dev_get_chnl_mgr(hdev_obj, &hchnl_mgr); + if (!hchnl_mgr || hchnl_mgr->hio_mgr) { + status = -EFAULT; + goto func_end; + } + /* + * Message manager will be created when a file is loaded, since + * size of message buffer in shared memory is configurable in + * the base image. + */ + dev_get_bridge_context(hdev_obj, &hbridge_context); + if (!hbridge_context) { + status = -EFAULT; + goto func_end; + } + dev_get_dev_type(hdev_obj, &dev_type); + /* + * DSP shared memory area will get set properly when + * a program is loaded. They are unknown until a COFF file is + * loaded. I chose the value -1 because it was less likely to be + * a valid address than 0. + */ + shared_mem = (struct shm *)-1; + + /* Allocate IO manager object */ + pio_mgr = kzalloc(sizeof(struct io_mgr), GFP_KERNEL); + if (pio_mgr == NULL) { + status = -ENOMEM; + goto func_end; + } + + /* Initialize chnl_mgr object */ +#ifndef DSP_TRACEBUF_DISABLED + pio_mgr->pmsg = NULL; +#endif + pio_mgr->hchnl_mgr = hchnl_mgr; + pio_mgr->word_size = pMgrAttrs->word_size; + pio_mgr->shared_mem = shared_mem; + + if (dev_type == DSP_UNIT) { + /* Create an IO DPC */ + tasklet_init(&pio_mgr->dpc_tasklet, io_dpc, (u32) pio_mgr); + + /* Initialize DPC counters */ + pio_mgr->dpc_req = 0; + pio_mgr->dpc_sched = 0; + + spin_lock_init(&pio_mgr->dpc_lock); + + if (DSP_SUCCEEDED(status)) + status = dev_get_dev_node(hdev_obj, &dev_node_obj); + } + + if (DSP_SUCCEEDED(status)) { + pio_mgr->hbridge_context = hbridge_context; + pio_mgr->shared_irq = pMgrAttrs->irq_shared; + if (dsp_wdt_init()) + status = -EPERM; + } else { + status = -EIO; + } +func_end: + if (DSP_FAILED(status)) { + /* Cleanup */ + bridge_io_destroy(pio_mgr); + if (phIOMgr) + *phIOMgr = NULL; + } else { + /* Return IO manager object to caller... */ + hchnl_mgr->hio_mgr = pio_mgr; + *phIOMgr = pio_mgr; + } + return status; +} + +/* + * ======== bridge_io_destroy ======== + * Purpose: + * Disable interrupts, destroy the IO manager. + */ +int bridge_io_destroy(struct io_mgr *hio_mgr) +{ + int status = 0; + if (hio_mgr) { + /* Free IO DPC object */ + tasklet_kill(&hio_mgr->dpc_tasklet); + +#ifndef DSP_TRACEBUF_DISABLED + kfree(hio_mgr->pmsg); +#endif + dsp_wdt_exit(); + /* Free this IO manager object */ + kfree(hio_mgr); + } else { + status = -EFAULT; + } + + return status; +} + +/* + * ======== bridge_io_on_loaded ======== + * Purpose: + * Called when a new program is loaded to get shared memory buffer + * parameters from COFF file. ulSharedBufferBase and ulSharedBufferLimit + * are in DSP address units. + */ +int bridge_io_on_loaded(struct io_mgr *hio_mgr) +{ + struct cod_manager *cod_man; + struct chnl_mgr *hchnl_mgr; + struct msg_mgr *hmsg_mgr; + u32 ul_shm_base; + u32 ul_shm_base_offset; + u32 ul_shm_limit; + u32 ul_shm_length = -1; + u32 ul_mem_length = -1; + u32 ul_msg_base; + u32 ul_msg_limit; + u32 ul_msg_length = -1; + u32 ul_ext_end; + u32 ul_gpp_pa = 0; + u32 ul_gpp_va = 0; + u32 ul_dsp_va = 0; + u32 ul_seg_size = 0; + u32 ul_pad_size = 0; + u32 i; + int status = 0; + u8 num_procs = 0; + s32 ndx = 0; + /* DSP MMU setup table */ + struct bridge_ioctl_extproc ae_proc[BRDIOCTL_NUMOFMMUTLB]; + struct cfg_hostres *host_res; + struct bridge_dev_context *pbridge_context; + u32 map_attrs; + u32 shm0_end; + u32 ul_dyn_ext_base; + u32 ul_seg1_size = 0; + u32 pa_curr = 0; + u32 va_curr = 0; + u32 gpp_va_curr = 0; + u32 num_bytes = 0; + u32 all_bits = 0; + u32 page_size[] = { HW_PAGE_SIZE16MB, HW_PAGE_SIZE1MB, + HW_PAGE_SIZE64KB, HW_PAGE_SIZE4KB + }; + + status = dev_get_bridge_context(hio_mgr->hdev_obj, &pbridge_context); + if (!pbridge_context) { + status = -EFAULT; + goto func_end; + } + + host_res = pbridge_context->resources; + if (!host_res) { + status = -EFAULT; + goto func_end; + } + status = dev_get_cod_mgr(hio_mgr->hdev_obj, &cod_man); + if (!cod_man) { + status = -EFAULT; + goto func_end; + } + hchnl_mgr = hio_mgr->hchnl_mgr; + /* The message manager is destroyed when the board is stopped. */ + dev_get_msg_mgr(hio_mgr->hdev_obj, &hio_mgr->hmsg_mgr); + hmsg_mgr = hio_mgr->hmsg_mgr; + if (!hchnl_mgr || !hmsg_mgr) { + status = -EFAULT; + goto func_end; + } + if (hio_mgr->shared_mem) + hio_mgr->shared_mem = NULL; + + /* Get start and length of channel part of shared memory */ + status = cod_get_sym_value(cod_man, CHNL_SHARED_BUFFER_BASE_SYM, + &ul_shm_base); + if (DSP_FAILED(status)) { + status = -EFAULT; + goto func_end; + } + status = cod_get_sym_value(cod_man, CHNL_SHARED_BUFFER_LIMIT_SYM, + &ul_shm_limit); + if (DSP_FAILED(status)) { + status = -EFAULT; + goto func_end; + } + if (ul_shm_limit <= ul_shm_base) { + status = -EINVAL; + goto func_end; + } + /* Get total length in bytes */ + ul_shm_length = (ul_shm_limit - ul_shm_base + 1) * hio_mgr->word_size; + /* Calculate size of a PROCCOPY shared memory region */ + dev_dbg(bridge, "%s: (proc)proccopy shmmem size: 0x%x bytes\n", + __func__, (ul_shm_length - sizeof(struct shm))); + + if (DSP_SUCCEEDED(status)) { + /* Get start and length of message part of shared memory */ + status = cod_get_sym_value(cod_man, MSG_SHARED_BUFFER_BASE_SYM, + &ul_msg_base); + } + if (DSP_SUCCEEDED(status)) { + status = cod_get_sym_value(cod_man, MSG_SHARED_BUFFER_LIMIT_SYM, + &ul_msg_limit); + if (DSP_SUCCEEDED(status)) { + if (ul_msg_limit <= ul_msg_base) { + status = -EINVAL; + } else { + /* + * Length (bytes) of messaging part of shared + * memory. + */ + ul_msg_length = + (ul_msg_limit - ul_msg_base + + 1) * hio_mgr->word_size; + /* + * Total length (bytes) of shared memory: + * chnl + msg. + */ + ul_mem_length = ul_shm_length + ul_msg_length; + } + } else { + status = -EFAULT; + } + } else { + status = -EFAULT; + } + if (DSP_SUCCEEDED(status)) { +#ifndef DSP_TRACEBUF_DISABLED + status = + cod_get_sym_value(cod_man, DSP_TRACESEC_END, &shm0_end); +#else + status = cod_get_sym_value(cod_man, SHM0_SHARED_END_SYM, + &shm0_end); +#endif + if (DSP_FAILED(status)) + status = -EFAULT; + } + if (DSP_SUCCEEDED(status)) { + status = + cod_get_sym_value(cod_man, DYNEXTBASE, &ul_dyn_ext_base); + if (DSP_FAILED(status)) + status = -EFAULT; + } + if (DSP_SUCCEEDED(status)) { + status = cod_get_sym_value(cod_man, EXTEND, &ul_ext_end); + if (DSP_FAILED(status)) + status = -EFAULT; + } + if (DSP_SUCCEEDED(status)) { + /* Get memory reserved in host resources */ + (void)mgr_enum_processor_info(0, (struct dsp_processorinfo *) + &hio_mgr->ext_proc_info, + sizeof(struct + mgr_processorextinfo), + &num_procs); + + /* The first MMU TLB entry(TLB_0) in DCD is ShmBase. */ + ndx = 0; + ul_gpp_pa = host_res->dw_mem_phys[1]; + ul_gpp_va = host_res->dw_mem_base[1]; + /* This is the virtual uncached ioremapped address!!! */ + /* Why can't we directly take the DSPVA from the symbols? */ + ul_dsp_va = hio_mgr->ext_proc_info.ty_tlb[0].ul_dsp_virt; + ul_seg_size = (shm0_end - ul_dsp_va) * hio_mgr->word_size; + ul_seg1_size = + (ul_ext_end - ul_dyn_ext_base) * hio_mgr->word_size; + /* 4K align */ + ul_seg1_size = (ul_seg1_size + 0xFFF) & (~0xFFFUL); + /* 64K align */ + ul_seg_size = (ul_seg_size + 0xFFFF) & (~0xFFFFUL); + ul_pad_size = UL_PAGE_ALIGN_SIZE - ((ul_gpp_pa + ul_seg1_size) % + UL_PAGE_ALIGN_SIZE); + if (ul_pad_size == UL_PAGE_ALIGN_SIZE) + ul_pad_size = 0x0; + + dev_dbg(bridge, "%s: ul_gpp_pa %x, ul_gpp_va %x, ul_dsp_va %x, " + "shm0_end %x, ul_dyn_ext_base %x, ul_ext_end %x, " + "ul_seg_size %x ul_seg1_size %x \n", __func__, + ul_gpp_pa, ul_gpp_va, ul_dsp_va, shm0_end, + ul_dyn_ext_base, ul_ext_end, ul_seg_size, ul_seg1_size); + + if ((ul_seg_size + ul_seg1_size + ul_pad_size) > + host_res->dw_mem_length[1]) { + pr_err("%s: shm Error, reserved 0x%x required 0x%x\n", + __func__, host_res->dw_mem_length[1], + ul_seg_size + ul_seg1_size + ul_pad_size); + status = -ENOMEM; + } + } + if (DSP_FAILED(status)) + goto func_end; + + pa_curr = ul_gpp_pa; + va_curr = ul_dyn_ext_base * hio_mgr->word_size; + gpp_va_curr = ul_gpp_va; + num_bytes = ul_seg1_size; + + /* + * Try to fit into TLB entries. If not possible, push them to page + * tables. It is quite possible that if sections are not on + * bigger page boundary, we may end up making several small pages. + * So, push them onto page tables, if that is the case. + */ + map_attrs = 0x00000000; + map_attrs = DSP_MAPLITTLEENDIAN; + map_attrs |= DSP_MAPPHYSICALADDR; + map_attrs |= DSP_MAPELEMSIZE32; + map_attrs |= DSP_MAPDONOTLOCK; + + while (num_bytes) { + /* + * To find the max. page size with which both PA & VA are + * aligned. + */ + all_bits = pa_curr | va_curr; + dev_dbg(bridge, "all_bits %x, pa_curr %x, va_curr %x, " + "num_bytes %x\n", all_bits, pa_curr, va_curr, + num_bytes); + for (i = 0; i < 4; i++) { + if ((num_bytes >= page_size[i]) && ((all_bits & + (page_size[i] - + 1)) == 0)) { + status = + hio_mgr->intf_fxns-> + pfn_brd_mem_map(hio_mgr->hbridge_context, + pa_curr, va_curr, + page_size[i], map_attrs, + NULL); + if (DSP_FAILED(status)) + goto func_end; + pa_curr += page_size[i]; + va_curr += page_size[i]; + gpp_va_curr += page_size[i]; + num_bytes -= page_size[i]; + /* + * Don't try smaller sizes. Hopefully we have + * reached an address aligned to a bigger page + * size. + */ + break; + } + } + } + pa_curr += ul_pad_size; + va_curr += ul_pad_size; + gpp_va_curr += ul_pad_size; + + /* Configure the TLB entries for the next cacheable segment */ + num_bytes = ul_seg_size; + va_curr = ul_dsp_va * hio_mgr->word_size; + while (num_bytes) { + /* + * To find the max. page size with which both PA & VA are + * aligned. + */ + all_bits = pa_curr | va_curr; + dev_dbg(bridge, "all_bits for Seg1 %x, pa_curr %x, " + "va_curr %x, num_bytes %x\n", all_bits, pa_curr, + va_curr, num_bytes); + for (i = 0; i < 4; i++) { + if (!(num_bytes >= page_size[i]) || + !((all_bits & (page_size[i] - 1)) == 0)) + continue; + if (ndx < MAX_LOCK_TLB_ENTRIES) { + /* + * This is the physical address written to + * DSP MMU. + */ + ae_proc[ndx].ul_gpp_pa = pa_curr; + /* + * This is the virtual uncached ioremapped + * address!!! + */ + ae_proc[ndx].ul_gpp_va = gpp_va_curr; + ae_proc[ndx].ul_dsp_va = + va_curr / hio_mgr->word_size; + ae_proc[ndx].ul_size = page_size[i]; + ae_proc[ndx].endianism = HW_LITTLE_ENDIAN; + ae_proc[ndx].elem_size = HW_ELEM_SIZE16BIT; + ae_proc[ndx].mixed_mode = HW_MMU_CPUES; + dev_dbg(bridge, "shm MMU TLB entry PA %x" + " VA %x DSP_VA %x Size %x\n", + ae_proc[ndx].ul_gpp_pa, + ae_proc[ndx].ul_gpp_va, + ae_proc[ndx].ul_dsp_va * + hio_mgr->word_size, page_size[i]); + ndx++; + } else { + status = + hio_mgr->intf_fxns-> + pfn_brd_mem_map(hio_mgr->hbridge_context, + pa_curr, va_curr, + page_size[i], map_attrs, + NULL); + dev_dbg(bridge, + "shm MMU PTE entry PA %x" + " VA %x DSP_VA %x Size %x\n", + ae_proc[ndx].ul_gpp_pa, + ae_proc[ndx].ul_gpp_va, + ae_proc[ndx].ul_dsp_va * + hio_mgr->word_size, page_size[i]); + if (DSP_FAILED(status)) + goto func_end; + } + pa_curr += page_size[i]; + va_curr += page_size[i]; + gpp_va_curr += page_size[i]; + num_bytes -= page_size[i]; + /* + * Don't try smaller sizes. Hopefully we have reached + * an address aligned to a bigger page size. + */ + break; + } + } + + /* + * Copy remaining entries from CDB. All entries are 1 MB and + * should not conflict with shm entries on MPU or DSP side. + */ + for (i = 3; i < 7 && ndx < BRDIOCTL_NUMOFMMUTLB; i++) { + if (hio_mgr->ext_proc_info.ty_tlb[i].ul_gpp_phys == 0) + continue; + + if ((hio_mgr->ext_proc_info.ty_tlb[i].ul_gpp_phys > + ul_gpp_pa - 0x100000 + && hio_mgr->ext_proc_info.ty_tlb[i].ul_gpp_phys <= + ul_gpp_pa + ul_seg_size) + || (hio_mgr->ext_proc_info.ty_tlb[i].ul_dsp_virt > + ul_dsp_va - 0x100000 / hio_mgr->word_size + && hio_mgr->ext_proc_info.ty_tlb[i].ul_dsp_virt <= + ul_dsp_va + ul_seg_size / hio_mgr->word_size)) { + dev_dbg(bridge, + "CDB MMU entry %d conflicts with " + "shm.\n\tCDB: GppPa %x, DspVa %x.\n\tSHM: " + "GppPa %x, DspVa %x, Bytes %x.\n", i, + hio_mgr->ext_proc_info.ty_tlb[i].ul_gpp_phys, + hio_mgr->ext_proc_info.ty_tlb[i].ul_dsp_virt, + ul_gpp_pa, ul_dsp_va, ul_seg_size); + status = -EPERM; + } else { + if (ndx < MAX_LOCK_TLB_ENTRIES) { + ae_proc[ndx].ul_dsp_va = + hio_mgr->ext_proc_info.ty_tlb[i]. + ul_dsp_virt; + ae_proc[ndx].ul_gpp_pa = + hio_mgr->ext_proc_info.ty_tlb[i]. + ul_gpp_phys; + ae_proc[ndx].ul_gpp_va = 0; + /* 1 MB */ + ae_proc[ndx].ul_size = 0x100000; + dev_dbg(bridge, "shm MMU entry PA %x " + "DSP_VA 0x%x\n", ae_proc[ndx].ul_gpp_pa, + ae_proc[ndx].ul_dsp_va); + ndx++; + } else { + status = hio_mgr->intf_fxns->pfn_brd_mem_map + (hio_mgr->hbridge_context, + hio_mgr->ext_proc_info.ty_tlb[i]. + ul_gpp_phys, + hio_mgr->ext_proc_info.ty_tlb[i]. + ul_dsp_virt, 0x100000, map_attrs, + NULL); + } + } + if (DSP_FAILED(status)) + goto func_end; + } + + map_attrs = 0x00000000; + map_attrs = DSP_MAPLITTLEENDIAN; + map_attrs |= DSP_MAPPHYSICALADDR; + map_attrs |= DSP_MAPELEMSIZE32; + map_attrs |= DSP_MAPDONOTLOCK; + + /* Map the L4 peripherals */ + i = 0; + while (l4_peripheral_table[i].phys_addr) { + status = hio_mgr->intf_fxns->pfn_brd_mem_map + (hio_mgr->hbridge_context, l4_peripheral_table[i].phys_addr, + l4_peripheral_table[i].dsp_virt_addr, HW_PAGE_SIZE4KB, + map_attrs, NULL); + if (DSP_FAILED(status)) + goto func_end; + i++; + } + + for (i = ndx; i < BRDIOCTL_NUMOFMMUTLB; i++) { + ae_proc[i].ul_dsp_va = 0; + ae_proc[i].ul_gpp_pa = 0; + ae_proc[i].ul_gpp_va = 0; + ae_proc[i].ul_size = 0; + } + /* + * Set the shm physical address entry (grayed out in CDB file) + * to the virtual uncached ioremapped address of shm reserved + * on MPU. + */ + hio_mgr->ext_proc_info.ty_tlb[0].ul_gpp_phys = + (ul_gpp_va + ul_seg1_size + ul_pad_size); + + /* + * Need shm Phys addr. IO supports only one DSP for now: + * num_procs = 1. + */ + if (!hio_mgr->ext_proc_info.ty_tlb[0].ul_gpp_phys || num_procs != 1) { + status = -EFAULT; + goto func_end; + } else { + if (ae_proc[0].ul_dsp_va > ul_shm_base) { + status = -EPERM; + goto func_end; + } + /* ul_shm_base may not be at ul_dsp_va address */ + ul_shm_base_offset = (ul_shm_base - ae_proc[0].ul_dsp_va) * + hio_mgr->word_size; + /* + * bridge_dev_ctrl() will set dev context dsp-mmu info. In + * bridge_brd_start() the MMU will be re-programed with MMU + * DSPVa-GPPPa pair info while DSP is in a known + * (reset) state. + */ + + status = + hio_mgr->intf_fxns->pfn_dev_cntrl(hio_mgr->hbridge_context, + BRDIOCTL_SETMMUCONFIG, + ae_proc); + if (DSP_FAILED(status)) + goto func_end; + ul_shm_base = hio_mgr->ext_proc_info.ty_tlb[0].ul_gpp_phys; + ul_shm_base += ul_shm_base_offset; + ul_shm_base = (u32) MEM_LINEAR_ADDRESS((void *)ul_shm_base, + ul_mem_length); + if (ul_shm_base == 0) { + status = -EFAULT; + goto func_end; + } + /* Register SM */ + status = + register_shm_segs(hio_mgr, cod_man, ae_proc[0].ul_gpp_pa); + } + + hio_mgr->shared_mem = (struct shm *)ul_shm_base; + hio_mgr->input = (u8 *) hio_mgr->shared_mem + sizeof(struct shm); + hio_mgr->output = hio_mgr->input + (ul_shm_length - + sizeof(struct shm)) / 2; + hio_mgr->usm_buf_size = hio_mgr->output - hio_mgr->input; + + /* Set up Shared memory addresses for messaging. */ + hio_mgr->msg_input_ctrl = (struct msg_ctrl *)((u8 *) hio_mgr->shared_mem + + ul_shm_length); + hio_mgr->msg_input = + (u8 *) hio_mgr->msg_input_ctrl + sizeof(struct msg_ctrl); + hio_mgr->msg_output_ctrl = + (struct msg_ctrl *)((u8 *) hio_mgr->msg_input_ctrl + + ul_msg_length / 2); + hio_mgr->msg_output = + (u8 *) hio_mgr->msg_output_ctrl + sizeof(struct msg_ctrl); + hmsg_mgr->max_msgs = + ((u8 *) hio_mgr->msg_output_ctrl - hio_mgr->msg_input) + / sizeof(struct msg_dspmsg); + dev_dbg(bridge, "IO MGR shm details: shared_mem %p, input %p, " + "output %p, msg_input_ctrl %p, msg_input %p, " + "msg_output_ctrl %p, msg_output %p\n", + (u8 *) hio_mgr->shared_mem, hio_mgr->input, + hio_mgr->output, (u8 *) hio_mgr->msg_input_ctrl, + hio_mgr->msg_input, (u8 *) hio_mgr->msg_output_ctrl, + hio_mgr->msg_output); + dev_dbg(bridge, "(proc) Mas msgs in shared memory: 0x%x\n", + hmsg_mgr->max_msgs); + memset((void *)hio_mgr->shared_mem, 0, sizeof(struct shm)); + +#ifndef DSP_TRACEBUF_DISABLED + /* Get the start address of trace buffer */ + status = cod_get_sym_value(cod_man, SYS_PUTCBEG, + &hio_mgr->ul_trace_buffer_begin); + if (DSP_FAILED(status)) { + status = -EFAULT; + goto func_end; + } + + hio_mgr->ul_gpp_read_pointer = hio_mgr->ul_trace_buffer_begin = + (ul_gpp_va + ul_seg1_size + ul_pad_size) + + (hio_mgr->ul_trace_buffer_begin - ul_dsp_va); + /* Get the end address of trace buffer */ + status = cod_get_sym_value(cod_man, SYS_PUTCEND, + &hio_mgr->ul_trace_buffer_end); + if (DSP_FAILED(status)) { + status = -EFAULT; + goto func_end; + } + hio_mgr->ul_trace_buffer_end = + (ul_gpp_va + ul_seg1_size + ul_pad_size) + + (hio_mgr->ul_trace_buffer_end - ul_dsp_va); + /* Get the current address of DSP write pointer */ + status = cod_get_sym_value(cod_man, BRIDGE_SYS_PUTC_CURRENT, + &hio_mgr->ul_trace_buffer_current); + if (DSP_FAILED(status)) { + status = -EFAULT; + goto func_end; + } + hio_mgr->ul_trace_buffer_current = + (ul_gpp_va + ul_seg1_size + ul_pad_size) + + (hio_mgr->ul_trace_buffer_current - ul_dsp_va); + /* Calculate the size of trace buffer */ + kfree(hio_mgr->pmsg); + hio_mgr->pmsg = kmalloc(((hio_mgr->ul_trace_buffer_end - + hio_mgr->ul_trace_buffer_begin) * + hio_mgr->word_size) + 2, GFP_KERNEL); + if (!hio_mgr->pmsg) + status = -ENOMEM; + + hio_mgr->ul_dsp_va = ul_dsp_va; + hio_mgr->ul_gpp_va = (ul_gpp_va + ul_seg1_size + ul_pad_size); + +#endif +func_end: + return status; +} + +/* + * ======== io_buf_size ======== + * Size of shared memory I/O channel. + */ +u32 io_buf_size(struct io_mgr *hio_mgr) +{ + if (hio_mgr) + return hio_mgr->usm_buf_size; + else + return 0; +} + +/* + * ======== io_cancel_chnl ======== + * Cancel IO on a given PCPY channel. + */ +void io_cancel_chnl(struct io_mgr *hio_mgr, u32 ulChnl) +{ + struct io_mgr *pio_mgr = (struct io_mgr *)hio_mgr; + struct shm *sm; + + if (!hio_mgr) + goto func_end; + sm = hio_mgr->shared_mem; + + /* Inform DSP that we have no more buffers on this channel */ + IO_AND_VALUE(pio_mgr->hbridge_context, struct shm, sm, host_free_mask, + (~(1 << ulChnl))); + + sm_interrupt_dsp(pio_mgr->hbridge_context, MBX_PCPY_CLASS); +func_end: + return; +} + +/* + * ======== io_dispatch_chnl ======== + * Proc-copy chanl dispatch. + */ +static void io_dispatch_chnl(IN struct io_mgr *pio_mgr, + IN OUT struct chnl_object *pchnl, u8 iMode) +{ + if (!pio_mgr) + goto func_end; + + /* See if there is any data available for transfer */ + if (iMode != IO_SERVICE) + goto func_end; + + /* Any channel will do for this mode */ + input_chnl(pio_mgr, pchnl, iMode); + output_chnl(pio_mgr, pchnl, iMode); +func_end: + return; +} + +/* + * ======== io_dispatch_msg ======== + * Performs I/O dispatch on message queues. + */ +static void io_dispatch_msg(IN struct io_mgr *pio_mgr, struct msg_mgr *hmsg_mgr) +{ + if (!pio_mgr) + goto func_end; + + /* We are performing both input and output processing. */ + input_msg(pio_mgr, hmsg_mgr); + output_msg(pio_mgr, hmsg_mgr); +func_end: + return; +} + +/* + * ======== io_dispatch_pm ======== + * Performs I/O dispatch on PM related messages from DSP + */ +static void io_dispatch_pm(struct io_mgr *pio_mgr) +{ + int status; + u32 parg[2]; + + /* Perform Power message processing here */ + parg[0] = pio_mgr->intr_val; + + /* Send the command to the Bridge clk/pwr manager to handle */ + if (parg[0] == MBX_PM_HIBERNATE_EN) { + dev_dbg(bridge, "PM: Hibernate command\n"); + status = pio_mgr->intf_fxns-> + pfn_dev_cntrl(pio_mgr->hbridge_context, + BRDIOCTL_PWR_HIBERNATE, parg); + if (DSP_FAILED(status)) + pr_err("%s: hibernate cmd failed 0x%x\n", + __func__, status); + } else if (parg[0] == MBX_PM_OPP_REQ) { + parg[1] = pio_mgr->shared_mem->opp_request.rqst_opp_pt; + dev_dbg(bridge, "PM: Requested OPP = 0x%x\n", parg[1]); + status = pio_mgr->intf_fxns-> + pfn_dev_cntrl(pio_mgr->hbridge_context, + BRDIOCTL_CONSTRAINT_REQUEST, parg); + if (DSP_FAILED(status)) + dev_dbg(bridge, "PM: Failed to set constraint " + "= 0x%x \n", parg[1]); + } else { + dev_dbg(bridge, "PM: clk control value of msg = 0x%x\n", + parg[0]); + status = pio_mgr->intf_fxns-> + pfn_dev_cntrl(pio_mgr->hbridge_context, + BRDIOCTL_CLK_CTRL, parg); + if (DSP_FAILED(status)) + dev_dbg(bridge, "PM: Failed to ctrl the DSP clk" + "= 0x%x\n", *parg); + } +} + +/* + * ======== io_dpc ======== + * Deferred procedure call for shared memory channel driver ISR. Carries + * out the dispatch of I/O as a non-preemptible event.It can only be + * pre-empted by an ISR. + */ +void io_dpc(IN OUT unsigned long pRefData) +{ + struct io_mgr *pio_mgr = (struct io_mgr *)pRefData; + struct chnl_mgr *chnl_mgr_obj; + struct msg_mgr *msg_mgr_obj; + struct deh_mgr *hdeh_mgr; + u32 requested; + u32 serviced; + + if (!pio_mgr) + goto func_end; + chnl_mgr_obj = pio_mgr->hchnl_mgr; + dev_get_msg_mgr(pio_mgr->hdev_obj, &msg_mgr_obj); + dev_get_deh_mgr(pio_mgr->hdev_obj, &hdeh_mgr); + if (!chnl_mgr_obj) + goto func_end; + + requested = pio_mgr->dpc_req; + serviced = pio_mgr->dpc_sched; + + if (serviced == requested) + goto func_end; + + /* Process pending DPC's */ + do { + /* Check value of interrupt reg to ensure it's a valid error */ + if ((pio_mgr->intr_val > DEH_BASE) && + (pio_mgr->intr_val < DEH_LIMIT)) { + /* Notify DSP/BIOS exception */ + if (hdeh_mgr) { +#ifndef DSP_TRACE_BUF_DISABLED + print_dsp_debug_trace(pio_mgr); +#endif + bridge_deh_notify(hdeh_mgr, DSP_SYSERROR, + pio_mgr->intr_val); + } + } + io_dispatch_chnl(pio_mgr, NULL, IO_SERVICE); +#ifdef CHNL_MESSAGES + if (msg_mgr_obj) + io_dispatch_msg(pio_mgr, msg_mgr_obj); +#endif +#ifndef DSP_TRACEBUF_DISABLED + if (pio_mgr->intr_val & MBX_DBG_SYSPRINTF) { + /* Notify DSP Trace message */ + print_dsp_debug_trace(pio_mgr); + } +#endif + serviced++; + } while (serviced != requested); + pio_mgr->dpc_sched = requested; +func_end: + return; +} + +/* + * ======== io_mbox_msg ======== + * Main interrupt handler for the shared memory IO manager. + * Calls the Bridge's CHNL_ISR to determine if this interrupt is ours, then + * schedules a DPC to dispatch I/O. + */ +void io_mbox_msg(u32 msg) +{ + struct io_mgr *pio_mgr; + struct dev_object *dev_obj; + unsigned long flags; + + dev_obj = dev_get_first(); + dev_get_io_mgr(dev_obj, &pio_mgr); + + if (!pio_mgr) + return; + + pio_mgr->intr_val = (u16)msg; + if (pio_mgr->intr_val & MBX_PM_CLASS) + io_dispatch_pm(pio_mgr); + + if (pio_mgr->intr_val == MBX_DEH_RESET) { + pio_mgr->intr_val = 0; + } else { + spin_lock_irqsave(&pio_mgr->dpc_lock, flags); + pio_mgr->dpc_req++; + spin_unlock_irqrestore(&pio_mgr->dpc_lock, flags); + tasklet_schedule(&pio_mgr->dpc_tasklet); + } + return; +} + +/* + * ======== io_request_chnl ======== + * Purpose: + * Request chanenel I/O from the DSP. Sets flags in shared memory, then + * interrupts the DSP. + */ +void io_request_chnl(struct io_mgr *pio_mgr, struct chnl_object *pchnl, + u8 iMode, OUT u16 *pwMbVal) +{ + struct chnl_mgr *chnl_mgr_obj; + struct shm *sm; + + if (!pchnl || !pwMbVal) + goto func_end; + chnl_mgr_obj = pio_mgr->hchnl_mgr; + sm = pio_mgr->shared_mem; + if (iMode == IO_INPUT) { + /* + * Assertion fires if CHNL_AddIOReq() called on a stream + * which was cancelled, or attached to a dead board. + */ + DBC_ASSERT((pchnl->dw_state == CHNL_STATEREADY) || + (pchnl->dw_state == CHNL_STATEEOS)); + /* Indicate to the DSP we have a buffer available for input */ + IO_OR_VALUE(pio_mgr->hbridge_context, struct shm, sm, + host_free_mask, (1 << pchnl->chnl_id)); + *pwMbVal = MBX_PCPY_CLASS; + } else if (iMode == IO_OUTPUT) { + /* + * This assertion fails if CHNL_AddIOReq() was called on a + * stream which was cancelled, or attached to a dead board. + */ + DBC_ASSERT((pchnl->dw_state & ~CHNL_STATEEOS) == + CHNL_STATEREADY); + /* + * Record the fact that we have a buffer available for + * output. + */ + chnl_mgr_obj->dw_output_mask |= (1 << pchnl->chnl_id); + } else { + DBC_ASSERT(iMode); /* Shouldn't get here. */ + } +func_end: + return; +} + +/* + * ======== iosm_schedule ======== + * Schedule DPC for IO. + */ +void iosm_schedule(struct io_mgr *pio_mgr) +{ + unsigned long flags; + + if (!pio_mgr) + return; + + /* Increment count of DPC's pending. */ + spin_lock_irqsave(&pio_mgr->dpc_lock, flags); + pio_mgr->dpc_req++; + spin_unlock_irqrestore(&pio_mgr->dpc_lock, flags); + + /* Schedule DPC */ + tasklet_schedule(&pio_mgr->dpc_tasklet); +} + +/* + * ======== find_ready_output ======== + * Search for a host output channel which is ready to send. If this is + * called as a result of servicing the DPC, then implement a round + * robin search; otherwise, this was called by a client thread (via + * IO_Dispatch()), so just start searching from the current channel id. + */ +static u32 find_ready_output(struct chnl_mgr *chnl_mgr_obj, + struct chnl_object *pchnl, u32 dwMask) +{ + u32 ret = OUTPUTNOTREADY; + u32 id, start_id; + u32 shift; + + id = (pchnl != + NULL ? pchnl->chnl_id : (chnl_mgr_obj->dw_last_output + 1)); + id = ((id == CHNL_MAXCHANNELS) ? 0 : id); + if (id >= CHNL_MAXCHANNELS) + goto func_end; + if (dwMask) { + shift = (1 << id); + start_id = id; + do { + if (dwMask & shift) { + ret = id; + if (pchnl == NULL) + chnl_mgr_obj->dw_last_output = id; + break; + } + id = id + 1; + id = ((id == CHNL_MAXCHANNELS) ? 0 : id); + shift = (1 << id); + } while (id != start_id); + } +func_end: + return ret; +} + +/* + * ======== input_chnl ======== + * Dispatch a buffer on an input channel. + */ +static void input_chnl(struct io_mgr *pio_mgr, struct chnl_object *pchnl, + u8 iMode) +{ + struct chnl_mgr *chnl_mgr_obj; + struct shm *sm; + u32 chnl_id; + u32 bytes; + struct chnl_irp *chnl_packet_obj = NULL; + u32 dw_arg; + bool clear_chnl = false; + bool notify_client = false; + + sm = pio_mgr->shared_mem; + chnl_mgr_obj = pio_mgr->hchnl_mgr; + + /* Attempt to perform input */ + if (!IO_GET_VALUE(pio_mgr->hbridge_context, struct shm, sm, input_full)) + goto func_end; + + bytes = + IO_GET_VALUE(pio_mgr->hbridge_context, struct shm, sm, + input_size) * chnl_mgr_obj->word_size; + chnl_id = IO_GET_VALUE(pio_mgr->hbridge_context, struct shm, + sm, input_id); + dw_arg = IO_GET_LONG(pio_mgr->hbridge_context, struct shm, sm, arg); + if (chnl_id >= CHNL_MAXCHANNELS) { + /* Shouldn't be here: would indicate corrupted shm. */ + DBC_ASSERT(chnl_id); + goto func_end; + } + pchnl = chnl_mgr_obj->ap_channel[chnl_id]; + if ((pchnl != NULL) && CHNL_IS_INPUT(pchnl->chnl_mode)) { + if ((pchnl->dw_state & ~CHNL_STATEEOS) == CHNL_STATEREADY) { + if (!pchnl->pio_requests) + goto func_end; + /* Get the I/O request, and attempt a transfer */ + chnl_packet_obj = (struct chnl_irp *) + lst_get_head(pchnl->pio_requests); + if (chnl_packet_obj) { + pchnl->cio_reqs--; + if (pchnl->cio_reqs < 0) + goto func_end; + /* + * Ensure we don't overflow the client's + * buffer. + */ + bytes = min(bytes, chnl_packet_obj->byte_size); + /* Transfer buffer from DSP side */ + bytes = read_data(pio_mgr->hbridge_context, + chnl_packet_obj->host_sys_buf, + pio_mgr->input, bytes); + pchnl->bytes_moved += bytes; + chnl_packet_obj->byte_size = bytes; + chnl_packet_obj->dw_arg = dw_arg; + chnl_packet_obj->status = CHNL_IOCSTATCOMPLETE; + + if (bytes == 0) { + /* + * This assertion fails if the DSP + * sends EOS more than once on this + * channel. + */ + if (pchnl->dw_state & CHNL_STATEEOS) + goto func_end; + /* + * Zero bytes indicates EOS. Update + * IOC status for this chirp, and also + * the channel state. + */ + chnl_packet_obj->status |= + CHNL_IOCSTATEOS; + pchnl->dw_state |= CHNL_STATEEOS; + /* + * Notify that end of stream has + * occurred. + */ + ntfy_notify(pchnl->ntfy_obj, + DSP_STREAMDONE); + } + /* Tell DSP if no more I/O buffers available */ + if (!pchnl->pio_requests) + goto func_end; + if (LST_IS_EMPTY(pchnl->pio_requests)) { + IO_AND_VALUE(pio_mgr->hbridge_context, + struct shm, sm, + host_free_mask, + ~(1 << pchnl->chnl_id)); + } + clear_chnl = true; + notify_client = true; + } else { + /* + * Input full for this channel, but we have no + * buffers available. The channel must be + * "idling". Clear out the physical input + * channel. + */ + clear_chnl = true; + } + } else { + /* Input channel cancelled: clear input channel */ + clear_chnl = true; + } + } else { + /* DPC fired after host closed channel: clear input channel */ + clear_chnl = true; + } + if (clear_chnl) { + /* Indicate to the DSP we have read the input */ + IO_SET_VALUE(pio_mgr->hbridge_context, struct shm, sm, + input_full, 0); + sm_interrupt_dsp(pio_mgr->hbridge_context, MBX_PCPY_CLASS); + } + if (notify_client) { + /* Notify client with IO completion record */ + notify_chnl_complete(pchnl, chnl_packet_obj); + } +func_end: + return; +} + +/* + * ======== input_msg ======== + * Copies messages from shared memory to the message queues. + */ +static void input_msg(struct io_mgr *pio_mgr, struct msg_mgr *hmsg_mgr) +{ + u32 num_msgs; + u32 i; + u8 *msg_input; + struct msg_queue *msg_queue_obj; + struct msg_frame *pmsg; + struct msg_dspmsg msg; + struct msg_ctrl *msg_ctr_obj; + u32 input_empty; + u32 addr; + + msg_ctr_obj = pio_mgr->msg_input_ctrl; + /* Get the number of input messages to be read */ + input_empty = + IO_GET_VALUE(pio_mgr->hbridge_context, struct msg_ctrl, msg_ctr_obj, + buf_empty); + num_msgs = + IO_GET_VALUE(pio_mgr->hbridge_context, struct msg_ctrl, msg_ctr_obj, + size); + if (input_empty) + goto func_end; + + msg_input = pio_mgr->msg_input; + for (i = 0; i < num_msgs; i++) { + /* Read the next message */ + addr = (u32) &(((struct msg_dspmsg *)msg_input)->msg.dw_cmd); + msg.msg.dw_cmd = + read_ext32_bit_dsp_data(pio_mgr->hbridge_context, addr); + addr = (u32) &(((struct msg_dspmsg *)msg_input)->msg.dw_arg1); + msg.msg.dw_arg1 = + read_ext32_bit_dsp_data(pio_mgr->hbridge_context, addr); + addr = (u32) &(((struct msg_dspmsg *)msg_input)->msg.dw_arg2); + msg.msg.dw_arg2 = + read_ext32_bit_dsp_data(pio_mgr->hbridge_context, addr); + addr = (u32) &(((struct msg_dspmsg *)msg_input)->msgq_id); + msg.msgq_id = + read_ext32_bit_dsp_data(pio_mgr->hbridge_context, addr); + msg_input += sizeof(struct msg_dspmsg); + if (!hmsg_mgr->queue_list) + goto func_end; + + /* Determine which queue to put the message in */ + msg_queue_obj = + (struct msg_queue *)lst_first(hmsg_mgr->queue_list); + dev_dbg(bridge, "input msg: dw_cmd=0x%x dw_arg1=0x%x " + "dw_arg2=0x%x msgq_id=0x%x \n", msg.msg.dw_cmd, + msg.msg.dw_arg1, msg.msg.dw_arg2, msg.msgq_id); + /* + * Interrupt may occur before shared memory and message + * input locations have been set up. If all nodes were + * cleaned up, hmsg_mgr->max_msgs should be 0. + */ + while (msg_queue_obj != NULL) { + if (msg.msgq_id == msg_queue_obj->msgq_id) { + /* Found it */ + if (msg.msg.dw_cmd == RMS_EXITACK) { + /* + * Call the node exit notification. + * The exit message does not get + * queued. + */ + (*hmsg_mgr->on_exit) ((void *) + msg_queue_obj->arg, + msg.msg.dw_arg1); + } else { + /* + * Not an exit acknowledgement, queue + * the message. + */ + if (!msg_queue_obj->msg_free_list) + goto func_end; + pmsg = (struct msg_frame *)lst_get_head + (msg_queue_obj->msg_free_list); + if (msg_queue_obj->msg_used_list + && pmsg) { + pmsg->msg_data = msg; + lst_put_tail + (msg_queue_obj->msg_used_list, + (struct list_head *)pmsg); + ntfy_notify + (msg_queue_obj->ntfy_obj, + DSP_NODEMESSAGEREADY); + sync_set_event + (msg_queue_obj->sync_event); + } else { + /* + * No free frame to copy the + * message into. + */ + pr_err("%s: no free msg frames," + " discarding msg\n", + __func__); + } + } + break; + } + + if (!hmsg_mgr->queue_list || !msg_queue_obj) + goto func_end; + msg_queue_obj = + (struct msg_queue *)lst_next(hmsg_mgr->queue_list, + (struct list_head *) + msg_queue_obj); + } + } + /* Set the post SWI flag */ + if (num_msgs > 0) { + /* Tell the DSP we've read the messages */ + IO_SET_VALUE(pio_mgr->hbridge_context, struct msg_ctrl, + msg_ctr_obj, buf_empty, true); + IO_SET_VALUE(pio_mgr->hbridge_context, struct msg_ctrl, + msg_ctr_obj, post_swi, true); + sm_interrupt_dsp(pio_mgr->hbridge_context, MBX_PCPY_CLASS); + } +func_end: + return; +} + +/* + * ======== notify_chnl_complete ======== + * Purpose: + * Signal the channel event, notifying the client that I/O has completed. + */ +static void notify_chnl_complete(struct chnl_object *pchnl, + struct chnl_irp *chnl_packet_obj) +{ + bool signal_event; + + if (!pchnl || !pchnl->sync_event || + !pchnl->pio_completions || !chnl_packet_obj) + goto func_end; + + /* + * Note: we signal the channel event only if the queue of IO + * completions is empty. If it is not empty, the event is sure to be + * signalled by the only IO completion list consumer: + * bridge_chnl_get_ioc(). + */ + signal_event = LST_IS_EMPTY(pchnl->pio_completions); + /* Enqueue the IO completion info for the client */ + lst_put_tail(pchnl->pio_completions, + (struct list_head *)chnl_packet_obj); + pchnl->cio_cs++; + + if (pchnl->cio_cs > pchnl->chnl_packets) + goto func_end; + /* Signal the channel event (if not already set) that IO is complete */ + if (signal_event) + sync_set_event(pchnl->sync_event); + + /* Notify that IO is complete */ + ntfy_notify(pchnl->ntfy_obj, DSP_STREAMIOCOMPLETION); +func_end: + return; +} + +/* + * ======== output_chnl ======== + * Purpose: + * Dispatch a buffer on an output channel. + */ +static void output_chnl(struct io_mgr *pio_mgr, struct chnl_object *pchnl, + u8 iMode) +{ + struct chnl_mgr *chnl_mgr_obj; + struct shm *sm; + u32 chnl_id; + struct chnl_irp *chnl_packet_obj; + u32 dw_dsp_f_mask; + + chnl_mgr_obj = pio_mgr->hchnl_mgr; + sm = pio_mgr->shared_mem; + /* Attempt to perform output */ + if (IO_GET_VALUE(pio_mgr->hbridge_context, struct shm, sm, output_full)) + goto func_end; + + if (pchnl && !((pchnl->dw_state & ~CHNL_STATEEOS) == CHNL_STATEREADY)) + goto func_end; + + /* Look to see if both a PC and DSP output channel are ready */ + dw_dsp_f_mask = IO_GET_VALUE(pio_mgr->hbridge_context, struct shm, sm, + dsp_free_mask); + chnl_id = + find_ready_output(chnl_mgr_obj, pchnl, + (chnl_mgr_obj->dw_output_mask & dw_dsp_f_mask)); + if (chnl_id == OUTPUTNOTREADY) + goto func_end; + + pchnl = chnl_mgr_obj->ap_channel[chnl_id]; + if (!pchnl || !pchnl->pio_requests) { + /* Shouldn't get here */ + goto func_end; + } + /* Get the I/O request, and attempt a transfer */ + chnl_packet_obj = (struct chnl_irp *)lst_get_head(pchnl->pio_requests); + if (!chnl_packet_obj) + goto func_end; + + pchnl->cio_reqs--; + if (pchnl->cio_reqs < 0 || !pchnl->pio_requests) + goto func_end; + + /* Record fact that no more I/O buffers available */ + if (LST_IS_EMPTY(pchnl->pio_requests)) + chnl_mgr_obj->dw_output_mask &= ~(1 << chnl_id); + + /* Transfer buffer to DSP side */ + chnl_packet_obj->byte_size = + write_data(pio_mgr->hbridge_context, pio_mgr->output, + chnl_packet_obj->host_sys_buf, min(pio_mgr->usm_buf_size, + chnl_packet_obj->byte_size)); + pchnl->bytes_moved += chnl_packet_obj->byte_size; + /* Write all 32 bits of arg */ + IO_SET_LONG(pio_mgr->hbridge_context, struct shm, sm, arg, + chnl_packet_obj->dw_arg); +#if _CHNL_WORDSIZE == 2 + IO_SET_VALUE(pio_mgr->hbridge_context, struct shm, sm, output_id, + (u16) chnl_id); + IO_SET_VALUE(pio_mgr->hbridge_context, struct shm, sm, output_size, + (u16) (chnl_packet_obj->byte_size + + (chnl_mgr_obj->word_size - + 1)) / (u16) chnl_mgr_obj->word_size); +#else + IO_SET_VALUE(pio_mgr->hbridge_context, struct shm, sm, output_id, + chnl_id); + IO_SET_VALUE(pio_mgr->hbridge_context, struct shm, sm, output_size, + (chnl_packet_obj->byte_size + + (chnl_mgr_obj->word_size - 1)) / chnl_mgr_obj->word_size); +#endif + IO_SET_VALUE(pio_mgr->hbridge_context, struct shm, sm, output_full, 1); + /* Indicate to the DSP we have written the output */ + sm_interrupt_dsp(pio_mgr->hbridge_context, MBX_PCPY_CLASS); + /* Notify client with IO completion record (keep EOS) */ + chnl_packet_obj->status &= CHNL_IOCSTATEOS; + notify_chnl_complete(pchnl, chnl_packet_obj); + /* Notify if stream is done. */ + if (chnl_packet_obj->status & CHNL_IOCSTATEOS) + ntfy_notify(pchnl->ntfy_obj, DSP_STREAMDONE); + +func_end: + return; +} + +/* + * ======== output_msg ======== + * Copies messages from the message queues to the shared memory. + */ +static void output_msg(struct io_mgr *pio_mgr, struct msg_mgr *hmsg_mgr) +{ + u32 num_msgs = 0; + u32 i; + u8 *msg_output; + struct msg_frame *pmsg; + struct msg_ctrl *msg_ctr_obj; + u32 output_empty; + u32 val; + u32 addr; + + msg_ctr_obj = pio_mgr->msg_output_ctrl; + + /* Check if output has been cleared */ + output_empty = + IO_GET_VALUE(pio_mgr->hbridge_context, struct msg_ctrl, msg_ctr_obj, + buf_empty); + if (output_empty) { + num_msgs = (hmsg_mgr->msgs_pending > hmsg_mgr->max_msgs) ? + hmsg_mgr->max_msgs : hmsg_mgr->msgs_pending; + msg_output = pio_mgr->msg_output; + /* Copy num_msgs messages into shared memory */ + for (i = 0; i < num_msgs; i++) { + if (!hmsg_mgr->msg_used_list) { + pmsg = NULL; + goto func_end; + } else { + pmsg = (struct msg_frame *) + lst_get_head(hmsg_mgr->msg_used_list); + } + if (pmsg != NULL) { + val = (pmsg->msg_data).msgq_id; + addr = (u32) &(((struct msg_dspmsg *) + msg_output)->msgq_id); + write_ext32_bit_dsp_data( + pio_mgr->hbridge_context, addr, val); + val = (pmsg->msg_data).msg.dw_cmd; + addr = (u32) &((((struct msg_dspmsg *) + msg_output)->msg).dw_cmd); + write_ext32_bit_dsp_data( + pio_mgr->hbridge_context, addr, val); + val = (pmsg->msg_data).msg.dw_arg1; + addr = (u32) &((((struct msg_dspmsg *) + msg_output)->msg).dw_arg1); + write_ext32_bit_dsp_data( + pio_mgr->hbridge_context, addr, val); + val = (pmsg->msg_data).msg.dw_arg2; + addr = (u32) &((((struct msg_dspmsg *) + msg_output)->msg).dw_arg2); + write_ext32_bit_dsp_data( + pio_mgr->hbridge_context, addr, val); + msg_output += sizeof(struct msg_dspmsg); + if (!hmsg_mgr->msg_free_list) + goto func_end; + lst_put_tail(hmsg_mgr->msg_free_list, + (struct list_head *)pmsg); + sync_set_event(hmsg_mgr->sync_event); + } + } + + if (num_msgs > 0) { + hmsg_mgr->msgs_pending -= num_msgs; +#if _CHNL_WORDSIZE == 2 + IO_SET_VALUE(pio_mgr->hbridge_context, struct msg_ctrl, + msg_ctr_obj, size, (u16) num_msgs); +#else + IO_SET_VALUE(pio_mgr->hbridge_context, struct msg_ctrl, + msg_ctr_obj, size, num_msgs); +#endif + IO_SET_VALUE(pio_mgr->hbridge_context, struct msg_ctrl, + msg_ctr_obj, buf_empty, false); + /* Set the post SWI flag */ + IO_SET_VALUE(pio_mgr->hbridge_context, struct msg_ctrl, + msg_ctr_obj, post_swi, true); + /* Tell the DSP we have written the output. */ + sm_interrupt_dsp(pio_mgr->hbridge_context, + MBX_PCPY_CLASS); + } + } +func_end: + return; +} + +/* + * ======== register_shm_segs ======== + * purpose: + * Registers GPP SM segment with CMM. + */ +static int register_shm_segs(struct io_mgr *hio_mgr, + struct cod_manager *cod_man, + u32 dw_gpp_base_pa) +{ + int status = 0; + u32 ul_shm0_base = 0; + u32 shm0_end = 0; + u32 ul_shm0_rsrvd_start = 0; + u32 ul_rsrvd_size = 0; + u32 ul_gpp_phys; + u32 ul_dsp_virt; + u32 ul_shm_seg_id0 = 0; + u32 dw_offset, dw_gpp_base_va, ul_dsp_size; + + /* + * Read address and size info for first SM region. + * Get start of 1st SM Heap region. + */ + status = + cod_get_sym_value(cod_man, SHM0_SHARED_BASE_SYM, &ul_shm0_base); + if (ul_shm0_base == 0) { + status = -EPERM; + goto func_end; + } + /* Get end of 1st SM Heap region */ + if (DSP_SUCCEEDED(status)) { + /* Get start and length of message part of shared memory */ + status = cod_get_sym_value(cod_man, SHM0_SHARED_END_SYM, + &shm0_end); + if (shm0_end == 0) { + status = -EPERM; + goto func_end; + } + } + /* Start of Gpp reserved region */ + if (DSP_SUCCEEDED(status)) { + /* Get start and length of message part of shared memory */ + status = + cod_get_sym_value(cod_man, SHM0_SHARED_RESERVED_BASE_SYM, + &ul_shm0_rsrvd_start); + if (ul_shm0_rsrvd_start == 0) { + status = -EPERM; + goto func_end; + } + } + /* Register with CMM */ + if (DSP_SUCCEEDED(status)) { + status = dev_get_cmm_mgr(hio_mgr->hdev_obj, &hio_mgr->hcmm_mgr); + if (DSP_SUCCEEDED(status)) { + status = cmm_un_register_gppsm_seg(hio_mgr->hcmm_mgr, + CMM_ALLSEGMENTS); + } + } + /* Register new SM region(s) */ + if (DSP_SUCCEEDED(status) && (shm0_end - ul_shm0_base) > 0) { + /* Calc size (bytes) of SM the GPP can alloc from */ + ul_rsrvd_size = + (shm0_end - ul_shm0_rsrvd_start + 1) * hio_mgr->word_size; + if (ul_rsrvd_size <= 0) { + status = -EPERM; + goto func_end; + } + /* Calc size of SM DSP can alloc from */ + ul_dsp_size = + (ul_shm0_rsrvd_start - ul_shm0_base) * hio_mgr->word_size; + if (ul_dsp_size <= 0) { + status = -EPERM; + goto func_end; + } + /* First TLB entry reserved for Bridge SM use. */ + ul_gpp_phys = hio_mgr->ext_proc_info.ty_tlb[0].ul_gpp_phys; + /* Get size in bytes */ + ul_dsp_virt = + hio_mgr->ext_proc_info.ty_tlb[0].ul_dsp_virt * + hio_mgr->word_size; + /* + * Calc byte offset used to convert GPP phys <-> DSP byte + * address. + */ + if (dw_gpp_base_pa > ul_dsp_virt) + dw_offset = dw_gpp_base_pa - ul_dsp_virt; + else + dw_offset = ul_dsp_virt - dw_gpp_base_pa; + + if (ul_shm0_rsrvd_start * hio_mgr->word_size < ul_dsp_virt) { + status = -EPERM; + goto func_end; + } + /* + * Calc Gpp phys base of SM region. + * This is actually uncached kernel virtual address. + */ + dw_gpp_base_va = + ul_gpp_phys + ul_shm0_rsrvd_start * hio_mgr->word_size - + ul_dsp_virt; + /* + * Calc Gpp phys base of SM region. + * This is the physical address. + */ + dw_gpp_base_pa = + dw_gpp_base_pa + ul_shm0_rsrvd_start * hio_mgr->word_size - + ul_dsp_virt; + /* Register SM Segment 0. */ + status = + cmm_register_gppsm_seg(hio_mgr->hcmm_mgr, dw_gpp_base_pa, + ul_rsrvd_size, dw_offset, + (dw_gpp_base_pa > + ul_dsp_virt) ? CMM_ADDTODSPPA : + CMM_SUBFROMDSPPA, + (u32) (ul_shm0_base * + hio_mgr->word_size), + ul_dsp_size, &ul_shm_seg_id0, + dw_gpp_base_va); + /* First SM region is seg_id = 1 */ + if (ul_shm_seg_id0 != 1) + status = -EPERM; + } +func_end: + return status; +} + +/* + * ======== read_data ======== + * Copies buffers from the shared memory to the host buffer. + */ +static u32 read_data(struct bridge_dev_context *hDevContext, void *dest, + void *pSrc, u32 usize) +{ + memcpy(dest, pSrc, usize); + return usize; +} + +/* + * ======== write_data ======== + * Copies buffers from the host side buffer to the shared memory. + */ +static u32 write_data(struct bridge_dev_context *hDevContext, void *dest, + void *pSrc, u32 usize) +{ + memcpy(dest, pSrc, usize); + return usize; +} + +/* ZCPY IO routines. */ +void io_intr_dsp2(IN struct io_mgr *pio_mgr, IN u16 mb_val) +{ + sm_interrupt_dsp(pio_mgr->hbridge_context, mb_val); +} + +/* + * ======== IO_SHMcontrol ======== + * Sets the requested shm setting. + */ +int io_sh_msetting(struct io_mgr *hio_mgr, u8 desc, void *pargs) +{ +#ifdef CONFIG_BRIDGE_DVFS + u32 i; + struct dspbridge_platform_data *pdata = + omap_dspbridge_dev->dev.platform_data; + + switch (desc) { + case SHM_CURROPP: + /* Update the shared memory with requested OPP information */ + if (pargs != NULL) + hio_mgr->shared_mem->opp_table_struct.curr_opp_pt = + *(u32 *) pargs; + else + return -EPERM; + break; + case SHM_OPPINFO: + /* + * Update the shared memory with the voltage, frequency, + * min and max frequency values for an OPP. + */ + for (i = 0; i <= dsp_max_opps; i++) { + hio_mgr->shared_mem->opp_table_struct.opp_point[i]. + voltage = vdd1_dsp_freq[i][0]; + dev_dbg(bridge, "OPP-shm: voltage: %d\n", + vdd1_dsp_freq[i][0]); + hio_mgr->shared_mem->opp_table_struct. + opp_point[i].frequency = vdd1_dsp_freq[i][1]; + dev_dbg(bridge, "OPP-shm: frequency: %d\n", + vdd1_dsp_freq[i][1]); + hio_mgr->shared_mem->opp_table_struct.opp_point[i]. + min_freq = vdd1_dsp_freq[i][2]; + dev_dbg(bridge, "OPP-shm: min freq: %d\n", + vdd1_dsp_freq[i][2]); + hio_mgr->shared_mem->opp_table_struct.opp_point[i]. + max_freq = vdd1_dsp_freq[i][3]; + dev_dbg(bridge, "OPP-shm: max freq: %d\n", + vdd1_dsp_freq[i][3]); + } + hio_mgr->shared_mem->opp_table_struct.num_opp_pts = + dsp_max_opps; + dev_dbg(bridge, "OPP-shm: max OPP number: %d\n", dsp_max_opps); + /* Update the current OPP number */ + if (pdata->dsp_get_opp) + i = (*pdata->dsp_get_opp) (); + hio_mgr->shared_mem->opp_table_struct.curr_opp_pt = i; + dev_dbg(bridge, "OPP-shm: value programmed = %d\n", i); + break; + case SHM_GETOPP: + /* Get the OPP that DSP has requested */ + *(u32 *) pargs = hio_mgr->shared_mem->opp_request.rqst_opp_pt; + break; + default: + break; + } +#endif + return 0; +} + +/* + * ======== bridge_io_get_proc_load ======== + * Gets the Processor's Load information + */ +int bridge_io_get_proc_load(IN struct io_mgr *hio_mgr, + OUT struct dsp_procloadstat *pProcStat) +{ + pProcStat->curr_load = hio_mgr->shared_mem->load_mon_info.curr_dsp_load; + pProcStat->predicted_load = + hio_mgr->shared_mem->load_mon_info.pred_dsp_load; + pProcStat->curr_dsp_freq = + hio_mgr->shared_mem->load_mon_info.curr_dsp_freq; + pProcStat->predicted_freq = + hio_mgr->shared_mem->load_mon_info.pred_dsp_freq; + + dev_dbg(bridge, "Curr Load = %d, Pred Load = %d, Curr Freq = %d, " + "Pred Freq = %d\n", pProcStat->curr_load, + pProcStat->predicted_load, pProcStat->curr_dsp_freq, + pProcStat->predicted_freq); + return 0; +} + +#ifndef DSP_TRACEBUF_DISABLED +void print_dsp_debug_trace(struct io_mgr *hio_mgr) +{ + u32 ul_new_message_length = 0, ul_gpp_cur_pointer; + + while (true) { + /* Get the DSP current pointer */ + ul_gpp_cur_pointer = + *(u32 *) (hio_mgr->ul_trace_buffer_current); + ul_gpp_cur_pointer = + hio_mgr->ul_gpp_va + (ul_gpp_cur_pointer - + hio_mgr->ul_dsp_va); + + /* No new debug messages available yet */ + if (ul_gpp_cur_pointer == hio_mgr->ul_gpp_read_pointer) { + break; + } else if (ul_gpp_cur_pointer > hio_mgr->ul_gpp_read_pointer) { + /* Continuous data */ + ul_new_message_length = + ul_gpp_cur_pointer - hio_mgr->ul_gpp_read_pointer; + + memcpy(hio_mgr->pmsg, + (char *)hio_mgr->ul_gpp_read_pointer, + ul_new_message_length); + hio_mgr->pmsg[ul_new_message_length] = '\0'; + /* + * Advance the GPP trace pointer to DSP current + * pointer. + */ + hio_mgr->ul_gpp_read_pointer += ul_new_message_length; + /* Print the trace messages */ + pr_info("DSPTrace: %s\n", hio_mgr->pmsg); + } else if (ul_gpp_cur_pointer < hio_mgr->ul_gpp_read_pointer) { + /* Handle trace buffer wraparound */ + memcpy(hio_mgr->pmsg, + (char *)hio_mgr->ul_gpp_read_pointer, + hio_mgr->ul_trace_buffer_end - + hio_mgr->ul_gpp_read_pointer); + ul_new_message_length = + ul_gpp_cur_pointer - hio_mgr->ul_trace_buffer_begin; + memcpy(&hio_mgr->pmsg[hio_mgr->ul_trace_buffer_end - + hio_mgr->ul_gpp_read_pointer], + (char *)hio_mgr->ul_trace_buffer_begin, + ul_new_message_length); + hio_mgr->pmsg[hio_mgr->ul_trace_buffer_end - + hio_mgr->ul_gpp_read_pointer + + ul_new_message_length] = '\0'; + /* + * Advance the GPP trace pointer to DSP current + * pointer. + */ + hio_mgr->ul_gpp_read_pointer = + hio_mgr->ul_trace_buffer_begin + + ul_new_message_length; + /* Print the trace messages */ + pr_info("DSPTrace: %s\n", hio_mgr->pmsg); + } + } +} +#endif + +/* + * ======== print_dsp_trace_buffer ======== + * Prints the trace buffer returned from the DSP (if DBG_Trace is enabled). + * Parameters: + * hdeh_mgr: Handle to DEH manager object + * number of extra carriage returns to generate. + * Returns: + * 0: Success. + * -ENOMEM: Unable to allocate memory. + * Requires: + * hdeh_mgr muse be valid. Checked in bridge_deh_notify. + */ +int print_dsp_trace_buffer(struct bridge_dev_context *hbridge_context) +{ + int status = 0; + struct cod_manager *cod_mgr; + u32 ul_trace_end; + u32 ul_trace_begin; + u32 trace_cur_pos; + u32 ul_num_bytes = 0; + u32 ul_num_words = 0; + u32 ul_word_size = 2; + char *psz_buf; + char *str_beg; + char *trace_end; + char *buf_end; + char *new_line; + + struct bridge_dev_context *pbridge_context = hbridge_context; + struct bridge_drv_interface *intf_fxns; + struct dev_object *dev_obj = (struct dev_object *) + pbridge_context->hdev_obj; + + status = dev_get_cod_mgr(dev_obj, &cod_mgr); + + if (cod_mgr) { + /* Look for SYS_PUTCBEG/SYS_PUTCEND */ + status = + cod_get_sym_value(cod_mgr, COD_TRACEBEG, &ul_trace_begin); + } else { + status = -EFAULT; + } + if (DSP_SUCCEEDED(status)) + status = + cod_get_sym_value(cod_mgr, COD_TRACEEND, &ul_trace_end); + + if (DSP_SUCCEEDED(status)) + /* trace_cur_pos will hold the address of a DSP pointer */ + status = cod_get_sym_value(cod_mgr, COD_TRACECURPOS, + &trace_cur_pos); + + if (DSP_FAILED(status)) + goto func_end; + + ul_num_bytes = (ul_trace_end - ul_trace_begin); + + ul_num_words = ul_num_bytes * ul_word_size; + status = dev_get_intf_fxns(dev_obj, &intf_fxns); + + if (DSP_FAILED(status)) + goto func_end; + + psz_buf = kzalloc(ul_num_bytes + 2, GFP_ATOMIC); + if (psz_buf != NULL) { + /* Read trace buffer data */ + status = (*intf_fxns->pfn_brd_read)(pbridge_context, + (u8 *)psz_buf, (u32)ul_trace_begin, + ul_num_bytes, 0); + + if (DSP_FAILED(status)) + goto func_end; + + /* Pack and do newline conversion */ + pr_debug("PrintDspTraceBuffer: " + "before pack and unpack.\n"); + pr_debug("%s: DSP Trace Buffer Begin:\n" + "=======================\n%s\n", + __func__, psz_buf); + + /* Read the value at the DSP address in trace_cur_pos. */ + status = (*intf_fxns->pfn_brd_read)(pbridge_context, + (u8 *)&trace_cur_pos, (u32)trace_cur_pos, + 4, 0); + if (DSP_FAILED(status)) + goto func_end; + /* Pack and do newline conversion */ + pr_info("DSP Trace Buffer Begin:\n" + "=======================\n%s\n", + psz_buf); + + + /* convert to offset */ + trace_cur_pos = trace_cur_pos - ul_trace_begin; + + if (ul_num_bytes) { + /* + * The buffer is not full, find the end of the + * data -- buf_end will be >= pszBuf after + * while. + */ + buf_end = &psz_buf[ul_num_bytes+1]; + /* DSP print position */ + trace_end = &psz_buf[trace_cur_pos]; + + /* + * Search buffer for a new_line and replace it + * with '\0', then print as string. + * Continue until end of buffer is reached. + */ + str_beg = trace_end; + ul_num_bytes = buf_end - str_beg; + + while (str_beg < buf_end) { + new_line = strnchr(str_beg, ul_num_bytes, + '\n'); + if (new_line && new_line < buf_end) { + *new_line = 0; + pr_debug("%s\n", str_beg); + str_beg = ++new_line; + ul_num_bytes = buf_end - str_beg; + } else { + /* + * Assume buffer empty if it contains + * a zero + */ + if (*str_beg != '\0') { + str_beg[ul_num_bytes] = 0; + pr_debug("%s\n", str_beg); + } + str_beg = buf_end; + ul_num_bytes = 0; + } + } + /* + * Search buffer for a nNewLine and replace it + * with '\0', then print as string. + * Continue until buffer is exhausted. + */ + str_beg = psz_buf; + ul_num_bytes = trace_end - str_beg; + + while (str_beg < trace_end) { + new_line = strnchr(str_beg, ul_num_bytes, '\n'); + if (new_line != NULL && new_line < trace_end) { + *new_line = 0; + pr_debug("%s\n", str_beg); + str_beg = ++new_line; + ul_num_bytes = trace_end - str_beg; + } else { + /* + * Assume buffer empty if it contains + * a zero + */ + if (*str_beg != '\0') { + str_beg[ul_num_bytes] = 0; + pr_debug("%s\n", str_beg); + } + str_beg = trace_end; + ul_num_bytes = 0; + } + } + } + pr_info("\n=======================\n" + "DSP Trace Buffer End:\n"); + kfree(psz_buf); + } else { + status = -ENOMEM; + } +func_end: + if (DSP_FAILED(status)) + dev_dbg(bridge, "%s Failed, status 0x%x\n", __func__, status); + return status; +} + +void io_sm_init(void) +{ + /* Do nothing */ +} +/** + * dump_dsp_stack() - This function dumps the data on the DSP stack. + * @bridge_context: Bridge driver's device context pointer. + * + */ +int dump_dsp_stack(struct bridge_dev_context *bridge_context) +{ + int status = 0; + struct cod_manager *code_mgr; + struct node_mgr *node_mgr; + u32 trace_begin; + char name[256]; + struct { + u32 head[2]; + u32 size; + } mmu_fault_dbg_info; + u32 *buffer; + u32 *buffer_beg; + u32 *buffer_end; + u32 exc_type; + u32 dyn_ext_base; + u32 i; + u32 offset_output; + u32 total_size; + u32 poll_cnt; + const char *dsp_regs[] = {"EFR", "IERR", "ITSR", "NTSR", + "IRP", "NRP", "AMR", "SSR", + "ILC", "RILC", "IER", "CSR"}; + const char *exec_ctxt[] = {"Task", "SWI", "HWI", "Unknown"}; + struct bridge_drv_interface *intf_fxns; + struct dev_object *dev_object = bridge_context->hdev_obj; + + status = dev_get_cod_mgr(dev_object, &code_mgr); + if (!code_mgr) { + pr_debug("%s: Failed on dev_get_cod_mgr.\n", __func__); + status = -EFAULT; + } + + if (DSP_SUCCEEDED(status)) { + status = dev_get_node_manager(dev_object, &node_mgr); + if (!node_mgr) { + pr_debug("%s: Failed on dev_get_node_manager.\n", + __func__); + status = -EFAULT; + } + } + + if (DSP_SUCCEEDED(status)) { + /* Look for SYS_PUTCBEG/SYS_PUTCEND: */ + status = + cod_get_sym_value(code_mgr, COD_TRACEBEG, &trace_begin); + pr_debug("%s: trace_begin Value 0x%x\n", + __func__, trace_begin); + if (DSP_FAILED(status)) + pr_debug("%s: Failed on cod_get_sym_value.\n", + __func__); + } + if (DSP_SUCCEEDED(status)) + status = dev_get_intf_fxns(dev_object, &intf_fxns); + /* + * Check for the "magic number" in the trace buffer. If it has + * yet to appear then poll the trace buffer to wait for it. Its + * appearance signals that the DSP has finished dumping its state. + */ + mmu_fault_dbg_info.head[0] = 0; + mmu_fault_dbg_info.head[1] = 0; + if (DSP_SUCCEEDED(status)) { + poll_cnt = 0; + while ((mmu_fault_dbg_info.head[0] != MMU_FAULT_HEAD1 || + mmu_fault_dbg_info.head[1] != MMU_FAULT_HEAD2) && + poll_cnt < POLL_MAX) { + + /* Read DSP dump size from the DSP trace buffer... */ + status = (*intf_fxns->pfn_brd_read)(bridge_context, + (u8 *)&mmu_fault_dbg_info, (u32)trace_begin, + sizeof(mmu_fault_dbg_info), 0); + + if (DSP_FAILED(status)) + break; + + poll_cnt++; + } + + if (mmu_fault_dbg_info.head[0] != MMU_FAULT_HEAD1 && + mmu_fault_dbg_info.head[1] != MMU_FAULT_HEAD2) { + status = -ETIME; + pr_err("%s:No DSP MMU-Fault information available.\n", + __func__); + } + } + + if (DSP_SUCCEEDED(status)) { + total_size = mmu_fault_dbg_info.size; + /* Limit the size in case DSP went crazy */ + if (total_size > MAX_MMU_DBGBUFF) + total_size = MAX_MMU_DBGBUFF; + + buffer = kzalloc(total_size, GFP_ATOMIC); + if (!buffer) { + status = -ENOMEM; + pr_debug("%s: Failed to " + "allocate stack dump buffer.\n", __func__); + goto func_end; + } + + buffer_beg = buffer; + buffer_end = buffer + total_size / 4; + + /* Read bytes from the DSP trace buffer... */ + status = (*intf_fxns->pfn_brd_read)(bridge_context, + (u8 *)buffer, (u32)trace_begin, + total_size, 0); + if (DSP_FAILED(status)) { + pr_debug("%s: Failed to Read Trace Buffer.\n", + __func__); + goto func_end; + } + + pr_err("\nAproximate Crash Position:\n" + "--------------------------\n"); + + exc_type = buffer[3]; + if (!exc_type) + i = buffer[79]; /* IRP */ + else + i = buffer[80]; /* NRP */ + + status = + cod_get_sym_value(code_mgr, DYNEXTBASE, &dyn_ext_base); + if (DSP_FAILED(status)) { + status = -EFAULT; + goto func_end; + } + + if ((i > dyn_ext_base) && (node_find_addr(node_mgr, i, + 0x1000, &offset_output, name) == 0)) + pr_err("0x%-8x [\"%s\" + 0x%x]\n", i, name, + i - offset_output); + else + pr_err("0x%-8x [Unable to match to a symbol.]\n", i); + + buffer += 4; + + pr_err("\nExecution Info:\n" + "---------------\n"); + + if (*buffer < ARRAY_SIZE(exec_ctxt)) { + pr_err("Execution context \t%s\n", + exec_ctxt[*buffer++]); + } else { + pr_err("Execution context corrupt\n"); + kfree(buffer_beg); + return -EFAULT; + } + pr_err("Task Handle\t\t0x%x\n", *buffer++); + pr_err("Stack Pointer\t\t0x%x\n", *buffer++); + pr_err("Stack Top\t\t0x%x\n", *buffer++); + pr_err("Stack Bottom\t\t0x%x\n", *buffer++); + pr_err("Stack Size\t\t0x%x\n", *buffer++); + pr_err("Stack Size In Use\t0x%x\n", *buffer++); + + pr_err("\nCPU Registers\n" + "---------------\n"); + + for (i = 0; i < 32; i++) { + if (i == 4 || i == 6 || i == 8) + pr_err("A%d 0x%-8x [Function Argument %d]\n", + i, *buffer++, i-3); + else if (i == 15) + pr_err("A15 0x%-8x [Frame Pointer]\n", + *buffer++); + else + pr_err("A%d 0x%x\n", i, *buffer++); + } + + pr_err("\nB0 0x%x\n", *buffer++); + pr_err("B1 0x%x\n", *buffer++); + pr_err("B2 0x%x\n", *buffer++); + + if ((*buffer > dyn_ext_base) && (node_find_addr(node_mgr, + *buffer, 0x1000, &offset_output, name) == 0)) + + pr_err("B3 0x%-8x [Function Return Pointer:" + " \"%s\" + 0x%x]\n", *buffer, name, + *buffer - offset_output); + else + pr_err("B3 0x%-8x [Function Return Pointer:" + "Unable to match to a symbol.]\n", *buffer); + + buffer++; + + for (i = 4; i < 32; i++) { + if (i == 4 || i == 6 || i == 8) + pr_err("B%d 0x%-8x [Function Argument %d]\n", + i, *buffer++, i-2); + else if (i == 14) + pr_err("B14 0x%-8x [Data Page Pointer]\n", + *buffer++); + else + pr_err("B%d 0x%x\n", i, *buffer++); + } + + pr_err("\n"); + + for (i = 0; i < ARRAY_SIZE(dsp_regs); i++) + pr_err("%s 0x%x\n", dsp_regs[i], *buffer++); + + pr_err("\nStack:\n" + "------\n"); + + for (i = 0; buffer < buffer_end; i++, buffer++) { + if ((*buffer > dyn_ext_base) && ( + node_find_addr(node_mgr, *buffer , 0x600, + &offset_output, name) == 0)) + pr_err("[%d] 0x%-8x [\"%s\" + 0x%x]\n", + i, *buffer, name, + *buffer - offset_output); + else + pr_err("[%d] 0x%x\n", i, *buffer); + } + kfree(buffer_beg); + } +func_end: + return status; +} + +/** + * dump_dl_modules() - This functions dumps the _DLModules loaded in DSP side + * @bridge_context: Bridge driver's device context pointer. + * + */ +void dump_dl_modules(struct bridge_dev_context *bridge_context) +{ + struct cod_manager *code_mgr; + struct bridge_drv_interface *intf_fxns; + struct bridge_dev_context *bridge_ctxt = bridge_context; + struct dev_object *dev_object = bridge_ctxt->hdev_obj; + struct modules_header modules_hdr; + struct dll_module *module_struct = NULL; + u32 module_dsp_addr; + u32 module_size; + u32 module_struct_size = 0; + u32 sect_ndx; + char *sect_str ; + int status = 0; + + status = dev_get_intf_fxns(dev_object, &intf_fxns); + if (DSP_FAILED(status)) { + pr_debug("%s: Failed on dev_get_intf_fxns.\n", __func__); + goto func_end; + } + + status = dev_get_cod_mgr(dev_object, &code_mgr); + if (!code_mgr) { + pr_debug("%s: Failed on dev_get_cod_mgr.\n", __func__); + status = -EFAULT; + goto func_end; + } + + /* Lookup the address of the modules_header structure */ + status = cod_get_sym_value(code_mgr, "_DLModules", &module_dsp_addr); + if (DSP_FAILED(status)) { + pr_debug("%s: Failed on cod_get_sym_value for _DLModules.\n", + __func__); + goto func_end; + } + + pr_debug("%s: _DLModules at 0x%x\n", __func__, module_dsp_addr); + + /* Copy the modules_header structure from DSP memory. */ + status = (*intf_fxns->pfn_brd_read)(bridge_context, (u8 *) &modules_hdr, + (u32) module_dsp_addr, sizeof(modules_hdr), 0); + + if (DSP_FAILED(status)) { + pr_debug("%s: Failed failed to read modules header.\n", + __func__); + goto func_end; + } + + module_dsp_addr = modules_hdr.first_module; + module_size = modules_hdr.first_module_size; + + pr_debug("%s: dll_module_header 0x%x %d\n", __func__, module_dsp_addr, + module_size); + + pr_err("\nDynamically Loaded Modules:\n" + "---------------------------\n"); + + /* For each dll_module structure in the list... */ + while (module_size) { + /* + * Allocate/re-allocate memory to hold the dll_module + * structure. The memory is re-allocated only if the existing + * allocation is too small. + */ + if (module_size > module_struct_size) { + kfree(module_struct); + module_struct = kzalloc(module_size+128, GFP_ATOMIC); + module_struct_size = module_size+128; + pr_debug("%s: allocated module struct %p %d\n", + __func__, module_struct, module_struct_size); + if (!module_struct) + goto func_end; + } + /* Copy the dll_module structure from DSP memory */ + status = (*intf_fxns->pfn_brd_read)(bridge_context, + (u8 *)module_struct, module_dsp_addr, module_size, 0); + + if (DSP_FAILED(status)) { + pr_debug( + "%s: Failed to read dll_module stuct for 0x%x.\n", + __func__, module_dsp_addr); + break; + } + + /* Update info regarding the _next_ module in the list. */ + module_dsp_addr = module_struct->next_module; + module_size = module_struct->next_module_size; + + pr_debug("%s: next module 0x%x %d, this module num sects %d\n", + __func__, module_dsp_addr, module_size, + module_struct->num_sects); + + /* + * The section name strings start immedialty following + * the array of dll_sect structures. + */ + sect_str = (char *) &module_struct-> + sects[module_struct->num_sects]; + pr_err("%s\n", sect_str); + + /* + * Advance to the first section name string. + * Each string follows the one before. + */ + sect_str += strlen(sect_str) + 1; + + /* Access each dll_sect structure and its name string. */ + for (sect_ndx = 0; + sect_ndx < module_struct->num_sects; sect_ndx++) { + pr_err(" Section: 0x%x ", + module_struct->sects[sect_ndx].sect_load_adr); + + if (((u32) sect_str - (u32) module_struct) < + module_struct_size) { + pr_err("%s\n", sect_str); + /* Each string follows the one before. */ + sect_str += strlen(sect_str)+1; + } else { + pr_err("<string error>\n"); + pr_debug("%s: section name sting address " + "is invalid %p\n", __func__, sect_str); + } + } + } +func_end: + kfree(module_struct); +} + |