aboutsummaryrefslogtreecommitdiff
path: root/drivers/marvell/mci.c
blob: 06fe88e13adc2ac2a70b4bf71662578958695124 (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
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
/*
 * Copyright (C) 2018 Marvell International Ltd.
 *
 * SPDX-License-Identifier:     BSD-3-Clause
 * https://spdx.org/licenses
 */

/* MCI bus driver for Marvell ARMADA 8K and 8K+ SoCs */

#include <common/debug.h>
#include <drivers/delay_timer.h>
#include <drivers/marvell/mci.h>
#include <lib/mmio.h>

#include <mvebu.h>
#include <mvebu_def.h>
#include <plat_marvell.h>

/* /HB /Units /Direct_regs /Direct regs
 * /Configuration Register Write/Read Data Register
 */
#define MCI_WRITE_READ_DATA_REG(mci_index)	\
					MVEBU_MCI_REG_BASE_REMAP(mci_index)
/* /HB /Units /Direct_regs /Direct regs
 * /Configuration Register Access Command Register
 */
#define MCI_ACCESS_CMD_REG(mci_index)		\
				(MVEBU_MCI_REG_BASE_REMAP(mci_index) + 0x4)

/* Access Command fields :
 * bit[3:0]   - Sub command: 1 => Peripheral Config Register Read,
 *			     0 => Peripheral Config Register Write,
 *			     2 => Peripheral Assign ID request,
 *			     3 => Circular Config Write
 * bit[5]     - 1 => Local (same chip access) 0 => Remote
 * bit[15:8]  - Destination hop ID. Put Global ID (GID) here (see scheme below).
 * bit[23:22] - 0x3 IHB PHY REG address space, 0x0 IHB Controller space
 * bit[21:16] - Low 6 bits of offset. Hight 2 bits are taken from bit[28:27]
 *		of IHB_PHY_CTRL
 *		(must be set before any PHY register access occurs):
 *		/IHB_REG /IHB_REGInterchip Hopping Bus Registers
 *		/IHB Version Control Register
 *
 *		ixi_ihb_top		IHB PHY
 *  AXI -----------------------------   -------------
 *   <--| axi_hb_top | ihb_pipe_top |-->|           |
 *   -->|   GID=1    |     GID=0    |<--|           |
 *      -----------------------------   -------------
 */
#define MCI_INDIRECT_CTRL_READ_CMD		0x1
#define MCI_INDIRECT_CTRL_ASSIGN_CMD		0x2
#define MCI_INDIRECT_CTRL_CIRCULAR_CMD		0x3
#define MCI_INDIRECT_CTRL_LOCAL_PKT		(1 << 5)
#define MCI_INDIRECT_CTRL_CMD_DONE_OFFSET	6
#define MCI_INDIRECT_CTRL_CMD_DONE		\
				(1 << MCI_INDIRECT_CTRL_CMD_DONE_OFFSET)
#define MCI_INDIRECT_CTRL_DATA_READY_OFFSET	7
#define MCI_INDIRECT_CTRL_DATA_READY		\
				(1 << MCI_INDIRECT_CTRL_DATA_READY_OFFSET)
#define MCI_INDIRECT_CTRL_HOPID_OFFSET		8
#define MCI_INDIRECT_CTRL_HOPID(id)		\
			(((id) & 0xFF) << MCI_INDIRECT_CTRL_HOPID_OFFSET)
#define MCI_INDIRECT_CTRL_REG_CHIPID_OFFSET	16
#define MCI_INDIRECT_REG_CTRL_ADDR(reg_num)	\
			(reg_num << MCI_INDIRECT_CTRL_REG_CHIPID_OFFSET)

/* Hop ID values */
#define GID_IHB_PIPE					0
#define GID_AXI_HB					1
#define GID_IHB_EXT					2

#define MCI_DID_GLOBAL_ASSIGNMENT_REQUEST_REG		0x2
/* Target MCi Local ID (LID, which is = self DID) */
#define MCI_DID_GLOBAL_ASSIGN_REQ_MCI_LOCAL_ID(val)	(((val) & 0xFF) << 16)
/* Bits [15:8]: Number of MCis on chip of target MCi */
#define MCI_DID_GLOBAL_ASSIGN_REQ_MCI_COUNT(val)	(((val) & 0xFF) << 8)
/* Bits [7:0]: Number of hops on chip of target MCi */
#define MCI_DID_GLOBAL_ASSIGN_REQ_HOPS_NUM(val)		(((val) & 0xFF) << 0)

/* IHB_REG domain registers */
/* /HB /Units /IHB_REG /IHB_REGInterchip Hopping Bus Registers/
 * Rx Memory Configuration Register (RX_MEM_CFG)
 */
#define MCI_CTRL_RX_MEM_CFG_REG_NUM			0x0
#define MCI_CTRL_RX_TX_MEM_CFG_RQ_THRESH(val)		(((val) & 0xFF) << 24)
#define MCI_CTRL_RX_TX_MEM_CFG_PQ_THRESH(val)		(((val) & 0xFF) << 16)
#define MCI_CTRL_RX_TX_MEM_CFG_NQ_THRESH(val)		(((val) & 0xFF) << 8)
#define MCI_CTRL_RX_TX_MEM_CFG_DELTA_THRESH(val)	(((val) & 0xF) << 4)
#define MCI_CTRL_RX_TX_MEM_CFG_RTC(val)			(((val) & 0x3) << 2)
#define MCI_CTRL_RX_TX_MEM_CFG_WTC(val)			(((val) & 0x3) << 0)
#define MCI_CTRL_RX_MEM_CFG_REG_DEF_CP_VAL		\
				(MCI_CTRL_RX_TX_MEM_CFG_RQ_THRESH(0x07) | \
				MCI_CTRL_RX_TX_MEM_CFG_PQ_THRESH(0x3f) | \
				MCI_CTRL_RX_TX_MEM_CFG_NQ_THRESH(0x3f) | \
				MCI_CTRL_RX_TX_MEM_CFG_DELTA_THRESH(0xf) | \
				MCI_CTRL_RX_TX_MEM_CFG_RTC(1) | \
				MCI_CTRL_RX_TX_MEM_CFG_WTC(1))

#define MCI_CTRL_RX_MEM_CFG_REG_DEF_AP_VAL		\
				(MCI_CTRL_RX_TX_MEM_CFG_RQ_THRESH(0x3f) | \
				MCI_CTRL_RX_TX_MEM_CFG_PQ_THRESH(0x03) | \
				MCI_CTRL_RX_TX_MEM_CFG_NQ_THRESH(0x3f) | \
				MCI_CTRL_RX_TX_MEM_CFG_DELTA_THRESH(0xf) | \
				MCI_CTRL_RX_TX_MEM_CFG_RTC(1) | \
				MCI_CTRL_RX_TX_MEM_CFG_WTC(1))


/* /HB /Units /IHB_REG /IHB_REGInterchip Hopping Bus Registers/
 * Tx Memory Configuration Register (TX_MEM_CFG)
 */
#define MCI_CTRL_TX_MEM_CFG_REG_NUM			0x1
/* field mapping for TX mem config register
 * are the same as for RX register - see register above
 */
#define MCI_CTRL_TX_MEM_CFG_REG_DEF_VAL			\
				(MCI_CTRL_RX_TX_MEM_CFG_RQ_THRESH(0x20) | \
				MCI_CTRL_RX_TX_MEM_CFG_PQ_THRESH(0x20) | \
				MCI_CTRL_RX_TX_MEM_CFG_NQ_THRESH(0x20) | \
				MCI_CTRL_RX_TX_MEM_CFG_DELTA_THRESH(2) | \
				MCI_CTRL_RX_TX_MEM_CFG_RTC(1) | \
				MCI_CTRL_RX_TX_MEM_CFG_WTC(1))

/* /HB /Units /IHB_REG /IHB_REGInterchip Hopping Bus Registers
 * /IHB Link CRC Control
 */
/* MCi Link CRC Control Register (MCi_CRC_CTRL) */
#define MCI_LINK_CRC_CTRL_REG_NUM			0x4

/* /HB /Units /IHB_REG /IHB_REGInterchip Hopping Bus Registers
 * /IHB Status Register
 */
/* MCi Status Register (MCi_STS) */
#define MCI_CTRL_STATUS_REG_NUM				0x5
#define MCI_CTRL_STATUS_REG_PHY_READY			(1 << 12)
#define MCI_CTRL_STATUS_REG_LINK_PRESENT		(1 << 15)
#define MCI_CTRL_STATUS_REG_PHY_CID_VIO_OFFSET		24
#define MCI_CTRL_STATUS_REG_PHY_CID_VIO_MASK		\
				(0xF << MCI_CTRL_STATUS_REG_PHY_CID_VIO_OFFSET)
/* Expected successful Link result, including reserved bit */
#define MCI_CTRL_PHY_READY		(MCI_CTRL_STATUS_REG_PHY_READY | \
					MCI_CTRL_STATUS_REG_LINK_PRESENT | \
					MCI_CTRL_STATUS_REG_PHY_CID_VIO_MASK)

