summaryrefslogtreecommitdiff
path: root/arch/sparc/cpu/leon3/ambapp_low.S
blob: 2863586dc1b97be39447a52f456fca8409aaf9fe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
/* GRLIB AMBA Plug&Play information scanning implemented without
 * using memory (stack) and one register window. The code scan
 * the PnP info and inserts the AHB bridges/buses into register
 * i0-i5.
 * The code support
 *  - up to 6 AHB buses
 *  - multiple APB buses
 *  - support for AHB2AHB & L2CACHE bridges
 *
 * (C) Copyright 2010, 2015
 * Daniel Hellstrom, Cobham Gaisler, daniel@gaisler.com.
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

#include <ambapp.h>

	.seg	"text"
	.globl	_nomem_amba_init
	.globl	_nomem_ambapp_find_buses
	.globl	_nomem_find_apb
	.globl	_nomem_find_ahb

/* Overview
 * ========
 *
 * _nomem_amba_init         - Init AMBA bus and calls _nomem_ambapp_find_buses
 * _nomem_ambapp_find_buses - Scan AMBA PnP info for AHB buses/bridges and
 *                            place them in i0-i5, see below
 * _nomem_find_apb          - Find one APB device identified by VENDOR:DEVICE
 *                            ID and an index.
 * _nomem_find_ahb          - Find one AHB Master or Slave device identified
 *                            by VENDOR:DEVICE ID and an index.
 * init_ahb_bridges         - Local function. Clears i0-i5
 * insert_ahb_bridge        - Local function. Insert a new AHB bus into first
 *                            free register in i0-i5. It also checks that the
 *                            bus has not already been added.
 * get_ahb_bridge           - Local function. Get AHB bus from registers,
 *                            return register iN, where N is defined by o0.
 *
 * The _nomem_find_apb and _nomem_find_ahb function requires that i0-i5
 * are populated with the AHB buses of the system. The registers are
 * initialized by _nomem_ambapp_find_buses.
 *
 * AHB Bus result and requirements of i0-i5
 * ========================================
 *
 * i0: AHB BUS0 IOAREA, no parent bus
 * i1: AHB BUS1 IOAREA, parent bus is always i0 (AHB BUS0) and bridge address
 * i2: AHB BUS2 IOAREA, 3-bit parent bus number and bridge address
 * i3: AHB BUS3 IOAREA, 3-bit parent bus number and bridge address
 * i4: AHB BUS4 IOAREA, 3-bit parent bus number and bridge address
 * i5: AHB BUS5 IOAREA, 3-bit parent bus number and bridge address
 *
 * AHB BUS
 * -------
 * Bits 31-20 (0xfff00000) contain the found bus I/O Area (AHB PnP area).
 *
 * 3-bit Parent bus
 * ----------------
 * Bits 2-0 (0x00000007) contain parent bus number. Zero if no parent
 * bus, 1 = parent is AHB BUS 0 (i0), 2 = parent is AHB BUS 1 (i1)..
 *
 * Bridge Address
 * --------------
 * Bits 10-5 (0x000007e0) contain the index of the Bridge's PnP
 * information on the parent. Since all bridges are found in the
 * PnP information they all have a PnP entry. Together with the
 * parent bus number the PnP entry can be found:
 *  PnPEntry = (BRIDGE_ADDRESS + (iN & 0xfff00000)) | 0x000ff800
 *  where N is the parent bus minus one.
 *
 */

/* Function initializes the AHB Bridge I/O AREA storage. (Clears i0-i5)
 *
 * Arguments
 *  none
 *
 * Results
 *  none
 *
 * Clobbered
 *  none
 */

init_ahb_bridges:
	mov	%g0, %i0
	mov	%g0, %i1
	mov	%g0, %i2
	mov	%g0, %i3
	mov	%g0, %i4
	retl
	 mov	%g0, %i5

/* Function returns AHB Bridge I/O AREA for specified bus.
 *
 * Arguments
 *  - o0 = bus number
 *
 * Results
 *  - o0 = I/O AREA
 *
 * Clobbered
 *  none
 */
