1 | /* General part of a AMBA Plug & Play bus driver. |
---|
2 | * |
---|
3 | * COPYRIGHT (c) 2008. |
---|
4 | * Cobham Gaisler AB. |
---|
5 | * |
---|
6 | * The license and distribution terms for this file may be |
---|
7 | * found in the file LICENSE in this distribution or at |
---|
8 | * http://www.rtems.org/license/LICENSE. |
---|
9 | * |
---|
10 | * This is the general part of the different AMBA Plug & Play |
---|
11 | * drivers. The drivers are wrappers around this driver, making |
---|
12 | * the code size smaller for systems with multiple AMBA Plug & |
---|
13 | * Play buses. |
---|
14 | * |
---|
15 | * The BSP define APBUART_INFO_AVAIL in order to add the info routine |
---|
16 | * used for debugging. |
---|
17 | */ |
---|
18 | |
---|
19 | #include <stdio.h> |
---|
20 | #include <stdlib.h> |
---|
21 | #include <string.h> |
---|
22 | |
---|
23 | #include <drvmgr/drvmgr.h> |
---|
24 | #include <drvmgr/ambapp_bus.h> |
---|
25 | |
---|
26 | #include <bsp.h> |
---|
27 | #include <ambapp.h> |
---|
28 | #include <rtems/bspIo.h> |
---|
29 | |
---|
30 | /*#define DEBUG 1*/ |
---|
31 | #define DBG(args...) |
---|
32 | /*#define DBG(args...) printk(args)*/ |
---|
33 | |
---|
34 | struct grlib_gptimer_regs { |
---|
35 | volatile unsigned int scaler_value; /* common timer registers */ |
---|
36 | volatile unsigned int scaler_reload; |
---|
37 | volatile unsigned int status; |
---|
38 | volatile unsigned int notused; |
---|
39 | }; |
---|
40 | |
---|
41 | /* AMBA IMPLEMENTATION */ |
---|
42 | |
---|
43 | int ambapp_bus_init1(struct drvmgr_bus *bus); |
---|
44 | int ambapp_bus_remove(struct drvmgr_bus *bus); |
---|
45 | int ambapp_unite(struct drvmgr_drv *drv, struct drvmgr_dev *dev); |
---|
46 | int ambapp_int_register(struct drvmgr_dev *dev, int index, const char *info, drvmgr_isr isr, void *arg); |
---|
47 | int ambapp_int_unregister(struct drvmgr_dev *dev, int index, drvmgr_isr isr, void *arg); |
---|
48 | int ambapp_int_clear(struct drvmgr_dev *dev, int index); |
---|
49 | int ambapp_int_mask(struct drvmgr_dev *dev, int index); |
---|
50 | int ambapp_int_unmask(struct drvmgr_dev *dev, int index); |
---|
51 | int ambapp_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params); |
---|
52 | int ambapp_bus_freq_get( |
---|
53 | struct drvmgr_dev *dev, |
---|
54 | int options, |
---|
55 | unsigned int *freq_hz); |
---|
56 | void ambapp_dev_info(struct drvmgr_dev *, void (*print)(void *p, char *str), void *p); |
---|
57 | |
---|
58 | #ifdef RTEMS_SMP |
---|
59 | int ambapp_int_set_affinity( |
---|
60 | struct drvmgr_dev *dev, |
---|
61 | int index, |
---|
62 | const Processor_mask *cpus); |
---|
63 | #endif |
---|
64 | |
---|
65 | struct drvmgr_bus_ops ambapp_bus_ops = |
---|
66 | { |
---|
67 | .init = |
---|
68 | { |
---|
69 | /* init1 */ ambapp_bus_init1, |
---|
70 | /* init2 */ NULL, |
---|
71 | /* init3 */ NULL, |
---|
72 | /* init4 */ NULL |
---|
73 | }, |
---|
74 | .remove = ambapp_bus_remove, |
---|
75 | .unite = ambapp_unite, |
---|
76 | .int_register = ambapp_int_register, |
---|
77 | .int_unregister = ambapp_int_unregister, |
---|
78 | .int_clear = ambapp_int_clear, |
---|
79 | .int_mask = ambapp_int_mask, |
---|
80 | #ifdef RTEMS_SMP |
---|
81 | .int_set_affinity = ambapp_int_set_affinity, |
---|
82 | #endif |
---|
83 | .int_unmask = ambapp_int_unmask, |
---|
84 | .get_params = ambapp_get_params, |
---|
85 | .get_freq = ambapp_bus_freq_get, |
---|
86 | #ifdef AMBAPPBUS_INFO_AVAIL |
---|
87 | .get_info_dev = ambapp_dev_info, |
---|
88 | #endif |
---|
89 | }; |
---|
90 | |
---|
91 | struct ambapp_priv { |
---|
92 | struct ambapp_config *config; |
---|
93 | }; |
---|
94 | |
---|
95 | int ambapp_unite(struct drvmgr_drv *drv, struct drvmgr_dev *dev) |
---|
96 | { |
---|
97 | struct amba_drv_info *adrv; |
---|
98 | struct amba_dev_id *id; |
---|
99 | struct amba_dev_info *amba; |
---|
100 | |
---|
101 | if ( !drv || !dev || !dev->parent ) |
---|
102 | return 0; |
---|
103 | |
---|
104 | if ( ! (((drv->bus_type == DRVMGR_BUS_TYPE_AMBAPP) && (dev->parent->bus_type == DRVMGR_BUS_TYPE_AMBAPP)) || |
---|
105 | ((drv->bus_type == DRVMGR_BUS_TYPE_AMBAPP_RMAP) && (dev->parent->bus_type == DRVMGR_BUS_TYPE_AMBAPP_RMAP)) || |
---|
106 | ((drv->bus_type == DRVMGR_BUS_TYPE_AMBAPP_DIST) && (dev->parent->bus_type == DRVMGR_BUS_TYPE_AMBAPP_DIST))) |
---|
107 | ) { |
---|
108 | return 0; |
---|
109 | } |
---|
110 | |
---|
111 | amba = (struct amba_dev_info *)dev->businfo; |
---|
112 | if ( !amba ) |
---|
113 | return 0; |
---|
114 | |
---|
115 | adrv = (struct amba_drv_info *)drv; |
---|
116 | id = adrv->ids; |
---|
117 | if ( !id ) |
---|
118 | return 0; |
---|
119 | while( id->vendor != 0 ) { |
---|
120 | if ( (id->vendor == amba->id.vendor) && |
---|
121 | (id->device == amba->id.device) ) { |
---|
122 | /* Unite device and driver */ |
---|
123 | DBG("DRV 0x%x and DEV 0x%x united\n", (unsigned int)drv, (unsigned int)dev); |
---|
124 | return 1; |
---|
125 | } |
---|
126 | id++; |
---|
127 | } |
---|
128 | |
---|
129 | return 0; |
---|
130 | } |
---|
131 | |
---|
132 | static int ambapp_int_get(struct drvmgr_dev *dev, int index) |
---|
133 | { |
---|
134 | int irq; |
---|
135 | |
---|
136 | /* Relative (positive) or absolute (negative) IRQ number */ |
---|
137 | if ( index >= 0 ) { |
---|
138 | /* IRQ Index relative to Cores base IRQ */ |
---|
139 | |
---|
140 | /* Get Base IRQ */ |
---|
141 | irq = ((struct amba_dev_info *)dev->businfo)->info.irq; |
---|
142 | if ( irq < 0 ) |
---|
143 | return -1; |
---|
144 | irq += index; |
---|
145 | } else { |
---|
146 | /* Absolute IRQ number */ |
---|
147 | irq = -index; |
---|
148 | } |
---|
149 | return irq; |
---|
150 | } |
---|
151 | |
---|
152 | int ambapp_int_register( |
---|
153 | struct drvmgr_dev *dev, |
---|
154 | int index, |
---|
155 | const char *info, |
---|
156 | drvmgr_isr isr, |
---|
157 | void *arg) |
---|
158 | { |
---|
159 | struct ambapp_priv *priv; |
---|
160 | int irq; |
---|
161 | |
---|
162 | priv = dev->parent->priv; |
---|
163 | |
---|
164 | /* Get IRQ number from index and device information */ |
---|
165 | irq = ambapp_int_get(dev, index); |
---|
166 | if ( irq < 0 ) |
---|
167 | return DRVMGR_EINVAL; |
---|
168 | |
---|
169 | DBG("Register interrupt on 0x%x for dev 0x%x (IRQ: %d)\n", |
---|
170 | (unsigned int)dev->parent->dev, (unsigned int)dev, irq); |
---|
171 | |
---|
172 | if ( priv->config->ops->int_register ) { |
---|
173 | /* Let device override driver default */ |
---|
174 | return priv->config->ops->int_register(dev, irq, info, isr, arg); |
---|
175 | } else { |
---|
176 | return DRVMGR_ENOSYS; |
---|
177 | } |
---|
178 | } |
---|
179 | |
---|
180 | int ambapp_int_unregister( |
---|
181 | struct drvmgr_dev *dev, |
---|
182 | int index, |
---|
183 | drvmgr_isr isr, |
---|
184 | void *arg) |
---|
185 | { |
---|
186 | struct ambapp_priv *priv; |
---|
187 | int irq; |
---|
188 | |
---|
189 | priv = dev->parent->priv; |
---|
190 | |
---|
191 | /* Get IRQ number from index and device information */ |
---|
192 | irq = ambapp_int_get(dev, index); |
---|
193 | if ( irq < 0 ) |
---|
194 | return DRVMGR_EINVAL; |
---|
195 | |
---|
196 | DBG("Unregister interrupt on 0x%x for dev 0x%x (IRQ: %d)\n", |
---|
197 | (unsigned int)dev->parent->dev, (unsigned int)dev, irq); |
---|
198 | |
---|
199 | if ( priv->config->ops->int_unregister ) { |
---|
200 | /* Let device override driver default */ |
---|
201 | return priv->config->ops->int_unregister(dev, irq, isr, arg); |
---|
202 | } else { |
---|
203 | return DRVMGR_ENOSYS; |
---|
204 | } |
---|
205 | } |
---|
206 | |
---|
207 | int ambapp_int_clear( |
---|
208 | struct drvmgr_dev *dev, |
---|
209 | int index) |
---|
210 | { |
---|
211 | struct ambapp_priv *priv; |
---|
212 | int irq; |
---|
213 | |
---|
214 | priv = dev->parent->priv; |
---|
215 | |
---|
216 | /* Get IRQ number from index and device information */ |
---|
217 | irq = ambapp_int_get(dev, index); |
---|
218 | if ( irq < 0 ) |
---|
219 | return -1; |
---|
220 | |
---|
221 | DBG("Clear interrupt on 0x%x for dev 0x%x (IRQ: %d)\n", |
---|
222 | (unsigned int)dev->parent->dev, (unsigned int)dev, irq); |
---|
223 | |
---|
224 | if ( priv->config->ops->int_clear ) { |
---|
225 | /* Let device override driver default */ |
---|
226 | return priv->config->ops->int_clear(dev, irq); |
---|
227 | } else { |
---|
228 | return DRVMGR_ENOSYS; |
---|
229 | } |
---|
230 | } |
---|
231 | |
---|
232 | int ambapp_int_mask( |
---|
233 | struct drvmgr_dev *dev, |
---|
234 | int index) |
---|
235 | { |
---|
236 | struct ambapp_priv *priv; |
---|
237 | int irq; |
---|
238 | |
---|
239 | priv = dev->parent->priv; |
---|
240 | |
---|
241 | /* Get IRQ number from index and device information */ |
---|
242 | irq = ambapp_int_get(dev, index); |
---|
243 | if ( irq < 0 ) |
---|
244 | return -1; |
---|
245 | |
---|
246 | DBG("MASK interrupt on 0x%x for dev 0x%x (IRQ: %d)\n", |
---|
247 | (unsigned int)dev->parent->dev, (unsigned int)dev, irq); |
---|
248 | |
---|
249 | if ( priv->config->ops->int_mask ) { |
---|
250 | /* Let device override driver default */ |
---|
251 | return priv->config->ops->int_mask(dev, irq); |
---|
252 | } else { |
---|
253 | return DRVMGR_ENOSYS; |
---|
254 | } |
---|
255 | } |
---|
256 | |
---|
257 | int ambapp_int_unmask( |
---|
258 | struct drvmgr_dev *dev, |
---|
259 | int index) |
---|
260 | { |
---|
261 | struct ambapp_priv *priv; |
---|
262 | int irq; |
---|
263 | |
---|
264 | priv = dev->parent->priv; |
---|
265 | |
---|
266 | /* Get IRQ number from index and device information */ |
---|
267 | irq = ambapp_int_get(dev, index); |
---|
268 | if ( irq < 0 ) |
---|
269 | return DRVMGR_EINVAL; |
---|
270 | |
---|
271 | DBG("UNMASK interrupt on 0x%x for dev 0x%x (IRQ: %d)\n", |
---|
272 | (unsigned int)dev->parent->dev, (unsigned int)dev, irq); |
---|
273 | |
---|
274 | if ( priv->config->ops->int_unmask ) { |
---|
275 | /* Let device override driver default */ |
---|
276 | return priv->config->ops->int_unmask(dev, irq); |
---|
277 | } else { |
---|
278 | return DRVMGR_ENOSYS; |
---|
279 | } |
---|
280 | } |
---|
281 | |
---|
282 | /* Assign frequency to an AMBA Bus */ |
---|
283 | void ambapp_bus_freq_register( |
---|
284 | struct drvmgr_dev *dev, |
---|
285 | int amba_interface, |
---|
286 | unsigned int freq_hz |
---|
287 | ) |
---|
288 | { |
---|
289 | struct ambapp_priv *priv = (struct ambapp_priv *)dev->parent->priv; |
---|
290 | struct ambapp_dev *adev; |
---|
291 | struct amba_dev_info *pnp = dev->businfo; |
---|
292 | |
---|
293 | if ( freq_hz == 0 ) |
---|
294 | return; |
---|
295 | |
---|
296 | if ( amba_interface == DEV_AHB_MST ) { |
---|
297 | adev = (struct ambapp_dev *) |
---|
298 | ((unsigned int)pnp->info.ahb_mst - |
---|
299 | sizeof(struct ambapp_dev)); |
---|
300 | } else if ( amba_interface == DEV_AHB_SLV ) { |
---|
301 | adev = (struct ambapp_dev *) |
---|
302 | ((unsigned int)pnp->info.ahb_slv - |
---|
303 | sizeof(struct ambapp_dev)); |
---|
304 | } else if ( amba_interface == DEV_APB_SLV ) { |
---|
305 | adev = (struct ambapp_dev *) |
---|
306 | ((unsigned int)pnp->info.apb_slv - |
---|
307 | sizeof(struct ambapp_dev)); |
---|
308 | } else { |
---|
309 | return; |
---|
310 | } |
---|
311 | |
---|
312 | /* Calculate Top bus frequency from lower part. The frequency comes |
---|
313 | * from some kind of hardware able to report local bus frequency. |
---|
314 | */ |
---|
315 | ambapp_freq_init(priv->config->abus, adev, freq_hz); |
---|
316 | } |
---|
317 | |
---|
318 | int ambapp_bus_freq_get( |
---|
319 | struct drvmgr_dev *dev, |
---|
320 | int options, |
---|
321 | unsigned int *freq_hz) |
---|
322 | { |
---|
323 | struct ambapp_priv *priv = (struct ambapp_priv *)dev->parent->priv; |
---|
324 | struct ambapp_dev *adev; |
---|
325 | struct amba_dev_info *pnp = dev->businfo; |
---|
326 | |
---|
327 | if ( options == DEV_AHB_MST ) { |
---|
328 | adev = (struct ambapp_dev *) |
---|
329 | ((unsigned int)pnp->info.ahb_mst - |
---|
330 | sizeof(struct ambapp_dev)); |
---|
331 | } else if ( options == DEV_AHB_SLV ) { |
---|
332 | adev = (struct ambapp_dev *) |
---|
333 | ((unsigned int)pnp->info.ahb_slv - |
---|
334 | sizeof(struct ambapp_dev)); |
---|
335 | } else if ( options == DEV_APB_SLV ) { |
---|
336 | adev = (struct ambapp_dev *) |
---|
337 | ((unsigned int)pnp->info.apb_slv - |
---|
338 | sizeof(struct ambapp_dev)); |
---|
339 | } else { |
---|
340 | *freq_hz = 0; |
---|
341 | return -1; |
---|
342 | } |
---|
343 | |
---|
344 | /* Calculate core/bus frequency from top most bus frequency. */ |
---|
345 | *freq_hz = ambapp_freq_get(priv->config->abus, adev); |
---|
346 | if ( *freq_hz == 0 ) |
---|
347 | return -1; |
---|
348 | return 0; |
---|
349 | } |
---|
350 | |
---|
351 | int ambapp_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params) |
---|
352 | { |
---|
353 | struct ambapp_priv *priv = dev->parent->priv; |
---|
354 | |
---|
355 | if ( priv->config->ops->get_params ) { |
---|
356 | /* Let device override driver default */ |
---|
357 | return priv->config->ops->get_params(dev, params); |
---|
358 | } else { |
---|
359 | return -1; |
---|
360 | } |
---|
361 | } |
---|
362 | |
---|
363 | #ifdef AMBAPPBUS_INFO_AVAIL |
---|
364 | void ambapp_dev_info( |
---|
365 | struct drvmgr_dev *dev, |
---|
366 | void (*print_line)(void *p, char *str), |
---|
367 | void *p) |
---|
368 | { |
---|
369 | struct amba_dev_info *devinfo; |
---|
370 | struct ambapp_core *core; |
---|
371 | char buf[64]; |
---|
372 | int ver, i; |
---|
373 | char *str1, *str2, *str3; |
---|
374 | unsigned int ahbmst_freq, ahbslv_freq, apbslv_freq; |
---|
375 | |
---|
376 | if (!dev) |
---|
377 | return; |
---|
378 | |
---|
379 | devinfo = (struct amba_dev_info *)dev->businfo; |
---|
380 | if (!devinfo) |
---|
381 | return; |
---|
382 | core = &devinfo->info; |
---|
383 | |
---|
384 | print_line(p, "AMBA PnP DEVICE"); |
---|
385 | |
---|
386 | str1 = ambapp_vendor_id2str(devinfo->id.vendor); |
---|
387 | if (str1 == NULL) |
---|
388 | str1 = "unknown"; |
---|
389 | sprintf(buf, "VENDOR ID: 0x%04x (%s)", devinfo->id.vendor, str1); |
---|
390 | print_line(p, buf); |
---|
391 | |
---|
392 | str1 = ambapp_device_id2str(devinfo->id.vendor, devinfo->id.device); |
---|
393 | if (str1 == NULL) |
---|
394 | str1 = "unknown"; |
---|
395 | sprintf(buf, "DEVICE ID: 0x%04x (%s)", devinfo->id.device, str1); |
---|
396 | print_line(p, buf); |
---|
397 | |
---|
398 | ahbmst_freq = ahbslv_freq = apbslv_freq = 0; |
---|
399 | ver = 0; |
---|
400 | str1 = str2 = str3 = ""; |
---|
401 | if (core->ahb_mst) { |
---|
402 | str1 = "AHBMST "; |
---|
403 | ver = core->ahb_mst->ver; |
---|
404 | ambapp_bus_freq_get(dev, DEV_AHB_MST, &ahbmst_freq); |
---|
405 | } |
---|
406 | if (core->ahb_slv) { |
---|
407 | str2 = "AHBSLV "; |
---|
408 | ver = core->ahb_slv->ver; |
---|
409 | ambapp_bus_freq_get(dev, DEV_AHB_SLV, &ahbslv_freq); |
---|
410 | } |
---|
411 | if (core->apb_slv) { |
---|
412 | str3 = "APBSLV"; |
---|
413 | ver = core->apb_slv->ver; |
---|
414 | ambapp_bus_freq_get(dev, DEV_APB_SLV, &apbslv_freq); |
---|
415 | } |
---|
416 | |
---|
417 | sprintf(buf, "IRQ: %d", ambapp_int_get(dev, 0)); |
---|
418 | print_line(p, buf); |
---|
419 | |
---|
420 | sprintf(buf, "VERSION: 0x%x", ver); |
---|
421 | print_line(p, buf); |
---|
422 | |
---|
423 | sprintf(buf, "ambapp_core: %p", core); |
---|
424 | print_line(p, buf); |
---|
425 | |
---|
426 | sprintf(buf, "interfaces: %s%s%s", str1, str2, str3); |
---|
427 | print_line(p, buf); |
---|
428 | |
---|
429 | if (ahbmst_freq != 0) { |
---|
430 | sprintf(buf, "AHBMST FREQ: %dkHz", ahbmst_freq/1000); |
---|
431 | print_line(p, buf); |
---|
432 | } |
---|
433 | |
---|
434 | if (ahbslv_freq != 0) { |
---|
435 | sprintf(buf, "AHBSLV FREQ: %dkHz", ahbslv_freq/1000); |
---|
436 | print_line(p, buf); |
---|
437 | } |
---|
438 | |
---|
439 | if (apbslv_freq != 0) { |
---|
440 | sprintf(buf, "APBSLV FREQ: %dkHz", apbslv_freq/1000); |
---|
441 | print_line(p, buf); |
---|
442 | } |
---|
443 | |
---|
444 | if (core->ahb_slv) { |
---|
445 | for(i=0; i<4; i++) { |
---|
446 | if (core->ahb_slv->type[i] == AMBA_TYPE_AHBIO) |
---|
447 | str1 = " ahbio"; |
---|
448 | else if (core->ahb_slv->type[i] == AMBA_TYPE_MEM) |
---|
449 | str1 = "ahbmem"; |
---|
450 | else |
---|
451 | continue; |
---|
452 | sprintf(buf, " %s[%d]: 0x%08x-0x%08x", str1, i, |
---|
453 | core->ahb_slv->start[i], |
---|
454 | core->ahb_slv->start[i]+core->ahb_slv->mask[i]-1); |
---|
455 | print_line(p, buf); |
---|
456 | } |
---|
457 | } |
---|
458 | if (core->apb_slv) { |
---|
459 | sprintf(buf, " apb: 0x%08x-0x%08x", |
---|
460 | core->apb_slv->start, |
---|
461 | core->apb_slv->start + core->apb_slv->mask - 1); |
---|
462 | print_line(p, buf); |
---|
463 | } |
---|
464 | } |
---|
465 | #endif |
---|
466 | |
---|
467 | /* Fix device in last stage and/or register additional devices. |
---|
468 | * Function returns: |
---|
469 | * 0 Register device as normal |
---|
470 | * 1 Fixup function handles registration |
---|
471 | */ |
---|
472 | static int ambapp_dev_fixup(struct drvmgr_dev *dev, struct amba_dev_info *pnp) |
---|
473 | { |
---|
474 | /* OCCAN speciality: |
---|
475 | * Mulitple cores are supported through the same amba AHB interface. |
---|
476 | * The number of "sub cores" can be detected by decoding the AMBA |
---|
477 | * Plug&Play version information. verion = ncores. A maximum of 8 |
---|
478 | * sub cores are supported, each separeated with 0x100 inbetween. |
---|
479 | * |
---|
480 | * Now, lets detect sub cores. |
---|
481 | */ |
---|
482 | if ( (pnp->info.device == GAISLER_CANAHB) && |
---|
483 | (pnp->info.vendor == VENDOR_GAISLER) ) { |
---|
484 | struct drvmgr_dev *newdev, *devs_to_register[8]; |
---|
485 | struct amba_dev_info *pnpinfo; |
---|
486 | int subcores; |
---|
487 | int core; |
---|
488 | |
---|
489 | devs_to_register[0] = dev; |
---|
490 | subcores = (pnp->info.ahb_slv->ver & 0x7) + 1; |
---|
491 | for(core = 1; core < subcores; core++) { |
---|
492 | drvmgr_alloc_dev(&newdev, sizeof(*pnpinfo)); |
---|
493 | memcpy(newdev, dev, sizeof(*newdev)); |
---|
494 | pnpinfo = (struct amba_dev_info *)(newdev+1); |
---|
495 | memcpy(pnpinfo, pnp, sizeof(*pnp)); |
---|
496 | pnpinfo->info.index = core; |
---|
497 | pnpinfo->info.irq += core; |
---|
498 | newdev->businfo = (void *)pnpinfo; |
---|
499 | |
---|
500 | devs_to_register[core] = newdev; |
---|
501 | } |
---|
502 | /* Register all CAN devices */ |
---|
503 | for(core = 0; core < subcores; core++) |
---|
504 | drvmgr_dev_register(devs_to_register[core]); |
---|
505 | return 1; |
---|
506 | } else if ( (pnp->info.device == GAISLER_GPIO) && |
---|
507 | (pnp->info.vendor == VENDOR_GAISLER) ) { |
---|
508 | /* PIO[N] is connected to IRQ[N]. */ |
---|
509 | pnp->info.irq = 0; |
---|
510 | } |
---|
511 | return 0; |
---|
512 | } |
---|
513 | |
---|
514 | struct ambapp_dev_reg_struct { |
---|
515 | struct ambapp_bus *abus; |
---|
516 | struct drvmgr_bus *bus; |
---|
517 | struct ambapp_dev *ahb_mst; |
---|
518 | struct ambapp_dev *ahb_slv; |
---|
519 | struct ambapp_dev *apb_slv; |
---|
520 | }; |
---|
521 | |
---|
522 | static void ambapp_core_register( |
---|
523 | struct ambapp_dev *ahb_mst, |
---|
524 | struct ambapp_dev *ahb_slv, |
---|
525 | struct ambapp_dev *apb_slv, |
---|
526 | struct ambapp_dev_reg_struct *arg |
---|
527 | ) |
---|
528 | { |
---|
529 | struct drvmgr_dev *newdev; |
---|
530 | struct amba_dev_info *pnpinfo; |
---|
531 | unsigned short device; |
---|
532 | unsigned char vendor; |
---|
533 | int namelen; |
---|
534 | char buf[64]; |
---|
535 | |
---|
536 | if ( ahb_mst ) { |
---|
537 | device = ahb_mst->device; |
---|
538 | vendor = ahb_mst->vendor; |
---|
539 | }else if ( ahb_slv ) { |
---|
540 | device = ahb_slv->device; |
---|
541 | vendor = ahb_slv->vendor; |
---|
542 | }else if( apb_slv ) { |
---|
543 | device = apb_slv->device; |
---|
544 | vendor = apb_slv->vendor; |
---|
545 | } else { |
---|
546 | DBG("NO DEV!\n"); |
---|
547 | return; |
---|
548 | } |
---|
549 | |
---|
550 | DBG("CORE REGISTER DEV [%x:%x] MST: 0x%x, SLV: 0x%x, APB: 0x%x\n", vendor, device, (unsigned int)ahb_mst, (unsigned int)ahb_slv, (unsigned int)apb_slv); |
---|
551 | |
---|
552 | /* Get unique device name from AMBA data base by combining VENDOR and |
---|
553 | * DEVICE short names |
---|
554 | */ |
---|
555 | namelen = ambapp_vendev_id2str(vendor, device, buf); |
---|
556 | |
---|
557 | /* Allocate a device */ |
---|
558 | drvmgr_alloc_dev(&newdev, sizeof(struct amba_dev_info) + namelen); |
---|
559 | pnpinfo = (struct amba_dev_info *)(newdev + 1); |
---|
560 | newdev->parent = arg->bus; /* Ourselfs */ |
---|
561 | newdev->minor_drv = 0; |
---|
562 | newdev->minor_bus = 0; |
---|
563 | newdev->priv = NULL; |
---|
564 | newdev->drv = NULL; |
---|
565 | if (namelen > 0) { |
---|
566 | newdev->name = (char *)(pnpinfo + 1); |
---|
567 | strcpy(newdev->name, buf); |
---|
568 | } else { |
---|
569 | newdev->name = NULL; |
---|
570 | } |
---|
571 | newdev->next_in_drv = NULL; |
---|
572 | newdev->bus = NULL; |
---|
573 | |
---|
574 | /* Init PnP information, Assign Core interfaces with this device */ |
---|
575 | pnpinfo->id.vendor = vendor; |
---|
576 | pnpinfo->id.device = device; |
---|
577 | pnpinfo->info.vendor = vendor; |
---|
578 | pnpinfo->info.device = device; |
---|
579 | pnpinfo->info.index = 0; |
---|
580 | if ( ahb_mst ) { |
---|
581 | pnpinfo->info.ahb_mst = (struct ambapp_ahb_info *) |
---|
582 | ahb_mst->devinfo; |
---|
583 | ambapp_alloc_dev(ahb_mst, (void *)newdev); |
---|
584 | if ( pnpinfo->info.ahb_mst->irq ) |
---|
585 | pnpinfo->info.irq = pnpinfo->info.ahb_mst->irq; |
---|
586 | } |
---|
587 | if ( ahb_slv ) { |
---|
588 | pnpinfo->info.ahb_slv = (struct ambapp_ahb_info *) |
---|
589 | ahb_slv->devinfo; |
---|
590 | ambapp_alloc_dev(ahb_slv, (void *)newdev); |
---|
591 | if ( pnpinfo->info.ahb_slv->irq ) |
---|
592 | pnpinfo->info.irq = pnpinfo->info.ahb_slv->irq; |
---|
593 | } |
---|
594 | if ( apb_slv ) { |
---|
595 | pnpinfo->info.apb_slv = (struct ambapp_apb_info *) |
---|
596 | apb_slv->devinfo; |
---|
597 | ambapp_alloc_dev(apb_slv, (void *)newdev); |
---|
598 | if ( pnpinfo->info.apb_slv->irq ) |
---|
599 | pnpinfo->info.irq = pnpinfo->info.apb_slv->irq; |
---|
600 | } |
---|
601 | if ( pnpinfo->info.irq == 0 ) |
---|
602 | pnpinfo->info.irq = -1; /* indicate no IRQ */ |
---|
603 | |
---|
604 | /* Connect device with PnP information */ |
---|
605 | newdev->businfo = (void *)pnpinfo; |
---|
606 | |
---|
607 | if ( ambapp_dev_fixup(newdev, pnpinfo) == 0 ) |
---|
608 | drvmgr_dev_register(newdev); /* Register New Device */ |
---|
609 | } |
---|
610 | |
---|
611 | /* Fix device registration. |
---|
612 | * Function returns: |
---|
613 | * 0 Register device as normal |
---|
614 | * 1 Fixup function handles registration |
---|
615 | */ |
---|
616 | static int ambapp_dev_register_fixup(struct ambapp_dev *dev, struct ambapp_dev_reg_struct *p) |
---|
617 | { |
---|
618 | /* GR740 GRPCI2 speciality: |
---|
619 | * - In the GR740 the APB_SLV is detected before the AHB_SLV |
---|
620 | * which makes the registration incorrect. We deal with it in |
---|
621 | * this function. */ |
---|
622 | if ( (dev->dev_type == DEV_APB_SLV) && |
---|
623 | (dev->device == GAISLER_GRPCI2) && |
---|
624 | (dev->vendor == VENDOR_GAISLER) && |
---|
625 | (p->ahb_slv == NULL) ) { |
---|
626 | DBG("GRPCI2 APB_SLV detected before AHB_SLV. Skipping APB_SLV registration.\n"); |
---|
627 | return 1; |
---|
628 | } |
---|
629 | return 0; |
---|
630 | } |
---|
631 | |
---|
632 | /* Register one AMBA device */ |
---|
633 | static int ambapp_dev_register(struct ambapp_dev *dev, int index, void *arg) |
---|
634 | { |
---|
635 | struct ambapp_dev_reg_struct *p = arg; |
---|
636 | |
---|
637 | #ifdef DEBUG |
---|
638 | char *type; |
---|
639 | |
---|
640 | if ( dev->dev_type == DEV_AHB_MST ) |
---|
641 | type = "AHB MST"; |
---|
642 | else if ( dev->dev_type == DEV_AHB_SLV ) |
---|
643 | type = "AHB SLV"; |
---|
644 | else if ( dev->dev_type == DEV_APB_SLV ) |
---|
645 | type = "APB SLV"; |
---|
646 | |
---|
647 | DBG("Found [%d:%x:%x], %s\n", index, dev->vendor, dev->device, type); |
---|
648 | #endif |
---|
649 | |
---|
650 | /* Fixup for device registration */ |
---|
651 | if (ambapp_dev_register_fixup(dev, p)){ |
---|
652 | return 0; |
---|
653 | } |
---|
654 | |
---|
655 | if ( dev->dev_type == DEV_AHB_MST ) { |
---|
656 | if ( p->ahb_mst ) { |
---|
657 | /* This should not happen */ |
---|
658 | printk("ambapp_dev_register: ahb_mst not NULL!\n"); |
---|
659 | exit(1); |
---|
660 | } |
---|
661 | |
---|
662 | /* Remember AHB Master */ |
---|
663 | p->ahb_mst = dev; |
---|
664 | |
---|
665 | /* Find AHB Slave and APB slave for this Core */ |
---|
666 | ambapp_for_each(p->abus, (OPTIONS_AHB_SLVS|OPTIONS_APB_SLVS|OPTIONS_FREE), dev->vendor, dev->device, ambapp_dev_register, p); |
---|
667 | |
---|
668 | ambapp_core_register(p->ahb_mst, p->ahb_slv, p->apb_slv, p); |
---|
669 | p->ahb_mst = p->ahb_slv = p->apb_slv = NULL; |
---|
670 | return 0; |
---|
671 | |
---|
672 | } else if ( dev->dev_type == DEV_AHB_SLV ) { |
---|
673 | if ( p->ahb_slv ) { |
---|
674 | /* Already got our AHB Slave interface */ |
---|
675 | return 0; |
---|
676 | } |
---|
677 | |
---|
678 | /* Remember AHB Slave */ |
---|
679 | p->ahb_slv = dev; |
---|
680 | |
---|
681 | if ( p->ahb_mst ) { |
---|
682 | /* Continue searching for APB Slave */ |
---|
683 | return 0; |
---|
684 | } else { |
---|
685 | /* Find APB Slave interface for this Core */ |
---|
686 | ambapp_for_each(p->abus, (OPTIONS_APB_SLVS|OPTIONS_FREE), dev->vendor, dev->device, ambapp_dev_register, p); |
---|
687 | |
---|
688 | ambapp_core_register(p->ahb_mst, p->ahb_slv, p->apb_slv, p); |
---|
689 | p->ahb_mst = p->ahb_slv = p->apb_slv = NULL; |
---|
690 | return 0; |
---|
691 | } |
---|
692 | } else if ( dev->dev_type == DEV_APB_SLV ) { |
---|
693 | if ( p->apb_slv ) { |
---|
694 | /* This should not happen */ |
---|
695 | printk("ambapp_dev_register: apb_slv not NULL!\n"); |
---|
696 | exit(1); |
---|
697 | } |
---|
698 | /* Remember APB Slave */ |
---|
699 | p->apb_slv = dev; |
---|
700 | |
---|
701 | if ( p->ahb_mst || p->ahb_slv ) { |
---|
702 | /* Stop scanning */ |
---|
703 | return 1; |
---|
704 | } else { |
---|
705 | ambapp_core_register(p->ahb_mst, p->ahb_slv, p->apb_slv, p); |
---|
706 | p->ahb_mst = p->ahb_slv = p->apb_slv = NULL; |
---|
707 | return 0; |
---|
708 | } |
---|
709 | } |
---|
710 | |
---|
711 | return 0; |
---|
712 | } |
---|
713 | |
---|
714 | /* Register all AMBA devices available on the AMBAPP bus */ |
---|
715 | static int ambapp_ids_register(struct drvmgr_bus *bus) |
---|
716 | { |
---|
717 | struct ambapp_priv *priv = bus->priv; |
---|
718 | struct ambapp_bus *abus; |
---|
719 | struct ambapp_dev_reg_struct arg; |
---|
720 | |
---|
721 | DBG("ambapp_ids_register:\n"); |
---|
722 | |
---|
723 | memset(&arg, 0, sizeof(arg)); |
---|
724 | |
---|
725 | abus = priv->config->abus; |
---|
726 | arg.abus = abus; |
---|
727 | arg.bus = bus; |
---|
728 | |
---|
729 | /* Combine the AHB MST, AHB SLV and APB SLV interfaces of a core. A core has often more than |
---|
730 | * one interface. A core can not have more than one interface of the same type. |
---|
731 | */ |
---|
732 | ambapp_for_each(abus, (OPTIONS_ALL_DEVS|OPTIONS_FREE), -1, -1, ambapp_dev_register, &arg); |
---|
733 | |
---|
734 | #ifdef DEBUG |
---|
735 | ambapp_print(abus->root, 1); |
---|
736 | #endif |
---|
737 | |
---|
738 | return DRVMGR_OK; |
---|
739 | } |
---|
740 | |
---|
741 | /*** DEVICE FUNCTIONS ***/ |
---|
742 | |
---|
743 | int ambapp_bus_register(struct drvmgr_dev *dev, struct ambapp_config *config) |
---|
744 | { |
---|
745 | struct ambapp_priv *priv; |
---|
746 | |
---|
747 | if ( !config || !config->ops ) |
---|
748 | return DRVMGR_OK; |
---|
749 | |
---|
750 | DBG("AMBAPP BUS: initializing\n"); |
---|
751 | |
---|
752 | /* Register BUS */ |
---|
753 | drvmgr_alloc_bus(&dev->bus, sizeof(struct ambapp_priv)); |
---|
754 | priv = (struct ambapp_priv *)(dev->bus + 1); |
---|
755 | priv->config = config; |
---|
756 | if ( priv->config->bus_type == DRVMGR_BUS_TYPE_AMBAPP_DIST ) |
---|
757 | dev->bus->bus_type = DRVMGR_BUS_TYPE_AMBAPP_DIST; |
---|
758 | else if ( priv->config->bus_type == DRVMGR_BUS_TYPE_AMBAPP_RMAP ) |
---|
759 | dev->bus->bus_type = DRVMGR_BUS_TYPE_AMBAPP_RMAP; |
---|
760 | else |
---|
761 | dev->bus->bus_type = DRVMGR_BUS_TYPE_AMBAPP; |
---|
762 | dev->bus->next = NULL; |
---|
763 | dev->bus->dev = dev; |
---|
764 | dev->bus->priv = priv; |
---|
765 | dev->bus->children = NULL; |
---|
766 | dev->bus->ops = &ambapp_bus_ops; |
---|
767 | dev->bus->funcs = config->funcs; |
---|
768 | dev->bus->dev_cnt = 0; |
---|
769 | dev->bus->reslist = NULL; |
---|
770 | dev->bus->maps_up = config->maps_up; |
---|
771 | dev->bus->maps_down = config->maps_down; |
---|
772 | |
---|
773 | /* Add resource configuration */ |
---|
774 | if ( priv->config->resources ) |
---|
775 | drvmgr_bus_res_add(dev->bus, priv->config->resources); |
---|
776 | |
---|
777 | drvmgr_bus_register(dev->bus); |
---|
778 | |
---|
779 | return DRVMGR_OK; |
---|
780 | } |
---|
781 | |
---|
782 | /*** BUS INITIALIZE FUNCTIONS ***/ |
---|
783 | |
---|
784 | /* Initialize the bus, register devices on this bus */ |
---|
785 | int ambapp_bus_init1(struct drvmgr_bus *bus) |
---|
786 | { |
---|
787 | /* Initialize the bus, register devices on this bus */ |
---|
788 | return ambapp_ids_register(bus); |
---|
789 | } |
---|
790 | |
---|
791 | int ambapp_bus_remove(struct drvmgr_bus *bus) |
---|
792 | { |
---|
793 | return DRVMGR_OK; |
---|
794 | } |
---|
795 | |
---|
796 | #ifdef RTEMS_SMP |
---|
797 | int ambapp_int_set_affinity( |
---|
798 | struct drvmgr_dev *dev, |
---|
799 | int index, |
---|
800 | const Processor_mask *cpus) |
---|
801 | { |
---|
802 | struct ambapp_priv *priv; |
---|
803 | int irq; |
---|
804 | |
---|
805 | priv = dev->parent->priv; |
---|
806 | |
---|
807 | /* Get IRQ number from index and device information */ |
---|
808 | irq = ambapp_int_get(dev, index); |
---|
809 | if (irq < 0) |
---|
810 | return DRVMGR_EINVAL; |
---|
811 | |
---|
812 | DBG("Set interrupt affinity on 0x%x for dev 0x%x (IRQ: %d)\n", |
---|
813 | (unsigned int)dev->parent->dev, (unsigned int)dev, irq); |
---|
814 | |
---|
815 | if (priv->config->ops->int_set_affinity) { |
---|
816 | /* Let device override driver default */ |
---|
817 | return priv->config->ops->int_set_affinity(dev, irq, cpus); |
---|
818 | } else { |
---|
819 | return DRVMGR_ENOSYS; |
---|
820 | } |
---|
821 | } |
---|
822 | #endif |
---|