aboutsummaryrefslogtreecommitdiff
path: root/board/armltd/vexpress64/pcie.c
blob: 733b190e594d1d529efe03954bff21b0d6fc35be (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
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (C) ARM Ltd 2015
 *
 * Author: Liviu Dudau <Liviu.Dudau@arm.com>
 */

#include <common.h>
#include <init.h>
#include <log.h>
#include <asm/io.h>
#include <linux/bitops.h>
#include <pci_ids.h>
#include <linux/delay.h>
#include "pcie.h"

/* XpressRICH3 support */
#define XR3_CONFIG_BASE			0x7ff30000
#define XR3_RESET_BASE			0x7ff20000

#define XR3_PCI_ECAM_START		0x40000000
#define XR3_PCI_ECAM_SIZE		28	/* as power of 2 = 0x10000000 */
#define XR3_PCI_IOSPACE_START		0x5f800000
#define XR3_PCI_IOSPACE_SIZE		23	/* as power of 2 = 0x800000 */
#define XR3_PCI_MEMSPACE_START		0x50000000
#define XR3_PCI_MEMSPACE_SIZE		27	/* as power of 2 = 0x8000000 */
#define XR3_PCI_MEMSPACE64_START	0x4000000000
#define XR3_PCI_MEMSPACE64_SIZE		33	/* as power of 2 = 0x200000000 */

#define JUNO_V2M_MSI_START		0x2c1c0000
#define JUNO_V2M_MSI_SIZE		12	/* as power of 2 = 4096 */

#define XR3PCI_BASIC_STATUS		0x18
#define XR3PCI_BS_GEN_MASK		(0xf << 8)
#define XR3PCI_BS_LINK_MASK		0xff

#define XR3PCI_VIRTCHAN_CREDITS		0x90
#define XR3PCI_BRIDGE_PCI_IDS		0x9c
#define XR3PCI_PEX_SPC2			0xd8

#define XR3PCI_ATR_PCIE_WIN0		0x600
#define XR3PCI_ATR_PCIE_WIN1		0x700
#define XR3PCI_ATR_AXI4_SLV0		0x800

#define XR3PCI_ATR_TABLE_SIZE		0x20
#define XR3PCI_ATR_SRC_ADDR_LOW		0x0
#define XR3PCI_ATR_SRC_ADDR_HIGH	0x4
#define XR3PCI_ATR_TRSL_ADDR_LOW	0x8
#define XR3PCI_ATR_TRSL_ADDR_HIGH	0xc
#define XR3PCI_ATR_TRSL_PARAM		0x10

/* IDs used in the XR3PCI_ATR_TRSL_PARAM */
#define XR3PCI_ATR_TRSLID_AXIDEVICE	(0x420004)
#define XR3PCI_ATR_TRSLID_AXIMEMORY	(0x4e0004)  /* Write-through, read/write allocate */
#define XR3PCI_ATR_TRSLID_PCIE_CONF	(0x000001)
#define XR3PCI_ATR_TRSLID_PCIE_IO	(0x020000)
#define XR3PCI_ATR_TRSLID_PCIE_MEMORY	(0x000000)

#define XR3PCI_ECAM_OFFSET(b, d, o)	(((b) << 20) | \
					(PCI_SLOT(d) << 15) | \
					(PCI_FUNC(d) << 12) | o)

#define JUNO_RESET_CTRL			0x1004
#define JUNO_RESET_CTRL_PHY		BIT(0)
#define JUNO_RESET_CTRL_RC		BIT(1)

#define JUNO_RESET_STATUS		0x1008
#define JUNO_RESET_STATUS_PLL		BIT(0)
#define JUNO_RESET_STATUS_PHY		BIT(1)
#define JUNO_RESET_STATUS_RC		BIT(2)
#define JUNO_RESET_STATUS_MASK		(JUNO_RESET_STATUS_PLL | \
					 JUNO_RESET_STATUS_PHY | \
					 JUNO_RESET_STATUS_RC)

static void xr3pci_set_atr_entry(unsigned long base, unsigned long src_addr,
				 unsigned long trsl_addr, int window_size,
				 int trsl_param)
{
	/* X3PCI_ATR_SRC_ADDR_LOW:
	     - bit 0: enable entry,
	     - bits 1-6: ATR window size: total size in bytes: 2^(ATR_WSIZE + 1)
	     - bits 7-11: reserved
	     - bits 12-31: start of source address
	*/
	writel((u32)(src_addr & 0xfffff000) | (window_size - 1) << 1 | 1,
	       base + XR3PCI_ATR_SRC_ADDR_LOW);
	writel((u32)(src_addr >> 32), base + XR3PCI_ATR_SRC_ADDR_HIGH);
	writel((u32)(trsl_addr & 0xfffff000), base + XR3PCI_ATR_TRSL_ADDR_LOW);
	writel((u32)(trsl_addr >> 32), base + XR3PCI_ATR_TRSL_ADDR_HIGH);
	writel(trsl_param, base + XR3PCI_ATR_TRSL_PARAM);

	debug("ATR entry: 0x%010lx %s 0x%010lx [0x%010llx] (param: 0x%06x)\n",
	       src_addr, (trsl_param & 0x400000) ? "<-" : "->", trsl_addr,
	       ((u64)1) << window_size, trsl_param);
}