/* /HB /Units /IHB_REG /IHB_REGInterchip Hopping Bus Registers/
 * MCi PHY Speed Settings Register (MCi_PHY_SETTING)
 */
#define MCI_CTRL_MCI_PHY_SETTINGS_REG_NUM		0x8
#define MCI_CTRL_MCI_PHY_SET_DLO_FIFO_FULL_TRESH(val)	(((val) & 0xF) << 28)
#define MCI_CTRL_MCI_PHY_SET_PHY_MAX_SPEED(val)		(((val) & 0xF) << 12)
#define MCI_CTRL_MCI_PHY_SET_PHYCLK_SEL(val)		(((val) & 0xF) << 8)
#define MCI_CTRL_MCI_PHY_SET_REFCLK_FREQ_SEL(val)	(((val) & 0xF) << 4)
#define MCI_CTRL_MCI_PHY_SET_AUTO_LINK_EN(val)		(((val) & 0x1) << 1)
#define MCI_CTRL_MCI_PHY_SET_REG_DEF_VAL		\
			(MCI_CTRL_MCI_PHY_SET_DLO_FIFO_FULL_TRESH(0x3) | \
			MCI_CTRL_MCI_PHY_SET_PHY_MAX_SPEED(0x3) | \
			MCI_CTRL_MCI_PHY_SET_PHYCLK_SEL(0x2) | \
			MCI_CTRL_MCI_PHY_SET_REFCLK_FREQ_SEL(0x1))