get_ahb_bridge:
	cmp	%o0, 1
	be,a	L1
	 mov	%i0, %o0

	cmp	%o0, 2
	be,a	L1
	 mov	%i1, %o0

	cmp	%o0, 3
	be,a	L1
	 mov	%i2, %o0

	cmp	%o0, 4
	be,a	L1
	 mov	%i3, %o0

	cmp	%o0, 5
	be,a	L1
	 mov	%i4, %o0

	cmp	%o0, 6
	be,a	L1
	 mov	%i5, %o0

	/* o0 > 6: only 6 buses supported */
	mov	%g0, %o0
L1:
	retl
	 nop

/* Function adds a AHB Bridge I/O AREA to the i0-i5 registers if
 * not already added. It stores the bus PnP start information.
 *
 * Arguments
 *  - o0 = AHB Bridge I/O area
 *
 * Results
 *  none
 *
 * Clobbered
 *  o2, o3
 */
insert_ahb_bridge:
	/* Check that bridge hasn't already been added */
	andn	%o0, 0x7ff, %o2
	andn	%i0, 0x7ff, %o3
	cmp	%o3, %o2
	be	L2
	 andn	%i1, 0x7ff, %o3
	cmp	%o3, %o2
	be	L2
	 andn	%i2, 0x7ff, %o3
	cmp	%o3, %o2
	be	L2
	 andn	%i3, 0x7ff, %o3
	cmp	%o3, %o2
	be	L2
	 andn	%i4, 0x7ff, %o3
	cmp	%o3, %o2
	be	L2
	 andn	%i5, 0x7ff, %o3
	cmp	%o3, %o2
	be	L2

	/* Insert into first free posistion */
	 cmp	%i0, %g0
	be,a	L2
	 mov	%o0, %i0

	cmp	%i1, %g0
	be,a	L2
	 mov	%o0, %i1

	cmp	%i2, %g0
	be,a	L2
	 mov	%o0, %i2

	cmp	%i3, %g0
	be,a	L2
	 mov	%o0, %i3

	cmp	%i4, %g0
	be,a	L2
	 mov	%o0, %i4

	cmp	%i5, %g0
	be,a	L2
	 mov	%o0, %i5
L2:
	retl
	 nop

/* FUNCTION int _nomem_find_ahb_bus(
 *	unsigned int bridge,
 *	int vendor_device,
 *	int index,
 *	void **pconf,
 *	int not_used,
 *	int option
 *	)
 *
 * Scans the AHB Master or Slave area for a matching VENDOR:DEVICE, the
 * index is decremented when a matching device is found but index is
 * greater than zero. When index is zero and a matching DEVICE:VENDOR
 * is found the AHB configuration address and AHB I/O area is returned.
 *
 * i0-i7,l0,l1,l2,l3,l4,g2,o6 is not available for use.
 * o1,o5 Must be left untouched
 *
 * Results
 *  - o0 Number of found devices (1 or 0)
 *  - o2 is decremented for each matching VENDOR:DEVICE found, zero if found
 *  - o3 Address of the AHB PnP configuration entry (Only valid if o0=1)
 *
 * Clobbered
 *  - o3 (Clobbered when no device was found)
 *  - o4 (Number of Devices left to search)
 *  - o0 (Bus ID, PnP ID, Device)
 */
_nomem_find_ahb_bus:

	/* Get the number of Slaves/Masters.
	 * Only AHB Bus 0 has 64 AHB Masters/Slaves the
	 * other AHB buses has 16 slaves and 16 masters.
	 */
	add	%g0, 16, %o4		/* Defaulting to 16 */
	andcc	%o0, 0x7, %g0		/* 3-bit bus id */
	be,a	.L_maxloops_detected
	 add	%g0, 64, %o4		/* AHB Bus 0 has 64 AHB Masters/Slaves */
