]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
a4a40437 CJ |
2 | /* |
3 | * Copyright 2015-2016 Freescale Semiconductor, Inc. | |
4 | * Copyright 2017 NXP | |
a4a40437 CJ |
5 | */ |
6 | #include <common.h> | |
7 | #include <dm.h> | |
8 | #include <dm/platform_data/pfe_dm_eth.h> | |
9 | #include <net.h> | |
10 | #include <net/pfe_eth/pfe_eth.h> | |
11 | ||
12 | extern struct gemac_s gem_info[]; | |
13 | #if defined(CONFIG_PHYLIB) | |
14 | ||
15 | #define MDIO_TIMEOUT 5000 | |
16 | static int pfe_write_addr(struct mii_dev *bus, int phy_addr, int dev_addr, | |
17 | int reg_addr) | |
18 | { | |
19 | void *reg_base = bus->priv; | |
20 | u32 devadr; | |
21 | u32 phy; | |
22 | u32 reg_data; | |
23 | int timeout = MDIO_TIMEOUT; | |
24 | ||
25 | devadr = ((dev_addr & EMAC_MII_DATA_RA_MASK) << EMAC_MII_DATA_RA_SHIFT); | |
26 | phy = ((phy_addr & EMAC_MII_DATA_PA_MASK) << EMAC_MII_DATA_PA_SHIFT); | |
27 | ||
28 | reg_data = (EMAC_MII_DATA_TA | phy | devadr | reg_addr); | |
29 | ||
30 | writel(reg_data, reg_base + EMAC_MII_DATA_REG); | |
31 | ||
32 | /* | |
33 | * wait for the MII interrupt | |
34 | */ | |
35 | while (!(readl(reg_base + EMAC_IEVENT_REG) & EMAC_IEVENT_MII)) { | |
36 | if (timeout-- <= 0) { | |
37 | printf("Phy MDIO read/write timeout\n"); | |
38 | return -1; | |
39 | } | |
40 | } | |
41 | ||
42 | /* | |
43 | * clear MII interrupt | |
44 | */ | |
45 | writel(EMAC_IEVENT_MII, reg_base + EMAC_IEVENT_REG); | |
46 | ||
47 | return 0; | |
48 | } | |
49 | ||
50 | static int pfe_phy_read(struct mii_dev *bus, int phy_addr, int dev_addr, | |
51 | int reg_addr) | |
52 | { | |
53 | void *reg_base = bus->priv; | |
54 | u32 reg; | |
55 | u32 phy; | |
56 | u32 reg_data; | |
57 | u16 val; | |
58 | int timeout = MDIO_TIMEOUT; | |
59 | ||
60 | if (dev_addr == MDIO_DEVAD_NONE) { | |
61 | reg = ((reg_addr & EMAC_MII_DATA_RA_MASK) << | |
62 | EMAC_MII_DATA_RA_SHIFT); | |
63 | } else { | |
64 | pfe_write_addr(bus, phy_addr, dev_addr, reg_addr); | |
65 | reg = ((dev_addr & EMAC_MII_DATA_RA_MASK) << | |
66 | EMAC_MII_DATA_RA_SHIFT); | |
67 | } | |
68 | ||
69 | phy = ((phy_addr & EMAC_MII_DATA_PA_MASK) << EMAC_MII_DATA_PA_SHIFT); | |
70 | ||
71 | if (dev_addr == MDIO_DEVAD_NONE) | |
72 | reg_data = (EMAC_MII_DATA_ST | EMAC_MII_DATA_OP_RD | | |
73 | EMAC_MII_DATA_TA | phy | reg); | |
74 | else | |
75 | reg_data = (EMAC_MII_DATA_OP_CL45_RD | EMAC_MII_DATA_TA | | |
76 | phy | reg); | |
77 | ||
78 | writel(reg_data, reg_base + EMAC_MII_DATA_REG); | |
79 | ||
80 | /* | |
81 | * wait for the MII interrupt | |
82 | */ | |
83 | while (!(readl(reg_base + EMAC_IEVENT_REG) & EMAC_IEVENT_MII)) { | |
84 | if (timeout-- <= 0) { | |
85 | printf("Phy MDIO read/write timeout\n"); | |
86 | return -1; | |
87 | } | |
88 | } | |
89 | ||
90 | /* | |
91 | * clear MII interrupt | |
92 | */ | |
93 | writel(EMAC_IEVENT_MII, reg_base + EMAC_IEVENT_REG); | |
94 | ||
95 | /* | |
96 | * it's now safe to read the PHY's register | |
97 | */ | |
98 | val = (u16)readl(reg_base + EMAC_MII_DATA_REG); | |
99 | debug("%s: %p phy: 0x%x reg:0x%08x val:%#x\n", __func__, reg_base, | |
100 | phy_addr, reg_addr, val); | |
101 | ||
102 | return val; | |
103 | } | |
104 | ||
105 | static int pfe_phy_write(struct mii_dev *bus, int phy_addr, int dev_addr, | |
106 | int reg_addr, u16 data) | |
107 | { | |
108 | void *reg_base = bus->priv; | |
109 | u32 reg; | |
110 | u32 phy; | |
111 | u32 reg_data; | |
112 | int timeout = MDIO_TIMEOUT; | |
113 | int val; | |
114 | ||
115 | if (dev_addr == MDIO_DEVAD_NONE) { | |
116 | reg = ((reg_addr & EMAC_MII_DATA_RA_MASK) << | |
117 | EMAC_MII_DATA_RA_SHIFT); | |
118 | } else { | |
119 | pfe_write_addr(bus, phy_addr, dev_addr, reg_addr); | |
120 | reg = ((dev_addr & EMAC_MII_DATA_RA_MASK) << | |
121 | EMAC_MII_DATA_RA_SHIFT); | |
122 | } | |
123 | ||
124 | phy = ((phy_addr & EMAC_MII_DATA_PA_MASK) << EMAC_MII_DATA_PA_SHIFT); | |
125 | ||
126 | if (dev_addr == MDIO_DEVAD_NONE) | |
127 | reg_data = (EMAC_MII_DATA_ST | EMAC_MII_DATA_OP_WR | | |
128 | EMAC_MII_DATA_TA | phy | reg | data); | |
129 | else | |
130 | reg_data = (EMAC_MII_DATA_OP_CL45_WR | EMAC_MII_DATA_TA | | |
131 | phy | reg | data); | |
132 | ||
133 | writel(reg_data, reg_base + EMAC_MII_DATA_REG); | |
134 | ||
135 | /* | |
136 | * wait for the MII interrupt | |
137 | */ | |
138 | while (!(readl(reg_base + EMAC_IEVENT_REG) & EMAC_IEVENT_MII)) { | |
139 | if (timeout-- <= 0) { | |
140 | printf("Phy MDIO read/write timeout\n"); | |
141 | return -1; | |
142 | } | |
143 | } | |
144 | ||
145 | /* | |
146 | * clear MII interrupt | |
147 | */ | |
148 | writel(EMAC_IEVENT_MII, reg_base + EMAC_IEVENT_REG); | |
149 | ||
150 | debug("%s: phy: %02x reg:%02x val:%#x\n", __func__, phy_addr, | |
151 | reg_addr, data); | |
152 | ||
153 | return val; | |
154 | } | |
155 | ||
156 | static void pfe_configure_serdes(struct pfe_eth_dev *priv) | |
157 | { | |
158 | struct mii_dev bus; | |
159 | int value, sgmii_2500 = 0; | |
160 | struct gemac_s *gem = priv->gem; | |
161 | ||
162 | if (gem->phy_mode == PHY_INTERFACE_MODE_SGMII_2500) | |
163 | sgmii_2500 = 1; | |
164 | ||
165 | printf("%s %d\n", __func__, priv->gemac_port); | |
166 | ||
167 | /* PCS configuration done with corresponding GEMAC */ | |
168 | bus.priv = gem_info[priv->gemac_port].gemac_base; | |
169 | ||
170 | pfe_phy_read(&bus, 0, MDIO_DEVAD_NONE, 0x0); | |
171 | pfe_phy_read(&bus, 0, MDIO_DEVAD_NONE, 0x1); | |
172 | pfe_phy_read(&bus, 0, MDIO_DEVAD_NONE, 0x2); | |
173 | pfe_phy_read(&bus, 0, MDIO_DEVAD_NONE, 0x3); | |
174 | ||
175 | /* Reset serdes */ | |
176 | pfe_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x0, 0x8000); | |
177 | ||
178 | /* SGMII IF mode + AN enable only for 1G SGMII, not for 2.5G */ | |
179 | value = PHY_SGMII_IF_MODE_SGMII; | |
180 | if (!sgmii_2500) | |
181 | value |= PHY_SGMII_IF_MODE_AN; | |
182 | else | |
183 | value |= PHY_SGMII_IF_MODE_SGMII_GBT; | |
184 | ||
185 | pfe_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x14, value); | |
186 | ||
187 | /* Dev ability according to SGMII specification */ | |
188 | value = PHY_SGMII_DEV_ABILITY_SGMII; | |
189 | pfe_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x4, value); | |
190 | ||
191 | /* These values taken from validation team */ | |
192 | if (!sgmii_2500) { | |
193 | pfe_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x13, 0x0); | |
194 | pfe_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x12, 0x400); | |
195 | } else { | |
196 | pfe_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x13, 0x7); | |
197 | pfe_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x12, 0xa120); | |
198 | } | |
199 | ||
200 | /* Restart AN */ | |
201 | value = PHY_SGMII_CR_DEF_VAL; | |
202 | if (!sgmii_2500) | |
203 | value |= PHY_SGMII_CR_RESET_AN; | |
204 | /* Disable Auto neg for 2.5G SGMII as it doesn't support auto neg*/ | |
205 | if (sgmii_2500) | |
206 | value &= ~PHY_SGMII_ENABLE_AN; | |
207 | pfe_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0, value); | |
208 | } | |
209 | ||
210 | int pfe_phy_configure(struct pfe_eth_dev *priv, int dev_id, int phy_id) | |
211 | { | |
212 | struct phy_device *phydev = NULL; | |
213 | struct udevice *dev = priv->dev; | |
214 | struct gemac_s *gem = priv->gem; | |
215 | struct ccsr_scfg *scfg = (struct ccsr_scfg *)CONFIG_SYS_FSL_SCFG_ADDR; | |
216 | ||
217 | if (!gem->bus) | |
218 | return -1; | |
219 | ||
220 | /* Configure SGMII PCS */ | |
221 | if (gem->phy_mode == PHY_INTERFACE_MODE_SGMII || | |
222 | gem->phy_mode == PHY_INTERFACE_MODE_SGMII_2500) { | |
223 | out_be32(&scfg->mdioselcr, 0x00000000); | |
224 | pfe_configure_serdes(priv); | |
225 | } | |
226 | ||
227 | mdelay(100); | |
228 | ||
229 | /* By this time on-chip SGMII initialization is done | |
230 | * we can switch mdio interface to external PHYs | |
231 | */ | |
232 | out_be32(&scfg->mdioselcr, 0x80000000); | |
233 | ||
234 | phydev = phy_connect(gem->bus, phy_id, dev, gem->phy_mode); | |
235 | if (!phydev) { | |
236 | printf("phy_connect failed\n"); | |
237 | return -ENODEV; | |
238 | } | |
239 | ||
240 | phy_config(phydev); | |
241 | ||
242 | priv->phydev = phydev; | |
243 | ||
244 | return 0; | |
245 | } | |
246 | #endif | |
247 | ||
248 | struct mii_dev *pfe_mdio_init(struct pfe_mdio_info *mdio_info) | |
249 | { | |
250 | struct mii_dev *bus; | |
251 | int ret; | |
252 | u32 mdio_speed; | |
253 | u32 pclk = 250000000; | |
254 | ||
255 | bus = mdio_alloc(); | |
256 | if (!bus) { | |
257 | printf("mdio_alloc failed\n"); | |
258 | return NULL; | |
259 | } | |
260 | bus->read = pfe_phy_read; | |
261 | bus->write = pfe_phy_write; | |
262 | ||
263 | /* MAC1 MDIO used to communicate with external PHYS */ | |
264 | bus->priv = mdio_info->reg_base; | |
265 | sprintf(bus->name, mdio_info->name); | |
266 | ||
267 | /* configure mdio speed */ | |
268 | mdio_speed = (DIV_ROUND_UP(pclk, 4000000) << EMAC_MII_SPEED_SHIFT); | |
269 | mdio_speed |= EMAC_HOLDTIME(0x5); | |
270 | writel(mdio_speed, mdio_info->reg_base + EMAC_MII_CTRL_REG); | |
271 | ||
272 | ret = mdio_register(bus); | |
273 | if (ret) { | |
274 | printf("mdio_register failed\n"); | |
275 | free(bus); | |
276 | return NULL; | |
277 | } | |
278 | return bus; | |
279 | } | |
280 | ||
281 | void pfe_set_mdio(int dev_id, struct mii_dev *bus) | |
282 | { | |
283 | gem_info[dev_id].bus = bus; | |
284 | } | |
285 | ||
286 | void pfe_set_phy_address_mode(int dev_id, int phy_id, int phy_mode) | |
287 | { | |
288 | gem_info[dev_id].phy_address = phy_id; | |
289 | gem_info[dev_id].phy_mode = phy_mode; | |
290 | } |