#define MCI_CTRL_MCI_PHY_SET_REG_DEF_VAL2		\
			(MCI_CTRL_MCI_PHY_SET_DLO_FIFO_FULL_TRESH(0x3) | \
			MCI_CTRL_MCI_PHY_SET_PHY_MAX_SPEED(0x3) | \
			MCI_CTRL_MCI_PHY_SET_PHYCLK_SEL(0x5) | \
			MCI_CTRL_MCI_PHY_SET_REFCLK_FREQ_SEL(0x1))

/* /HB /Units /IHB_REG /IHB_REGInterchip Hopping Bus Registers
 * /IHB Mode Config
 */
#define MCI_CTRL_IHB_MODE_CFG_REG_NUM			0x25
#define MCI_CTRL_IHB_MODE_HBCLK_DIV(val)		((val) & 0xFF)
#define MCI_CTRL_IHB_MODE_CHUNK_MOD_OFFSET		8
#define MCI_CTRL_IHB_MODE_CHUNK_MOD			\
				(1 << MCI_CTRL_IHB_MODE_CHUNK_MOD_OFFSET)
#define MCI_CTRL_IHB_MODE_FWD_MOD_OFFSET		9
#define MCI_CTRL_IHB_MODE_FWD_MOD			\
				(1 << MCI_CTRL_IHB_MODE_FWD_MOD_OFFSET)
#define MCI_CTRL_IHB_MODE_SEQFF_FINE_MOD(val)		(((val) & 0xF) << 12)
#define MCI_CTRL_IHB_MODE_RX_COMB_THRESH(val)		(((val) & 0xFF) << 16)
#define MCI_CTRL_IHB_MODE_TX_COMB_THRESH(val)		(((val) & 0xFF) << 24)

#define MCI_CTRL_IHB_MODE_CFG_REG_DEF_VAL		\
				(MCI_CTRL_IHB_MODE_HBCLK_DIV(6) | \
				MCI_CTRL_IHB_MODE_FWD_MOD | \
				MCI_CTRL_IHB_MODE_SEQFF_FINE_MOD(0xF) | \
				MCI_CTRL_IHB_MODE_RX_COMB_THRESH(0x3f) | \
				MCI_CTRL_IHB_MODE_TX_COMB_THRESH(0x40))
/* AXI_HB registers */
#define MCI_AXI_ACCESS_DATA_REG_NUM			0x0
#define MCI_AXI_ACCESS_PCIE_MODE			1
#define MCI_AXI_ACCESS_CACHE_CHECK_OFFSET		5
#define MCI_AXI_ACCESS_CACHE_CHECK			\
				(1 << MCI_AXI_ACCESS_CACHE_CHECK_OFFSET)
#define MCI_AXI_ACCESS_FORCE_POST_WR_OFFSET		6
#define MCI_AXI_ACCESS_FORCE_POST_WR			\
				(1 << MCI_AXI_ACCESS_FORCE_POST_WR_OFFSET)
#define MCI_AXI_ACCESS_DISABLE_CLK_GATING_OFFSET	9
#define MCI_AXI_ACCESS_DISABLE_CLK_GATING		\
				(1 << MCI_AXI_ACCESS_DISABLE_CLK_GATING_OFFSET)

/* /HB /Units /HB_REG /HB_REGHopping Bus Registers
 * /Window 0 Address Mask Register
 */
#define MCI_HB_CTRL_WIN0_ADDRESS_MASK_REG_NUM		0x2

/* /HB /Units /HB_REG /HB_REGHopping Bus Registers
 * /Window 0 Destination Register
 */
#define MCI_HB_CTRL_WIN0_DESTINATION_REG_NUM		0x3
#define MCI_HB_CTRL_WIN0_DEST_VALID_FLAG(val)		(((val) & 0x1) << 16)
#define MCI_HB_CTRL_WIN0_DEST_ID(val)			(((val) & 0xFF) << 0)

/* /HB /Units /HB_REG /HB_REGHopping Bus Registers /Tx Control Register */
#define MCI_HB_CTRL_TX_CTRL_REG_NUM			0xD
#define MCI_HB_CTRL_TX_CTRL_PCIE_MODE_OFFSET		24
#define MCI_HB_CTRL_TX_CTRL_PCIE_MODE			\
				(1 << MCI_HB_CTRL_TX_CTRL_PCIE_MODE_OFFSET)
#define MCI_HB_CTRL_TX_CTRL_PRI_TH_QOS(val)		(((val) & 0xF) << 12)
#define MCI_HB_CTRL_TX_CTRL_MAX_RD_CNT(val)		(((val) & 0x1F) << 6)
#define MCI_HB_CTRL_TX_CTRL_MAX_WR_CNT(val)		(((val) & 0x1F) << 0)

/* /HB /Units /IHB_REG /IHB_REGInterchip Hopping Bus Registers
 * /IHB Version Control Register
 */
#define MCI_PHY_CTRL_REG_NUM				0x7
#define MCI_PHY_CTRL_MCI_MINOR				0x8 /* BITS [3:0] */
#define MCI_PHY_CTRL_MCI_MAJOR_OFFSET			4
#define MCI_PHY_CTRL_MCI_MAJOR				\
				(1 << MCI_PHY_CTRL_MCI_MAJOR_OFFSET)