.L_maxloops_detected:

	/* Get start address of AHB Slave or AHB Master area depending on what
	 * we are searching for.
	 */
	andn	%o0, 0x7ff, %o0		/* Remove Bus ID and 5-bit AHB/AHB
					 * Bridge PnP Address to get I/O Area */
	set	AMBA_CONF_AREA,	%o3
	or	%o3, %o0, %o3		/* Master area address */

	cmp	%o5, DEV_AHB_SLV
	be,a	.L_conf_area_calculated
	 or	%o3, AMBA_AHB_SLAVE_CONF_AREA, %o3	/* Add 0x800 to get to slave area */
.L_conf_area_calculated:

	/* Iterate over all AHB device and try to find matching DEVICE:VENDOR
	 * o1 - VENDOR|DEVICE
	 * o2 - Index
	 * o3 - Current AHB Device Configuration address
	 * o5 - Type (leave untouched)
	 *
	 * o4 - Number of AHB device left to process
	 * o0 - tmp
	 */
.L_process_one_conf:
	ld	[%o3], %o0
	andn	%o0, 0xfff, %o0
	cmp	%o0, 0			/* No device if zero */
	beq	.L_next_conf
	 cmp	%o1, 0			/* If VENDOR:DEVICE==0, consider all matching */
	beq	.L_process_ahb_dev_found
	 cmp	%o0, %o1		/* Does VENDOR and DEVICE Match? */
	bne	.L_next_conf
	 nop
.L_process_ahb_dev_found:
	/* Found a Matching VENDOR:DEVICE, index must also match */
	cmp	%o2, %g0
	bne	.L_next_conf
	 dec	%o2
	/* Index matches also, return happy with o3 set to AHB Conf Address */
	mov	%g0, %o2
	retl
	 add	%g0, 1, %o0

.L_next_conf:
	subcc	%o4, 1, %o4		/* One device has been processed,
					 * Are there more devices to process? */
	bne	.L_process_one_conf
	 add	%o3, AMBA_AHB_CONF_LENGH, %o3	/* Next Configuration entry */
	/* No Matching device found */
	retl
	 mov	%g0, %o0

/* FUNCTION int _nomem_find_ahb(
 *      int unused,
 *	int vendor_device,
 *	int index,
 *	void **pconf,
 *	int *ahb_bus_index,
 *	int option,
 *	)
 *
 * Find a AHB Master or AHB Slave device, it puts the address of the AHB PnP
 * configuration in o3 (pconf), the I/O Area base address in o4 (pioarea).
 *
 * Calls _nomem_find_ahb_bus for every AHB bus.
 *
 * i0-i7, l0, l1, o6, g1, g4-g7 is not available for use.
 *
 * Arguments
 *  - o0 Unused
 *
 * Results
 *  - o0 Number of found devices (1 or 0)
 *  - o2 Decremented Index (Zero if found)
 *  - o3 Address of the AHB PnP configuration entry
 *  - o4 AHB Bus index the device was found on (if o0=1)
 *  - o5 Left untouched
 *
 * Clobbered
 *  - o0 (AHB Bridge and used by _nomem_find_ahb_bus)
 *  - o2 (index is decremented)
 *  - l2 (Current AHB Bus index)
 *  - g2 (return address)
 */
_nomem_find_ahb:
	mov	%o7, %g2		/* Save return address */
	/* Scan all AHB Buses found for the AHB Master/Slave matching VENDOR:DEVICE */
	clr	%l2
.L_search_next_ahb_bus:
	add	%l2, 1, %l2
	call	get_ahb_bridge			/* Get bus %l0 I/O Area */
	 mov	%l2, %o0
	cmp	%o0, %g0
	be	.L_no_device_found		/* If no more AHB bus is left to be scanned, proceed */
	 nop
	call	_nomem_find_ahb_bus		/* Scan AHB bus %o0 for VENDOR:DEVICE. Index in o3 is decremented  */
	 nop
	cmp	%o0, %g0			/* If VENDOR:DEVICE was not found scan next AHB Bus */
	be	.L_search_next_ahb_bus		/* Do next bus is o0=0 (not found) */
	 nop
	/* The device was found, o0 is 1 */
	mov	%g2, %o7		/* Restore return address */
	retl
	 mov	%l2, %o4		/* The AHB bus index the device was found on */

	/* No device found matching */