static void xr3pci_setup_atr(void)
{
	/* setup PCIe to CPU address translation tables */
	unsigned long base = XR3_CONFIG_BASE + XR3PCI_ATR_PCIE_WIN0;

	/* forward all writes from PCIe to GIC V2M (used for MSI) */
	xr3pci_set_atr_entry(base, JUNO_V2M_MSI_START, JUNO_V2M_MSI_START,
			     JUNO_V2M_MSI_SIZE, XR3PCI_ATR_TRSLID_AXIDEVICE);

	base += XR3PCI_ATR_TABLE_SIZE;

	/* PCIe devices can write anywhere in memory */
	xr3pci_set_atr_entry(base, PHYS_SDRAM_1, PHYS_SDRAM_1,
			     31 /* grant access to all RAM under 4GB */,
			     XR3PCI_ATR_TRSLID_AXIMEMORY);
	base += XR3PCI_ATR_TABLE_SIZE;
	xr3pci_set_atr_entry(base, PHYS_SDRAM_2, PHYS_SDRAM_2,
			     XR3_PCI_MEMSPACE64_SIZE,
			     XR3PCI_ATR_TRSLID_AXIMEMORY);


	/* setup CPU to PCIe address translation table */
	base = XR3_CONFIG_BASE + XR3PCI_ATR_AXI4_SLV0;

	/* setup ECAM space to bus configuration interface */
	xr3pci_set_atr_entry(base, XR3_PCI_ECAM_START, 0, XR3_PCI_ECAM_SIZE,
			     XR3PCI_ATR_TRSLID_PCIE_CONF);

	base += XR3PCI_ATR_TABLE_SIZE;

	/* setup IO space translation */
	xr3pci_set_atr_entry(base, XR3_PCI_IOSPACE_START, 0,
			     XR3_PCI_IOSPACE_SIZE, XR3PCI_ATR_TRSLID_PCIE_IO);

	base += XR3PCI_ATR_TABLE_SIZE;

	/* setup 32bit MEM space translation */
	xr3pci_set_atr_entry(base, XR3_PCI_MEMSPACE_START, XR3_PCI_MEMSPACE_START,
			     XR3_PCI_MEMSPACE_SIZE, XR3PCI_ATR_TRSLID_PCIE_MEMORY);

	base += XR3PCI_ATR_TABLE_SIZE;

	/* setup 64bit MEM space translation */
	xr3pci_set_atr_entry(base, XR3_PCI_MEMSPACE64_START, XR3_PCI_MEMSPACE64_START,
			     XR3_PCI_MEMSPACE64_SIZE, XR3PCI_ATR_TRSLID_PCIE_MEMORY);
}

static void xr3pci_init(void)
{
	u32 val;
	int timeout = 200;

	/* Initialise the XpressRICH3 PCIe host bridge */

	/* add credits */
	writel(0x00f0b818, XR3_CONFIG_BASE + XR3PCI_VIRTCHAN_CREDITS);
	writel(0x1, XR3_CONFIG_BASE + XR3PCI_VIRTCHAN_CREDITS + 4);
	/* allow ECRC */
	writel(0x6006, XR3_CONFIG_BASE + XR3PCI_PEX_SPC2);
	/* setup the correct class code for the host bridge */
	writel(PCI_CLASS_BRIDGE_PCI << 16, XR3_CONFIG_BASE + XR3PCI_BRIDGE_PCI_IDS);

	/* reset phy and root complex */
	writel(JUNO_RESET_CTRL_PHY | JUNO_RESET_CTRL_RC,
	       XR3_RESET_BASE + JUNO_RESET_CTRL);

	do {
		mdelay(1);
		val = readl(XR3_RESET_BASE + JUNO_RESET_STATUS);
	} while (--timeout &&
		(val & JUNO_RESET_STATUS_MASK) != JUNO_RESET_STATUS_MASK);

	if (!timeout) {
		printf("PCI XR3 Root complex reset timed out\n");
		return;
	}

	/* Wait for the link to train */
	mdelay(20);
	timeout = 20;

	do {
		mdelay(1);
		val = readl(XR3_CONFIG_BASE + XR3PCI_BASIC_STATUS);
	} while (--timeout && !(val & XR3PCI_BS_LINK_MASK));

	if (!(val & XR3PCI_BS_LINK_MASK)) {
		printf("Failed to negotiate a link!\n");
		return;
	}

	printf("PCIe XR3 Host Bridge enabled: x%d link (Gen %d)\n",
	       val & XR3PCI_BS_LINK_MASK, (val & XR3PCI_BS_GEN_MASK) >> 8);

	xr3pci_setup_atr();
}

void vexpress64_pcie_init(void)
{
	/* Initialise and configure the PCIe host bridge. */
	xr3pci_init();

	/* Register the now ECAM complaint PCIe host controller with U-Boot. */
	pci_init();
}