#define MCI_PHY_CTRL_MCI_SLEEP_REQ_OFFSET		11
#define MCI_PHY_CTRL_MCI_SLEEP_REQ			\
				(1 << MCI_PHY_CTRL_MCI_SLEEP_REQ_OFFSET)
/* Host=1 / Device=0 PHY mode */
#define MCI_PHY_CTRL_MCI_PHY_MODE_OFFSET		24
#define MCI_PHY_CTRL_MCI_PHY_MODE_HOST			\
				(1 << MCI_PHY_CTRL_MCI_PHY_MODE_OFFSET)
/* Register=1 / PWM=0 interface */
#define MCI_PHY_CTRL_MCI_PHY_REG_IF_MODE_OFFSET		25
#define MCI_PHY_CTRL_MCI_PHY_REG_IF_MODE		\
				(1 << MCI_PHY_CTRL_MCI_PHY_REG_IF_MODE_OFFSET)
 /* PHY code InReset=1 */
#define MCI_PHY_CTRL_MCI_PHY_RESET_CORE_OFFSET		26
#define MCI_PHY_CTRL_MCI_PHY_RESET_CORE			\
				(1 << MCI_PHY_CTRL_MCI_PHY_RESET_CORE_OFFSET)
#define MCI_PHY_CTRL_PHY_ADDR_MSB_OFFSET		27
#define MCI_PHY_CTRL_PHY_ADDR_MSB(addr)			\
				(((addr) & 0x3) << \
				MCI_PHY_CTRL_PHY_ADDR_MSB_OFFSET)
#define MCI_PHY_CTRL_PIDI_MODE_OFFSET			31
#define MCI_PHY_CTRL_PIDI_MODE				\
				(1U << MCI_PHY_CTRL_PIDI_MODE_OFFSET)

/* Number of times to wait for the MCI link ready after MCI configurations
 * Normally takes 34-35 successive reads
 */
#define LINK_READY_TIMEOUT				100

enum mci_register_type {
	MCI_REG_TYPE_PHY = 0,
	MCI_REG_TYPE_CTRL,
};

enum {
	MCI_CMD_WRITE,
	MCI_CMD_READ
};

/* Write wrapper callback for debug:
 * will print written data in case LOG_LEVEL >= 40
 */
static void mci_mmio_write_32(uintptr_t addr, uint32_t value)
{
	VERBOSE("Write:\t0x%x = 0x%x\n", (uint32_t)addr, value);
	mmio_write_32(addr, value);
}
/* Read wrapper callback for debug:
 * will print read data in case LOG_LEVEL >= 40
 */
static uint32_t mci_mmio_read_32(uintptr_t addr)
{
	uint32_t value;

	value = mmio_read_32(addr);
	VERBOSE("Read:\t0x%x = 0x%x\n", (uint32_t)addr, value);
	return value;
}

/* MCI indirect access command completion polling:
 * Each write/read command done via MCI indirect registers must be polled
 * for command completions status.
 *
 * Returns 1 in case of error
 * Returns 0 in case of command completed successfully.
 */
static int mci_poll_command_completion(int mci_index, int command_type)
{
	uint32_t mci_cmd_value = 0, retry_count = 100, ret = 0;
	uint32_t completion_flags = MCI_INDIRECT_CTRL_CMD_DONE;

	debug_enter();
	/* Read commands require validating that requested data is ready */
	if (command_type == MCI_CMD_READ)
		completion_flags |= MCI_INDIRECT_CTRL_DATA_READY;

	do {
		/* wait 1 ms before each polling */
		mdelay(1);
		mci_cmd_value = mci_mmio_read_32(MCI_ACCESS_CMD_REG(mci_index));
	} while (((mci_cmd_value & completion_flags) != completion_flags) &&
			 (retry_count-- > 0));

	if (retry_count == 0) {
		ERROR("%s: MCI command timeout (command status = 0x%x)\n",
		      __func__, mci_cmd_value);
		ret = 1;
	}

	debug_exit();
	return ret;
}

int mci_read(int mci_idx, uint32_t cmd, uint32_t *value)
{
	int rval;

	mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_idx), cmd);

	rval = mci_poll_command_completion(mci_idx, MCI_CMD_READ);

	*value = mci_mmio_read_32(MCI_WRITE_READ_DATA_REG(mci_idx));

	return rval;
}

int  mci_write(int mci_idx, uint32_t cmd, uint32_t data)
{
	mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_idx), data);
	mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_idx), cmd);

	return mci_poll_command_completion(mci_idx, MCI_CMD_WRITE);
}

/* Perform 3 configurations in one command: PCI mode,
 * queues separation and cache bit
 */
