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