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>
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
;
43 bridge_deh_notify(deh
, DSP_MMUFAULT
, 0);
46 int mmu_fault_isr(struct iommu
*mmu
)
50 dev_get_deh_mgr(dev_get_first(), &dm
);
55 iommu_write_reg(mmu
, 0, MMU_IRQENABLE
);
56 tasklet_schedule(&dm
->dpc_tasklet
);
60 int bridge_deh_create(struct deh_mgr
**ret_deh
,
61 struct dev_object
*hdev_obj
)
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
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
);
79 /* Create an NTFY object to manage notifications */
80 deh
->ntfy_obj
= kmalloc(sizeof(struct ntfy_object
), GFP_KERNEL
);
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
;
97 bridge_deh_destroy(deh
);
102 int bridge_deh_destroy(struct deh_mgr
*deh
)
107 /* If notification object exists, delete it */
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 */
122 int bridge_deh_register_notify(struct deh_mgr
*deh
, u32 event_mask
,
124 struct dsp_notification
*hnotification
)
130 return ntfy_register(deh
->ntfy_obj
, hnotification
,
131 event_mask
, notify_type
);
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
)
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
);
158 e
.pgsz
= IOVMF_PGSZ_4K
& MMU_CAM_PGSZ_MASK
;
159 e
.endian
= MMU_RAM_ENDIAN_LITTLE
;
160 e
.elsz
= MMU_RAM_ELSZ_32
;
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
);
181 static inline const char *event_to_string(int 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
);
201 dev_dbg(bridge
, "%s: device exception", __func__
);
202 dev_context
= deh
->hbridge_context
;
206 dev_err(bridge
, "%s: %s, info=0x%x", __func__
,
208 #ifdef CONFIG_TIDSPBRIDGE_BACKTRACE
209 dump_dl_modules(dev_context
);
210 dump_dsp_stack(dev_context
);
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
);
223 dev_err(bridge
, "%s: %s", __func__
, str
);
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();
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);