static ssize_t wdt3_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { u32 wdt3; struct DEV_OBJECT *dev_object; struct WMD_DEV_CONTEXT *dev_ctxt; if (sscanf(buf, "%d", &wdt3) != 1) return -EINVAL; dev_object = DEV_GetFirst(); if (dev_object == NULL) goto func_end; DEV_GetWMDContext(dev_object, &dev_ctxt); if (dev_ctxt == NULL) goto func_end; /* enable WDT */ if (wdt3 == 1) { if (dsp_wdt_get_enable()) goto func_end; dsp_wdt_set_enable(true); if (!CLK_Get_UseCnt(SERVICESCLK_wdt3_fck) && dev_ctxt->dwBrdState != BRD_DSP_HIBERNATION) dsp_wdt_enable(true); } else if (wdt3 == 0) { if (!dsp_wdt_get_enable()) goto func_end; if (CLK_Get_UseCnt(SERVICESCLK_wdt3_fck)) dsp_wdt_enable(false); dsp_wdt_set_enable(false); } func_end: return n; }
static ssize_t wdt3_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { u32 wdt3; struct dev_object *dev_object; struct wmd_dev_context *dev_ctxt; if (sscanf(buf, "%d", &wdt3) != 1) return -EINVAL; dev_object = dev_get_first(); if (dev_object == NULL) goto func_end; dev_get_wmd_context(dev_object, &dev_ctxt); if (dev_ctxt == NULL) goto func_end; /* enable WDT */ if (wdt3 == 1) { if (dsp_wdt_get_enable()) goto func_end; dsp_wdt_set_enable(true); if (!clk_get_use_cnt(SERVICESCLK_WDT3_FCK) && dev_ctxt->dw_brd_state != BRD_DSP_HIBERNATION) dsp_wdt_enable(true); } else if (wdt3 == 0) { if (!dsp_wdt_get_enable()) goto func_end; if (clk_get_use_cnt(SERVICESCLK_WDT3_FCK)) dsp_wdt_enable(false); dsp_wdt_set_enable(false); } func_end: return n; }
int send_mbox_callback(void *arg) { struct wmd_dev_context *dev_context; struct cfg_hostres *resources; u32 temp; struct dspbridge_platform_data *pdata = omap_dspbridge_dev->dev.platform_data; dev_get_wmd_context(dev_get_first(), &dev_context); if (!dev_context || !dev_context->resources) return -EFAULT; resources = dev_context->resources; if (dev_context->dw_brd_state == BRD_DSP_HIBERNATION || dev_context->dw_brd_state == BRD_HIBERNATION) { /* Restart the peripheral clocks */ dsp_peripheral_clocks_enable(dev_context, NULL); #ifdef CONFIG_BRIDGE_WDT3 dsp_wdt_enable(true); #endif /* * 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 = *(reg_uword32 *) (resources->dw_dmmu_base + 0x10); dev_context->dw_brd_state = BRD_RUNNING; } else if (dev_context->dw_brd_state == BRD_RETENTION) { /* Restart the peripheral clocks */ dsp_peripheral_clocks_enable(dev_context, NULL); dev_context->dw_brd_state = BRD_RUNNING; } return 0; }
void bridge_deh_notify(struct deh_mgr *deh, int event, int info) { struct bridge_dev_context *dev_context; const char *str = event_to_string(event); if (!deh) return; dev_dbg(bridge, "%s: device exception", __func__); dev_context = deh->bridge_context; switch (event) { case DSP_SYSERROR: dev_err(bridge, "%s: %s, info=0x%x", __func__, str, info); #ifdef CONFIG_TIDSPBRIDGE_BACKTRACE dump_dl_modules(dev_context); dump_dsp_stack(dev_context); #endif break; case DSP_MMUFAULT: dev_err(bridge, "%s: %s, addr=0x%x", __func__, str, fault_addr); #ifdef CONFIG_TIDSPBRIDGE_BACKTRACE print_dsp_trace_buffer(dev_context); dump_dl_modules(dev_context); mmu_fault_print_stack(dev_context); #endif break; default: dev_err(bridge, "%s: %s", __func__, str); break; } /* Filter subsequent notifications when an error occurs */ if (dev_context->brd_state != BRD_ERROR) { ntfy_notify(deh->ntfy_obj, event); #ifdef CONFIG_TIDSPBRIDGE_RECOVERY bridge_recover_schedule(); #endif } /* Set the Board state as ERROR */ dev_context->brd_state = BRD_ERROR; /* Disable all the clocks that were enabled by DSP */ dsp_clock_disable_all(dev_context->dsp_per_clks); /* * Avoid the subsequent WDT if it happens once, * also if fatal error occurs. */ dsp_wdt_enable(false); }
/* * ======== WMD_DEH_Notify ======== * DEH error notification function. Informs user about the error. */ void WMD_DEH_Notify(struct DEH_MGR *hDehMgr, u32 ulEventMask, u32 dwErrInfo) { struct DEH_MGR *pDehMgr = (struct DEH_MGR *)hDehMgr; struct WMD_DEV_CONTEXT *pDevContext; u32 memPhysical = 0; u32 HW_MMU_MAX_TLB_COUNT = 31; extern u32 faultAddr; u32 cnt = 0; if (MEM_IsValidHandle(pDehMgr, SIGNATURE)) { printk(KERN_INFO "WMD_DEH_Notify: ********** DEVICE EXCEPTION " "**********\n"); pDevContext = (struct WMD_DEV_CONTEXT *)pDehMgr->hWmdContext; switch (ulEventMask) { case DSP_SYSERROR: /* reset errInfo structure before use */ pDehMgr->errInfo.dwErrMask = DSP_SYSERROR; pDehMgr->errInfo.dwVal1 = 0L; pDehMgr->errInfo.dwVal2 = 0L; pDehMgr->errInfo.dwVal3 = 0L; pDehMgr->errInfo.dwVal1 = dwErrInfo; printk(KERN_ERR "WMD_DEH_Notify: DSP_SYSERROR, errInfo " "= 0x%x\n", dwErrInfo); dump_dl_modules(pDevContext); dump_dsp_stack(pDevContext); break; case DSP_MMUFAULT: /* MMU fault routine should have set err info * structure */ pDehMgr->errInfo.dwErrMask = DSP_MMUFAULT; printk(KERN_INFO "WMD_DEH_Notify: DSP_MMUFAULT," "errInfo = 0x%x\n", dwErrInfo); printk(KERN_INFO "WMD_DEH_Notify: DSP_MMUFAULT, High " "Address = 0x%x\n", (unsigned int)pDehMgr->errInfo.dwVal1); printk(KERN_INFO "WMD_DEH_Notify: DSP_MMUFAULT, Low " "Address = 0x%x\n", (unsigned int)pDehMgr->errInfo.dwVal2); printk(KERN_INFO "WMD_DEH_Notify: DSP_MMUFAULT, fault " "address = 0x%x\n", (unsigned int)faultAddr); PrintDspTraceBuffer(pDevContext); dump_dl_modules(pDevContext); dummyVaAddr = (u32)MEM_Calloc(sizeof(char) * 0x1000, MEM_PAGED); memPhysical = VirtToPhys(PG_ALIGN_LOW((u32)dummyVaAddr, PG_SIZE_4K)); pDevContext = (struct WMD_DEV_CONTEXT *) pDehMgr->hWmdContext; /* Reset the dynamic mmu index to fixed count if it * exceeds 31. So that the dynmmuindex is always * between the range of standard/fixed entries * and 31. */ if (pDevContext->numTLBEntries > HW_MMU_MAX_TLB_COUNT) { pDevContext->numTLBEntries = pDevContext-> fixedTLBEntries; } HW_MMU_TLBAdd(pDevContext->dwDSPMmuBase, memPhysical, faultAddr, HW_PAGE_SIZE_4KB, 1, &mapAttrs, HW_SET, HW_SET); /* * Send a GP Timer interrupt to DSP * The DSP expects a GP timer interrupt after an * MMU-Fault Request GPTimer */ if (timer) { omap_dm_timer_enable(timer); /* Enable overflow interrupt */ omap_dm_timer_set_int_enable(timer, GPTIMER_IRQ_OVERFLOW); /* * Set counter value to overflow counter after * one tick and start timer */ omap_dm_timer_set_load_start(timer, 0, 0xfffffffe); /* Wait 80us for timer to overflow */ udelay(80); /* Check interrupt status and */ /* wait for interrupt */ cnt = 0; while (!(omap_dm_timer_read_status(timer) & GPTIMER_IRQ_OVERFLOW)) { if (cnt++ >= GPTIMER_IRQ_WAIT_MAX_CNT) { pr_err("%s: GPTimer interrupt" " failed\n", __func__); break; } } } /* Clear MMU interrupt */ HW_MMU_EventAck(pDevContext->dwDSPMmuBase, HW_MMU_TRANSLATION_FAULT); dump_dsp_stack(hDehMgr->hWmdContext); if (timer) omap_dm_timer_disable(timer); break; #ifdef CONFIG_BRIDGE_NTFY_PWRERR case DSP_PWRERROR: /* reset errInfo structure before use */ pDehMgr->errInfo.dwErrMask = DSP_PWRERROR; pDehMgr->errInfo.dwVal1 = 0L; pDehMgr->errInfo.dwVal2 = 0L; pDehMgr->errInfo.dwVal3 = 0L; pDehMgr->errInfo.dwVal1 = dwErrInfo; printk(KERN_ERR "WMD_DEH_Notify: DSP_PWRERROR, errInfo " "= 0x%x\n", dwErrInfo); break; #endif /* CONFIG_BRIDGE_NTFY_PWRERR */ #ifdef CONFIG_BRIDGE_WDT3 case DSP_WDTOVERFLOW: pDehMgr->errInfo.dwErrMask = DSP_WDTOVERFLOW; pDehMgr->errInfo.dwVal1 = 0L; pDehMgr->errInfo.dwVal2 = 0L; pDehMgr->errInfo.dwVal3 = 0L; pr_err("WMD_DEH_Notify: DSP_WDTOVERFLOW \n "); break; #endif default: DBG_Trace(DBG_LEVEL6, "WMD_DEH_Notify: Unknown Error, errInfo = " "0x%x\n", dwErrInfo); break; } /* Filter subsequent notifications when an error occurs */ if (pDevContext->dwBrdState != BRD_ERROR) { NTFY_Notify(pDehMgr->hNtfy, ulEventMask); #ifdef CONFIG_BRIDGE_RECOVERY bridge_recover_schedule(); #endif } /* Set the Board state as ERROR */ pDevContext->dwBrdState = BRD_ERROR; /* Disable all the clocks that were enabled by DSP */ (void)DSP_PeripheralClocks_Disable(pDevContext, NULL); #ifdef CONFIG_BRIDGE_WDT3 /* * Avoid the subsequent WDT if it happens once, * also If MMU fault occurs */ dsp_wdt_enable(false); #endif } }
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 = mbox_send_message(dev_context->mbox, (void *)((u32)mb_val)); if (status < 0) { pr_err("mbox_send_message failed, status = %d\n", status); status = -EPERM; } return 0; }
int sm_interrupt_dsp(struct wmd_dev_context *dev_context, u16 mb_val) { int status = 0; struct cfg_hostres *resources; u32 temp; struct dspbridge_platform_data *pdata = omap_dspbridge_dev->dev.platform_data; if (!dev_context || !dev_context->resources) return -EFAULT; if (!dev_context->mbox) return status; resources = dev_context->resources; if (dev_context->dw_brd_state == BRD_DSP_HIBERNATION || dev_context->dw_brd_state == BRD_HIBERNATION) { /* Restart the peripheral clocks */ dsp_peripheral_clocks_enable(dev_context, NULL); #ifdef CONFIG_BRIDGE_WDT3 dsp_wdt_enable(true); #endif /* * 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 = *(reg_uword32 *) (resources->dw_dmmu_base + 0x10); dev_context->dw_brd_state = BRD_RUNNING; } else if (dev_context->dw_brd_state == BRD_RETENTION) { /* Restart the peripheral clocks */ dsp_peripheral_clocks_enable(dev_context, NULL); dev_context->dw_brd_state = BRD_RUNNING; } 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; } dev_dbg(bridge, "MBX: writing %x to Mailbox\n", mb_val); return status; }
int send_mbox_callback(void *arg) { struct WMD_DEV_CONTEXT *dev_ctxt = (struct WMD_DEV_CONTEXT *)arg; u32 temp; unsigned long flags; DEFINE_SPINLOCK(irq_lock); if (!dev_ctxt) return -EFAULT; if (dev_ctxt->dwBrdState == BRD_DSP_HIBERNATION || dev_ctxt->dwBrdState == BRD_HIBERNATION) { /* Restart the peripheral clocks */ DSP_PeripheralClocks_Enable(dev_ctxt, NULL); #ifdef CONFIG_BRIDGE_WDT3 dsp_wdt_enable(true); #endif spin_lock_irqsave(&irq_lock, flags); /* Enabling Dpll in lock mode*/ temp = (u32) *((REG_UWORD32 *) ((u32) (dev_ctxt->cmbase) + 0x34)); temp = (temp & 0xFFFFFFFE) | 0x1; *((REG_UWORD32 *) ((u32) (dev_ctxt->cmbase) + 0x34)) = (u32) temp; temp = (u32) *((REG_UWORD32 *) ((u32) (dev_ctxt->cmbase) + 0x4)); temp = (temp & 0xFFFFF08) | 0x37; *((REG_UWORD32 *) ((u32) (dev_ctxt->cmbase) + 0x4)) = (u32) temp; /* Restore mailbox settings */ omap_mbox_restore_ctx(dev_ctxt->mbox); /* * Short wake up source is any read access to an MMU * registers. Making a MMU flush or accessing any other MMU * register before getting to this point will have the IVA in * ON state or Retention if the DSP has moved to that state, * having the Short Wakeup again is redundant and brings * issues when accessing the MMU after the DSP has started * its restore sequence. We will access only if the DSP * is not in RET or ON. */ HW_PWRST_IVA2RegGet(dev_ctxt->prmbase, &temp); if (!(temp & HW_PWR_STATE_RET) && !(temp & HW_PWR_STATE_ON)) /* * Flush the TLBs, this will generate the * shortwakeup. Also wait for the DSP to restore. */ tlb_flush_all(dev_ctxt->dwDSPMmuBase); udelay(10); spin_unlock_irqrestore(&irq_lock, flags); dev_ctxt->dwBrdState = BRD_RUNNING; } else if (dev_ctxt->dwBrdState == BRD_RETENTION) { /* Restart the peripheral clocks */ DSP_PeripheralClocks_Enable(dev_ctxt, NULL); dev_ctxt->dwBrdState = BRD_RUNNING; } return 0; }
/* * ======== bridge_deh_notify ======== * DEH error notification function. Informs user about the error. */ void bridge_deh_notify(struct deh_mgr *hdeh_mgr, u32 ulEventMask, u32 dwErrInfo) { struct deh_mgr *deh_mgr_obj = (struct deh_mgr *)hdeh_mgr; struct wmd_dev_context *dev_context; int status = 0; u32 mem_physical = 0; u32 hw_mmu_max_tlb_count = 31; extern u32 fault_addr; struct cfg_hostres *resources; hw_status hw_status_obj; u32 cnt = 0; if (deh_mgr_obj) { printk(KERN_INFO "bridge_deh_notify: ********** DEVICE EXCEPTION " "**********\n"); dev_context = (struct wmd_dev_context *)deh_mgr_obj->hwmd_context; resources = dev_context->resources; switch (ulEventMask) { case DSP_SYSERROR: /* reset err_info structure before use */ deh_mgr_obj->err_info.dw_err_mask = DSP_SYSERROR; deh_mgr_obj->err_info.dw_val1 = 0L; deh_mgr_obj->err_info.dw_val2 = 0L; deh_mgr_obj->err_info.dw_val3 = 0L; deh_mgr_obj->err_info.dw_val1 = dwErrInfo; printk(KERN_ERR "bridge_deh_notify: DSP_SYSERROR, err_info " "= 0x%x\n", dwErrInfo); dump_dl_modules(dev_context); dump_dsp_stack(dev_context); break; case DSP_MMUFAULT: /* MMU fault routine should have set err info * structure */ deh_mgr_obj->err_info.dw_err_mask = DSP_MMUFAULT; printk(KERN_INFO "bridge_deh_notify: DSP_MMUFAULT," "err_info = 0x%x\n", dwErrInfo); printk(KERN_INFO "bridge_deh_notify: DSP_MMUFAULT, High " "Address = 0x%x\n", (unsigned int)deh_mgr_obj->err_info.dw_val1); printk(KERN_INFO "bridge_deh_notify: DSP_MMUFAULT, Low " "Address = 0x%x\n", (unsigned int)deh_mgr_obj->err_info.dw_val2); printk(KERN_INFO "bridge_deh_notify: DSP_MMUFAULT, fault " "address = 0x%x\n", (unsigned int)fault_addr); dummy_va_addr = (u32) kzalloc(sizeof(char) * 0x1000, GFP_ATOMIC); mem_physical = VIRT_TO_PHYS(PG_ALIGN_LOW ((u32) dummy_va_addr, PG_SIZE4K)); dev_context = (struct wmd_dev_context *) deh_mgr_obj->hwmd_context; print_dsp_trace_buffer(dev_context); dump_dl_modules(dev_context); /* Reset the dynamic mmu index to fixed count if it * exceeds 31. So that the dynmmuindex is always * between the range of standard/fixed entries * and 31. */ if (dev_context->num_tlb_entries > hw_mmu_max_tlb_count) { dev_context->num_tlb_entries = dev_context->fixed_tlb_entries; } if (DSP_SUCCEEDED(status)) { hw_status_obj = hw_mmu_tlb_add(resources->dw_dmmu_base, mem_physical, fault_addr, HW_PAGE_SIZE4KB, 1, &map_attrs, HW_SET, HW_SET); } /* * Send a GP Timer interrupt to DSP * The DSP expects a GP timer interrupt after an * MMU-Fault Request GPTimer */ if (timer) { omap_dm_timer_enable(timer); /* Enable overflow interrupt */ omap_dm_timer_set_int_enable(timer, GPTIMER_IRQ_OVERFLOW); /* * Set counter value to overflow counter after * one tick and start timer */ omap_dm_timer_set_load_start(timer, 0, 0xfffffffe); /* Wait 80us for timer to overflow */ udelay(80); /* * Check interrupt status and * wait for interrupt */ cnt = 0; while (!(omap_dm_timer_read_status(timer) & GPTIMER_IRQ_OVERFLOW)) { if (cnt++ >= GPTIMER_IRQ_WAIT_MAX_CNT) { pr_err("%s: GPTimer interrupt" " failed\n", __func__); break; } } } /* Clear MMU interrupt */ hw_mmu_event_ack(resources->dw_dmmu_base, HW_MMU_TRANSLATION_FAULT); dump_dsp_stack(deh_mgr_obj->hwmd_context); if (timer) omap_dm_timer_disable(timer); break; #ifdef CONFIG_BRIDGE_NTFY_PWRERR case DSP_PWRERROR: /* reset err_info structure before use */ deh_mgr_obj->err_info.dw_err_mask = DSP_PWRERROR; deh_mgr_obj->err_info.dw_val1 = 0L; deh_mgr_obj->err_info.dw_val2 = 0L; deh_mgr_obj->err_info.dw_val3 = 0L; deh_mgr_obj->err_info.dw_val1 = dwErrInfo; printk(KERN_ERR "bridge_deh_notify: DSP_PWRERROR, err_info " "= 0x%x\n", dwErrInfo); break; #endif /* CONFIG_BRIDGE_NTFY_PWRERR */ #ifdef CONFIG_BRIDGE_WDT3 case DSP_WDTOVERFLOW: deh_mgr_obj->err_info.dw_err_mask = DSP_WDTOVERFLOW; deh_mgr_obj->err_info.dw_val1 = 0L; deh_mgr_obj->err_info.dw_val2 = 0L; deh_mgr_obj->err_info.dw_val3 = 0L; pr_err("bridge_deh_notify: DSP_WDTOVERFLOW\n "); break; #endif default: dev_dbg(bridge, "%s: Unknown Error, err_info = 0x%x\n", __func__, dwErrInfo); break; } /* Filter subsequent notifications when an error occurs */ if (dev_context->dw_brd_state != BRD_ERROR) { ntfy_notify(deh_mgr_obj->ntfy_obj, ulEventMask); #ifdef CONFIG_BRIDGE_RECOVERY bridge_recover_schedule(); #endif } /* Set the Board state as ERROR */ dev_context->dw_brd_state = BRD_ERROR; /* Disable all the clocks that were enabled by DSP */ (void)dsp_peripheral_clocks_disable(dev_context, NULL); #ifdef CONFIG_BRIDGE_WDT3 /* * Avoid the subsequent WDT if it happens once, * also If MMU fault occurs */ dsp_wdt_enable(false); #endif } }
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 dsp_clock_enable_all(dev_context->dsp_per_clks); dsp_wdt_enable(true); (*pdata->dsp_cm_write)(1 << OMAP3430_AUTO_IVA2_DPLL_SHIFT, OMAP3430_IVA2_MOD, OMAP3430_CM_AUTOIDLE_PLL); (*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); omap_mbox_restore_ctx(dev_context->mbox); temp = readl(resources->dmmu_base + 0x10); dev_context->brd_state = BRD_RUNNING; } else if (dev_context->brd_state == BRD_RETENTION) { 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; }
/* * ======== handle_hibernation_from_dsp ======== * Handle Hibernation requested from DSP */ int handle_hibernation_from_dsp(struct bridge_dev_context *dev_context) { int status = 0; #ifdef CONFIG_PM u16 timeout = PWRSTST_TIMEOUT / 10; u32 pwr_state; #ifdef CONFIG_TIDSPBRIDGE_DVFS u32 opplevel; struct io_mgr *hio_mgr; #endif struct omap_dsp_platform_data *pdata = omap_dspbridge_dev->dev.platform_data; pwr_state = (*pdata->dsp_prm_read)(OMAP3430_IVA2_MOD, OMAP2_PM_PWSTST) & OMAP_POWERSTATEST_MASK; /* Wait for DSP to move into OFF state */ while ((pwr_state != PWRDM_POWER_OFF) && --timeout) { if (msleep_interruptible(10)) { pr_err("Waiting for DSP OFF mode interrupted\n"); return -EPERM; } pwr_state = (*pdata->dsp_prm_read)(OMAP3430_IVA2_MOD, OMAP2_PM_PWSTST) & OMAP_POWERSTATEST_MASK; } if (timeout == 0) { pr_err("%s: Timed out waiting for DSP off mode\n", __func__); status = -ETIMEDOUT; return status; } else { /* Save mailbox settings */ omap_mbox_save_ctx(dev_context->mbox); /* Turn off DSP Peripheral clocks and DSP Load monitor timer */ status = dsp_clock_disable_all(dev_context->dsp_per_clks); /* Disable wdt on hibernation. */ dsp_wdt_enable(false); if (!status) { /* Update the Bridger Driver state */ dev_context->brd_state = BRD_DSP_HIBERNATION; #ifdef CONFIG_TIDSPBRIDGE_DVFS status = dev_get_io_mgr(dev_context->dev_obj, &hio_mgr); if (!hio_mgr) { status = DSP_EHANDLE; return status; } io_sh_msetting(hio_mgr, SHM_GETOPP, &opplevel); /* * Set the OPP to low level before moving to OFF * mode */ if (pdata->dsp_set_min_opp) (*pdata->dsp_set_min_opp) (VDD1_OPP1); status = 0; #endif /* CONFIG_TIDSPBRIDGE_DVFS */ } } #endif 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; }
/* * ======== bridge_brd_start ======== * purpose: * Initializes DSP MMU and Starts DSP. * * Preconditions: * a) DSP domain is 'ACTIVE'. * b) DSP_RST1 is asserted. * b) DSP_RST2 is released. */ static int bridge_brd_start(struct bridge_dev_context *dev_ctxt, u32 dsp_addr) { int status = 0; struct bridge_dev_context *dev_context = dev_ctxt; struct iommu *mmu = NULL; struct shm_segs *sm_sg; int l4_i = 0, tlb_i = 0; u32 sg0_da = 0, sg1_da = 0; struct bridge_ioctl_extproc *tlb = dev_context->atlb_entry; u32 dw_sync_addr = 0; u32 ul_shm_base; /* Gpp Phys SM base addr(byte) */ u32 ul_shm_base_virt; /* Dsp Virt SM base addr */ u32 ul_tlb_base_virt; /* Base of MMU TLB entry */ /* Offset of shm_base_virt from tlb_base_virt */ u32 ul_shm_offset_virt; struct cfg_hostres *resources = NULL; u32 temp; u32 ul_dsp_clk_rate; u32 ul_dsp_clk_addr; u32 ul_bios_gp_timer; u32 clk_cmd; struct io_mgr *hio_mgr; u32 ul_load_monitor_timer; struct omap_dsp_platform_data *pdata = omap_dspbridge_dev->dev.platform_data; /* The device context contains all the mmu setup info from when the * last dsp base image was loaded. The first entry is always * SHMMEM base. */ /* Get SHM_BEG - convert to byte address */ (void)dev_get_symbol(dev_context->hdev_obj, SHMBASENAME, &ul_shm_base_virt); ul_shm_base_virt *= DSPWORDSIZE; DBC_ASSERT(ul_shm_base_virt != 0); /* DSP Virtual address */ ul_tlb_base_virt = dev_context->sh_s.seg0_da; DBC_ASSERT(ul_tlb_base_virt <= ul_shm_base_virt); ul_shm_offset_virt = ul_shm_base_virt - (ul_tlb_base_virt * DSPWORDSIZE); /* Kernel logical address */ ul_shm_base = dev_context->sh_s.seg0_va + ul_shm_offset_virt; DBC_ASSERT(ul_shm_base != 0); /* 2nd wd is used as sync field */ dw_sync_addr = ul_shm_base + SHMSYNCOFFSET; /* Write a signature into the shm base + offset; this will * get cleared when the DSP program starts. */ if ((ul_shm_base_virt == 0) || (ul_shm_base == 0)) { pr_err("%s: Illegal SM base\n", __func__); status = -EPERM; } else __raw_writel(0xffffffff, dw_sync_addr); if (!status) { resources = dev_context->resources; if (!resources) status = -EPERM; /* Assert RST1 i.e only the RST only for DSP megacell */ if (!status) { (*pdata->dsp_prm_rmw_bits)(OMAP3430_RST1_IVA2_MASK, OMAP3430_RST1_IVA2_MASK, OMAP3430_IVA2_MOD, OMAP2_RM_RSTCTRL); /* Mask address with 1K for compatibility */ __raw_writel(dsp_addr & OMAP3_IVA2_BOOTADDR_MASK, OMAP343X_CTRL_REGADDR( OMAP343X_CONTROL_IVA2_BOOTADDR)); /* * Set bootmode to self loop if dsp_debug flag is true */ __raw_writel((dsp_debug) ? OMAP3_IVA2_BOOTMOD_IDLE : 0, OMAP343X_CTRL_REGADDR( OMAP343X_CONTROL_IVA2_BOOTMOD)); } } if (!status) { (*pdata->dsp_prm_rmw_bits)(OMAP3430_RST2_IVA2_MASK, 0, OMAP3430_IVA2_MOD, OMAP2_RM_RSTCTRL); mmu = dev_context->dsp_mmu; if (mmu) dsp_mmu_exit(mmu); mmu = dsp_mmu_init(); if (IS_ERR(mmu)) { dev_err(bridge, "dsp_mmu_init failed!\n"); dev_context->dsp_mmu = NULL; status = (int)mmu; } } if (!status) { dev_context->dsp_mmu = mmu; sm_sg = &dev_context->sh_s; sg0_da = iommu_kmap(mmu, sm_sg->seg0_da, sm_sg->seg0_pa, sm_sg->seg0_size, IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_32); if (IS_ERR_VALUE(sg0_da)) { status = (int)sg0_da; sg0_da = 0; } } if (!status) { sg1_da = iommu_kmap(mmu, sm_sg->seg1_da, sm_sg->seg1_pa, sm_sg->seg1_size, IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_32); if (IS_ERR_VALUE(sg1_da)) { status = (int)sg1_da; sg1_da = 0; } } if (!status) { u32 da; for (tlb_i = 0; tlb_i < BRDIOCTL_NUMOFMMUTLB; tlb_i++) { if (!tlb[tlb_i].ul_gpp_pa) continue; dev_dbg(bridge, "IOMMU %d GppPa: 0x%x DspVa 0x%x Size" " 0x%x\n", tlb_i, tlb[tlb_i].ul_gpp_pa, tlb[tlb_i].ul_dsp_va, tlb[tlb_i].ul_size); da = iommu_kmap(mmu, tlb[tlb_i].ul_dsp_va, tlb[tlb_i].ul_gpp_pa, PAGE_SIZE, IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_32); if (IS_ERR_VALUE(da)) { status = (int)da; break; } } } if (!status) { u32 da; l4_i = 0; while (l4_peripheral_table[l4_i].phys_addr) { da = iommu_kmap(mmu, l4_peripheral_table[l4_i]. dsp_virt_addr, l4_peripheral_table[l4_i]. phys_addr, PAGE_SIZE, IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_32); if (IS_ERR_VALUE(da)) { status = (int)da; break; } l4_i++; } } /* Lock the above TLB entries and get the BIOS and load monitor timer * information */ if (!status) { /* Enable the BIOS clock */ (void)dev_get_symbol(dev_context->hdev_obj, BRIDGEINIT_BIOSGPTIMER, &ul_bios_gp_timer); (void)dev_get_symbol(dev_context->hdev_obj, BRIDGEINIT_LOADMON_GPTIMER, &ul_load_monitor_timer); if (ul_load_monitor_timer != 0xFFFF) { clk_cmd = (BPWR_ENABLE_CLOCK << MBX_PM_CLK_CMDSHIFT) | ul_load_monitor_timer; dsp_peripheral_clk_ctrl(dev_context, &clk_cmd); } else { dev_dbg(bridge, "Not able to get the symbol for Load " "Monitor Timer\n"); } if (ul_bios_gp_timer != 0xFFFF) { clk_cmd = (BPWR_ENABLE_CLOCK << MBX_PM_CLK_CMDSHIFT) | ul_bios_gp_timer; dsp_peripheral_clk_ctrl(dev_context, &clk_cmd); } else { dev_dbg(bridge, "Not able to get the symbol for BIOS Timer\n"); } /* Set the DSP clock rate */ (void)dev_get_symbol(dev_context->hdev_obj, "_BRIDGEINIT_DSP_FREQ", &ul_dsp_clk_addr); /*Set Autoidle Mode for IVA2 PLL */ (*pdata->dsp_cm_write)(1 << OMAP3430_AUTO_IVA2_DPLL_SHIFT, OMAP3430_IVA2_MOD, OMAP3430_CM_AUTOIDLE_PLL); if ((unsigned int *)ul_dsp_clk_addr != NULL) { /* Get the clock rate */ ul_dsp_clk_rate = dsp_clk_get_iva2_rate(); dev_dbg(bridge, "%s: DSP clock rate (KHZ): 0x%x \n", __func__, ul_dsp_clk_rate); (void)bridge_brd_write(dev_context, (u8 *) &ul_dsp_clk_rate, ul_dsp_clk_addr, sizeof(u32), 0); } /* * Enable Mailbox events and also drain any pending * stale messages. */ dev_context->mbox = omap_mbox_get("dsp"); if (IS_ERR(dev_context->mbox)) { dev_context->mbox = NULL; pr_err("%s: Failed to get dsp mailbox handle\n", __func__); status = -EPERM; } } if (!status) { dev_context->mbox->rxq->callback = (int (*)(void *))io_mbox_msg; /*PM_IVA2GRPSEL_PER = 0xC0;*/ temp = readl(resources->dw_per_pm_base + 0xA8); temp = (temp & 0xFFFFFF30) | 0xC0; writel(temp, resources->dw_per_pm_base + 0xA8); /*PM_MPUGRPSEL_PER &= 0xFFFFFF3F; */ temp = readl(resources->dw_per_pm_base + 0xA4); temp = (temp & 0xFFFFFF3F); writel(temp, resources->dw_per_pm_base + 0xA4); /*CM_SLEEPDEP_PER |= 0x04; */ temp = readl(resources->dw_per_base + 0x44); temp = (temp & 0xFFFFFFFB) | 0x04; writel(temp, resources->dw_per_base + 0x44); /*CM_CLKSTCTRL_IVA2 = 0x00000003 -To Allow automatic transitions */ (*pdata->dsp_cm_write)(OMAP34XX_CLKSTCTRL_ENABLE_AUTO, OMAP3430_IVA2_MOD, OMAP2_CM_CLKSTCTRL); /* Let DSP go */ dev_dbg(bridge, "%s Unreset\n", __func__); /* release the RST1, DSP starts executing now .. */ (*pdata->dsp_prm_rmw_bits)(OMAP3430_RST1_IVA2_MASK, 0, OMAP3430_IVA2_MOD, OMAP2_RM_RSTCTRL); dev_dbg(bridge, "Waiting for Sync @ 0x%x\n", dw_sync_addr); dev_dbg(bridge, "DSP c_int00 Address = 0x%x\n", dsp_addr); if (dsp_debug) while (__raw_readw(dw_sync_addr)) ;; /* Wait for DSP to clear word in shared memory */ /* Read the Location */ if (!wait_for_start(dev_context, dw_sync_addr)) status = -ETIMEDOUT; /* Start wdt */ dsp_wdt_sm_set((void *)ul_shm_base); dsp_wdt_enable(true); status = dev_get_io_mgr(dev_context->hdev_obj, &hio_mgr); if (hio_mgr) { io_sh_msetting(hio_mgr, SHM_OPPINFO, NULL); /* Write the synchronization bit to indicate the * completion of OPP table update to DSP */ __raw_writel(0XCAFECAFE, dw_sync_addr); /* update board state */ dev_context->dw_brd_state = BRD_RUNNING; return 0; } else { dev_context->dw_brd_state = BRD_UNKNOWN; } } while (tlb_i--) { if (!tlb[tlb_i].ul_gpp_pa) continue; iommu_kunmap(mmu, tlb[tlb_i].ul_gpp_va); } while (l4_i--) iommu_kunmap(mmu, l4_peripheral_table[l4_i].dsp_virt_addr); if (sg0_da) iommu_kunmap(mmu, sg0_da); if (sg1_da) iommu_kunmap(mmu, sg1_da); return status; }