]>
Commit | Line | Data |
---|---|---|
999e07d6 ORL |
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 | * | |
10 | * This package is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License version 2 as | |
12 | * published by the Free Software Foundation. | |
13 | * | |
14 | * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | |
15 | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | |
16 | * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | |
17 | */ | |
18 | ||
19 | /* ----------------------------------- Host OS */ | |
20 | #include <dspbridge/host_os.h> | |
21 | ||
22 | /* ----------------------------------- DSP/BIOS Bridge */ | |
23 | #include <dspbridge/std.h> | |
24 | #include <dspbridge/dbdefs.h> | |
25 | ||
26 | /* ----------------------------------- Trace & Debug */ | |
27 | #include <dspbridge/dbc.h> | |
28 | ||
29 | /* ----------------------------------- OS Adaptation Layer */ | |
30 | #include <dspbridge/cfg.h> | |
31 | #include <dspbridge/clk.h> | |
32 | #include <dspbridge/ntfy.h> | |
33 | #include <dspbridge/drv.h> | |
34 | ||
35 | /* ----------------------------------- Link Driver */ | |
36 | #include <dspbridge/dspdeh.h> | |
37 | ||
38 | /* ----------------------------------- Platform Manager */ | |
39 | #include <dspbridge/dev.h> | |
40 | #include <dspbridge/dspapi.h> | |
41 | #include <dspbridge/wdt.h> | |
42 | ||
43 | /* ------------------------------------ Hardware Abstraction Layer */ | |
44 | #include <hw_defs.h> | |
45 | #include <hw_mmu.h> | |
46 | ||
47 | /* ----------------------------------- This */ | |
48 | #include "mmu_fault.h" | |
49 | #include "_tiomap.h" | |
50 | #include "_deh.h" | |
51 | #include "_tiomap_pwr.h" | |
52 | #include <dspbridge/io_sm.h> | |
53 | ||
54 | ||
999e07d6 ORL |
55 | int bridge_deh_create(struct deh_mgr **ret_deh_mgr, |
56 | struct dev_object *hdev_obj) | |
57 | { | |
58 | int status = 0; | |
59 | struct deh_mgr *deh_mgr; | |
60 | struct bridge_dev_context *hbridge_context = NULL; | |
61 | ||
62 | /* Message manager will be created when a file is loaded, since | |
63 | * size of message buffer in shared memory is configurable in | |
64 | * the base image. */ | |
65 | /* Get Bridge context info. */ | |
66 | dev_get_bridge_context(hdev_obj, &hbridge_context); | |
999e07d6 ORL |
67 | /* Allocate IO manager object: */ |
68 | deh_mgr = kzalloc(sizeof(struct deh_mgr), GFP_KERNEL); | |
69 | if (!deh_mgr) { | |
70 | status = -ENOMEM; | |
0a466f69 | 71 | goto err; |
999e07d6 ORL |
72 | } |
73 | ||
74 | /* Create an NTFY object to manage notifications */ | |
75 | deh_mgr->ntfy_obj = kmalloc(sizeof(struct ntfy_object), GFP_KERNEL); | |
0a466f69 | 76 | if (!deh_mgr->ntfy_obj) { |
999e07d6 ORL |
77 | status = -ENOMEM; |
78 | goto err; | |
79 | } | |
0a466f69 | 80 | ntfy_init(deh_mgr->ntfy_obj); |
999e07d6 ORL |
81 | |
82 | /* Create a MMUfault DPC */ | |
83 | tasklet_init(&deh_mgr->dpc_tasklet, mmu_fault_dpc, (u32) deh_mgr); | |
84 | ||
85 | /* Fill in context structure */ | |
86 | deh_mgr->hbridge_context = hbridge_context; | |
87 | deh_mgr->err_info.dw_err_mask = 0L; | |
88 | deh_mgr->err_info.dw_val1 = 0L; | |
89 | deh_mgr->err_info.dw_val2 = 0L; | |
90 | deh_mgr->err_info.dw_val3 = 0L; | |
91 | ||
92 | /* Install ISR function for DSP MMU fault */ | |
0a466f69 FC |
93 | status = request_irq(INT_DSP_MMU_IRQ, mmu_fault_isr, 0, |
94 | "DspBridge\tiommu fault", deh_mgr); | |
95 | if (status < 0) | |
96 | goto err; | |
999e07d6 | 97 | |
999e07d6 | 98 | *ret_deh_mgr = deh_mgr; |
0a466f69 | 99 | return 0; |
999e07d6 | 100 | |
0a466f69 FC |
101 | err: |
102 | bridge_deh_destroy(deh_mgr); | |
103 | *ret_deh_mgr = NULL; | |
999e07d6 ORL |
104 | return status; |
105 | } | |
106 | ||
107 | int bridge_deh_destroy(struct deh_mgr *deh_mgr) | |
108 | { | |
109 | if (!deh_mgr) | |
110 | return -EFAULT; | |
111 | ||
999e07d6 ORL |
112 | /* If notification object exists, delete it */ |
113 | if (deh_mgr->ntfy_obj) { | |
114 | ntfy_delete(deh_mgr->ntfy_obj); | |
115 | kfree(deh_mgr->ntfy_obj); | |
116 | } | |
117 | /* Disable DSP MMU fault */ | |
118 | free_irq(INT_DSP_MMU_IRQ, deh_mgr); | |
119 | ||
120 | /* Free DPC object */ | |
121 | tasklet_kill(&deh_mgr->dpc_tasklet); | |
122 | ||
123 | /* Deallocate the DEH manager object */ | |
124 | kfree(deh_mgr); | |
125 | ||
126 | return 0; | |
127 | } | |
128 | ||
129 | int bridge_deh_register_notify(struct deh_mgr *deh_mgr, u32 event_mask, | |
130 | u32 notify_type, | |
131 | struct dsp_notification *hnotification) | |
132 | { | |
999e07d6 ORL |
133 | if (!deh_mgr) |
134 | return -EFAULT; | |
135 | ||
136 | if (event_mask) | |
0a466f69 FC |
137 | return ntfy_register(deh_mgr->ntfy_obj, hnotification, |
138 | event_mask, notify_type); | |
999e07d6 | 139 | else |
0a466f69 | 140 | return ntfy_unregister(deh_mgr->ntfy_obj, hnotification); |
999e07d6 ORL |
141 | } |
142 | ||
143 | void bridge_deh_notify(struct deh_mgr *deh_mgr, u32 ulEventMask, u32 dwErrInfo) | |
144 | { | |
145 | struct bridge_dev_context *dev_context; | |
999e07d6 | 146 | struct cfg_hostres *resources; |
0a466f69 FC |
147 | struct hw_mmu_map_attrs_t map_attrs = { |
148 | .endianism = HW_LITTLE_ENDIAN, | |
149 | .element_size = HW_ELEM_SIZE16BIT, | |
150 | .mixed_size = HW_MMU_CPUES, | |
151 | }; | |
0d8631d2 | 152 | void *dummy_va_addr; |
999e07d6 ORL |
153 | |
154 | if (!deh_mgr) | |
155 | return; | |
156 | ||
157 | dev_info(bridge, "%s: device exception\n", __func__); | |
0a466f69 | 158 | dev_context = deh_mgr->hbridge_context; |
999e07d6 ORL |
159 | resources = dev_context->resources; |
160 | ||
161 | switch (ulEventMask) { | |
162 | case DSP_SYSERROR: | |
163 | /* reset err_info structure before use */ | |
164 | deh_mgr->err_info.dw_err_mask = DSP_SYSERROR; | |
165 | deh_mgr->err_info.dw_val1 = 0L; | |
166 | deh_mgr->err_info.dw_val2 = 0L; | |
167 | deh_mgr->err_info.dw_val3 = 0L; | |
168 | deh_mgr->err_info.dw_val1 = dwErrInfo; | |
169 | dev_err(bridge, "%s: %s, err_info = 0x%x\n", | |
170 | __func__, "DSP_SYSERROR", dwErrInfo); | |
171 | dump_dl_modules(dev_context); | |
172 | dump_dsp_stack(dev_context); | |
173 | break; | |
174 | case DSP_MMUFAULT: | |
175 | /* MMU fault routine should have set err info structure. */ | |
176 | deh_mgr->err_info.dw_err_mask = DSP_MMUFAULT; | |
177 | dev_err(bridge, "%s: %s, err_info = 0x%x\n", | |
178 | __func__, "DSP_MMUFAULT", dwErrInfo); | |
179 | dev_info(bridge, "%s: %s, high=0x%x, low=0x%x, " | |
180 | "fault=0x%x\n", __func__, "DSP_MMUFAULT", | |
181 | (unsigned int) deh_mgr->err_info.dw_val1, | |
182 | (unsigned int) deh_mgr->err_info.dw_val2, | |
183 | (unsigned int) fault_addr); | |
184 | dummy_va_addr = (void*)__get_free_page(GFP_ATOMIC); | |
999e07d6 ORL |
185 | |
186 | print_dsp_trace_buffer(dev_context); | |
187 | dump_dl_modules(dev_context); | |
188 | ||
0a466f69 FC |
189 | hw_mmu_tlb_add(resources->dw_dmmu_base, |
190 | virt_to_phys(dummy_va_addr), fault_addr, | |
191 | HW_PAGE_SIZE4KB, 1, | |
192 | &map_attrs, HW_SET, HW_SET); | |
999e07d6 ORL |
193 | |
194 | dsp_clk_enable(DSP_CLK_GPT8); | |
195 | ||
196 | dsp_gpt_wait_overflow(DSP_CLK_GPT8, 0xfffffffe); | |
197 | ||
198 | /* Clear MMU interrupt */ | |
199 | hw_mmu_event_ack(resources->dw_dmmu_base, | |
200 | HW_MMU_TRANSLATION_FAULT); | |
0a466f69 | 201 | dump_dsp_stack(dev_context); |
999e07d6 | 202 | dsp_clk_disable(DSP_CLK_GPT8); |
0d8631d2 FC |
203 | |
204 | hw_mmu_disable(resources->dw_dmmu_base); | |
205 | free_page((unsigned long)dummy_va_addr); | |
999e07d6 ORL |
206 | break; |
207 | #ifdef CONFIG_BRIDGE_NTFY_PWRERR | |
208 | case DSP_PWRERROR: | |
209 | /* reset err_info structure before use */ | |
210 | deh_mgr->err_info.dw_err_mask = DSP_PWRERROR; | |
211 | deh_mgr->err_info.dw_val1 = 0L; | |
212 | deh_mgr->err_info.dw_val2 = 0L; | |
213 | deh_mgr->err_info.dw_val3 = 0L; | |
214 | deh_mgr->err_info.dw_val1 = dwErrInfo; | |
215 | dev_err(bridge, "%s: %s, err_info = 0x%x\n", | |
216 | __func__, "DSP_PWRERROR", dwErrInfo); | |
217 | break; | |
218 | #endif /* CONFIG_BRIDGE_NTFY_PWRERR */ | |
219 | case DSP_WDTOVERFLOW: | |
220 | deh_mgr->err_info.dw_err_mask = DSP_WDTOVERFLOW; | |
221 | deh_mgr->err_info.dw_val1 = 0L; | |
222 | deh_mgr->err_info.dw_val2 = 0L; | |
223 | deh_mgr->err_info.dw_val3 = 0L; | |
224 | dev_err(bridge, "%s: DSP_WDTOVERFLOW\n", __func__); | |
225 | break; | |
226 | default: | |
227 | dev_dbg(bridge, "%s: Unknown Error, err_info = 0x%x\n", | |
228 | __func__, dwErrInfo); | |
229 | break; | |
230 | } | |
231 | ||
232 | /* Filter subsequent notifications when an error occurs */ | |
233 | if (dev_context->dw_brd_state != BRD_ERROR) { | |
234 | ntfy_notify(deh_mgr->ntfy_obj, ulEventMask); | |
235 | #ifdef CONFIG_BRIDGE_RECOVERY | |
236 | bridge_recover_schedule(); | |
237 | #endif | |
238 | } | |
239 | ||
240 | /* Set the Board state as ERROR */ | |
241 | dev_context->dw_brd_state = BRD_ERROR; | |
242 | /* Disable all the clocks that were enabled by DSP */ | |
243 | dsp_clock_disable_all(dev_context->dsp_per_clks); | |
244 | /* | |
245 | * Avoid the subsequent WDT if it happens once, | |
246 | * also if fatal error occurs. | |
247 | */ | |
248 | dsp_wdt_enable(false); | |
249 | } | |
250 | ||
251 | int bridge_deh_get_info(struct deh_mgr *deh_mgr, | |
252 | struct dsp_errorinfo *pErrInfo) | |
253 | { | |
999e07d6 ORL |
254 | if (!deh_mgr) |
255 | return -EFAULT; | |
256 | ||
257 | /* Copy DEH error info structure to PROC error info structure. */ | |
258 | pErrInfo->dw_err_mask = deh_mgr->err_info.dw_err_mask; | |
259 | pErrInfo->dw_val1 = deh_mgr->err_info.dw_val1; | |
260 | pErrInfo->dw_val2 = deh_mgr->err_info.dw_val2; | |
261 | pErrInfo->dw_val3 = deh_mgr->err_info.dw_val3; | |
262 | ||
263 | return 0; | |
264 | } |