.L_no_device_found:
	mov	%g2, %o7		/* Restore return address */
	retl
	 mov	%g0, %o0


/* FUNCTION int _nomem_find_apb_bus(
 *      int apbmst,
 *	int vendor_device,
 *	int index,
 *	void **pconf
 *	)
 *
 * Find a APB Slave device, it puts the address of the APB PnP configuration
 * in o3 (pconf).
 *
 * Calls _nomem_find_ahb_bus for every AHB bus searching for AHB/APB Bridges.
 * The AHB/APB bridges are AHB Slaves with ID GAISLER_APBMST.
 *
 * Results
 *  - o0 Number of found devices (1 or 0)
 *  - o2 Decremented Index
 *  - o3 Address of the found APB device PnP configuration entry
 *
 * Clobbered
 *  - o5 PnP VENDOR:DEVICE ID
 */

_nomem_find_apb_bus:
	set	AMBA_CONF_AREA, %o3
	or	%o0, %o3, %o3		/* Calc start of APB device PnP info */
	add	%g0, 16, %o0		/* o0, number of APB Slaves left to scan */
.L_process_one_apb_conf:
	ld	[%o3], %o5
	andn	%o5, 0xfff, %o5
	cmp	%o5, 0			/* No device if zero */
	beq	.L_process_apb_dev_not_found
	 cmp	%o1, 0			/* If VENDOR:DEVICE == -1, consider all matching */
	beq	.L_process_apb_dev_found
	 cmp	%o1, %o5		/* Found VENDOR:DEVICE */
	bne	.L_process_apb_dev_not_found
	 nop

.L_process_apb_dev_found:
	/* Found matching device, compare index */
	cmp	%o2, %g0
	bne	.L_process_apb_dev_not_found
	 dec	%o2
	/* Matching index and VENDOR:DEVICE */
	retl
	 add	%g0, 1, %o0

.L_process_apb_dev_not_found:
	subcc	%o0, 1, %o0
	bne	.L_process_one_apb_conf
	 add	%o3, 8, %o3
	retl
	 mov	%g0, %o0

/* FUNCTION int _nomem_find_apb(
 *      int unused,
 *	int vendor_device,
 *	int index,
 *	void **pconf,
 *	int *ahb_bus_index
 *	)
 *
 * Find a APB Slave device, it puts the address of the APB PnP configuration
 * in o3 (pconf), the APB Master I/O Area base address in o4 (papbarea).
 *
 * Calls _nomem_find_ahb_bus for every AHB bus searching for AHB/APB Bridges.
 * The AHB/APB bridges are AHB Slaves with ID GAISLER_APBMST.
 *
 * i0-i7, l0, l1, o6 is not available for use.
 *
 * Arguments
 *  - o0 Unused
 *
 * Results
 *  - o0 Number of found devices (1 or 0)
 *  - o2 Decremented Index if not found
 *  - o3 Address of the APB PnP configuration entry
 *  - o4 AHB Bus index of APB Bridge/APB Device
 *
 * Clobbered
 *  - o0 (AHB Bridge)
 *  - o2 (index is decremented)
 *  - l2 (APB DEV Index [7..4] : APBMST AHB Index [3..0])
 *  - l3 (Current AHB Bus index)
 *  - l4 (temporary storage for APB VENDOR:DEVICE)
 *  - o5 (AHB Slave ID)
 *  - o0 (clobbered by _nomem_find_ahb_bus)
 *  - g2 (Return address)
 */
_nomem_find_apb:
	/* Scan all AHB Buses found for AHB/APB Bridges */
	mov	%o7, %g2		/* Save return address */
	mov	%o1, %l4		/* Save APB VENDOR:DEVICE */
	sll	%o2, 4, %l2		/* APB MST index = 0 */
	add	%g0, 1, %l3		/* AHB Bus index = 0 */