static int mci_axi_set_pcie_mode(int mci_index)
{
	uint32_t reg_data, ret = 1;

	debug_enter();
	/* This configuration makes MCI IP behave consistently with AXI protocol
	 * It should be configured at one side only (for example locally at AP).
	 * The IP takes care of performing the same configurations at MCI on
	 * another side (for example remotely at CP).
	 */
	mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index),
			  MCI_AXI_ACCESS_PCIE_MODE |
			  MCI_AXI_ACCESS_CACHE_CHECK |
			  MCI_AXI_ACCESS_FORCE_POST_WR |
			  MCI_AXI_ACCESS_DISABLE_CLK_GATING);
	mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index),
			  MCI_INDIRECT_REG_CTRL_ADDR(
				MCI_AXI_ACCESS_DATA_REG_NUM)  |
			  MCI_INDIRECT_CTRL_HOPID(GID_AXI_HB) |
			  MCI_INDIRECT_CTRL_LOCAL_PKT |
			  MCI_INDIRECT_CTRL_CIRCULAR_CMD);

	/* if Write command was successful, verify PCIe mode */
	if (mci_poll_command_completion(mci_index, MCI_CMD_WRITE) == 0) {
		/* Verify the PCIe mode selected */
		mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index),
				  MCI_INDIRECT_REG_CTRL_ADDR(
					MCI_HB_CTRL_TX_CTRL_REG_NUM)  |
				  MCI_INDIRECT_CTRL_HOPID(GID_AXI_HB) |
				  MCI_INDIRECT_CTRL_LOCAL_PKT |
				  MCI_INDIRECT_CTRL_READ_CMD);
		/* if read was completed, verify PCIe mode */
		if (mci_poll_command_completion(mci_index, MCI_CMD_READ) == 0) {
			reg_data = mci_mmio_read_32(
					MCI_WRITE_READ_DATA_REG(mci_index));
			if (reg_data & MCI_HB_CTRL_TX_CTRL_PCIE_MODE)
				ret = 0;
		}
	}

	debug_exit();
	return ret;
}

/* Reduce sequence FIFO timer expiration threshold */
static int mci_axi_set_fifo_thresh(int mci_index)
{
	uint32_t reg_data, ret = 0;

	debug_enter();
	/* This configuration reduces sequence FIFO timer expiration threshold
	 * (to 0x7 instead of 0xA).
	 * In MCI 1.6 version this configuration prevents possible functional
	 * issues.
	 * In version 1.82 the configuration prevents performance degradation
	 */

	/* Configure local AP side */
	reg_data = MCI_PHY_CTRL_PIDI_MODE |
		   MCI_PHY_CTRL_MCI_PHY_REG_IF_MODE |
		   MCI_PHY_CTRL_MCI_PHY_MODE_HOST |
		   MCI_PHY_CTRL_MCI_MAJOR |
		   MCI_PHY_CTRL_MCI_MINOR;
	mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index), reg_data);
	mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index),
			  MCI_INDIRECT_REG_CTRL_ADDR(MCI_PHY_CTRL_REG_NUM) |
			  MCI_INDIRECT_CTRL_LOCAL_PKT);
	ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE);

	/* Reduce the threshold */
	mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index),
			  MCI_CTRL_IHB_MODE_CFG_REG_DEF_VAL);

	mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index),
			  MCI_INDIRECT_REG_CTRL_ADDR(
				MCI_CTRL_IHB_MODE_CFG_REG_NUM) |
			  MCI_INDIRECT_CTRL_LOCAL_PKT);
	ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE);

	/* Exit PIDI mode */
	reg_data = MCI_PHY_CTRL_MCI_PHY_REG_IF_MODE |
		   MCI_PHY_CTRL_MCI_PHY_MODE_HOST |
		   MCI_PHY_CTRL_MCI_MAJOR |
		   MCI_PHY_CTRL_MCI_MINOR;
	mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index), reg_data);
	mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index),
			  MCI_INDIRECT_REG_CTRL_ADDR(MCI_PHY_CTRL_REG_NUM) |
			  MCI_INDIRECT_CTRL_LOCAL_PKT);
	ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE);

	/* Configure remote CP side */
	reg_data = MCI_PHY_CTRL_PIDI_MODE |
		   MCI_PHY_CTRL_MCI_MAJOR |
		   MCI_PHY_CTRL_MCI_MINOR |
		   MCI_PHY_CTRL_MCI_PHY_REG_IF_MODE;
	mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index), reg_data);
	mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index),
			  MCI_INDIRECT_REG_CTRL_ADDR(MCI_PHY_CTRL_REG_NUM) |
			  MCI_CTRL_IHB_MODE_FWD_MOD);
	ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE);

	/* Reduce the threshold */
	mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index),
			  MCI_CTRL_IHB_MODE_CFG_REG_DEF_VAL);
	mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index),
			  MCI_INDIRECT_REG_CTRL_ADDR(
				MCI_CTRL_IHB_MODE_CFG_REG_NUM) |
			  MCI_INDIRECT_CTRL_HOPID(GID_IHB_EXT));
	ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE);

	/* Exit PIDI mode */
	reg_data = MCI_PHY_CTRL_MCI_MAJOR |
		   MCI_PHY_CTRL_MCI_MINOR |
		   MCI_PHY_CTRL_MCI_PHY_REG_IF_MODE;
	mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index), reg_data);
	mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index),
			  MCI_INDIRECT_REG_CTRL_ADDR(MCI_PHY_CTRL_REG_NUM) |
			  MCI_CTRL_IHB_MODE_FWD_MOD);

	ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE);

	debug_exit();
	return ret;
}

/* Configure:
 * 1. AP & CP TX thresholds and delta configurations
 * 2. DLO & DLI FIFO full threshold
 * 3. RX thresholds and delta configurations
 * 4. CP AR and AW outstanding
 * 5. AP AR and AW outstanding
 */
