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
|
// SPDX-License-Identifier: GPL-2.0
/*
* SoC specific PCIe PHY setup for Marvell MVEBU SoCs
*
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
*
* based on Marvell BSP code (C) Marvell International Ltd.
*/
#include <common.h>
#include <of.h>
#include <of_address.h>
#include "pci-mvebu.h"
static u32 mvebu_pcie_phy_indirect(void __iomem *phybase, u8 lane,
u8 off, u16 val, bool is_read)
{
u32 reg = (lane << 24) | (off << 16) | val;
if (is_read)
reg |= BIT(31);
writel(reg, phybase);
return (is_read) ? readl(phybase) & 0xffff : 0;
}
static inline u32 mvebu_pcie_phy_read(void __iomem *phybase, u8 lane,
u8 off)
{
return mvebu_pcie_phy_indirect(phybase, lane, off, 0, true);
}
static inline void mvebu_pcie_phy_write(void __iomem *phybase, u8 lane,
u8 off, u16 val)
{
mvebu_pcie_phy_indirect(phybase, lane, off, val, false);
}
/* PCIe registers */
#define ARMADA_370_XP_PCIE_LINK_CAPS 0x6c
#define MAX_LINK_WIDTH_MASK MAX_LINK_WIDTH(0x3f)
#define MAX_LINK_WIDTH(x) ((x) << 4)
#define MAX_LINK_SPEED_MASK 0xf
#define MAX_LINK_SPEED_5G0 0x2
#define MAX_LINK_SPEED_2G5 0x1
#define ARMADA_370_XP_PHY_OFFSET 0x1b00
/* System Control registers */
#define ARMADA_370_XP_SOC_CTRL 0x04
#define PCIE1_QUADX1_EN BIT(8) /* Armada XP */
#define PCIE0_QUADX1_EN BIT(7) /* Armada XP */
#define PCIE0_EN BIT(0)
#define ARMADA_370_XP_SERDES03_SEL 0x70
#define ARMADA_370_XP_SERDES47_SEL 0x74
#define SERDES(x, v) ((v) << ((x) * 0x4))
#define SERDES_MASK(x) SERDES((x), 0xf)
int armada_370_phy_setup(struct mvebu_pcie *pcie)
{
struct device_node *np = of_find_compatible_node(NULL, NULL,
"marvell,armada-370-xp-system-controller");
void __iomem *sysctrl = of_iomap(np, 0);
void __iomem *phybase = pcie->base + ARMADA_370_XP_PHY_OFFSET;
u32 reg;
if (!sysctrl)
return -ENODEV;
/* Enable PEX */
reg = readl(sysctrl + ARMADA_370_XP_SOC_CTRL);
reg |= PCIE0_EN << pcie->port;
writel(reg, sysctrl + ARMADA_370_XP_SOC_CTRL);
/* Set SERDES selector */
reg = readl(sysctrl + ARMADA_370_XP_SERDES03_SEL);
reg &= ~SERDES_MASK(pcie->port);
reg |= SERDES(pcie->port, 0x1);
writel(reg, sysctrl + ARMADA_370_XP_SERDES03_SEL);
/* BTS #232 - PCIe clock (undocumented) */
writel(0x00000077, sysctrl + 0x2f0);
/* Set x1 Link Capability */
reg = readl(pcie->base + ARMADA_370_XP_PCIE_LINK_CAPS);
reg &= ~(MAX_LINK_WIDTH_MASK | MAX_LINK_SPEED_MASK);
reg |= MAX_LINK_WIDTH(0x1) | MAX_LINK_SPEED_5G0;
writel(reg, pcie->base + ARMADA_370_XP_PCIE_LINK_CAPS);
/* PEX pipe configuration */
mvebu_pcie_phy_write(phybase, pcie->lane, 0xc1, 0x0025);
mvebu_pcie_phy_write(phybase, pcie->lane, 0xc3, 0x000f);
mvebu_pcie_phy_write(phybase, pcie->lane, 0xc8, 0x0005);
mvebu_pcie_phy_write(phybase, pcie->lane, 0xd0, 0x0100);
mvebu_pcie_phy_write(phybase, pcie->lane, 0xd1, 0x3014);
mvebu_pcie_phy_write(phybase, pcie->lane, 0xc5, 0x011f);
mvebu_pcie_phy_write(phybase, pcie->lane, 0x80, 0x1000);
mvebu_pcie_phy_write(phybase, pcie->lane, 0x81, 0x0011);
mvebu_pcie_phy_write(phybase, pcie->lane, 0x0f, 0x2a21);
mvebu_pcie_phy_write(phybase, pcie->lane, 0x45, 0x00df);
mvebu_pcie_phy_write(phybase, pcie->lane, 0x4f, 0x6219);
mvebu_pcie_phy_write(phybase, pcie->lane, 0x01, 0xfc60);
mvebu_pcie_phy_write(phybase, pcie->lane, 0x46, 0x0000);
reg = mvebu_pcie_phy_read(phybase, pcie->lane, 0x48) & ~0x4;
mvebu_pcie_phy_write(phybase, pcie->lane, 0x48, reg & 0xffff);
mvebu_pcie_phy_write(phybase, pcie->lane, 0x02, 0x0040);
mvebu_pcie_phy_write(phybase, pcie->lane, 0xc1, 0x0024);
mdelay(15);
return 0;
}
/*
* MV78230: 2 PCIe units Gen2.0, one unit 1x4 or 4x1, one unit 1x1
* MV78260: 3 PCIe units Gen2.0, two units 1x4 or 4x1, one unit 1x1/1x4
* MV78460: 4 PCIe units Gen2.0, two units 1x4 or 4x1, two units 1x1/1x4
*/
#define ARMADA_XP_COMM_PHY_REFCLK_ALIGN 0xf8
#define REFCLK_ALIGN(x) (0xf << ((x) * 0x4))
int armada_xp_phy_setup(struct mvebu_pcie *pcie)
{
struct device_node *np = of_find_compatible_node(NULL, NULL,
"marvell,armada-370-xp-system-controller");
void __iomem *sysctrl = of_iomap(np, 0);
void __iomem *phybase = pcie->base + ARMADA_370_XP_PHY_OFFSET;
u32 serdes_off = (pcie->port < 2) ? ARMADA_370_XP_SERDES03_SEL :
ARMADA_370_XP_SERDES47_SEL;
bool single_x4 = (pcie->lane_mask == 0xf);
u32 reg, mask;
if (!sysctrl)
return -ENODEV;
/* Prepare PEX */
reg = readl(sysctrl + ARMADA_370_XP_SOC_CTRL);
reg &= ~(PCIE0_EN << pcie->port);
writel(reg, sysctrl + ARMADA_370_XP_SOC_CTRL);
if (pcie->port < 2) {
mask = PCIE0_QUADX1_EN << pcie->port;
if (single_x4)
reg &= ~mask;
else
reg |= mask;
}
reg |= PCIE0_EN << pcie->port;
writel(reg, sysctrl + ARMADA_370_XP_SOC_CTRL);
/* Set SERDES selector */
reg = readl(sysctrl + serdes_off);
for (mask = pcie->lane_mask; mask;) {
u32 l = ffs(mask)-1;
u32 off = 4 * (pcie->port % 2);
reg &= ~SERDES_MASK(off + l);
reg |= SERDES(off + l, 0x1);
mask &= ~BIT(l);
}
reg &= ~SERDES_MASK(pcie->port % 2);
reg |= SERDES(pcie->port % 2, 0x1);
writel(reg, sysctrl + serdes_off);
/* Reference Clock Alignment for 1x4 */
reg = readl(sysctrl + ARMADA_XP_COMM_PHY_REFCLK_ALIGN);
if (single_x4)
reg |= REFCLK_ALIGN(pcie->port);
else
reg &= ~REFCLK_ALIGN(pcie->port);
writel(reg, sysctrl + ARMADA_XP_COMM_PHY_REFCLK_ALIGN);
/* Set x1/x4 Link Capability */
reg = readl(pcie->base + ARMADA_370_XP_PCIE_LINK_CAPS);
reg &= ~(MAX_LINK_WIDTH_MASK | MAX_LINK_SPEED_MASK);
if (single_x4)
reg |= MAX_LINK_WIDTH(0x4);
else
reg |= MAX_LINK_WIDTH(0x1);
reg |= MAX_LINK_SPEED_5G0;
writel(reg, pcie->base + ARMADA_370_XP_PCIE_LINK_CAPS);
/* PEX pipe configuration */
mvebu_pcie_phy_write(phybase, pcie->lane, 0xc1, 0x0025);
if (single_x4) {
mvebu_pcie_phy_write(phybase, pcie->lane, 0xc2, 0x0200);
mvebu_pcie_phy_write(phybase, pcie->lane, 0xc3, 0x0001);
} else {
mvebu_pcie_phy_write(phybase, pcie->lane, 0xc2, 0x0000);
mvebu_pcie_phy_write(phybase, pcie->lane, 0xc3, 0x000f);
}
mvebu_pcie_phy_write(phybase, pcie->lane, 0xc8, 0x0005);
mvebu_pcie_phy_write(phybase, pcie->lane, 0x01, 0xfc60);
mvebu_pcie_phy_write(phybase, pcie->lane, 0x46, 0x0000);
mvebu_pcie_phy_write(phybase, pcie->lane, 0x02, 0x0040);
mvebu_pcie_phy_write(phybase, pcie->lane, 0xc1, 0x0024);
if (single_x4)
mvebu_pcie_phy_write(phybase, pcie->lane, 0x48, 0x1080);
else
mvebu_pcie_phy_write(phybase, pcie->lane, 0x48, 0x9080);
mdelay(15);
return 0;
}
|