void io_cancel_chnl(struct io_mgr *hio_mgr, u32 chnl) { struct io_mgr *pio_mgr = (struct io_mgr *)hio_mgr; struct shm *sm; if (!hio_mgr) goto func_end; sm = hio_mgr->shared_mem; set_chnl_free(sm, chnl); sm_interrupt_dsp(pio_mgr->bridge_context, MBX_PCPY_CLASS); func_end: return; }
/* * ======== wake_dsp ======== * Wake up DSP from sleep. */ int wake_dsp(struct bridge_dev_context *dev_context, void *pargs) { int status = 0; #ifdef CONFIG_PM /* Check the board state, if it is not 'SLEEP' then return */ if (dev_context->brd_state == BRD_RUNNING || dev_context->brd_state == BRD_STOPPED) { /* The Device is in 'RET' or 'OFF' state and Bridge state is not * 'SLEEP', this means state inconsistency, so return */ return 0; } /* Send a wakeup message to DSP */ sm_interrupt_dsp(dev_context, MBX_PM_DSPWAKEUP); /* Set the device state to RUNNIG */ dev_context->brd_state = BRD_RUNNING; #endif /* CONFIG_PM */ return status; }
/* * ======== bridge_chnl_add_io_req ======== * Enqueue an I/O request for data transfer on a channel to the DSP. * The direction (mode) is specified in the channel object. Note the DSP * address is specified for channels opened in direct I/O mode. */ int bridge_chnl_add_io_req(struct chnl_object *chnl_obj, void *host_buf, u32 byte_size, u32 buf_size, u32 dw_dsp_addr, u32 dw_arg) { int status = 0; struct chnl_object *pchnl = (struct chnl_object *)chnl_obj; struct chnl_irp *chnl_packet_obj = NULL; struct bridge_dev_context *dev_ctxt; struct dev_object *dev_obj; u8 dw_state; bool is_eos; struct chnl_mgr *chnl_mgr_obj = pchnl->chnl_mgr_obj; u8 *host_sys_buf = NULL; bool sched_dpc = false; u16 mb_val = 0; is_eos = (byte_size == 0); /* Validate args */ if (!host_buf || !pchnl) { status = -EFAULT; } else if (is_eos && CHNL_IS_INPUT(pchnl->chnl_mode)) { status = -EPERM; } else { /* * Check the channel state: only queue chirp if channel state * allows it. */ dw_state = pchnl->dw_state; if (dw_state != CHNL_STATEREADY) { if (dw_state & CHNL_STATECANCEL) status = -ECANCELED; else if ((dw_state & CHNL_STATEEOS) && CHNL_IS_OUTPUT(pchnl->chnl_mode)) status = -EPIPE; else /* No other possible states left */ DBC_ASSERT(0); } } dev_obj = dev_get_first(); dev_get_bridge_context(dev_obj, &dev_ctxt); if (!dev_ctxt) status = -EFAULT; if (status) goto func_end; if (pchnl->chnl_type == CHNL_PCPY && pchnl->chnl_id > 1 && host_buf) { if (!(host_buf < (void *)USERMODE_ADDR)) { host_sys_buf = host_buf; goto func_cont; } /* if addr in user mode, then copy to kernel space */ host_sys_buf = kmalloc(buf_size, GFP_KERNEL); if (host_sys_buf == NULL) { status = -ENOMEM; goto func_end; } if (CHNL_IS_OUTPUT(pchnl->chnl_mode)) { status = copy_from_user(host_sys_buf, host_buf, buf_size); if (status) { kfree(host_sys_buf); host_sys_buf = NULL; status = -EFAULT; goto func_end; } } } func_cont: /* Mailbox IRQ is disabled to avoid race condition with DMA/ZCPY * channels. DPCCS is held to avoid race conditions with PCPY channels. * If DPC is scheduled in process context (iosm_schedule) and any * non-mailbox interrupt occurs, that DPC will run and break CS. Hence * we disable ALL DPCs. We will try to disable ONLY IO DPC later. */ spin_lock_bh(&chnl_mgr_obj->chnl_mgr_lock); omap_mbox_disable_irq(dev_ctxt->mbox, IRQ_RX); if (pchnl->chnl_type == CHNL_PCPY) { /* This is a processor-copy channel. */ if (!status && CHNL_IS_OUTPUT(pchnl->chnl_mode)) { /* Check buffer size on output channels for fit. */ if (byte_size > io_buf_size(pchnl->chnl_mgr_obj->hio_mgr)) status = -EINVAL; } } if (!status) { /* Get a free chirp: */ chnl_packet_obj = (struct chnl_irp *)lst_get_head(pchnl->free_packets_list); if (chnl_packet_obj == NULL) status = -EIO; } if (!status) { /* Enqueue the chirp on the chnl's IORequest queue: */ chnl_packet_obj->host_user_buf = chnl_packet_obj->host_sys_buf = host_buf; if (pchnl->chnl_type == CHNL_PCPY && pchnl->chnl_id > 1) chnl_packet_obj->host_sys_buf = host_sys_buf; /* * Note: for dma chans dw_dsp_addr contains dsp address * of SM buffer. */ DBC_ASSERT(chnl_mgr_obj->word_size != 0); /* DSP address */ chnl_packet_obj->dsp_tx_addr = dw_dsp_addr / chnl_mgr_obj->word_size; chnl_packet_obj->byte_size = byte_size; chnl_packet_obj->buf_size = buf_size; /* Only valid for output channel */ chnl_packet_obj->dw_arg = dw_arg; chnl_packet_obj->status = (is_eos ? CHNL_IOCSTATEOS : CHNL_IOCSTATCOMPLETE); lst_put_tail(pchnl->pio_requests, (struct list_head *)chnl_packet_obj); pchnl->cio_reqs++; DBC_ASSERT(pchnl->cio_reqs <= pchnl->chnl_packets); /* * If end of stream, update the channel state to prevent * more IOR's. */ if (is_eos) pchnl->dw_state |= CHNL_STATEEOS; /* Legacy DSM Processor-Copy */ DBC_ASSERT(pchnl->chnl_type == CHNL_PCPY); /* Request IO from the DSP */ io_request_chnl(chnl_mgr_obj->hio_mgr, pchnl, (CHNL_IS_INPUT(pchnl->chnl_mode) ? IO_INPUT : IO_OUTPUT), &mb_val); sched_dpc = true; } omap_mbox_enable_irq(dev_ctxt->mbox, IRQ_RX); spin_unlock_bh(&chnl_mgr_obj->chnl_mgr_lock); if (mb_val != 0) sm_interrupt_dsp(dev_ctxt, mb_val); /* Schedule a DPC, to do the actual data transfer */ if (sched_dpc) iosm_schedule(chnl_mgr_obj->hio_mgr); func_end: return status; }
/* * ======== sleep_dsp ======== * Put DSP in low power consuming state. */ int sleep_dsp(struct bridge_dev_context *dev_context, u32 dw_cmd, void *pargs) { int status = 0; #ifdef CONFIG_PM #ifdef CONFIG_TIDSPBRIDGE_NTFY_PWRERR struct deh_mgr *hdeh_mgr; #endif /* CONFIG_TIDSPBRIDGE_NTFY_PWRERR */ u16 timeout = PWRSTST_TIMEOUT / 10; u32 pwr_state, target_pwr_state; struct omap_dsp_platform_data *pdata = omap_dspbridge_dev->dev.platform_data; /* Check if sleep code is valid */ if ((dw_cmd != PWR_DEEPSLEEP) && (dw_cmd != PWR_EMERGENCYDEEPSLEEP)) return -EINVAL; switch (dev_context->brd_state) { case BRD_RUNNING: omap_mbox_save_ctx(dev_context->mbox); if (dsp_test_sleepstate == PWRDM_POWER_OFF) { sm_interrupt_dsp(dev_context, MBX_PM_DSPHIBERNATE); dev_dbg(bridge, "PM: %s - sent hibernate cmd to DSP\n", __func__); target_pwr_state = PWRDM_POWER_OFF; } else { sm_interrupt_dsp(dev_context, MBX_PM_DSPRETENTION); target_pwr_state = PWRDM_POWER_RET; } break; case BRD_RETENTION: omap_mbox_save_ctx(dev_context->mbox); if (dsp_test_sleepstate == PWRDM_POWER_OFF) { sm_interrupt_dsp(dev_context, MBX_PM_DSPHIBERNATE); target_pwr_state = PWRDM_POWER_OFF; } else return 0; break; case BRD_HIBERNATION: case BRD_DSP_HIBERNATION: /* Already in Hibernation, so just return */ dev_dbg(bridge, "PM: %s - DSP already in hibernation\n", __func__); return 0; case BRD_STOPPED: dev_dbg(bridge, "PM: %s - Board in STOP state\n", __func__); return 0; default: dev_dbg(bridge, "PM: %s - Bridge in Illegal state\n", __func__); return -EPERM; } /* Get the PRCM DSP power domain status */ pwr_state = (*pdata->dsp_prm_read)(OMAP3430_IVA2_MOD, OMAP2_PM_PWSTST) & OMAP_POWERSTATEST_MASK; /* Wait for DSP to move into target power state */ while ((pwr_state != target_pwr_state) && --timeout) { if (msleep_interruptible(10)) { pr_err("Waiting for DSP to Suspend interrupted\n"); return -EPERM; } pwr_state = (*pdata->dsp_prm_read)(OMAP3430_IVA2_MOD, OMAP2_PM_PWSTST) & OMAP_POWERSTATEST_MASK; } if (!timeout) { pr_err("%s: Timed out waiting for DSP off mode, state %x\n", __func__, pwr_state); #ifdef CONFIG_TIDSPBRIDGE_NTFY_PWRERR dev_get_deh_mgr(dev_context->dev_obj, &hdeh_mgr); bridge_deh_notify(hdeh_mgr, DSP_PWRERROR, 0); #endif /* CONFIG_TIDSPBRIDGE_NTFY_PWRERR */ return -ETIMEDOUT; } else { /* Update the Bridger Driver state */ if (dsp_test_sleepstate == PWRDM_POWER_OFF) dev_context->brd_state = BRD_HIBERNATION; else dev_context->brd_state = BRD_RETENTION; /* Disable wdt on hibernation. */ dsp_wdt_enable(false); /* Turn off DSP Peripheral clocks */ status = dsp_clock_disable_all(dev_context->dsp_per_clks); if (status) return status; #ifdef CONFIG_TIDSPBRIDGE_DVFS else if (target_pwr_state == PWRDM_POWER_OFF) { /* * Set the OPP to low level before moving to OFF mode */ if (pdata->dsp_set_min_opp) (*pdata->dsp_set_min_opp) (VDD1_OPP1); } #endif /* CONFIG_TIDSPBRIDGE_DVFS */ } #endif /* CONFIG_PM */ return status; }
/* * ======== bridge_brd_stop ======== * purpose: * Puts DSP in self loop. * * Preconditions : * a) None */ static int bridge_brd_stop(struct bridge_dev_context *dev_ctxt) { int status = 0; struct bridge_dev_context *dev_context = dev_ctxt; u32 dsp_pwr_state; int i; struct bridge_ioctl_extproc *tlb = dev_context->atlb_entry; struct omap_dsp_platform_data *pdata = omap_dspbridge_dev->dev.platform_data; if (dev_context->dw_brd_state == BRD_STOPPED) return status; /* as per TRM, it is advised to first drive the IVA2 to 'Standby' mode, * before turning off the clocks.. This is to ensure that there are no * pending L3 or other transactons from IVA2 */ dsp_pwr_state = (*pdata->dsp_prm_read)(OMAP3430_IVA2_MOD, OMAP2_PM_PWSTST) & OMAP_POWERSTATEST_MASK; if (dsp_pwr_state != PWRDM_POWER_OFF) { (*pdata->dsp_prm_rmw_bits)(OMAP3430_RST2_IVA2_MASK, 0, OMAP3430_IVA2_MOD, OMAP2_RM_RSTCTRL); sm_interrupt_dsp(dev_context, MBX_PM_DSPIDLE); mdelay(10); /* IVA2 is not in OFF state */ /* Set PM_PWSTCTRL_IVA2 to OFF */ (*pdata->dsp_prm_rmw_bits)(OMAP_POWERSTATEST_MASK, PWRDM_POWER_OFF, OMAP3430_IVA2_MOD, OMAP2_PM_PWSTCTRL); /* Set the SW supervised state transition for Sleep */ (*pdata->dsp_cm_write)(OMAP34XX_CLKSTCTRL_FORCE_SLEEP, OMAP3430_IVA2_MOD, OMAP2_CM_CLKSTCTRL); } udelay(10); /* Release the Ext Base virtual Address as the next DSP Program * may have a different load address */ if (dev_context->dw_dsp_ext_base_addr) dev_context->dw_dsp_ext_base_addr = 0; dev_context->dw_brd_state = BRD_STOPPED; /* update board state */ dsp_wdt_enable(false); /* Reset DSP */ (*pdata->dsp_prm_rmw_bits)(OMAP3430_RST1_IVA2_MASK, OMAP3430_RST1_IVA2_MASK, OMAP3430_IVA2_MOD, OMAP2_RM_RSTCTRL); /* Disable the mailbox interrupts */ if (dev_context->mbox) { omap_mbox_disable_irq(dev_context->mbox, IRQ_RX); omap_mbox_put(dev_context->mbox); dev_context->mbox = NULL; } if (dev_context->dsp_mmu) { pr_err("Proc stop mmu if statement\n"); for (i = 0; i < BRDIOCTL_NUMOFMMUTLB; i++) { if (!tlb[i].ul_gpp_pa) continue; iommu_kunmap(dev_context->dsp_mmu, tlb[i].ul_gpp_va); } i = 0; while (l4_peripheral_table[i].phys_addr) { iommu_kunmap(dev_context->dsp_mmu, l4_peripheral_table[i].dsp_virt_addr); i++; } iommu_kunmap(dev_context->dsp_mmu, dev_context->sh_s.seg0_da); iommu_kunmap(dev_context->dsp_mmu, dev_context->sh_s.seg1_da); dsp_mmu_exit(dev_context->dsp_mmu); dev_context->dsp_mmu = NULL; } /* Reset IVA IOMMU*/ (*pdata->dsp_prm_rmw_bits)(OMAP3430_RST2_IVA2_MASK, OMAP3430_RST2_IVA2_MASK, OMAP3430_IVA2_MOD, OMAP2_RM_RSTCTRL); dsp_clock_disable_all(dev_context->dsp_per_clks); dsp_clk_disable(DSP_CLK_IVA2); return status; }