static int mci_axi_set_fifo_rx_tx_thresh(int mci_index)
{
	uint32_t ret = 0;

	debug_enter();
	/* AP TX thresholds and delta configurations (IHB_reg 0x1) */
	mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index),
			  MCI_CTRL_TX_MEM_CFG_REG_DEF_VAL);
	mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index),
			  MCI_INDIRECT_REG_CTRL_ADDR(
				MCI_CTRL_TX_MEM_CFG_REG_NUM) |
			  MCI_INDIRECT_CTRL_LOCAL_PKT);
	ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE);

	/* CP TX thresholds and delta configurations (IHB_reg 0x1) */
	mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index),
			  MCI_CTRL_TX_MEM_CFG_REG_DEF_VAL);
	mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index),
			  MCI_INDIRECT_REG_CTRL_ADDR(
				MCI_CTRL_TX_MEM_CFG_REG_NUM) |
			  MCI_INDIRECT_CTRL_HOPID(GID_IHB_EXT));
	ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE);

	/* AP DLO & DLI FIFO full threshold & Auto-Link enable (IHB_reg 0x8) */
	mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index),
			  MCI_CTRL_MCI_PHY_SET_REG_DEF_VAL |
			  MCI_CTRL_MCI_PHY_SET_AUTO_LINK_EN(1));
	mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index),
			  MCI_INDIRECT_REG_CTRL_ADDR(
				MCI_CTRL_MCI_PHY_SETTINGS_REG_NUM) |
			  MCI_INDIRECT_CTRL_LOCAL_PKT);
	ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE);

	/* CP DLO & DLI FIFO full threshold (IHB_reg 0x8) */
	mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index),
			  MCI_CTRL_MCI_PHY_SET_REG_DEF_VAL);
	mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index),
			  MCI_INDIRECT_REG_CTRL_ADDR(
				MCI_CTRL_MCI_PHY_SETTINGS_REG_NUM) |
			  MCI_INDIRECT_CTRL_HOPID(GID_IHB_EXT));
	ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE);

	/* AP RX thresholds and delta configurations (IHB_reg 0x0) */
	mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index),
			  MCI_CTRL_RX_MEM_CFG_REG_DEF_AP_VAL);
	mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index),
			  MCI_INDIRECT_REG_CTRL_ADDR(
				MCI_CTRL_RX_MEM_CFG_REG_NUM) |
			  MCI_INDIRECT_CTRL_LOCAL_PKT);
	ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE);

	/* CP RX thresholds and delta configurations (IHB_reg 0x0) */
	mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index),
			  MCI_CTRL_RX_MEM_CFG_REG_DEF_CP_VAL);
	mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index),
			  MCI_INDIRECT_REG_CTRL_ADDR(
				MCI_CTRL_RX_MEM_CFG_REG_NUM) |
			  MCI_INDIRECT_CTRL_HOPID(GID_IHB_EXT));
	ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE);

	/* AP AR & AW maximum AXI outstanding request cfg (HB_reg 0xd) */
	mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index),
			  MCI_HB_CTRL_TX_CTRL_PRI_TH_QOS(8) |
			  MCI_HB_CTRL_TX_CTRL_MAX_RD_CNT(3) |
			  MCI_HB_CTRL_TX_CTRL_MAX_WR_CNT(3));
	mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index),
			  MCI_INDIRECT_REG_CTRL_ADDR(
				MCI_HB_CTRL_TX_CTRL_REG_NUM) |
			  MCI_INDIRECT_CTRL_HOPID(GID_AXI_HB) |
			  MCI_INDIRECT_CTRL_LOCAL_PKT);
	ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE);

	/* CP AR & AW maximum AXI outstanding request cfg (HB_reg 0xd) */
	mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(mci_index),
			  MCI_HB_CTRL_TX_CTRL_PRI_TH_QOS(8) |
			  MCI_HB_CTRL_TX_CTRL_MAX_RD_CNT(0xB) |
			  MCI_HB_CTRL_TX_CTRL_MAX_WR_CNT(0x11));
	mci_mmio_write_32(MCI_ACCESS_CMD_REG(mci_index),
			  MCI_INDIRECT_REG_CTRL_ADDR(
				MCI_HB_CTRL_TX_CTRL_REG_NUM) |
			  MCI_INDIRECT_CTRL_HOPID(GID_IHB_EXT) |
			  MCI_INDIRECT_CTRL_HOPID(GID_AXI_HB));
	ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE);

	debug_exit();
	return ret;
}

/* configure MCI to allow read & write transactions to arrive at the same time.
 * Without the below configuration, MCI won't sent response to CPU for
 * transactions which arrived simultaneously and will lead to CPU hang.
 * The below will configure MCI to be able to pass transactions from/to CP/AP.
 */
