]>
Commit | Line | Data |
---|---|---|
9622972a ÁFR |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Copyright (C) 2018 Álvaro Fernández Rojas <[email protected]> | |
4 | * | |
5 | * Derived from linux/drivers/net/ethernet/broadcom/bcm63xx_enet.c: | |
6 | * Copyright (C) 2008 Maxime Bizon <[email protected]> | |
7 | */ | |
8 | ||
9 | #include <common.h> | |
10 | #include <clk.h> | |
11 | #include <dm.h> | |
12 | #include <dma.h> | |
f7ae49fc | 13 | #include <log.h> |
336d4615 | 14 | #include <malloc.h> |
9622972a ÁFR |
15 | #include <miiphy.h> |
16 | #include <net.h> | |
17 | #include <reset.h> | |
18 | #include <wait_bit.h> | |
19 | #include <asm/io.h> | |
336d4615 | 20 | #include <dm/device_compat.h> |
c05ed00a | 21 | #include <linux/delay.h> |
9622972a ÁFR |
22 | |
23 | #define ETH_PORT_STR "brcm,enetsw-port" | |
24 | ||
25 | #define ETH_RX_DESC PKTBUFSRX | |
26 | #define ETH_ZLEN 60 | |
27 | #define ETH_TIMEOUT 100 | |
28 | ||
29 | #define ETH_MAX_PORT 8 | |
30 | #define ETH_RGMII_PORT0 4 | |
31 | ||
32 | /* Port traffic control */ | |
33 | #define ETH_PTCTRL_REG(x) (0x0 + (x)) | |
34 | #define ETH_PTCTRL_RXDIS_SHIFT 0 | |
35 | #define ETH_PTCTRL_RXDIS_MASK (1 << ETH_PTCTRL_RXDIS_SHIFT) | |
36 | #define ETH_PTCTRL_TXDIS_SHIFT 1 | |
37 | #define ETH_PTCTRL_TXDIS_MASK (1 << ETH_PTCTRL_TXDIS_SHIFT) | |
38 | ||
39 | /* Switch mode register */ | |
40 | #define ETH_SWMODE_REG 0xb | |
41 | #define ETH_SWMODE_FWD_EN_SHIFT 1 | |
42 | #define ETH_SWMODE_FWD_EN_MASK (1 << ETH_SWMODE_FWD_EN_SHIFT) | |
43 | ||
44 | /* IMP override Register */ | |
45 | #define ETH_IMPOV_REG 0xe | |
46 | #define ETH_IMPOV_LINKUP_SHIFT 0 | |
47 | #define ETH_IMPOV_LINKUP_MASK (1 << ETH_IMPOV_LINKUP_SHIFT) | |
48 | #define ETH_IMPOV_FDX_SHIFT 1 | |
49 | #define ETH_IMPOV_FDX_MASK (1 << ETH_IMPOV_FDX_SHIFT) | |
50 | #define ETH_IMPOV_100_SHIFT 2 | |
51 | #define ETH_IMPOV_100_MASK (1 << ETH_IMPOV_100_SHIFT) | |
52 | #define ETH_IMPOV_1000_SHIFT 3 | |
53 | #define ETH_IMPOV_1000_MASK (1 << ETH_IMPOV_1000_SHIFT) | |
54 | #define ETH_IMPOV_RXFLOW_SHIFT 4 | |
55 | #define ETH_IMPOV_RXFLOW_MASK (1 << ETH_IMPOV_RXFLOW_SHIFT) | |
56 | #define ETH_IMPOV_TXFLOW_SHIFT 5 | |
57 | #define ETH_IMPOV_TXFLOW_MASK (1 << ETH_IMPOV_TXFLOW_SHIFT) | |
58 | #define ETH_IMPOV_FORCE_SHIFT 7 | |
59 | #define ETH_IMPOV_FORCE_MASK (1 << ETH_IMPOV_FORCE_SHIFT) | |
60 | ||
61 | /* Port override Register */ | |
62 | #define ETH_PORTOV_REG(x) (0x58 + (x)) | |
63 | #define ETH_PORTOV_LINKUP_SHIFT 0 | |
64 | #define ETH_PORTOV_LINKUP_MASK (1 << ETH_PORTOV_LINKUP_SHIFT) | |
65 | #define ETH_PORTOV_FDX_SHIFT 1 | |
66 | #define ETH_PORTOV_FDX_MASK (1 << ETH_PORTOV_FDX_SHIFT) | |
67 | #define ETH_PORTOV_100_SHIFT 2 | |
68 | #define ETH_PORTOV_100_MASK (1 << ETH_PORTOV_100_SHIFT) | |
69 | #define ETH_PORTOV_1000_SHIFT 3 | |
70 | #define ETH_PORTOV_1000_MASK (1 << ETH_PORTOV_1000_SHIFT) | |
71 | #define ETH_PORTOV_RXFLOW_SHIFT 4 | |
72 | #define ETH_PORTOV_RXFLOW_MASK (1 << ETH_PORTOV_RXFLOW_SHIFT) | |
73 | #define ETH_PORTOV_TXFLOW_SHIFT 5 | |
74 | #define ETH_PORTOV_TXFLOW_MASK (1 << ETH_PORTOV_TXFLOW_SHIFT) | |
75 | #define ETH_PORTOV_ENABLE_SHIFT 6 | |
76 | #define ETH_PORTOV_ENABLE_MASK (1 << ETH_PORTOV_ENABLE_SHIFT) | |
77 | ||
78 | /* Port RGMII control register */ | |
79 | #define ETH_RGMII_CTRL_REG(x) (0x60 + (x)) | |
80 | #define ETH_RGMII_CTRL_GMII_CLK_EN (1 << 7) | |
81 | #define ETH_RGMII_CTRL_MII_OVERRIDE_EN (1 << 6) | |
82 | #define ETH_RGMII_CTRL_MII_MODE_MASK (3 << 4) | |
83 | #define ETH_RGMII_CTRL_RGMII_MODE (0 << 4) | |
84 | #define ETH_RGMII_CTRL_MII_MODE (1 << 4) | |
85 | #define ETH_RGMII_CTRL_RVMII_MODE (2 << 4) | |
86 | #define ETH_RGMII_CTRL_TIMING_SEL_EN (1 << 0) | |
87 | ||
88 | /* Port RGMII timing register */ | |
89 | #define ENETSW_RGMII_TIMING_REG(x) (0x68 + (x)) | |
90 | ||
91 | /* MDIO control register */ | |
92 | #define MII_SC_REG 0xb0 | |
93 | #define MII_SC_EXT_SHIFT 16 | |
94 | #define MII_SC_EXT_MASK (1 << MII_SC_EXT_SHIFT) | |
95 | #define MII_SC_REG_SHIFT 20 | |
96 | #define MII_SC_PHYID_SHIFT 25 | |
97 | #define MII_SC_RD_SHIFT 30 | |
98 | #define MII_SC_RD_MASK (1 << MII_SC_RD_SHIFT) | |
99 | #define MII_SC_WR_SHIFT 31 | |
100 | #define MII_SC_WR_MASK (1 << MII_SC_WR_SHIFT) | |
101 | ||
102 | /* MDIO data register */ | |
103 | #define MII_DAT_REG 0xb4 | |
104 | ||
105 | /* Global Management Configuration Register */ | |
106 | #define ETH_GMCR_REG 0x200 | |
107 | #define ETH_GMCR_RST_MIB_SHIFT 0 | |
108 | #define ETH_GMCR_RST_MIB_MASK (1 << ETH_GMCR_RST_MIB_SHIFT) | |
109 | ||
110 | /* Jumbo control register port mask register */ | |
111 | #define ETH_JMBCTL_PORT_REG 0x4004 | |
112 | ||
113 | /* Jumbo control mib good frame register */ | |
114 | #define ETH_JMBCTL_MAXSIZE_REG 0x4008 | |
115 | ||
116 | /* ETH port data */ | |
117 | struct bcm_enetsw_port { | |
118 | bool used; | |
119 | const char *name; | |
120 | /* Config */ | |
121 | bool bypass_link; | |
122 | int force_speed; | |
123 | bool force_duplex_full; | |
124 | /* PHY */ | |
125 | int phy_id; | |
126 | }; | |
127 | ||
128 | /* ETH data */ | |
129 | struct bcm6368_eth_priv { | |
130 | void __iomem *base; | |
131 | /* DMA */ | |
132 | struct dma rx_dma; | |
133 | struct dma tx_dma; | |
134 | /* Ports */ | |
135 | uint8_t num_ports; | |
136 | struct bcm_enetsw_port used_ports[ETH_MAX_PORT]; | |
137 | int sw_port_link[ETH_MAX_PORT]; | |
138 | bool rgmii_override; | |
139 | bool rgmii_timing; | |
140 | /* PHY */ | |
141 | int phy_id; | |
142 | }; | |
143 | ||
144 | static inline bool bcm_enet_port_is_rgmii(int portid) | |
145 | { | |
146 | return portid >= ETH_RGMII_PORT0; | |
147 | } | |
148 | ||
149 | static int bcm6368_mdio_read(struct bcm6368_eth_priv *priv, uint8_t ext, | |
150 | int phy_id, int reg) | |
151 | { | |
152 | uint32_t val; | |
153 | ||
154 | writel_be(0, priv->base + MII_SC_REG); | |
155 | ||
156 | val = MII_SC_RD_MASK | | |
157 | (phy_id << MII_SC_PHYID_SHIFT) | | |
158 | (reg << MII_SC_REG_SHIFT); | |
159 | ||
160 | if (ext) | |
161 | val |= MII_SC_EXT_MASK; | |
162 | ||
163 | writel_be(val, priv->base + MII_SC_REG); | |
164 | udelay(50); | |
165 | ||
166 | return readw_be(priv->base + MII_DAT_REG); | |
167 | } | |
168 | ||
169 | static int bcm6368_mdio_write(struct bcm6368_eth_priv *priv, uint8_t ext, | |
170 | int phy_id, int reg, u16 data) | |
171 | { | |
172 | uint32_t val; | |
173 | ||
174 | writel_be(0, priv->base + MII_SC_REG); | |
175 | ||
176 | val = MII_SC_WR_MASK | | |
177 | (phy_id << MII_SC_PHYID_SHIFT) | | |
178 | (reg << MII_SC_REG_SHIFT); | |
179 | ||
180 | if (ext) | |
181 | val |= MII_SC_EXT_MASK; | |
182 | ||
183 | val |= data; | |
184 | ||
185 | writel_be(val, priv->base + MII_SC_REG); | |
186 | udelay(50); | |
187 | ||
188 | return 0; | |
189 | } | |
190 | ||
191 | static int bcm6368_eth_free_pkt(struct udevice *dev, uchar *packet, int len) | |
192 | { | |
193 | struct bcm6368_eth_priv *priv = dev_get_priv(dev); | |
194 | ||
195 | return dma_prepare_rcv_buf(&priv->rx_dma, packet, len); | |
196 | } | |
197 | ||
198 | static int bcm6368_eth_recv(struct udevice *dev, int flags, uchar **packetp) | |
199 | { | |
200 | struct bcm6368_eth_priv *priv = dev_get_priv(dev); | |
201 | ||
202 | return dma_receive(&priv->rx_dma, (void**)packetp, NULL); | |
203 | } | |
204 | ||
205 | static int bcm6368_eth_send(struct udevice *dev, void *packet, int length) | |
206 | { | |
207 | struct bcm6368_eth_priv *priv = dev_get_priv(dev); | |
208 | ||
209 | /* pad packets smaller than ETH_ZLEN */ | |
210 | if (length < ETH_ZLEN) { | |
211 | memset(packet + length, 0, ETH_ZLEN - length); | |
212 | length = ETH_ZLEN; | |
213 | } | |
214 | ||
215 | return dma_send(&priv->tx_dma, packet, length, NULL); | |
216 | } | |
217 | ||
218 | static int bcm6368_eth_adjust_link(struct udevice *dev) | |
219 | { | |
220 | struct bcm6368_eth_priv *priv = dev_get_priv(dev); | |
221 | unsigned int i; | |
222 | ||
223 | for (i = 0; i < priv->num_ports; i++) { | |
224 | struct bcm_enetsw_port *port; | |
225 | int val, j, up, adv, lpa, speed, duplex, media; | |
226 | int external_phy = bcm_enet_port_is_rgmii(i); | |
227 | u8 override; | |
228 | ||
229 | port = &priv->used_ports[i]; | |
230 | if (!port->used) | |
231 | continue; | |
232 | ||
233 | if (port->bypass_link) | |
234 | continue; | |
235 | ||
236 | /* dummy read to clear */ | |
237 | for (j = 0; j < 2; j++) | |
238 | val = bcm6368_mdio_read(priv, external_phy, | |
239 | port->phy_id, MII_BMSR); | |
240 | ||
241 | if (val == 0xffff) | |
242 | continue; | |
243 | ||
244 | up = (val & BMSR_LSTATUS) ? 1 : 0; | |
245 | if (!(up ^ priv->sw_port_link[i])) | |
246 | continue; | |
247 | ||
248 | priv->sw_port_link[i] = up; | |
249 | ||
250 | /* link changed */ | |
251 | if (!up) { | |
1485d649 | 252 | dev_info(dev, "link DOWN on %s\n", port->name); |
9622972a ÁFR |
253 | writeb_be(ETH_PORTOV_ENABLE_MASK, |
254 | priv->base + ETH_PORTOV_REG(i)); | |
255 | writeb_be(ETH_PTCTRL_RXDIS_MASK | | |
256 | ETH_PTCTRL_TXDIS_MASK, | |
257 | priv->base + ETH_PTCTRL_REG(i)); | |
258 | continue; | |
259 | } | |
260 | ||
261 | adv = bcm6368_mdio_read(priv, external_phy, | |
262 | port->phy_id, MII_ADVERTISE); | |
263 | ||
264 | lpa = bcm6368_mdio_read(priv, external_phy, port->phy_id, | |
265 | MII_LPA); | |
266 | ||
267 | /* figure out media and duplex from advertise and LPA values */ | |
268 | media = mii_nway_result(lpa & adv); | |
269 | duplex = (media & ADVERTISE_FULL) ? 1 : 0; | |
270 | ||
271 | if (media & (ADVERTISE_100FULL | ADVERTISE_100HALF)) | |
272 | speed = 100; | |
273 | else | |
274 | speed = 10; | |
275 | ||
276 | if (val & BMSR_ESTATEN) { | |
277 | adv = bcm6368_mdio_read(priv, external_phy, | |
278 | port->phy_id, MII_CTRL1000); | |
279 | ||
280 | lpa = bcm6368_mdio_read(priv, external_phy, | |
281 | port->phy_id, MII_STAT1000); | |
282 | ||
283 | if ((adv & (ADVERTISE_1000FULL | ADVERTISE_1000HALF)) && | |
284 | (lpa & (LPA_1000FULL | LPA_1000HALF))) { | |
285 | speed = 1000; | |
286 | duplex = (lpa & LPA_1000FULL); | |
287 | } | |
288 | } | |
289 | ||
290 | pr_alert("link UP on %s, %dMbps, %s-duplex\n", | |
291 | port->name, speed, duplex ? "full" : "half"); | |
292 | ||
293 | override = ETH_PORTOV_ENABLE_MASK | | |
294 | ETH_PORTOV_LINKUP_MASK; | |
295 | ||
296 | if (speed == 1000) | |
297 | override |= ETH_PORTOV_1000_MASK; | |
298 | else if (speed == 100) | |
299 | override |= ETH_PORTOV_100_MASK; | |
300 | if (duplex) | |
301 | override |= ETH_PORTOV_FDX_MASK; | |
302 | ||
303 | writeb_be(override, priv->base + ETH_PORTOV_REG(i)); | |
304 | writeb_be(0, priv->base + ETH_PTCTRL_REG(i)); | |
305 | } | |
306 | ||
307 | return 0; | |
308 | } | |
309 | ||
310 | static int bcm6368_eth_start(struct udevice *dev) | |
311 | { | |
312 | struct bcm6368_eth_priv *priv = dev_get_priv(dev); | |
313 | uint8_t i; | |
314 | ||
a4ae4225 ÁFR |
315 | /* disable all ports */ |
316 | for (i = 0; i < priv->num_ports; i++) { | |
317 | setbits_8(priv->base + ETH_PORTOV_REG(i), | |
318 | ETH_PORTOV_ENABLE_MASK); | |
319 | setbits_8(priv->base + ETH_PTCTRL_REG(i), | |
320 | ETH_PTCTRL_RXDIS_MASK | ETH_PTCTRL_TXDIS_MASK); | |
321 | priv->sw_port_link[i] = 0; | |
322 | } | |
323 | ||
324 | /* enable external ports */ | |
325 | for (i = ETH_RGMII_PORT0; i < priv->num_ports; i++) { | |
326 | u8 rgmii_ctrl = ETH_RGMII_CTRL_GMII_CLK_EN; | |
327 | ||
328 | if (!priv->used_ports[i].used) | |
329 | continue; | |
330 | ||
331 | if (priv->rgmii_override) | |
332 | rgmii_ctrl |= ETH_RGMII_CTRL_MII_OVERRIDE_EN; | |
333 | if (priv->rgmii_timing) | |
334 | rgmii_ctrl |= ETH_RGMII_CTRL_TIMING_SEL_EN; | |
335 | ||
336 | setbits_8(priv->base + ETH_RGMII_CTRL_REG(i), rgmii_ctrl); | |
337 | } | |
338 | ||
339 | /* reset mib */ | |
340 | setbits_8(priv->base + ETH_GMCR_REG, ETH_GMCR_RST_MIB_MASK); | |
341 | mdelay(1); | |
342 | clrbits_8(priv->base + ETH_GMCR_REG, ETH_GMCR_RST_MIB_MASK); | |
343 | mdelay(1); | |
344 | ||
345 | /* force CPU port state */ | |
346 | setbits_8(priv->base + ETH_IMPOV_REG, | |
347 | ETH_IMPOV_FORCE_MASK | ETH_IMPOV_LINKUP_MASK); | |
348 | ||
349 | /* enable switch forward engine */ | |
350 | setbits_8(priv->base + ETH_SWMODE_REG, ETH_SWMODE_FWD_EN_MASK); | |
351 | ||
9622972a ÁFR |
352 | /* prepare rx dma buffers */ |
353 | for (i = 0; i < ETH_RX_DESC; i++) { | |
354 | int ret = dma_prepare_rcv_buf(&priv->rx_dma, net_rx_packets[i], | |
355 | PKTSIZE_ALIGN); | |
356 | if (ret < 0) | |
357 | break; | |
358 | } | |
359 | ||
360 | /* enable dma rx channel */ | |
361 | dma_enable(&priv->rx_dma); | |
362 | ||
363 | /* enable dma tx channel */ | |
364 | dma_enable(&priv->tx_dma); | |
365 | ||
366 | /* apply override config for bypass_link ports here. */ | |
367 | for (i = 0; i < priv->num_ports; i++) { | |
368 | struct bcm_enetsw_port *port; | |
369 | u8 override; | |
370 | ||
371 | port = &priv->used_ports[i]; | |
372 | if (!port->used) | |
373 | continue; | |
374 | ||
375 | if (!port->bypass_link) | |
376 | continue; | |
377 | ||
378 | override = ETH_PORTOV_ENABLE_MASK | | |
379 | ETH_PORTOV_LINKUP_MASK; | |
380 | ||
381 | switch (port->force_speed) { | |
382 | case 1000: | |
383 | override |= ETH_PORTOV_1000_MASK; | |
384 | break; | |
385 | case 100: | |
386 | override |= ETH_PORTOV_100_MASK; | |
387 | break; | |
388 | case 10: | |
389 | break; | |
390 | default: | |
391 | pr_warn("%s: invalid forced speed on port %s\n", | |
392 | __func__, port->name); | |
393 | break; | |
394 | } | |
395 | ||
396 | if (port->force_duplex_full) | |
397 | override |= ETH_PORTOV_FDX_MASK; | |
398 | ||
399 | writeb_be(override, priv->base + ETH_PORTOV_REG(i)); | |
400 | writeb_be(0, priv->base + ETH_PTCTRL_REG(i)); | |
401 | } | |
402 | ||
403 | bcm6368_eth_adjust_link(dev); | |
404 | ||
405 | return 0; | |
406 | } | |
407 | ||
408 | static void bcm6368_eth_stop(struct udevice *dev) | |
409 | { | |
410 | struct bcm6368_eth_priv *priv = dev_get_priv(dev); | |
a4ae4225 ÁFR |
411 | uint8_t i; |
412 | ||
413 | /* disable all ports */ | |
414 | for (i = 0; i < priv->num_ports; i++) { | |
415 | setbits_8(priv->base + ETH_PORTOV_REG(i), | |
416 | ETH_PORTOV_ENABLE_MASK); | |
417 | setbits_8(priv->base + ETH_PTCTRL_REG(i), | |
418 | ETH_PTCTRL_RXDIS_MASK | ETH_PTCTRL_TXDIS_MASK); | |
419 | } | |
420 | ||
421 | /* disable external ports */ | |
422 | for (i = ETH_RGMII_PORT0; i < priv->num_ports; i++) { | |
423 | if (!priv->used_ports[i].used) | |
424 | continue; | |
425 | ||
426 | clrbits_8(priv->base + ETH_RGMII_CTRL_REG(i), | |
427 | ETH_RGMII_CTRL_GMII_CLK_EN); | |
428 | } | |
429 | ||
430 | /* disable CPU port */ | |
431 | clrbits_8(priv->base + ETH_IMPOV_REG, | |
432 | ETH_IMPOV_FORCE_MASK | ETH_IMPOV_LINKUP_MASK); | |
433 | ||
434 | /* disable switch forward engine */ | |
435 | clrbits_8(priv->base + ETH_SWMODE_REG, ETH_SWMODE_FWD_EN_MASK); | |
9622972a ÁFR |
436 | |
437 | /* disable dma rx channel */ | |
438 | dma_disable(&priv->rx_dma); | |
439 | ||
440 | /* disable dma tx channel */ | |
441 | dma_disable(&priv->tx_dma); | |
442 | } | |
443 | ||
444 | static const struct eth_ops bcm6368_eth_ops = { | |
445 | .free_pkt = bcm6368_eth_free_pkt, | |
446 | .recv = bcm6368_eth_recv, | |
447 | .send = bcm6368_eth_send, | |
448 | .start = bcm6368_eth_start, | |
449 | .stop = bcm6368_eth_stop, | |
450 | }; | |
451 | ||
452 | static const struct udevice_id bcm6368_eth_ids[] = { | |
453 | { .compatible = "brcm,bcm6368-enet", }, | |
454 | { /* sentinel */ } | |
455 | }; | |
456 | ||
457 | static bool bcm6368_phy_is_external(struct bcm6368_eth_priv *priv, int phy_id) | |
458 | { | |
459 | uint8_t i; | |
460 | ||
461 | for (i = 0; i < priv->num_ports; ++i) { | |
462 | if (!priv->used_ports[i].used) | |
463 | continue; | |
464 | if (priv->used_ports[i].phy_id == phy_id) | |
465 | return bcm_enet_port_is_rgmii(i); | |
466 | } | |
467 | ||
468 | return true; | |
469 | } | |
470 | ||
471 | static int bcm6368_mii_mdio_read(struct mii_dev *bus, int addr, int devaddr, | |
472 | int reg) | |
473 | { | |
474 | struct bcm6368_eth_priv *priv = bus->priv; | |
475 | bool ext = bcm6368_phy_is_external(priv, addr); | |
476 | ||
477 | return bcm6368_mdio_read(priv, ext, addr, reg); | |
478 | } | |
479 | ||
480 | static int bcm6368_mii_mdio_write(struct mii_dev *bus, int addr, int devaddr, | |
481 | int reg, u16 data) | |
482 | { | |
483 | struct bcm6368_eth_priv *priv = bus->priv; | |
484 | bool ext = bcm6368_phy_is_external(priv, addr); | |
485 | ||
486 | return bcm6368_mdio_write(priv, ext, addr, reg, data); | |
487 | } | |
488 | ||
489 | static int bcm6368_mdio_init(const char *name, struct bcm6368_eth_priv *priv) | |
490 | { | |
491 | struct mii_dev *bus; | |
492 | ||
493 | bus = mdio_alloc(); | |
494 | if (!bus) { | |
495 | pr_err("%s: failed to allocate MDIO bus\n", __func__); | |
496 | return -ENOMEM; | |
497 | } | |
498 | ||
499 | bus->read = bcm6368_mii_mdio_read; | |
500 | bus->write = bcm6368_mii_mdio_write; | |
501 | bus->priv = priv; | |
502 | snprintf(bus->name, sizeof(bus->name), "%s", name); | |
503 | ||
504 | return mdio_register(bus); | |
505 | } | |
506 | ||
507 | static int bcm6368_eth_probe(struct udevice *dev) | |
508 | { | |
c69cda25 | 509 | struct eth_pdata *pdata = dev_get_plat(dev); |
9622972a ÁFR |
510 | struct bcm6368_eth_priv *priv = dev_get_priv(dev); |
511 | int num_ports, ret, i; | |
9622972a ÁFR |
512 | ofnode node; |
513 | ||
514 | /* get base address */ | |
515 | priv->base = dev_remap_addr(dev); | |
516 | if (!priv->base) | |
517 | return -EINVAL; | |
518 | pdata->iobase = (phys_addr_t) priv->base; | |
519 | ||
520 | /* get number of ports */ | |
521 | num_ports = dev_read_u32_default(dev, "brcm,num-ports", ETH_MAX_PORT); | |
522 | if (!num_ports || num_ports > ETH_MAX_PORT) | |
523 | return -EINVAL; | |
524 | ||
525 | /* get dma channels */ | |
526 | ret = dma_get_by_name(dev, "tx", &priv->tx_dma); | |
527 | if (ret) | |
528 | return -EINVAL; | |
529 | ||
530 | ret = dma_get_by_name(dev, "rx", &priv->rx_dma); | |
531 | if (ret) | |
532 | return -EINVAL; | |
533 | ||
534 | /* try to enable clocks */ | |
535 | for (i = 0; ; i++) { | |
536 | struct clk clk; | |
537 | int ret; | |
538 | ||
539 | ret = clk_get_by_index(dev, i, &clk); | |
540 | if (ret < 0) | |
541 | break; | |
542 | ||
543 | ret = clk_enable(&clk); | |
544 | if (ret < 0) { | |
545 | pr_err("%s: error enabling clock %d\n", __func__, i); | |
546 | return ret; | |
547 | } | |
548 | ||
549 | ret = clk_free(&clk); | |
550 | if (ret < 0) { | |
551 | pr_err("%s: error freeing clock %d\n", __func__, i); | |
552 | return ret; | |
553 | } | |
554 | } | |
555 | ||
556 | /* try to perform resets */ | |
557 | for (i = 0; ; i++) { | |
558 | struct reset_ctl reset; | |
559 | int ret; | |
560 | ||
561 | ret = reset_get_by_index(dev, i, &reset); | |
562 | if (ret < 0) | |
563 | break; | |
564 | ||
565 | ret = reset_deassert(&reset); | |
566 | if (ret < 0) { | |
567 | pr_err("%s: error deasserting reset %d\n", __func__, i); | |
568 | return ret; | |
569 | } | |
570 | ||
571 | ret = reset_free(&reset); | |
572 | if (ret < 0) { | |
573 | pr_err("%s: error freeing reset %d\n", __func__, i); | |
574 | return ret; | |
575 | } | |
576 | } | |
577 | ||
578 | /* set priv data */ | |
579 | priv->num_ports = num_ports; | |
580 | if (dev_read_bool(dev, "brcm,rgmii-override")) | |
581 | priv->rgmii_override = true; | |
582 | if (dev_read_bool(dev, "brcm,rgmii-timing")) | |
583 | priv->rgmii_timing = true; | |
584 | ||
585 | /* get ports */ | |
586 | dev_for_each_subnode(node, dev) { | |
587 | const char *comp; | |
588 | const char *label; | |
589 | unsigned int p; | |
590 | int phy_id; | |
591 | int speed; | |
592 | ||
593 | comp = ofnode_read_string(node, "compatible"); | |
594 | if (!comp || memcmp(comp, ETH_PORT_STR, sizeof(ETH_PORT_STR))) | |
595 | continue; | |
596 | ||
597 | p = ofnode_read_u32_default(node, "reg", ETH_MAX_PORT); | |
598 | if (p >= num_ports) | |
599 | return -EINVAL; | |
600 | ||
601 | label = ofnode_read_string(node, "label"); | |
602 | if (!label) { | |
603 | debug("%s: node %s has no label\n", __func__, | |
604 | ofnode_get_name(node)); | |
605 | return -EINVAL; | |
606 | } | |
607 | ||
608 | phy_id = ofnode_read_u32_default(node, "brcm,phy-id", -1); | |
609 | ||
610 | priv->used_ports[p].used = true; | |
611 | priv->used_ports[p].name = label; | |
612 | priv->used_ports[p].phy_id = phy_id; | |
613 | ||
614 | if (ofnode_read_bool(node, "full-duplex")) | |
615 | priv->used_ports[p].force_duplex_full = true; | |
616 | if (ofnode_read_bool(node, "bypass-link")) | |
617 | priv->used_ports[p].bypass_link = true; | |
618 | speed = ofnode_read_u32_default(node, "speed", 0); | |
619 | if (speed) | |
620 | priv->used_ports[p].force_speed = speed; | |
621 | } | |
622 | ||
623 | /* init mii bus */ | |
624 | ret = bcm6368_mdio_init(dev->name, priv); | |
625 | if (ret) | |
626 | return ret; | |
627 | ||
9622972a ÁFR |
628 | /* enable jumbo on all ports */ |
629 | writel_be(0x1ff, priv->base + ETH_JMBCTL_PORT_REG); | |
630 | writew_be(9728, priv->base + ETH_JMBCTL_MAXSIZE_REG); | |
631 | ||
632 | return 0; | |
633 | } | |
634 | ||
635 | U_BOOT_DRIVER(bcm6368_eth) = { | |
636 | .name = "bcm6368_eth", | |
637 | .id = UCLASS_ETH, | |
638 | .of_match = bcm6368_eth_ids, | |
639 | .ops = &bcm6368_eth_ops, | |
caa4daa2 | 640 | .plat_auto = sizeof(struct eth_pdata), |
41575d8e | 641 | .priv_auto = sizeof(struct bcm6368_eth_priv), |
9622972a ÁFR |
642 | .probe = bcm6368_eth_probe, |
643 | }; |