Revert "staging: tidspbridge - move all iommu related code to a new file"
[linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git] / drivers / staging / tidspbridge / core / ue_deh.c
blob2e1ac89644d34a47719ddef3e9c47a20e2b6373b
1 /*
2 * ue_deh.c
4 * DSP-BIOS Bridge driver support functions for TI OMAP processors.
6 * Implements upper edge DSP exception handling (DEH) functions.
8 * Copyright (C) 2005-2006 Texas Instruments, Inc.
9 * Copyright (C) 2010 Felipe Contreras
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.
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.
20 #include <linux/kernel.h>
21 #include <linux/interrupt.h>
22 #include <plat/dmtimer.h>
24 #include <dspbridge/dbdefs.h>
25 #include <dspbridge/dspdeh.h>
26 #include <dspbridge/dev.h>
27 #include "_tiomap.h"
28 #include "_deh.h"
30 #include <dspbridge/io_sm.h>
31 #include <dspbridge/drv.h>
32 #include <dspbridge/wdt.h>
34 #define MMU_CNTL_TWL_EN (1 << 2)
36 static void mmu_fault_dpc(unsigned long data)
38 struct deh_mgr *deh = (void *)data;
40 if (!deh)
41 return;
43 bridge_deh_notify(deh, DSP_MMUFAULT, 0);
46 int mmu_fault_isr(struct iommu *mmu)
48 struct deh_mgr *dm;
50 dev_get_deh_mgr(dev_get_first(), &dm);
52 if (!dm)
53 return -EPERM;
55 iommu_write_reg(mmu, 0, MMU_IRQENABLE);
56 tasklet_schedule(&dm->dpc_tasklet);
57 return 0;
60 int bridge_deh_create(struct deh_mgr **ret_deh,
61 struct dev_object *hdev_obj)
63 int status;
64 struct deh_mgr *deh;
65 struct bridge_dev_context *hbridge_context = NULL;
67 /* Message manager will be created when a file is loaded, since
68 * size of message buffer in shared memory is configurable in
69 * the base image. */
70 /* Get Bridge context info. */
71 dev_get_bridge_context(hdev_obj, &hbridge_context);
72 /* Allocate IO manager object: */
73 deh = kzalloc(sizeof(*deh), GFP_KERNEL);
74 if (!deh) {
75 status = -ENOMEM;
76 goto err;
79 /* Create an NTFY object to manage notifications */
80 deh->ntfy_obj = kmalloc(sizeof(struct ntfy_object), GFP_KERNEL);
81 if (!deh->ntfy_obj) {
82 status = -ENOMEM;
83 goto err;
85 ntfy_init(deh->ntfy_obj);
87 /* Create a MMUfault DPC */
88 tasklet_init(&deh->dpc_tasklet, mmu_fault_dpc, (u32) deh);
90 /* Fill in context structure */
91 deh->hbridge_context = hbridge_context;
93 *ret_deh = deh;
94 return 0;
96 err:
97 bridge_deh_destroy(deh);
98 *ret_deh = NULL;
99 return status;
102 int bridge_deh_destroy(struct deh_mgr *deh)
104 if (!deh)
105 return -EFAULT;
107 /* If notification object exists, delete it */
108 if (deh->ntfy_obj) {
109 ntfy_delete(deh->ntfy_obj);
110 kfree(deh->ntfy_obj);
113 /* Free DPC object */
114 tasklet_kill(&deh->dpc_tasklet);
116 /* Deallocate the DEH manager object */
117 kfree(deh);
119 return 0;
122 int bridge_deh_register_notify(struct deh_mgr *deh, u32 event_mask,
123 u32 notify_type,
124 struct dsp_notification *hnotification)
126 if (!deh)
127 return -EFAULT;
129 if (event_mask)
130 return ntfy_register(deh->ntfy_obj, hnotification,
131 event_mask, notify_type);
132 else
133 return ntfy_unregister(deh->ntfy_obj, hnotification);
136 #ifdef CONFIG_TIDSPBRIDGE_BACKTRACE
137 static void mmu_fault_print_stack(struct bridge_dev_context *dev_context)
139 void *dummy_addr;
140 u32 fa, tmp;
141 struct iotlb_entry e;
142 struct iommu *mmu = dev_context->dsp_mmu;
143 dummy_addr = (void *)__get_free_page(GFP_ATOMIC);
146 * Before acking the MMU fault, let's make sure MMU can only
147 * access entry #0. Then add a new entry so that the DSP OS
148 * can continue in order to dump the stack.
150 tmp = iommu_read_reg(mmu, MMU_CNTL);
151 tmp &= ~MMU_CNTL_TWL_EN;
152 iommu_write_reg(mmu, tmp, MMU_CNTL);
153 fa = iommu_read_reg(mmu, MMU_FAULT_AD);
154 e.da = fa & PAGE_MASK;
155 e.pa = virt_to_phys(dummy_addr);
156 e.valid = 1;
157 e.prsvd = 1;
158 e.pgsz = IOVMF_PGSZ_4K & MMU_CAM_PGSZ_MASK;
159 e.endian = MMU_RAM_ENDIAN_LITTLE;
160 e.elsz = MMU_RAM_ELSZ_32;
161 e.mixed = 0;
163 load_iotlb_entry(dev_context->dsp_mmu, &e);
165 dsp_clk_enable(DSP_CLK_GPT8);
167 dsp_gpt_wait_overflow(DSP_CLK_GPT8, 0xfffffffe);
169 /* Clear MMU interrupt */
170 tmp = iommu_read_reg(mmu, MMU_IRQSTATUS);
171 iommu_write_reg(mmu, tmp, MMU_IRQSTATUS);
173 dump_dsp_stack(dev_context);
174 dsp_clk_disable(DSP_CLK_GPT8);
176 iopgtable_clear_entry(mmu, fa);
177 free_page((unsigned long)dummy_addr);
179 #endif
181 static inline const char *event_to_string(int event)
183 switch (event) {
184 case DSP_SYSERROR: return "DSP_SYSERROR"; break;
185 case DSP_MMUFAULT: return "DSP_MMUFAULT"; break;
186 case DSP_PWRERROR: return "DSP_PWRERROR"; break;
187 case DSP_WDTOVERFLOW: return "DSP_WDTOVERFLOW"; break;
188 default: return "unkown event"; break;
192 void bridge_deh_notify(struct deh_mgr *deh, int event, int info)
194 struct bridge_dev_context *dev_context;
195 const char *str = event_to_string(event);
196 u32 fa;
198 if (!deh)
199 return;
201 dev_dbg(bridge, "%s: device exception", __func__);
202 dev_context = deh->hbridge_context;
204 switch (event) {
205 case DSP_SYSERROR:
206 dev_err(bridge, "%s: %s, info=0x%x", __func__,
207 str, info);
208 #ifdef CONFIG_TIDSPBRIDGE_BACKTRACE
209 dump_dl_modules(dev_context);
210 dump_dsp_stack(dev_context);
211 #endif
212 break;
213 case DSP_MMUFAULT:
214 fa = iommu_read_reg(dev_context->dsp_mmu, MMU_FAULT_AD);
215 dev_err(bridge, "%s: %s, addr=0x%x", __func__, str, fa);
216 #ifdef CONFIG_TIDSPBRIDGE_BACKTRACE
217 print_dsp_trace_buffer(dev_context);
218 dump_dl_modules(dev_context);
219 mmu_fault_print_stack(dev_context);
220 #endif
221 break;
222 default:
223 dev_err(bridge, "%s: %s", __func__, str);
224 break;
227 /* Filter subsequent notifications when an error occurs */
228 if (dev_context->dw_brd_state != BRD_ERROR) {
229 ntfy_notify(deh->ntfy_obj, event);
230 #ifdef CONFIG_TIDSPBRIDGE_RECOVERY
231 bridge_recover_schedule();
232 #endif
235 /* Set the Board state as ERROR */
236 dev_context->dw_brd_state = BRD_ERROR;
237 /* Disable all the clocks that were enabled by DSP */
238 dsp_clock_disable_all(dev_context->dsp_per_clks);
240 * Avoid the subsequent WDT if it happens once,
241 * also if fatal error occurs.
243 dsp_wdt_enable(false);