.L2_search_next_ahb_bus:
	call	get_ahb_bridge		/* Get bus %l3 I/O Area */
	 mov	%l3, %o0
	cmp	%o0, %g0
	be	.L2_no_device_found	/* If no more AHB bus is left to be scanned, proceed */
	 add	%g0, DEV_AHB_SLV, %o5	/* Search for AHB Slave */
	sethi	%hi(AMBA_PNP_ID(VENDOR_GAISLER, GAISLER_APBMST)), %o1
	call	_nomem_find_ahb_bus	/* Scan AHB bus %o0 for VENDOR:DEVICE. Index in o3 is decremented */
	 and	%l2, 0xf, %o2		/* Set APBMST index */
	cmp	%o0, %g0		/* If no AHB/APB Bridge was not found, scan next AHB Bus */
	be	.L_no_apb_bridge_found	/* Do next bus */
	 nop

	/* The AHB/APB Bridge was found.
         * Search for the requested APB Device on the APB bus using
	 * find_apb_bus, it will decrement the index.
         */
	ld	[%o3 + AMBA_AHB_MBAR0_OFS], %o3
	sll	%o3, 16, %o0
	and	%o0, %o3, %o0		/* Address AND Address Mask */
	sethi	%hi(0xfff00000), %o3
	and	%o0, %o3, %o0		/* AHB/APB Bridge address */

	srl	%l2, 4, %o2		/* APB DEV Index */
	call	_nomem_find_apb_bus
	 mov	%l4, %o1		/* APB VENDOR:DEVICE */
	cmp	%o0, %g0
	be	.L_apb_dev_not_found
	 mov	%g2, %o7		/* Restore return address */
	/* APB Device found
	 * o0 1
	 * o2 Index is decremented to zero
	 * o3 APB configuration address,
	 * o4 APB Bridge Configuration address.
	 */
	mov	%g0, %o2
	retl
	 mov	%l3, %o4

.L_apb_dev_not_found:
	/* Update APB DEV Index by saving output from find_apb_bus
	 * (index parameter) into bits [31..4] in L2.
	 */
	sll	%o2, 4, %o2
	and	%l2, 0xf, %l2
	or	%o2, %l2, %l2
	/* Try finding the next AHB/APB Bridge on the same AHB bus
	 * to find more APB devices
	 */
	ba	.L2_search_next_ahb_bus	/* Find next AHB/APB bridge */
	 inc	%l2

.L_no_apb_bridge_found:
	inc	%l3			/* Next AHB Bus */
	ba	.L2_search_next_ahb_bus	/* Process next AHB bus */
	 andn	%l2, 0xf, %l2		/* Start at APB Bridge index 0 at every AHB Bus */
	/* No device found matching */
.L2_no_device_found:
	mov	%g2, %o7		/* Restore return address */
	srl	%l2, 4, %o2		/* APB DEV Index */
	retl
	 mov	%g0, %o0



/* FUNCTION _nomem_amba_scan_gaisler_ahb2ahb_bridge(unsigned int bridge, int bus)
 *
 * Constraints:
 *   - o1 may not be used
 *   - o0, o2, o3 may be used.
 *
 * Arguments
 *  - o0 PnP Address of Bridge AHB device
 *  - o2 PnP ID of AHB device
 *
 * Results
 *  - o0 Address of new bus PnP area or a 1 if AHB device is no bridge
 *
 * Clobbered
 *   - o0, o2
 *
 */
_nomem_amba_scan_gaisler_ahb2ahb_bridge:
	andn	%o2, 0xfff, %o2
	sethi	%hi(AMBA_PNP_ID(VENDOR_GAISLER,GAISLER_AHB2AHB)), %o3
	cmp	%o2, %o3
	beq	.L_is_ahb2ahb_bridge
	 nop

	retl
	 add	%g0, 1, %o0

.L_is_ahb2ahb_bridge:
	/* Found a GAISLER AHB2AHB bridge */
	retl
	 ld	[%o0 + AMBA_AHB_CUSTOM1_OFS], %o0 /* Get address of bridge PnP area */


/* FUNCTION _nomem_amba_scan_gaisler_l2cache_bridge(unsigned int bridge, int bus)
 *
 * Constraints:
 *   - o1 may not be used
 *   - o0, o2, o3 may be used.
 *
 * Arguments
 *  - o0 PnP Address of Bridge AHB device
 *  - o2 PnP ID of AHB device
 *
 * Results
 *  - o0 Address of new bus PnP area or a 1 if AHB device is no bridge
 *
 * Clobbered
 *   - o0, o2
 *
 */