static int mci_enable_simultaneous_transactions(int mci_index)
{
	uint32_t ret = 0;

	debug_enter();
	/* ID assignment (assigning global ID offset to CP) */
	mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(0),
			  MCI_DID_GLOBAL_ASSIGN_REQ_MCI_LOCAL_ID(2) |
			  MCI_DID_GLOBAL_ASSIGN_REQ_MCI_COUNT(2) |
			  MCI_DID_GLOBAL_ASSIGN_REQ_HOPS_NUM(2));
	mci_mmio_write_32(MCI_ACCESS_CMD_REG(0),
			  MCI_INDIRECT_REG_CTRL_ADDR(
				MCI_DID_GLOBAL_ASSIGNMENT_REQUEST_REG) |
			  MCI_INDIRECT_CTRL_ASSIGN_CMD);
	ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE);

	/* Assigning dest. ID=3 to all transactions entering from AXI at AP */
	mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(0),
			  MCI_HB_CTRL_WIN0_DEST_VALID_FLAG(1) |
			  MCI_HB_CTRL_WIN0_DEST_ID(3));
	mci_mmio_write_32(MCI_ACCESS_CMD_REG(0),
			  MCI_INDIRECT_REG_CTRL_ADDR(
				MCI_HB_CTRL_WIN0_DESTINATION_REG_NUM) |
			  MCI_INDIRECT_CTRL_HOPID(GID_AXI_HB) |
			  MCI_INDIRECT_CTRL_LOCAL_PKT);
	ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE);

	/* Assigning dest. ID=1 to all transactions entering from AXI at CP */
	mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(0),
			  MCI_HB_CTRL_WIN0_DEST_VALID_FLAG(1) |
			  MCI_HB_CTRL_WIN0_DEST_ID(1));
	mci_mmio_write_32(MCI_ACCESS_CMD_REG(0),
			  MCI_INDIRECT_REG_CTRL_ADDR(
				MCI_HB_CTRL_WIN0_DESTINATION_REG_NUM) |
			  MCI_INDIRECT_CTRL_HOPID(GID_IHB_EXT) |
			  MCI_INDIRECT_CTRL_HOPID(GID_AXI_HB));
	ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE);

	/* End address to all transactions entering from AXI at AP.
	 * This will lead to get match for any AXI address
	 * and receive destination ID=3
	 */
	mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(0), 0xffffffff);
	mci_mmio_write_32(MCI_ACCESS_CMD_REG(0),
			  MCI_INDIRECT_REG_CTRL_ADDR(
				MCI_HB_CTRL_WIN0_ADDRESS_MASK_REG_NUM) |
			  MCI_INDIRECT_CTRL_HOPID(GID_AXI_HB) |
			  MCI_INDIRECT_CTRL_LOCAL_PKT);
	ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE);

	/* End address to all transactions entering from AXI at CP.
	 * This will lead to get match for any AXI address
	 * and receive destination ID=1
	 */
	mci_mmio_write_32(MCI_WRITE_READ_DATA_REG(0), 0xffffffff);
	mci_mmio_write_32(MCI_ACCESS_CMD_REG(0),
			  MCI_INDIRECT_REG_CTRL_ADDR(
				MCI_HB_CTRL_WIN0_ADDRESS_MASK_REG_NUM) |
			  MCI_INDIRECT_CTRL_HOPID(GID_IHB_EXT) |
			  MCI_INDIRECT_CTRL_HOPID(GID_AXI_HB));
	ret |= mci_poll_command_completion(mci_index, MCI_CMD_WRITE);

	debug_exit();
	return ret;
}

/* Check if MCI simultaneous transaction was already enabled.
 * Currently bootrom does this mci configuration only when the boot source is
 * SAR_MCIX4, in other cases it should be done at this stage.
 * It is worth noticing that in case of booting from uart, the bootrom
 * flow is different and this mci initialization is skipped even if boot
 * source is SAR_MCIX4. Therefore new verification bases on appropriate mci's
 * register content: if the appropriate reg contains 0x0 it means that the
 * bootrom didn't perform required mci configuration.
 *
 * Returns:
 * 0 - configuration already done
 * 1 - configuration missing
 */
static _Bool mci_simulatenous_trans_missing(int mci_index)
{
	uint32_t reg, ret;

	/* read 'Window 0 Destination ID assignment' from HB register 0x3
	 * (TX_CFG_W0_DST_ID) to check whether ID assignment was already
	 * performed by BootROM.
	 */
	debug_enter();
	mci_mmio_write_32(MCI_ACCESS_CMD_REG(0),
			  MCI_INDIRECT_REG_CTRL_ADDR(
				MCI_HB_CTRL_WIN0_DESTINATION_REG_NUM) |
			  MCI_INDIRECT_CTRL_HOPID(GID_AXI_HB) |
			  MCI_INDIRECT_CTRL_LOCAL_PKT |
			  MCI_INDIRECT_CTRL_READ_CMD);
	ret = mci_poll_command_completion(mci_index, MCI_CMD_READ);

	reg = mci_mmio_read_32(MCI_WRITE_READ_DATA_REG(mci_index));

	if (ret)
		ERROR("Failed to verify MCI simultaneous read/write status\n");

	debug_exit();
	/* default ID assignment is 0, so if register doesn't contain zeros
	 * it means that bootrom already performed required configuration.
	 */
	if (reg != 0)
		return 0;

	return 1;
}

/* For A1 revision, configure the MCI link for performance improvement:
 * - set MCI to support read/write transactions to arrive at the same time
 * - Switch AXI to PCIe mode
 * - Reduce sequence FIFO threshold
 * - Configure RX/TX FIFO thresholds
 *
 *   Note:
 *   We don't exit on error code from any sub routine, to try (best effort) to
 *   complete the MCI configuration.
 *   (If we exit - Bootloader will surely fail to boot)
 */
