ue_deh.c revision b3d23688a15c4c332942f9428e0d708e7362e29e
1/* 2 * ue_deh.c 3 * 4 * DSP-BIOS Bridge driver support functions for TI OMAP processors. 5 * 6 * Implements upper edge DSP exception handling (DEH) functions. 7 * 8 * Copyright (C) 2005-2006 Texas Instruments, Inc. 9 * Copyright (C) 2010 Felipe Contreras 10 * 11 * This package is free software; you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License version 2 as 13 * published by the Free Software Foundation. 14 * 15 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 17 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 18 */ 19 20#include <linux/kernel.h> 21#include <linux/interrupt.h> 22#include <plat/dmtimer.h> 23 24#include <dspbridge/dbdefs.h> 25#include <dspbridge/dspdeh.h> 26#include <dspbridge/dev.h> 27#include "_tiomap.h" 28#include "_deh.h" 29 30#include <dspbridge/io_sm.h> 31#include <dspbridge/drv.h> 32#include <dspbridge/wdt.h> 33 34static u32 fault_addr; 35 36static void mmu_fault_dpc(unsigned long data) 37{ 38 struct deh_mgr *deh = (void *)data; 39 40 if (!deh) 41 return; 42 43 bridge_deh_notify(deh, DSP_MMUFAULT, 0); 44} 45 46static irqreturn_t mmu_fault_isr(int irq, void *data) 47{ 48 struct deh_mgr *deh = data; 49 struct cfg_hostres *resources; 50 u32 event; 51 52 if (!deh) 53 return IRQ_HANDLED; 54 55 resources = deh->hbridge_context->resources; 56 if (!resources) { 57 dev_dbg(bridge, "%s: Failed to get Host Resources\n", 58 __func__); 59 return IRQ_HANDLED; 60 } 61 62 hw_mmu_event_status(resources->dw_dmmu_base, &event); 63 if (event == HW_MMU_TRANSLATION_FAULT) { 64 hw_mmu_fault_addr_read(resources->dw_dmmu_base, &fault_addr); 65 dev_dbg(bridge, "%s: event=0x%x, fault_addr=0x%x\n", __func__, 66 event, fault_addr); 67 /* 68 * Schedule a DPC directly. In the future, it may be 69 * necessary to check if DSP MMU fault is intended for 70 * Bridge. 71 */ 72 tasklet_schedule(&deh->dpc_tasklet); 73 74 /* Disable the MMU events, else once we clear it will 75 * start to raise INTs again */ 76 hw_mmu_event_disable(resources->dw_dmmu_base, 77 HW_MMU_TRANSLATION_FAULT); 78 } else { 79 hw_mmu_event_disable(resources->dw_dmmu_base, 80 HW_MMU_ALL_INTERRUPTS); 81 } 82 return IRQ_HANDLED; 83} 84 85int bridge_deh_create(struct deh_mgr **ret_deh, 86 struct dev_object *hdev_obj) 87{ 88 int status; 89 struct deh_mgr *deh; 90 struct bridge_dev_context *hbridge_context = NULL; 91 92 /* Message manager will be created when a file is loaded, since 93 * size of message buffer in shared memory is configurable in 94 * the base image. */ 95 /* Get Bridge context info. */ 96 dev_get_bridge_context(hdev_obj, &hbridge_context); 97 /* Allocate IO manager object: */ 98 deh = kzalloc(sizeof(*deh), GFP_KERNEL); 99 if (!deh) { 100 status = -ENOMEM; 101 goto err; 102 } 103 104 /* Create an NTFY object to manage notifications */ 105 deh->ntfy_obj = kmalloc(sizeof(struct ntfy_object), GFP_KERNEL); 106 if (!deh->ntfy_obj) { 107 status = -ENOMEM; 108 goto err; 109 } 110 ntfy_init(deh->ntfy_obj); 111 112 /* Create a MMUfault DPC */ 113 tasklet_init(&deh->dpc_tasklet, mmu_fault_dpc, (u32) deh); 114 115 /* Fill in context structure */ 116 deh->hbridge_context = hbridge_context; 117 118 /* Install ISR function for DSP MMU fault */ 119 status = request_irq(INT_DSP_MMU_IRQ, mmu_fault_isr, 0, 120 "DspBridge\tiommu fault", deh); 121 if (status < 0) 122 goto err; 123 124 *ret_deh = deh; 125 return 0; 126 127err: 128 bridge_deh_destroy(deh); 129 *ret_deh = NULL; 130 return status; 131} 132 133int bridge_deh_destroy(struct deh_mgr *deh) 134{ 135 if (!deh) 136 return -EFAULT; 137 138 /* If notification object exists, delete it */ 139 if (deh->ntfy_obj) { 140 ntfy_delete(deh->ntfy_obj); 141 kfree(deh->ntfy_obj); 142 } 143 /* Disable DSP MMU fault */ 144 free_irq(INT_DSP_MMU_IRQ, deh); 145 146 /* Free DPC object */ 147 tasklet_kill(&deh->dpc_tasklet); 148 149 /* Deallocate the DEH manager object */ 150 kfree(deh); 151 152 return 0; 153} 154 155int bridge_deh_register_notify(struct deh_mgr *deh, u32 event_mask, 156 u32 notify_type, 157 struct dsp_notification *hnotification) 158{ 159 if (!deh) 160 return -EFAULT; 161 162 if (event_mask) 163 return ntfy_register(deh->ntfy_obj, hnotification, 164 event_mask, notify_type); 165 else 166 return ntfy_unregister(deh->ntfy_obj, hnotification); 167} 168 169static void mmu_fault_print_stack(struct bridge_dev_context *dev_context) 170{ 171 struct cfg_hostres *resources; 172 struct hw_mmu_map_attrs_t map_attrs = { 173 .endianism = HW_LITTLE_ENDIAN, 174 .element_size = HW_ELEM_SIZE16BIT, 175 .mixed_size = HW_MMU_CPUES, 176 }; 177 void *dummy_va_addr; 178 179 resources = dev_context->resources; 180 dummy_va_addr = (void*)__get_free_page(GFP_ATOMIC); 181 182 /* 183 * Before acking the MMU fault, let's make sure MMU can only 184 * access entry #0. Then add a new entry so that the DSP OS 185 * can continue in order to dump the stack. 186 */ 187 hw_mmu_twl_disable(resources->dw_dmmu_base); 188 hw_mmu_tlb_flush_all(resources->dw_dmmu_base); 189 190 hw_mmu_tlb_add(resources->dw_dmmu_base, 191 virt_to_phys(dummy_va_addr), fault_addr, 192 HW_PAGE_SIZE4KB, 1, 193 &map_attrs, HW_SET, HW_SET); 194 195 dsp_clk_enable(DSP_CLK_GPT8); 196 197 dsp_gpt_wait_overflow(DSP_CLK_GPT8, 0xfffffffe); 198 199 /* Clear MMU interrupt */ 200 hw_mmu_event_ack(resources->dw_dmmu_base, 201 HW_MMU_TRANSLATION_FAULT); 202 dump_dsp_stack(dev_context); 203 dsp_clk_disable(DSP_CLK_GPT8); 204 205 hw_mmu_disable(resources->dw_dmmu_base); 206 free_page((unsigned long)dummy_va_addr); 207} 208 209static inline const char *event_to_string(int event) 210{ 211 switch (event) { 212 case DSP_SYSERROR: return "DSP_SYSERROR"; break; 213 case DSP_MMUFAULT: return "DSP_MMUFAULT"; break; 214 case DSP_PWRERROR: return "DSP_PWRERROR"; break; 215 case DSP_WDTOVERFLOW: return "DSP_WDTOVERFLOW"; break; 216 default: return "unkown event"; break; 217 } 218} 219 220void bridge_deh_notify(struct deh_mgr *deh, int event, int info) 221{ 222 struct bridge_dev_context *dev_context; 223 const char *str = event_to_string(event); 224 225 if (!deh) 226 return; 227 228 dev_dbg(bridge, "%s: device exception", __func__); 229 dev_context = deh->hbridge_context; 230 231 switch (event) { 232 case DSP_SYSERROR: 233 dev_err(bridge, "%s: %s, info=0x%x", __func__, 234 str, info); 235 dump_dl_modules(dev_context); 236 dump_dsp_stack(dev_context); 237 break; 238 case DSP_MMUFAULT: 239 dev_err(bridge, "%s: %s, addr=0x%x", __func__, 240 str, fault_addr); 241 print_dsp_trace_buffer(dev_context); 242 dump_dl_modules(dev_context); 243 mmu_fault_print_stack(dev_context); 244 break; 245 default: 246 dev_err(bridge, "%s: %s", __func__, str); 247 break; 248 } 249 250 /* Filter subsequent notifications when an error occurs */ 251 if (dev_context->dw_brd_state != BRD_ERROR) { 252 ntfy_notify(deh->ntfy_obj, event); 253#ifdef CONFIG_TIDSPBRIDGE_RECOVERY 254 bridge_recover_schedule(); 255#endif 256 } 257 258 /* Set the Board state as ERROR */ 259 dev_context->dw_brd_state = BRD_ERROR; 260 /* Disable all the clocks that were enabled by DSP */ 261 dsp_clock_disable_all(dev_context->dsp_per_clks); 262 /* 263 * Avoid the subsequent WDT if it happens once, 264 * also if fatal error occurs. 265 */ 266 dsp_wdt_enable(false); 267} 268