_nomem_amba_scan_gaisler_l2cache_bridge:
	andn	%o2, 0xfff, %o2
	sethi	%hi(AMBA_PNP_ID(VENDOR_GAISLER,GAISLER_L2CACHE)), %o3
	cmp	%o2, %o3
	beq	.L_is_l2cache_bridge
	 nop

	retl
	 add	%g0, 1, %o0

.L_is_l2cache_bridge:
	/* Found a GAISLER l2cache bridge */
	retl
	 ld	[%o0 + AMBA_AHB_CUSTOM1_OFS], %o0 /* Get address of bridge PnP area */


/* FUNCTION _nomem_amba_scan(unsigned int bridge, int bus)
 *
 * Constraints:
 *  i0-i7, l0 is used by caller
 *  o5-o7 may not be used.
 *
 * Arguments
 *  - o0 Bridge Information: I/O AREA and parent bus
 *  - o1 Bus
 *
 * Results
 *  - o0 Number of AHB bridges found
 *
 * Clobbered
 *  - o0 (Current AHB slave conf address)
 *  - o2 (Used by insert_bridge)
 *  - o3 (Used by insert_bridge)
 *  - l1 (Number of AHB Slaves left to process)
 *  - l2 (Current AHB slave conf address)
 *  - g2 (Return address)
 */
_nomem_amba_scan:
	mov	%o7, %g2	/* Save return address */
	set	16, %l1
	cmp	%o1, 1
	be,a	.L2_maxloops_detected
	 add	%g0, 64, %l1
.L2_maxloops_detected:

	/* Clear 3-bit parent bus from bridge to get I/O AREA, then or
	 * (AMBA_CONF_AREA | AMBA_AHB_SLAVE_CONF_AREA) to get first AHB slave
	 * conf address.
	 */
	andn	%o0, 0x7ff, %o0
	set	(AMBA_CONF_AREA | AMBA_AHB_SLAVE_CONF_AREA), %l2
	or	%o0, %l2, %l2

	/* Scan AHB Slave area for AHB<->AHB bridges. For each AHB device
	 * all "bridge drivers" are called, the driver function interface:
	 *
	 * Input:
	 *   - o0 PnP Address of Bridge AHB device
	 *   - o2 PnP ID of AHB device
	 * Return values:
	 *   - o0 Address of new bus PnP area, returning a 1 in o2 means not found
	 *
	 * Constraints:
	 *   - o1 may not be used
	 *   - o0, o2, o3 may be used.
	 *
	 */
.L_scan_one_ahb_slave:
	ld	[%l2], %o2

	cmp	%o2, %g0
	beq	.L_scan_next_ahb_slave
	 nop

	/* Call the GAISLER AHB2AHB bridge driver */
	call	_nomem_amba_scan_gaisler_ahb2ahb_bridge
	 mov	%l2, %o0
	cmp	%o0, 1
	bne	.L_found_bridge
	 ld	[%l2], %o2

	/* Call the GAISLER L2CACHE bridge driver */
	call	_nomem_amba_scan_gaisler_l2cache_bridge
	 mov	%l2, %o0
	cmp	%o0, 1
	bne	.L_found_bridge
	 ld	[%l2], %o2

	/* Insert next bridge "driver" function here */


	/* The PnP ID did not match a bridge - a new bus was not found ==>
	 * step to next AHB device */
	ba	.L_scan_next_ahb_slave
	 nop

	/* Add Found bus */
.L_found_bridge:
	and	%l2, 0x7e0, %o2
	or	%o2, %o0, %o0		/* Add AHB/AHB Bridge PnP address */
	call	insert_ahb_bridge	/* Insert Bridge into found buses storage */
	 or	%o1, %o0, %o0		/* Add parent bus LSB 3-bits */

