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