int mci_configure(int mci_index)
{
	int rval;

	debug_enter();
	/* According to design guidelines the MCI simultaneous transaction
	 * shouldn't be enabled more then once - therefore make sure that it
	 * wasn't already enabled in bootrom.
	 */
	if (mci_simulatenous_trans_missing(mci_index)) {
		VERBOSE("Enabling MCI simultaneous transaction\n");
		/* set MCI to support read/write transactions
		 * to arrive at the same time
		 */
		rval = mci_enable_simultaneous_transactions(mci_index);
		if (rval)
			ERROR("Failed to set MCI simultaneous read/write\n");
	} else
		VERBOSE("Skip MCI ID assignment - already done by bootrom\n");

	/* Configure MCI for more consistent behavior with AXI protocol */
	rval = mci_axi_set_pcie_mode(mci_index);
	if (rval)
		ERROR("Failed to set MCI to AXI PCIe mode\n");

	/* reduce FIFO global threshold */
	rval = mci_axi_set_fifo_thresh(mci_index);
	if (rval)
		ERROR("Failed to set MCI FIFO global threshold\n");

	/* configure RX/TX FIFO thresholds */
	rval = mci_axi_set_fifo_rx_tx_thresh(mci_index);
	if (rval)
		ERROR("Failed to set MCI RX/TX FIFO threshold\n");

	debug_exit();
	return 1;
}

int mci_get_link_status(void)
{
	uint32_t cmd, data;

	cmd = (MCI_INDIRECT_REG_CTRL_ADDR(MCI_CTRL_STATUS_REG_NUM) |
		MCI_INDIRECT_CTRL_LOCAL_PKT | MCI_INDIRECT_CTRL_READ_CMD);
	if (mci_read(0, cmd, &data)) {
		ERROR("Failed to read status register\n");
		return -1;
	}

	/* Check if the link is ready */
	if (data != MCI_CTRL_PHY_READY) {
		ERROR("Bad link status %x\n", data);
		return -1;
	}

	return 0;
}

void mci_turn_link_down(void)
{
	uint32_t cmd, data;
	int rval = 0;

	debug_enter();

	/* Turn off auto-link */
	cmd = (MCI_INDIRECT_REG_CTRL_ADDR(MCI_CTRL_MCI_PHY_SETTINGS_REG_NUM) |
			MCI_INDIRECT_CTRL_LOCAL_PKT);
	data = (MCI_CTRL_MCI_PHY_SET_REG_DEF_VAL2 |
		MCI_CTRL_MCI_PHY_SET_AUTO_LINK_EN(0));
	rval = mci_write(0, cmd, data);
	if (rval)
		ERROR("Failed to turn off auto-link\n");

	/* Reset AP PHY */
	cmd = (MCI_INDIRECT_REG_CTRL_ADDR(MCI_PHY_CTRL_REG_NUM) |
		MCI_INDIRECT_CTRL_LOCAL_PKT);
	data = (MCI_PHY_CTRL_MCI_MINOR |
		MCI_PHY_CTRL_MCI_MAJOR |
		MCI_PHY_CTRL_MCI_PHY_MODE_HOST |
		MCI_PHY_CTRL_MCI_PHY_RESET_CORE);
	rval = mci_write(0, cmd, data);
	if (rval)
		ERROR("Failed to reset AP PHY\n");

	/* Clear all status & CRC values */
	cmd = (MCI_INDIRECT_REG_CTRL_ADDR(MCI_LINK_CRC_CTRL_REG_NUM) |
	       MCI_INDIRECT_CTRL_LOCAL_PKT);
	data = 0x0;
	mci_write(0, cmd, data);
	cmd = (MCI_INDIRECT_REG_CTRL_ADDR(MCI_CTRL_STATUS_REG_NUM) |
	       MCI_INDIRECT_CTRL_LOCAL_PKT);
	data = 0x0;
	rval = mci_write(0, cmd, data);
	if (rval)
		ERROR("Failed to reset AP PHY\n");

	/* Wait 5ms before un-reset the PHY */
	mdelay(5);

	/* Un-reset AP PHY */
	cmd = (MCI_INDIRECT_REG_CTRL_ADDR(MCI_PHY_CTRL_REG_NUM) |
	       MCI_INDIRECT_CTRL_LOCAL_PKT);
	data = (MCI_PHY_CTRL_MCI_MINOR | MCI_PHY_CTRL_MCI_MAJOR |
		MCI_PHY_CTRL_MCI_PHY_MODE_HOST);
	rval = mci_write(0, cmd, data);
	if (rval)
		ERROR("Failed to un-reset AP PHY\n");

	debug_exit();
}

void mci_turn_link_on(void)
{
	uint32_t cmd, data;
	int rval = 0;

	debug_enter();
	/* Turn on auto-link */
	cmd = (MCI_INDIRECT_REG_CTRL_ADDR(MCI_CTRL_MCI_PHY_SETTINGS_REG_NUM) |
			MCI_INDIRECT_CTRL_LOCAL_PKT);
	data = (MCI_CTRL_MCI_PHY_SET_REG_DEF_VAL2 |
		MCI_CTRL_MCI_PHY_SET_AUTO_LINK_EN(1));
	rval = mci_write(0, cmd, data);
	if (rval)
		ERROR("Failed to turn on auto-link\n");

	debug_exit();
}

/* Initialize MCI for performance improvements */
int mci_initialize(int mci_index)
{
	int ret;

	debug_enter();
	INFO("MCI%d initialization:\n", mci_index);

	ret = mci_configure(mci_index);

	debug_exit();
	return ret;
}