.L_scan_next_ahb_slave:
	/* More Slaves to process? */
	subcc	%l1, 1, %l1
	bne	.L_scan_one_ahb_slave
	 add	%l2, AMBA_AHB_CONF_LENGH, %l2

	/* No more AHB devices to process */
	mov	%g2, %o7	/* Restore return address */
	retl
	 nop

/* FUNCTION _nomem_ambapp_find_buses(unsigned int ioarea)
 *
 * Find AMBA AHB buses.
 *
 * Constraints:
 *  i6-i7, l7 is used by caller
 *
 * Arguments
 *  - o0 Bridge Information: I/O AREA and parent bus
 *
 * Results
 *  - o0 Number of AHB bridges found
 *  - i0-i5 initialized
 *
 * Clobbered
 *  - o0 (Current AHB slave conf address)
 *  - o2 (Used by insert_bridge)
 *  - o3 (Used by insert_bridge)
 *  - l0 (Current AHB Bus)
 *  - l1 (Used by nomem_amba_scan)
 *  - l2 (Used by nomem_amba_scan)
 *  - l3 (Used by nomem_amba_scan)
 *  - l4 (Used by nomem_amba_scan)
 *
 *  - g1 (level 1 return address)
 *  - g2 (Used by nomem_amba_scan)
 */
_nomem_ambapp_find_buses:
	mov	%o7, %g1	/* Save return address */

	/* Initialize AHB Bus storage */
	call	init_ahb_bridges
	 nop

	/* Insert AHB Bus 0 */
	call	insert_ahb_bridge
	 nop			/* Argument already prepared by caller */

	/* Scan AHB Bus 0 for AHB Bridges */
	call	_nomem_amba_scan
	 add	%g0, 1, %o1

	/* Scan all AHB Buses found for more AHB Bridges */
	add	%g0, 2, %l0
.L100_search_next_ahb_bus:
	call	get_ahb_bridge			/* Get bus %l0 I/O Area */
	 mov	%l0, %o0
	cmp	%o0, %g0
	be	.L100_return			/* If no more AHB bus is left to be scanned, proceed */
	 nop
	call	_nomem_amba_scan		/* Scan bus %l0 for AHB Bridges. i0-i7,l0 is used */
	 mov	%l0, %o1			/* I/O AREA untouched in o0 */
	ba	.L100_search_next_ahb_bus	/* Do next bus */
	 add	%l0, 1, %l0

.L100_return:
	mov	%g1, %o7
	retl
	 nop


/* FUNCTION _nomem_amba_init(unsigned int ioarea)
 *
 *  Find all AHB buses
 *
 * Constraints:
 *  i6, i7, o6, o7, l7, l6, g3, g4, g5, g6, g7 is used by caller
 *
 * Arguments
 *  - o0 Bridge Information: I/O AREA and parent bus
 *
 * Results
 *  - o0 Number of AHB bridges found
 *
 * Clobbered
 *  - l0, l1, l2, l3, l4, g1, g2 (used by _nomem_ambapp_find_buses)
 *  - o0, o1, o2, o3 (Used as arguments)
 *  - o5 (return address)
 *  - g1 (level 1 return address)
 *  - g2 (level 2 return address)
 */
_nomem_amba_init:
	mov	%o7, %o5	/* Save return address, o5 not used */

	/* Scan for buses, it will init i0-i5 */
	call	_nomem_ambapp_find_buses
	 nop

	mov	%o5, %o7
	retl
	 nop

/* Call tree and their return address register
 *
 *_nomem_amba_scan           (g1)
 * -> init_ahb_bridges       (o7)
 * -> insert_ahb_bridge      (o7)
 * -> _nomem_amba_scan       (g2)
 *    -> insert_ahb_bridge   (o7)
 * -> get_ahb_bridge         (o7)
 *
 *
 * -> _nomem_find_apb        (g2)
 *    -> get_ahb_bridge      (o7)
 *    -> _nomem_find_ahb_bus (o7)
 *    -> _nomem_find_apb_bus (o7)
 * -> _nomem_find_ahb        (g2)
 *    -> get_ahb_bridge      (o7)
 *    -> _nomem_find_ahb_bus (o7)
 * -> mem_handler.func()     (o7)
 *
 */