19622972aSÁlvaro Fernández Rojas // SPDX-License-Identifier: GPL-2.0+
29622972aSÁlvaro Fernández Rojas /*
39622972aSÁlvaro Fernández Rojas * Copyright (C) 2018 Álvaro Fernández Rojas <noltari@gmail.com>
49622972aSÁlvaro Fernández Rojas *
59622972aSÁlvaro Fernández Rojas * Derived from linux/drivers/net/ethernet/broadcom/bcm63xx_enet.c:
69622972aSÁlvaro Fernández Rojas * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
79622972aSÁlvaro Fernández Rojas */
89622972aSÁlvaro Fernández Rojas
99622972aSÁlvaro Fernández Rojas #include <common.h>
109622972aSÁlvaro Fernández Rojas #include <clk.h>
119622972aSÁlvaro Fernández Rojas #include <dm.h>
129622972aSÁlvaro Fernández Rojas #include <dma.h>
139622972aSÁlvaro Fernández Rojas #include <miiphy.h>
149622972aSÁlvaro Fernández Rojas #include <net.h>
159622972aSÁlvaro Fernández Rojas #include <reset.h>
169622972aSÁlvaro Fernández Rojas #include <wait_bit.h>
179622972aSÁlvaro Fernández Rojas #include <asm/io.h>
189622972aSÁlvaro Fernández Rojas
199622972aSÁlvaro Fernández Rojas #define ETH_PORT_STR "brcm,enetsw-port"
209622972aSÁlvaro Fernández Rojas
219622972aSÁlvaro Fernández Rojas #define ETH_RX_DESC PKTBUFSRX
229622972aSÁlvaro Fernández Rojas #define ETH_ZLEN 60
239622972aSÁlvaro Fernández Rojas #define ETH_TIMEOUT 100
249622972aSÁlvaro Fernández Rojas
259622972aSÁlvaro Fernández Rojas #define ETH_MAX_PORT 8
269622972aSÁlvaro Fernández Rojas #define ETH_RGMII_PORT0 4
279622972aSÁlvaro Fernández Rojas
289622972aSÁlvaro Fernández Rojas /* Port traffic control */
299622972aSÁlvaro Fernández Rojas #define ETH_PTCTRL_REG(x) (0x0 + (x))
309622972aSÁlvaro Fernández Rojas #define ETH_PTCTRL_RXDIS_SHIFT 0
319622972aSÁlvaro Fernández Rojas #define ETH_PTCTRL_RXDIS_MASK (1 << ETH_PTCTRL_RXDIS_SHIFT)
329622972aSÁlvaro Fernández Rojas #define ETH_PTCTRL_TXDIS_SHIFT 1
339622972aSÁlvaro Fernández Rojas #define ETH_PTCTRL_TXDIS_MASK (1 << ETH_PTCTRL_TXDIS_SHIFT)
349622972aSÁlvaro Fernández Rojas
359622972aSÁlvaro Fernández Rojas /* Switch mode register */
369622972aSÁlvaro Fernández Rojas #define ETH_SWMODE_REG 0xb
379622972aSÁlvaro Fernández Rojas #define ETH_SWMODE_FWD_EN_SHIFT 1
389622972aSÁlvaro Fernández Rojas #define ETH_SWMODE_FWD_EN_MASK (1 << ETH_SWMODE_FWD_EN_SHIFT)
399622972aSÁlvaro Fernández Rojas
409622972aSÁlvaro Fernández Rojas /* IMP override Register */
419622972aSÁlvaro Fernández Rojas #define ETH_IMPOV_REG 0xe
429622972aSÁlvaro Fernández Rojas #define ETH_IMPOV_LINKUP_SHIFT 0
439622972aSÁlvaro Fernández Rojas #define ETH_IMPOV_LINKUP_MASK (1 << ETH_IMPOV_LINKUP_SHIFT)
449622972aSÁlvaro Fernández Rojas #define ETH_IMPOV_FDX_SHIFT 1
459622972aSÁlvaro Fernández Rojas #define ETH_IMPOV_FDX_MASK (1 << ETH_IMPOV_FDX_SHIFT)
469622972aSÁlvaro Fernández Rojas #define ETH_IMPOV_100_SHIFT 2
479622972aSÁlvaro Fernández Rojas #define ETH_IMPOV_100_MASK (1 << ETH_IMPOV_100_SHIFT)
489622972aSÁlvaro Fernández Rojas #define ETH_IMPOV_1000_SHIFT 3
499622972aSÁlvaro Fernández Rojas #define ETH_IMPOV_1000_MASK (1 << ETH_IMPOV_1000_SHIFT)
509622972aSÁlvaro Fernández Rojas #define ETH_IMPOV_RXFLOW_SHIFT 4
519622972aSÁlvaro Fernández Rojas #define ETH_IMPOV_RXFLOW_MASK (1 << ETH_IMPOV_RXFLOW_SHIFT)
529622972aSÁlvaro Fernández Rojas #define ETH_IMPOV_TXFLOW_SHIFT 5
539622972aSÁlvaro Fernández Rojas #define ETH_IMPOV_TXFLOW_MASK (1 << ETH_IMPOV_TXFLOW_SHIFT)
549622972aSÁlvaro Fernández Rojas #define ETH_IMPOV_FORCE_SHIFT 7
559622972aSÁlvaro Fernández Rojas #define ETH_IMPOV_FORCE_MASK (1 << ETH_IMPOV_FORCE_SHIFT)
569622972aSÁlvaro Fernández Rojas
579622972aSÁlvaro Fernández Rojas /* Port override Register */
589622972aSÁlvaro Fernández Rojas #define ETH_PORTOV_REG(x) (0x58 + (x))
599622972aSÁlvaro Fernández Rojas #define ETH_PORTOV_LINKUP_SHIFT 0
609622972aSÁlvaro Fernández Rojas #define ETH_PORTOV_LINKUP_MASK (1 << ETH_PORTOV_LINKUP_SHIFT)
619622972aSÁlvaro Fernández Rojas #define ETH_PORTOV_FDX_SHIFT 1
629622972aSÁlvaro Fernández Rojas #define ETH_PORTOV_FDX_MASK (1 << ETH_PORTOV_FDX_SHIFT)
639622972aSÁlvaro Fernández Rojas #define ETH_PORTOV_100_SHIFT 2
649622972aSÁlvaro Fernández Rojas #define ETH_PORTOV_100_MASK (1 << ETH_PORTOV_100_SHIFT)
659622972aSÁlvaro Fernández Rojas #define ETH_PORTOV_1000_SHIFT 3
669622972aSÁlvaro Fernández Rojas #define ETH_PORTOV_1000_MASK (1 << ETH_PORTOV_1000_SHIFT)
679622972aSÁlvaro Fernández Rojas #define ETH_PORTOV_RXFLOW_SHIFT 4
689622972aSÁlvaro Fernández Rojas #define ETH_PORTOV_RXFLOW_MASK (1 << ETH_PORTOV_RXFLOW_SHIFT)
699622972aSÁlvaro Fernández Rojas #define ETH_PORTOV_TXFLOW_SHIFT 5
709622972aSÁlvaro Fernández Rojas #define ETH_PORTOV_TXFLOW_MASK (1 << ETH_PORTOV_TXFLOW_SHIFT)
719622972aSÁlvaro Fernández Rojas #define ETH_PORTOV_ENABLE_SHIFT 6
729622972aSÁlvaro Fernández Rojas #define ETH_PORTOV_ENABLE_MASK (1 << ETH_PORTOV_ENABLE_SHIFT)
739622972aSÁlvaro Fernández Rojas
749622972aSÁlvaro Fernández Rojas /* Port RGMII control register */
759622972aSÁlvaro Fernández Rojas #define ETH_RGMII_CTRL_REG(x) (0x60 + (x))
769622972aSÁlvaro Fernández Rojas #define ETH_RGMII_CTRL_GMII_CLK_EN (1 << 7)
779622972aSÁlvaro Fernández Rojas #define ETH_RGMII_CTRL_MII_OVERRIDE_EN (1 << 6)
789622972aSÁlvaro Fernández Rojas #define ETH_RGMII_CTRL_MII_MODE_MASK (3 << 4)
799622972aSÁlvaro Fernández Rojas #define ETH_RGMII_CTRL_RGMII_MODE (0 << 4)
809622972aSÁlvaro Fernández Rojas #define ETH_RGMII_CTRL_MII_MODE (1 << 4)
819622972aSÁlvaro Fernández Rojas #define ETH_RGMII_CTRL_RVMII_MODE (2 << 4)
829622972aSÁlvaro Fernández Rojas #define ETH_RGMII_CTRL_TIMING_SEL_EN (1 << 0)
839622972aSÁlvaro Fernández Rojas
849622972aSÁlvaro Fernández Rojas /* Port RGMII timing register */
859622972aSÁlvaro Fernández Rojas #define ENETSW_RGMII_TIMING_REG(x) (0x68 + (x))
869622972aSÁlvaro Fernández Rojas
879622972aSÁlvaro Fernández Rojas /* MDIO control register */
889622972aSÁlvaro Fernández Rojas #define MII_SC_REG 0xb0
899622972aSÁlvaro Fernández Rojas #define MII_SC_EXT_SHIFT 16
909622972aSÁlvaro Fernández Rojas #define MII_SC_EXT_MASK (1 << MII_SC_EXT_SHIFT)
919622972aSÁlvaro Fernández Rojas #define MII_SC_REG_SHIFT 20
929622972aSÁlvaro Fernández Rojas #define MII_SC_PHYID_SHIFT 25
939622972aSÁlvaro Fernández Rojas #define MII_SC_RD_SHIFT 30
949622972aSÁlvaro Fernández Rojas #define MII_SC_RD_MASK (1 << MII_SC_RD_SHIFT)
959622972aSÁlvaro Fernández Rojas #define MII_SC_WR_SHIFT 31
969622972aSÁlvaro Fernández Rojas #define MII_SC_WR_MASK (1 << MII_SC_WR_SHIFT)
979622972aSÁlvaro Fernández Rojas
989622972aSÁlvaro Fernández Rojas /* MDIO data register */
999622972aSÁlvaro Fernández Rojas #define MII_DAT_REG 0xb4
1009622972aSÁlvaro Fernández Rojas
1019622972aSÁlvaro Fernández Rojas /* Global Management Configuration Register */
1029622972aSÁlvaro Fernández Rojas #define ETH_GMCR_REG 0x200
1039622972aSÁlvaro Fernández Rojas #define ETH_GMCR_RST_MIB_SHIFT 0
1049622972aSÁlvaro Fernández Rojas #define ETH_GMCR_RST_MIB_MASK (1 << ETH_GMCR_RST_MIB_SHIFT)
1059622972aSÁlvaro Fernández Rojas
1069622972aSÁlvaro Fernández Rojas /* Jumbo control register port mask register */
1079622972aSÁlvaro Fernández Rojas #define ETH_JMBCTL_PORT_REG 0x4004
1089622972aSÁlvaro Fernández Rojas
1099622972aSÁlvaro Fernández Rojas /* Jumbo control mib good frame register */
1109622972aSÁlvaro Fernández Rojas #define ETH_JMBCTL_MAXSIZE_REG 0x4008
1119622972aSÁlvaro Fernández Rojas
1129622972aSÁlvaro Fernández Rojas /* ETH port data */
1139622972aSÁlvaro Fernández Rojas struct bcm_enetsw_port {
1149622972aSÁlvaro Fernández Rojas bool used;
1159622972aSÁlvaro Fernández Rojas const char *name;
1169622972aSÁlvaro Fernández Rojas /* Config */
1179622972aSÁlvaro Fernández Rojas bool bypass_link;
1189622972aSÁlvaro Fernández Rojas int force_speed;
1199622972aSÁlvaro Fernández Rojas bool force_duplex_full;
1209622972aSÁlvaro Fernández Rojas /* PHY */
1219622972aSÁlvaro Fernández Rojas int phy_id;
1229622972aSÁlvaro Fernández Rojas };
1239622972aSÁlvaro Fernández Rojas
1249622972aSÁlvaro Fernández Rojas /* ETH data */
1259622972aSÁlvaro Fernández Rojas struct bcm6368_eth_priv {
1269622972aSÁlvaro Fernández Rojas void __iomem *base;
1279622972aSÁlvaro Fernández Rojas /* DMA */
1289622972aSÁlvaro Fernández Rojas struct dma rx_dma;
1299622972aSÁlvaro Fernández Rojas struct dma tx_dma;
1309622972aSÁlvaro Fernández Rojas /* Ports */
1319622972aSÁlvaro Fernández Rojas uint8_t num_ports;
1329622972aSÁlvaro Fernández Rojas struct bcm_enetsw_port used_ports[ETH_MAX_PORT];
1339622972aSÁlvaro Fernández Rojas int sw_port_link[ETH_MAX_PORT];
1349622972aSÁlvaro Fernández Rojas bool rgmii_override;
1359622972aSÁlvaro Fernández Rojas bool rgmii_timing;
1369622972aSÁlvaro Fernández Rojas /* PHY */
1379622972aSÁlvaro Fernández Rojas int phy_id;
1389622972aSÁlvaro Fernández Rojas };
1399622972aSÁlvaro Fernández Rojas
bcm_enet_port_is_rgmii(int portid)1409622972aSÁlvaro Fernández Rojas static inline bool bcm_enet_port_is_rgmii(int portid)
1419622972aSÁlvaro Fernández Rojas {
1429622972aSÁlvaro Fernández Rojas return portid >= ETH_RGMII_PORT0;
1439622972aSÁlvaro Fernández Rojas }
1449622972aSÁlvaro Fernández Rojas
bcm6368_mdio_read(struct bcm6368_eth_priv * priv,uint8_t ext,int phy_id,int reg)1459622972aSÁlvaro Fernández Rojas static int bcm6368_mdio_read(struct bcm6368_eth_priv *priv, uint8_t ext,
1469622972aSÁlvaro Fernández Rojas int phy_id, int reg)
1479622972aSÁlvaro Fernández Rojas {
1489622972aSÁlvaro Fernández Rojas uint32_t val;
1499622972aSÁlvaro Fernández Rojas
1509622972aSÁlvaro Fernández Rojas writel_be(0, priv->base + MII_SC_REG);
1519622972aSÁlvaro Fernández Rojas
1529622972aSÁlvaro Fernández Rojas val = MII_SC_RD_MASK |
1539622972aSÁlvaro Fernández Rojas (phy_id << MII_SC_PHYID_SHIFT) |
1549622972aSÁlvaro Fernández Rojas (reg << MII_SC_REG_SHIFT);
1559622972aSÁlvaro Fernández Rojas
1569622972aSÁlvaro Fernández Rojas if (ext)
1579622972aSÁlvaro Fernández Rojas val |= MII_SC_EXT_MASK;
1589622972aSÁlvaro Fernández Rojas
1599622972aSÁlvaro Fernández Rojas writel_be(val, priv->base + MII_SC_REG);
1609622972aSÁlvaro Fernández Rojas udelay(50);
1619622972aSÁlvaro Fernández Rojas
1629622972aSÁlvaro Fernández Rojas return readw_be(priv->base + MII_DAT_REG);
1639622972aSÁlvaro Fernández Rojas }
1649622972aSÁlvaro Fernández Rojas
bcm6368_mdio_write(struct bcm6368_eth_priv * priv,uint8_t ext,int phy_id,int reg,u16 data)1659622972aSÁlvaro Fernández Rojas static int bcm6368_mdio_write(struct bcm6368_eth_priv *priv, uint8_t ext,
1669622972aSÁlvaro Fernández Rojas int phy_id, int reg, u16 data)
1679622972aSÁlvaro Fernández Rojas {
1689622972aSÁlvaro Fernández Rojas uint32_t val;
1699622972aSÁlvaro Fernández Rojas
1709622972aSÁlvaro Fernández Rojas writel_be(0, priv->base + MII_SC_REG);
1719622972aSÁlvaro Fernández Rojas
1729622972aSÁlvaro Fernández Rojas val = MII_SC_WR_MASK |
1739622972aSÁlvaro Fernández Rojas (phy_id << MII_SC_PHYID_SHIFT) |
1749622972aSÁlvaro Fernández Rojas (reg << MII_SC_REG_SHIFT);
1759622972aSÁlvaro Fernández Rojas
1769622972aSÁlvaro Fernández Rojas if (ext)
1779622972aSÁlvaro Fernández Rojas val |= MII_SC_EXT_MASK;
1789622972aSÁlvaro Fernández Rojas
1799622972aSÁlvaro Fernández Rojas val |= data;
1809622972aSÁlvaro Fernández Rojas
1819622972aSÁlvaro Fernández Rojas writel_be(val, priv->base + MII_SC_REG);
1829622972aSÁlvaro Fernández Rojas udelay(50);
1839622972aSÁlvaro Fernández Rojas
1849622972aSÁlvaro Fernández Rojas return 0;
1859622972aSÁlvaro Fernández Rojas }
1869622972aSÁlvaro Fernández Rojas
bcm6368_eth_free_pkt(struct udevice * dev,uchar * packet,int len)1879622972aSÁlvaro Fernández Rojas static int bcm6368_eth_free_pkt(struct udevice *dev, uchar *packet, int len)
1889622972aSÁlvaro Fernández Rojas {
1899622972aSÁlvaro Fernández Rojas struct bcm6368_eth_priv *priv = dev_get_priv(dev);
1909622972aSÁlvaro Fernández Rojas
1919622972aSÁlvaro Fernández Rojas return dma_prepare_rcv_buf(&priv->rx_dma, packet, len);
1929622972aSÁlvaro Fernández Rojas }
1939622972aSÁlvaro Fernández Rojas
bcm6368_eth_recv(struct udevice * dev,int flags,uchar ** packetp)1949622972aSÁlvaro Fernández Rojas static int bcm6368_eth_recv(struct udevice *dev, int flags, uchar **packetp)
1959622972aSÁlvaro Fernández Rojas {
1969622972aSÁlvaro Fernández Rojas struct bcm6368_eth_priv *priv = dev_get_priv(dev);
1979622972aSÁlvaro Fernández Rojas
1989622972aSÁlvaro Fernández Rojas return dma_receive(&priv->rx_dma, (void**)packetp, NULL);
1999622972aSÁlvaro Fernández Rojas }
2009622972aSÁlvaro Fernández Rojas
bcm6368_eth_send(struct udevice * dev,void * packet,int length)2019622972aSÁlvaro Fernández Rojas static int bcm6368_eth_send(struct udevice *dev, void *packet, int length)
2029622972aSÁlvaro Fernández Rojas {
2039622972aSÁlvaro Fernández Rojas struct bcm6368_eth_priv *priv = dev_get_priv(dev);
2049622972aSÁlvaro Fernández Rojas
2059622972aSÁlvaro Fernández Rojas /* pad packets smaller than ETH_ZLEN */
2069622972aSÁlvaro Fernández Rojas if (length < ETH_ZLEN) {
2079622972aSÁlvaro Fernández Rojas memset(packet + length, 0, ETH_ZLEN - length);
2089622972aSÁlvaro Fernández Rojas length = ETH_ZLEN;
2099622972aSÁlvaro Fernández Rojas }
2109622972aSÁlvaro Fernández Rojas
2119622972aSÁlvaro Fernández Rojas return dma_send(&priv->tx_dma, packet, length, NULL);
2129622972aSÁlvaro Fernández Rojas }
2139622972aSÁlvaro Fernández Rojas
bcm6368_eth_adjust_link(struct udevice * dev)2149622972aSÁlvaro Fernández Rojas static int bcm6368_eth_adjust_link(struct udevice *dev)
2159622972aSÁlvaro Fernández Rojas {
2169622972aSÁlvaro Fernández Rojas struct bcm6368_eth_priv *priv = dev_get_priv(dev);
2179622972aSÁlvaro Fernández Rojas unsigned int i;
2189622972aSÁlvaro Fernández Rojas
2199622972aSÁlvaro Fernández Rojas for (i = 0; i < priv->num_ports; i++) {
2209622972aSÁlvaro Fernández Rojas struct bcm_enetsw_port *port;
2219622972aSÁlvaro Fernández Rojas int val, j, up, adv, lpa, speed, duplex, media;
2229622972aSÁlvaro Fernández Rojas int external_phy = bcm_enet_port_is_rgmii(i);
2239622972aSÁlvaro Fernández Rojas u8 override;
2249622972aSÁlvaro Fernández Rojas
2259622972aSÁlvaro Fernández Rojas port = &priv->used_ports[i];
2269622972aSÁlvaro Fernández Rojas if (!port->used)
2279622972aSÁlvaro Fernández Rojas continue;
2289622972aSÁlvaro Fernández Rojas
2299622972aSÁlvaro Fernández Rojas if (port->bypass_link)
2309622972aSÁlvaro Fernández Rojas continue;
2319622972aSÁlvaro Fernández Rojas
2329622972aSÁlvaro Fernández Rojas /* dummy read to clear */
2339622972aSÁlvaro Fernández Rojas for (j = 0; j < 2; j++)
2349622972aSÁlvaro Fernández Rojas val = bcm6368_mdio_read(priv, external_phy,
2359622972aSÁlvaro Fernández Rojas port->phy_id, MII_BMSR);
2369622972aSÁlvaro Fernández Rojas
2379622972aSÁlvaro Fernández Rojas if (val == 0xffff)
2389622972aSÁlvaro Fernández Rojas continue;
2399622972aSÁlvaro Fernández Rojas
2409622972aSÁlvaro Fernández Rojas up = (val & BMSR_LSTATUS) ? 1 : 0;
2419622972aSÁlvaro Fernández Rojas if (!(up ^ priv->sw_port_link[i]))
2429622972aSÁlvaro Fernández Rojas continue;
2439622972aSÁlvaro Fernández Rojas
2449622972aSÁlvaro Fernández Rojas priv->sw_port_link[i] = up;
2459622972aSÁlvaro Fernández Rojas
2469622972aSÁlvaro Fernández Rojas /* link changed */
2479622972aSÁlvaro Fernández Rojas if (!up) {
2489622972aSÁlvaro Fernández Rojas dev_info(&priv->pdev->dev, "link DOWN on %s\n",
2499622972aSÁlvaro Fernández Rojas port->name);
2509622972aSÁlvaro Fernández Rojas writeb_be(ETH_PORTOV_ENABLE_MASK,
2519622972aSÁlvaro Fernández Rojas priv->base + ETH_PORTOV_REG(i));
2529622972aSÁlvaro Fernández Rojas writeb_be(ETH_PTCTRL_RXDIS_MASK |
2539622972aSÁlvaro Fernández Rojas ETH_PTCTRL_TXDIS_MASK,
2549622972aSÁlvaro Fernández Rojas priv->base + ETH_PTCTRL_REG(i));
2559622972aSÁlvaro Fernández Rojas continue;
2569622972aSÁlvaro Fernández Rojas }
2579622972aSÁlvaro Fernández Rojas
2589622972aSÁlvaro Fernández Rojas adv = bcm6368_mdio_read(priv, external_phy,
2599622972aSÁlvaro Fernández Rojas port->phy_id, MII_ADVERTISE);
2609622972aSÁlvaro Fernández Rojas
2619622972aSÁlvaro Fernández Rojas lpa = bcm6368_mdio_read(priv, external_phy, port->phy_id,
2629622972aSÁlvaro Fernández Rojas MII_LPA);
2639622972aSÁlvaro Fernández Rojas
2649622972aSÁlvaro Fernández Rojas /* figure out media and duplex from advertise and LPA values */
2659622972aSÁlvaro Fernández Rojas media = mii_nway_result(lpa & adv);
2669622972aSÁlvaro Fernández Rojas duplex = (media & ADVERTISE_FULL) ? 1 : 0;
2679622972aSÁlvaro Fernández Rojas
2689622972aSÁlvaro Fernández Rojas if (media & (ADVERTISE_100FULL | ADVERTISE_100HALF))
2699622972aSÁlvaro Fernández Rojas speed = 100;
2709622972aSÁlvaro Fernández Rojas else
2719622972aSÁlvaro Fernández Rojas speed = 10;
2729622972aSÁlvaro Fernández Rojas
2739622972aSÁlvaro Fernández Rojas if (val & BMSR_ESTATEN) {
2749622972aSÁlvaro Fernández Rojas adv = bcm6368_mdio_read(priv, external_phy,
2759622972aSÁlvaro Fernández Rojas port->phy_id, MII_CTRL1000);
2769622972aSÁlvaro Fernández Rojas
2779622972aSÁlvaro Fernández Rojas lpa = bcm6368_mdio_read(priv, external_phy,
2789622972aSÁlvaro Fernández Rojas port->phy_id, MII_STAT1000);
2799622972aSÁlvaro Fernández Rojas
2809622972aSÁlvaro Fernández Rojas if ((adv & (ADVERTISE_1000FULL | ADVERTISE_1000HALF)) &&
2819622972aSÁlvaro Fernández Rojas (lpa & (LPA_1000FULL | LPA_1000HALF))) {
2829622972aSÁlvaro Fernández Rojas speed = 1000;
2839622972aSÁlvaro Fernández Rojas duplex = (lpa & LPA_1000FULL);
2849622972aSÁlvaro Fernández Rojas }
2859622972aSÁlvaro Fernández Rojas }
2869622972aSÁlvaro Fernández Rojas
2879622972aSÁlvaro Fernández Rojas pr_alert("link UP on %s, %dMbps, %s-duplex\n",
2889622972aSÁlvaro Fernández Rojas port->name, speed, duplex ? "full" : "half");
2899622972aSÁlvaro Fernández Rojas
2909622972aSÁlvaro Fernández Rojas override = ETH_PORTOV_ENABLE_MASK |
2919622972aSÁlvaro Fernández Rojas ETH_PORTOV_LINKUP_MASK;
2929622972aSÁlvaro Fernández Rojas
2939622972aSÁlvaro Fernández Rojas if (speed == 1000)
2949622972aSÁlvaro Fernández Rojas override |= ETH_PORTOV_1000_MASK;
2959622972aSÁlvaro Fernández Rojas else if (speed == 100)
2969622972aSÁlvaro Fernández Rojas override |= ETH_PORTOV_100_MASK;
2979622972aSÁlvaro Fernández Rojas if (duplex)
2989622972aSÁlvaro Fernández Rojas override |= ETH_PORTOV_FDX_MASK;
2999622972aSÁlvaro Fernández Rojas
3009622972aSÁlvaro Fernández Rojas writeb_be(override, priv->base + ETH_PORTOV_REG(i));
3019622972aSÁlvaro Fernández Rojas writeb_be(0, priv->base + ETH_PTCTRL_REG(i));
3029622972aSÁlvaro Fernández Rojas }
3039622972aSÁlvaro Fernández Rojas
3049622972aSÁlvaro Fernández Rojas return 0;
3059622972aSÁlvaro Fernández Rojas }
3069622972aSÁlvaro Fernández Rojas
bcm6368_eth_start(struct udevice * dev)3079622972aSÁlvaro Fernández Rojas static int bcm6368_eth_start(struct udevice *dev)
3089622972aSÁlvaro Fernández Rojas {
3099622972aSÁlvaro Fernández Rojas struct bcm6368_eth_priv *priv = dev_get_priv(dev);
3109622972aSÁlvaro Fernández Rojas uint8_t i;
3119622972aSÁlvaro Fernández Rojas
312*a4ae4225SÁlvaro Fernández Rojas /* disable all ports */
313*a4ae4225SÁlvaro Fernández Rojas for (i = 0; i < priv->num_ports; i++) {
314*a4ae4225SÁlvaro Fernández Rojas setbits_8(priv->base + ETH_PORTOV_REG(i),
315*a4ae4225SÁlvaro Fernández Rojas ETH_PORTOV_ENABLE_MASK);
316*a4ae4225SÁlvaro Fernández Rojas setbits_8(priv->base + ETH_PTCTRL_REG(i),
317*a4ae4225SÁlvaro Fernández Rojas ETH_PTCTRL_RXDIS_MASK | ETH_PTCTRL_TXDIS_MASK);
318*a4ae4225SÁlvaro Fernández Rojas priv->sw_port_link[i] = 0;
319*a4ae4225SÁlvaro Fernández Rojas }
320*a4ae4225SÁlvaro Fernández Rojas
321*a4ae4225SÁlvaro Fernández Rojas /* enable external ports */
322*a4ae4225SÁlvaro Fernández Rojas for (i = ETH_RGMII_PORT0; i < priv->num_ports; i++) {
323*a4ae4225SÁlvaro Fernández Rojas u8 rgmii_ctrl = ETH_RGMII_CTRL_GMII_CLK_EN;
324*a4ae4225SÁlvaro Fernández Rojas
325*a4ae4225SÁlvaro Fernández Rojas if (!priv->used_ports[i].used)
326*a4ae4225SÁlvaro Fernández Rojas continue;
327*a4ae4225SÁlvaro Fernández Rojas
328*a4ae4225SÁlvaro Fernández Rojas if (priv->rgmii_override)
329*a4ae4225SÁlvaro Fernández Rojas rgmii_ctrl |= ETH_RGMII_CTRL_MII_OVERRIDE_EN;
330*a4ae4225SÁlvaro Fernández Rojas if (priv->rgmii_timing)
331*a4ae4225SÁlvaro Fernández Rojas rgmii_ctrl |= ETH_RGMII_CTRL_TIMING_SEL_EN;
332*a4ae4225SÁlvaro Fernández Rojas
333*a4ae4225SÁlvaro Fernández Rojas setbits_8(priv->base + ETH_RGMII_CTRL_REG(i), rgmii_ctrl);
334*a4ae4225SÁlvaro Fernández Rojas }
335*a4ae4225SÁlvaro Fernández Rojas
336*a4ae4225SÁlvaro Fernández Rojas /* reset mib */
337*a4ae4225SÁlvaro Fernández Rojas setbits_8(priv->base + ETH_GMCR_REG, ETH_GMCR_RST_MIB_MASK);
338*a4ae4225SÁlvaro Fernández Rojas mdelay(1);
339*a4ae4225SÁlvaro Fernández Rojas clrbits_8(priv->base + ETH_GMCR_REG, ETH_GMCR_RST_MIB_MASK);
340*a4ae4225SÁlvaro Fernández Rojas mdelay(1);
341*a4ae4225SÁlvaro Fernández Rojas
342*a4ae4225SÁlvaro Fernández Rojas /* force CPU port state */
343*a4ae4225SÁlvaro Fernández Rojas setbits_8(priv->base + ETH_IMPOV_REG,
344*a4ae4225SÁlvaro Fernández Rojas ETH_IMPOV_FORCE_MASK | ETH_IMPOV_LINKUP_MASK);
345*a4ae4225SÁlvaro Fernández Rojas
346*a4ae4225SÁlvaro Fernández Rojas /* enable switch forward engine */
347*a4ae4225SÁlvaro Fernández Rojas setbits_8(priv->base + ETH_SWMODE_REG, ETH_SWMODE_FWD_EN_MASK);
348*a4ae4225SÁlvaro Fernández Rojas
3499622972aSÁlvaro Fernández Rojas /* prepare rx dma buffers */
3509622972aSÁlvaro Fernández Rojas for (i = 0; i < ETH_RX_DESC; i++) {
3519622972aSÁlvaro Fernández Rojas int ret = dma_prepare_rcv_buf(&priv->rx_dma, net_rx_packets[i],
3529622972aSÁlvaro Fernández Rojas PKTSIZE_ALIGN);
3539622972aSÁlvaro Fernández Rojas if (ret < 0)
3549622972aSÁlvaro Fernández Rojas break;
3559622972aSÁlvaro Fernández Rojas }
3569622972aSÁlvaro Fernández Rojas
3579622972aSÁlvaro Fernández Rojas /* enable dma rx channel */
3589622972aSÁlvaro Fernández Rojas dma_enable(&priv->rx_dma);
3599622972aSÁlvaro Fernández Rojas
3609622972aSÁlvaro Fernández Rojas /* enable dma tx channel */
3619622972aSÁlvaro Fernández Rojas dma_enable(&priv->tx_dma);
3629622972aSÁlvaro Fernández Rojas
3639622972aSÁlvaro Fernández Rojas /* apply override config for bypass_link ports here. */
3649622972aSÁlvaro Fernández Rojas for (i = 0; i < priv->num_ports; i++) {
3659622972aSÁlvaro Fernández Rojas struct bcm_enetsw_port *port;
3669622972aSÁlvaro Fernández Rojas u8 override;
3679622972aSÁlvaro Fernández Rojas
3689622972aSÁlvaro Fernández Rojas port = &priv->used_ports[i];
3699622972aSÁlvaro Fernández Rojas if (!port->used)
3709622972aSÁlvaro Fernández Rojas continue;
3719622972aSÁlvaro Fernández Rojas
3729622972aSÁlvaro Fernández Rojas if (!port->bypass_link)
3739622972aSÁlvaro Fernández Rojas continue;
3749622972aSÁlvaro Fernández Rojas
3759622972aSÁlvaro Fernández Rojas override = ETH_PORTOV_ENABLE_MASK |
3769622972aSÁlvaro Fernández Rojas ETH_PORTOV_LINKUP_MASK;
3779622972aSÁlvaro Fernández Rojas
3789622972aSÁlvaro Fernández Rojas switch (port->force_speed) {
3799622972aSÁlvaro Fernández Rojas case 1000:
3809622972aSÁlvaro Fernández Rojas override |= ETH_PORTOV_1000_MASK;
3819622972aSÁlvaro Fernández Rojas break;
3829622972aSÁlvaro Fernández Rojas case 100:
3839622972aSÁlvaro Fernández Rojas override |= ETH_PORTOV_100_MASK;
3849622972aSÁlvaro Fernández Rojas break;
3859622972aSÁlvaro Fernández Rojas case 10:
3869622972aSÁlvaro Fernández Rojas break;
3879622972aSÁlvaro Fernández Rojas default:
3889622972aSÁlvaro Fernández Rojas pr_warn("%s: invalid forced speed on port %s\n",
3899622972aSÁlvaro Fernández Rojas __func__, port->name);
3909622972aSÁlvaro Fernández Rojas break;
3919622972aSÁlvaro Fernández Rojas }
3929622972aSÁlvaro Fernández Rojas
3939622972aSÁlvaro Fernández Rojas if (port->force_duplex_full)
3949622972aSÁlvaro Fernández Rojas override |= ETH_PORTOV_FDX_MASK;
3959622972aSÁlvaro Fernández Rojas
3969622972aSÁlvaro Fernández Rojas writeb_be(override, priv->base + ETH_PORTOV_REG(i));
3979622972aSÁlvaro Fernández Rojas writeb_be(0, priv->base + ETH_PTCTRL_REG(i));
3989622972aSÁlvaro Fernández Rojas }
3999622972aSÁlvaro Fernández Rojas
4009622972aSÁlvaro Fernández Rojas bcm6368_eth_adjust_link(dev);
4019622972aSÁlvaro Fernández Rojas
4029622972aSÁlvaro Fernández Rojas return 0;
4039622972aSÁlvaro Fernández Rojas }
4049622972aSÁlvaro Fernández Rojas
bcm6368_eth_stop(struct udevice * dev)4059622972aSÁlvaro Fernández Rojas static void bcm6368_eth_stop(struct udevice *dev)
4069622972aSÁlvaro Fernández Rojas {
4079622972aSÁlvaro Fernández Rojas struct bcm6368_eth_priv *priv = dev_get_priv(dev);
408*a4ae4225SÁlvaro Fernández Rojas uint8_t i;
409*a4ae4225SÁlvaro Fernández Rojas
410*a4ae4225SÁlvaro Fernández Rojas /* disable all ports */
411*a4ae4225SÁlvaro Fernández Rojas for (i = 0; i < priv->num_ports; i++) {
412*a4ae4225SÁlvaro Fernández Rojas setbits_8(priv->base + ETH_PORTOV_REG(i),
413*a4ae4225SÁlvaro Fernández Rojas ETH_PORTOV_ENABLE_MASK);
414*a4ae4225SÁlvaro Fernández Rojas setbits_8(priv->base + ETH_PTCTRL_REG(i),
415*a4ae4225SÁlvaro Fernández Rojas ETH_PTCTRL_RXDIS_MASK | ETH_PTCTRL_TXDIS_MASK);
416*a4ae4225SÁlvaro Fernández Rojas }
417*a4ae4225SÁlvaro Fernández Rojas
418*a4ae4225SÁlvaro Fernández Rojas /* disable external ports */
419*a4ae4225SÁlvaro Fernández Rojas for (i = ETH_RGMII_PORT0; i < priv->num_ports; i++) {
420*a4ae4225SÁlvaro Fernández Rojas if (!priv->used_ports[i].used)
421*a4ae4225SÁlvaro Fernández Rojas continue;
422*a4ae4225SÁlvaro Fernández Rojas
423*a4ae4225SÁlvaro Fernández Rojas clrbits_8(priv->base + ETH_RGMII_CTRL_REG(i),
424*a4ae4225SÁlvaro Fernández Rojas ETH_RGMII_CTRL_GMII_CLK_EN);
425*a4ae4225SÁlvaro Fernández Rojas }
426*a4ae4225SÁlvaro Fernández Rojas
427*a4ae4225SÁlvaro Fernández Rojas /* disable CPU port */
428*a4ae4225SÁlvaro Fernández Rojas clrbits_8(priv->base + ETH_IMPOV_REG,
429*a4ae4225SÁlvaro Fernández Rojas ETH_IMPOV_FORCE_MASK | ETH_IMPOV_LINKUP_MASK);
430*a4ae4225SÁlvaro Fernández Rojas
431*a4ae4225SÁlvaro Fernández Rojas /* disable switch forward engine */
432*a4ae4225SÁlvaro Fernández Rojas clrbits_8(priv->base + ETH_SWMODE_REG, ETH_SWMODE_FWD_EN_MASK);
4339622972aSÁlvaro Fernández Rojas
4349622972aSÁlvaro Fernández Rojas /* disable dma rx channel */
4359622972aSÁlvaro Fernández Rojas dma_disable(&priv->rx_dma);
4369622972aSÁlvaro Fernández Rojas
4379622972aSÁlvaro Fernández Rojas /* disable dma tx channel */
4389622972aSÁlvaro Fernández Rojas dma_disable(&priv->tx_dma);
4399622972aSÁlvaro Fernández Rojas }
4409622972aSÁlvaro Fernández Rojas
4419622972aSÁlvaro Fernández Rojas static const struct eth_ops bcm6368_eth_ops = {
4429622972aSÁlvaro Fernández Rojas .free_pkt = bcm6368_eth_free_pkt,
4439622972aSÁlvaro Fernández Rojas .recv = bcm6368_eth_recv,
4449622972aSÁlvaro Fernández Rojas .send = bcm6368_eth_send,
4459622972aSÁlvaro Fernández Rojas .start = bcm6368_eth_start,
4469622972aSÁlvaro Fernández Rojas .stop = bcm6368_eth_stop,
4479622972aSÁlvaro Fernández Rojas };
4489622972aSÁlvaro Fernández Rojas
4499622972aSÁlvaro Fernández Rojas static const struct udevice_id bcm6368_eth_ids[] = {
4509622972aSÁlvaro Fernández Rojas { .compatible = "brcm,bcm6368-enet", },
4519622972aSÁlvaro Fernández Rojas { /* sentinel */ }
4529622972aSÁlvaro Fernández Rojas };
4539622972aSÁlvaro Fernández Rojas
bcm6368_phy_is_external(struct bcm6368_eth_priv * priv,int phy_id)4549622972aSÁlvaro Fernández Rojas static bool bcm6368_phy_is_external(struct bcm6368_eth_priv *priv, int phy_id)
4559622972aSÁlvaro Fernández Rojas {
4569622972aSÁlvaro Fernández Rojas uint8_t i;
4579622972aSÁlvaro Fernández Rojas
4589622972aSÁlvaro Fernández Rojas for (i = 0; i < priv->num_ports; ++i) {
4599622972aSÁlvaro Fernández Rojas if (!priv->used_ports[i].used)
4609622972aSÁlvaro Fernández Rojas continue;
4619622972aSÁlvaro Fernández Rojas if (priv->used_ports[i].phy_id == phy_id)
4629622972aSÁlvaro Fernández Rojas return bcm_enet_port_is_rgmii(i);
4639622972aSÁlvaro Fernández Rojas }
4649622972aSÁlvaro Fernández Rojas
4659622972aSÁlvaro Fernández Rojas return true;
4669622972aSÁlvaro Fernández Rojas }
4679622972aSÁlvaro Fernández Rojas
bcm6368_mii_mdio_read(struct mii_dev * bus,int addr,int devaddr,int reg)4689622972aSÁlvaro Fernández Rojas static int bcm6368_mii_mdio_read(struct mii_dev *bus, int addr, int devaddr,
4699622972aSÁlvaro Fernández Rojas int reg)
4709622972aSÁlvaro Fernández Rojas {
4719622972aSÁlvaro Fernández Rojas struct bcm6368_eth_priv *priv = bus->priv;
4729622972aSÁlvaro Fernández Rojas bool ext = bcm6368_phy_is_external(priv, addr);
4739622972aSÁlvaro Fernández Rojas
4749622972aSÁlvaro Fernández Rojas return bcm6368_mdio_read(priv, ext, addr, reg);
4759622972aSÁlvaro Fernández Rojas }
4769622972aSÁlvaro Fernández Rojas
bcm6368_mii_mdio_write(struct mii_dev * bus,int addr,int devaddr,int reg,u16 data)4779622972aSÁlvaro Fernández Rojas static int bcm6368_mii_mdio_write(struct mii_dev *bus, int addr, int devaddr,
4789622972aSÁlvaro Fernández Rojas int reg, u16 data)
4799622972aSÁlvaro Fernández Rojas {
4809622972aSÁlvaro Fernández Rojas struct bcm6368_eth_priv *priv = bus->priv;
4819622972aSÁlvaro Fernández Rojas bool ext = bcm6368_phy_is_external(priv, addr);
4829622972aSÁlvaro Fernández Rojas
4839622972aSÁlvaro Fernández Rojas return bcm6368_mdio_write(priv, ext, addr, reg, data);
4849622972aSÁlvaro Fernández Rojas }
4859622972aSÁlvaro Fernández Rojas
bcm6368_mdio_init(const char * name,struct bcm6368_eth_priv * priv)4869622972aSÁlvaro Fernández Rojas static int bcm6368_mdio_init(const char *name, struct bcm6368_eth_priv *priv)
4879622972aSÁlvaro Fernández Rojas {
4889622972aSÁlvaro Fernández Rojas struct mii_dev *bus;
4899622972aSÁlvaro Fernández Rojas
4909622972aSÁlvaro Fernández Rojas bus = mdio_alloc();
4919622972aSÁlvaro Fernández Rojas if (!bus) {
4929622972aSÁlvaro Fernández Rojas pr_err("%s: failed to allocate MDIO bus\n", __func__);
4939622972aSÁlvaro Fernández Rojas return -ENOMEM;
4949622972aSÁlvaro Fernández Rojas }
4959622972aSÁlvaro Fernández Rojas
4969622972aSÁlvaro Fernández Rojas bus->read = bcm6368_mii_mdio_read;
4979622972aSÁlvaro Fernández Rojas bus->write = bcm6368_mii_mdio_write;
4989622972aSÁlvaro Fernández Rojas bus->priv = priv;
4999622972aSÁlvaro Fernández Rojas snprintf(bus->name, sizeof(bus->name), "%s", name);
5009622972aSÁlvaro Fernández Rojas
5019622972aSÁlvaro Fernández Rojas return mdio_register(bus);
5029622972aSÁlvaro Fernández Rojas }
5039622972aSÁlvaro Fernández Rojas
bcm6368_eth_probe(struct udevice * dev)5049622972aSÁlvaro Fernández Rojas static int bcm6368_eth_probe(struct udevice *dev)
5059622972aSÁlvaro Fernández Rojas {
5069622972aSÁlvaro Fernández Rojas struct eth_pdata *pdata = dev_get_platdata(dev);
5079622972aSÁlvaro Fernández Rojas struct bcm6368_eth_priv *priv = dev_get_priv(dev);
5089622972aSÁlvaro Fernández Rojas int num_ports, ret, i;
5099622972aSÁlvaro Fernández Rojas ofnode node;
5109622972aSÁlvaro Fernández Rojas
5119622972aSÁlvaro Fernández Rojas /* get base address */
5129622972aSÁlvaro Fernández Rojas priv->base = dev_remap_addr(dev);
5139622972aSÁlvaro Fernández Rojas if (!priv->base)
5149622972aSÁlvaro Fernández Rojas return -EINVAL;
5159622972aSÁlvaro Fernández Rojas pdata->iobase = (phys_addr_t) priv->base;
5169622972aSÁlvaro Fernández Rojas
5179622972aSÁlvaro Fernández Rojas /* get number of ports */
5189622972aSÁlvaro Fernández Rojas num_ports = dev_read_u32_default(dev, "brcm,num-ports", ETH_MAX_PORT);
5199622972aSÁlvaro Fernández Rojas if (!num_ports || num_ports > ETH_MAX_PORT)
5209622972aSÁlvaro Fernández Rojas return -EINVAL;
5219622972aSÁlvaro Fernández Rojas
5229622972aSÁlvaro Fernández Rojas /* get dma channels */
5239622972aSÁlvaro Fernández Rojas ret = dma_get_by_name(dev, "tx", &priv->tx_dma);
5249622972aSÁlvaro Fernández Rojas if (ret)
5259622972aSÁlvaro Fernández Rojas return -EINVAL;
5269622972aSÁlvaro Fernández Rojas
5279622972aSÁlvaro Fernández Rojas ret = dma_get_by_name(dev, "rx", &priv->rx_dma);
5289622972aSÁlvaro Fernández Rojas if (ret)
5299622972aSÁlvaro Fernández Rojas return -EINVAL;
5309622972aSÁlvaro Fernández Rojas
5319622972aSÁlvaro Fernández Rojas /* try to enable clocks */
5329622972aSÁlvaro Fernández Rojas for (i = 0; ; i++) {
5339622972aSÁlvaro Fernández Rojas struct clk clk;
5349622972aSÁlvaro Fernández Rojas int ret;
5359622972aSÁlvaro Fernández Rojas
5369622972aSÁlvaro Fernández Rojas ret = clk_get_by_index(dev, i, &clk);
5379622972aSÁlvaro Fernández Rojas if (ret < 0)
5389622972aSÁlvaro Fernández Rojas break;
5399622972aSÁlvaro Fernández Rojas
5409622972aSÁlvaro Fernández Rojas ret = clk_enable(&clk);
5419622972aSÁlvaro Fernández Rojas if (ret < 0) {
5429622972aSÁlvaro Fernández Rojas pr_err("%s: error enabling clock %d\n", __func__, i);
5439622972aSÁlvaro Fernández Rojas return ret;
5449622972aSÁlvaro Fernández Rojas }
5459622972aSÁlvaro Fernández Rojas
5469622972aSÁlvaro Fernández Rojas ret = clk_free(&clk);
5479622972aSÁlvaro Fernández Rojas if (ret < 0) {
5489622972aSÁlvaro Fernández Rojas pr_err("%s: error freeing clock %d\n", __func__, i);
5499622972aSÁlvaro Fernández Rojas return ret;
5509622972aSÁlvaro Fernández Rojas }
5519622972aSÁlvaro Fernández Rojas }
5529622972aSÁlvaro Fernández Rojas
5539622972aSÁlvaro Fernández Rojas /* try to perform resets */
5549622972aSÁlvaro Fernández Rojas for (i = 0; ; i++) {
5559622972aSÁlvaro Fernández Rojas struct reset_ctl reset;
5569622972aSÁlvaro Fernández Rojas int ret;
5579622972aSÁlvaro Fernández Rojas
5589622972aSÁlvaro Fernández Rojas ret = reset_get_by_index(dev, i, &reset);
5599622972aSÁlvaro Fernández Rojas if (ret < 0)
5609622972aSÁlvaro Fernández Rojas break;
5619622972aSÁlvaro Fernández Rojas
5629622972aSÁlvaro Fernández Rojas ret = reset_deassert(&reset);
5639622972aSÁlvaro Fernández Rojas if (ret < 0) {
5649622972aSÁlvaro Fernández Rojas pr_err("%s: error deasserting reset %d\n", __func__, i);
5659622972aSÁlvaro Fernández Rojas return ret;
5669622972aSÁlvaro Fernández Rojas }
5679622972aSÁlvaro Fernández Rojas
5689622972aSÁlvaro Fernández Rojas ret = reset_free(&reset);
5699622972aSÁlvaro Fernández Rojas if (ret < 0) {
5709622972aSÁlvaro Fernández Rojas pr_err("%s: error freeing reset %d\n", __func__, i);
5719622972aSÁlvaro Fernández Rojas return ret;
5729622972aSÁlvaro Fernández Rojas }
5739622972aSÁlvaro Fernández Rojas }
5749622972aSÁlvaro Fernández Rojas
5759622972aSÁlvaro Fernández Rojas /* set priv data */
5769622972aSÁlvaro Fernández Rojas priv->num_ports = num_ports;
5779622972aSÁlvaro Fernández Rojas if (dev_read_bool(dev, "brcm,rgmii-override"))
5789622972aSÁlvaro Fernández Rojas priv->rgmii_override = true;
5799622972aSÁlvaro Fernández Rojas if (dev_read_bool(dev, "brcm,rgmii-timing"))
5809622972aSÁlvaro Fernández Rojas priv->rgmii_timing = true;
5819622972aSÁlvaro Fernández Rojas
5829622972aSÁlvaro Fernández Rojas /* get ports */
5839622972aSÁlvaro Fernández Rojas dev_for_each_subnode(node, dev) {
5849622972aSÁlvaro Fernández Rojas const char *comp;
5859622972aSÁlvaro Fernández Rojas const char *label;
5869622972aSÁlvaro Fernández Rojas unsigned int p;
5879622972aSÁlvaro Fernández Rojas int phy_id;
5889622972aSÁlvaro Fernández Rojas int speed;
5899622972aSÁlvaro Fernández Rojas
5909622972aSÁlvaro Fernández Rojas comp = ofnode_read_string(node, "compatible");
5919622972aSÁlvaro Fernández Rojas if (!comp || memcmp(comp, ETH_PORT_STR, sizeof(ETH_PORT_STR)))
5929622972aSÁlvaro Fernández Rojas continue;
5939622972aSÁlvaro Fernández Rojas
5949622972aSÁlvaro Fernández Rojas p = ofnode_read_u32_default(node, "reg", ETH_MAX_PORT);
5959622972aSÁlvaro Fernández Rojas if (p >= num_ports)
5969622972aSÁlvaro Fernández Rojas return -EINVAL;
5979622972aSÁlvaro Fernández Rojas
5989622972aSÁlvaro Fernández Rojas label = ofnode_read_string(node, "label");
5999622972aSÁlvaro Fernández Rojas if (!label) {
6009622972aSÁlvaro Fernández Rojas debug("%s: node %s has no label\n", __func__,
6019622972aSÁlvaro Fernández Rojas ofnode_get_name(node));
6029622972aSÁlvaro Fernández Rojas return -EINVAL;
6039622972aSÁlvaro Fernández Rojas }
6049622972aSÁlvaro Fernández Rojas
6059622972aSÁlvaro Fernández Rojas phy_id = ofnode_read_u32_default(node, "brcm,phy-id", -1);
6069622972aSÁlvaro Fernández Rojas
6079622972aSÁlvaro Fernández Rojas priv->used_ports[p].used = true;
6089622972aSÁlvaro Fernández Rojas priv->used_ports[p].name = label;
6099622972aSÁlvaro Fernández Rojas priv->used_ports[p].phy_id = phy_id;
6109622972aSÁlvaro Fernández Rojas
6119622972aSÁlvaro Fernández Rojas if (ofnode_read_bool(node, "full-duplex"))
6129622972aSÁlvaro Fernández Rojas priv->used_ports[p].force_duplex_full = true;
6139622972aSÁlvaro Fernández Rojas if (ofnode_read_bool(node, "bypass-link"))
6149622972aSÁlvaro Fernández Rojas priv->used_ports[p].bypass_link = true;
6159622972aSÁlvaro Fernández Rojas speed = ofnode_read_u32_default(node, "speed", 0);
6169622972aSÁlvaro Fernández Rojas if (speed)
6179622972aSÁlvaro Fernández Rojas priv->used_ports[p].force_speed = speed;
6189622972aSÁlvaro Fernández Rojas }
6199622972aSÁlvaro Fernández Rojas
6209622972aSÁlvaro Fernández Rojas /* init mii bus */
6219622972aSÁlvaro Fernández Rojas ret = bcm6368_mdio_init(dev->name, priv);
6229622972aSÁlvaro Fernández Rojas if (ret)
6239622972aSÁlvaro Fernández Rojas return ret;
6249622972aSÁlvaro Fernández Rojas
6259622972aSÁlvaro Fernández Rojas /* enable jumbo on all ports */
6269622972aSÁlvaro Fernández Rojas writel_be(0x1ff, priv->base + ETH_JMBCTL_PORT_REG);
6279622972aSÁlvaro Fernández Rojas writew_be(9728, priv->base + ETH_JMBCTL_MAXSIZE_REG);
6289622972aSÁlvaro Fernández Rojas
6299622972aSÁlvaro Fernández Rojas return 0;
6309622972aSÁlvaro Fernández Rojas }
6319622972aSÁlvaro Fernández Rojas
6329622972aSÁlvaro Fernández Rojas U_BOOT_DRIVER(bcm6368_eth) = {
6339622972aSÁlvaro Fernández Rojas .name = "bcm6368_eth",
6349622972aSÁlvaro Fernández Rojas .id = UCLASS_ETH,
6359622972aSÁlvaro Fernández Rojas .of_match = bcm6368_eth_ids,
6369622972aSÁlvaro Fernández Rojas .ops = &bcm6368_eth_ops,
6379622972aSÁlvaro Fernández Rojas .platdata_auto_alloc_size = sizeof(struct eth_pdata),
6389622972aSÁlvaro Fernández Rojas .priv_auto_alloc_size = sizeof(struct bcm6368_eth_priv),
6399622972aSÁlvaro Fernández Rojas .probe = bcm6368_eth_probe,
6409622972aSÁlvaro Fernández Rojas };
641