| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * Copyright (C) 2018 Álvaro Fernández Rojas <noltari@gmail.com> |
| * |
| * Derived from linux/drivers/net/ethernet/broadcom/bcm63xx_enet.c: |
| * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr> |
| */ |
| |
| #include <common.h> |
| #include <clk.h> |
| #include <dm.h> |
| #include <dma.h> |
| #include <log.h> |
| #include <malloc.h> |
| #include <miiphy.h> |
| #include <net.h> |
| #include <reset.h> |
| #include <wait_bit.h> |
| #include <asm/io.h> |
| #include <dm/device_compat.h> |
| #include <linux/delay.h> |
| #include <linux/printk.h> |
| |
| #define ETH_PORT_STR "brcm,enetsw-port" |
| |
| #define ETH_RX_DESC PKTBUFSRX |
| #define ETH_ZLEN 60 |
| #define ETH_TIMEOUT 100 |
| |
| #define ETH_MAX_PORT 8 |
| #define ETH_RGMII_PORT0 4 |
| |
| /* Port traffic control */ |
| #define ETH_PTCTRL_REG(x) (0x0 + (x)) |
| #define ETH_PTCTRL_RXDIS_SHIFT 0 |
| #define ETH_PTCTRL_RXDIS_MASK (1 << ETH_PTCTRL_RXDIS_SHIFT) |
| #define ETH_PTCTRL_TXDIS_SHIFT 1 |
| #define ETH_PTCTRL_TXDIS_MASK (1 << ETH_PTCTRL_TXDIS_SHIFT) |
| |
| /* Switch mode register */ |
| #define ETH_SWMODE_REG 0xb |
| #define ETH_SWMODE_FWD_EN_SHIFT 1 |
| #define ETH_SWMODE_FWD_EN_MASK (1 << ETH_SWMODE_FWD_EN_SHIFT) |
| |
| /* IMP override Register */ |
| #define ETH_IMPOV_REG 0xe |
| #define ETH_IMPOV_LINKUP_SHIFT 0 |
| #define ETH_IMPOV_LINKUP_MASK (1 << ETH_IMPOV_LINKUP_SHIFT) |
| #define ETH_IMPOV_FDX_SHIFT 1 |
| #define ETH_IMPOV_FDX_MASK (1 << ETH_IMPOV_FDX_SHIFT) |
| #define ETH_IMPOV_100_SHIFT 2 |
| #define ETH_IMPOV_100_MASK (1 << ETH_IMPOV_100_SHIFT) |
| #define ETH_IMPOV_1000_SHIFT 3 |
| #define ETH_IMPOV_1000_MASK (1 << ETH_IMPOV_1000_SHIFT) |
| #define ETH_IMPOV_RXFLOW_SHIFT 4 |
| #define ETH_IMPOV_RXFLOW_MASK (1 << ETH_IMPOV_RXFLOW_SHIFT) |
| #define ETH_IMPOV_TXFLOW_SHIFT 5 |
| #define ETH_IMPOV_TXFLOW_MASK (1 << ETH_IMPOV_TXFLOW_SHIFT) |
| #define ETH_IMPOV_FORCE_SHIFT 7 |
| #define ETH_IMPOV_FORCE_MASK (1 << ETH_IMPOV_FORCE_SHIFT) |
| |
| /* Port override Register */ |
| #define ETH_PORTOV_REG(x) (0x58 + (x)) |
| #define ETH_PORTOV_LINKUP_SHIFT 0 |
| #define ETH_PORTOV_LINKUP_MASK (1 << ETH_PORTOV_LINKUP_SHIFT) |
| #define ETH_PORTOV_FDX_SHIFT 1 |
| #define ETH_PORTOV_FDX_MASK (1 << ETH_PORTOV_FDX_SHIFT) |
| #define ETH_PORTOV_100_SHIFT 2 |
| #define ETH_PORTOV_100_MASK (1 << ETH_PORTOV_100_SHIFT) |
| #define ETH_PORTOV_1000_SHIFT 3 |
| #define ETH_PORTOV_1000_MASK (1 << ETH_PORTOV_1000_SHIFT) |
| #define ETH_PORTOV_RXFLOW_SHIFT 4 |
| #define ETH_PORTOV_RXFLOW_MASK (1 << ETH_PORTOV_RXFLOW_SHIFT) |
| #define ETH_PORTOV_TXFLOW_SHIFT 5 |
| #define ETH_PORTOV_TXFLOW_MASK (1 << ETH_PORTOV_TXFLOW_SHIFT) |
| #define ETH_PORTOV_ENABLE_SHIFT 6 |
| #define ETH_PORTOV_ENABLE_MASK (1 << ETH_PORTOV_ENABLE_SHIFT) |
| |
| /* Port RGMII control register */ |
| #define ETH_RGMII_CTRL_REG(x) (0x60 + (x)) |
| #define ETH_RGMII_CTRL_GMII_CLK_EN (1 << 7) |
| #define ETH_RGMII_CTRL_MII_OVERRIDE_EN (1 << 6) |
| #define ETH_RGMII_CTRL_MII_MODE_MASK (3 << 4) |
| #define ETH_RGMII_CTRL_RGMII_MODE (0 << 4) |
| #define ETH_RGMII_CTRL_MII_MODE (1 << 4) |
| #define ETH_RGMII_CTRL_RVMII_MODE (2 << 4) |
| #define ETH_RGMII_CTRL_TIMING_SEL_EN (1 << 0) |
| |
| /* Port RGMII timing register */ |
| #define ENETSW_RGMII_TIMING_REG(x) (0x68 + (x)) |
| |
| /* MDIO control register */ |
| #define MII_SC_REG 0xb0 |
| #define MII_SC_EXT_SHIFT 16 |
| #define MII_SC_EXT_MASK (1 << MII_SC_EXT_SHIFT) |
| #define MII_SC_REG_SHIFT 20 |
| #define MII_SC_PHYID_SHIFT 25 |
| #define MII_SC_RD_SHIFT 30 |
| #define MII_SC_RD_MASK (1 << MII_SC_RD_SHIFT) |
| #define MII_SC_WR_SHIFT 31 |
| #define MII_SC_WR_MASK (1 << MII_SC_WR_SHIFT) |
| |
| /* MDIO data register */ |
| #define MII_DAT_REG 0xb4 |
| |
| /* Global Management Configuration Register */ |
| #define ETH_GMCR_REG 0x200 |
| #define ETH_GMCR_RST_MIB_SHIFT 0 |
| #define ETH_GMCR_RST_MIB_MASK (1 << ETH_GMCR_RST_MIB_SHIFT) |
| |
| /* Jumbo control register port mask register */ |
| #define ETH_JMBCTL_PORT_REG 0x4004 |
| |
| /* Jumbo control mib good frame register */ |
| #define ETH_JMBCTL_MAXSIZE_REG 0x4008 |
| |
| /* ETH port data */ |
| struct bcm_enetsw_port { |
| bool used; |
| const char *name; |
| /* Config */ |
| bool bypass_link; |
| int force_speed; |
| bool force_duplex_full; |
| /* PHY */ |
| int phy_id; |
| }; |
| |
| /* ETH data */ |
| struct bcm6368_eth_priv { |
| void __iomem *base; |
| /* DMA */ |
| struct dma rx_dma; |
| struct dma tx_dma; |
| /* Ports */ |
| uint8_t num_ports; |
| struct bcm_enetsw_port used_ports[ETH_MAX_PORT]; |
| int sw_port_link[ETH_MAX_PORT]; |
| bool rgmii_override; |
| bool rgmii_timing; |
| /* PHY */ |
| int phy_id; |
| }; |
| |
| static inline bool bcm_enet_port_is_rgmii(int portid) |
| { |
| return portid >= ETH_RGMII_PORT0; |
| } |
| |
| static int bcm6368_mdio_read(struct bcm6368_eth_priv *priv, uint8_t ext, |
| int phy_id, int reg) |
| { |
| uint32_t val; |
| |
| writel_be(0, priv->base + MII_SC_REG); |
| |
| val = MII_SC_RD_MASK | |
| (phy_id << MII_SC_PHYID_SHIFT) | |
| (reg << MII_SC_REG_SHIFT); |
| |
| if (ext) |
| val |= MII_SC_EXT_MASK; |
| |
| writel_be(val, priv->base + MII_SC_REG); |
| udelay(50); |
| |
| return readw_be(priv->base + MII_DAT_REG); |
| } |
| |
| static int bcm6368_mdio_write(struct bcm6368_eth_priv *priv, uint8_t ext, |
| int phy_id, int reg, u16 data) |
| { |
| uint32_t val; |
| |
| writel_be(0, priv->base + MII_SC_REG); |
| |
| val = MII_SC_WR_MASK | |
| (phy_id << MII_SC_PHYID_SHIFT) | |
| (reg << MII_SC_REG_SHIFT); |
| |
| if (ext) |
| val |= MII_SC_EXT_MASK; |
| |
| val |= data; |
| |
| writel_be(val, priv->base + MII_SC_REG); |
| udelay(50); |
| |
| return 0; |
| } |
| |
| static int bcm6368_eth_free_pkt(struct udevice *dev, uchar *packet, int len) |
| { |
| struct bcm6368_eth_priv *priv = dev_get_priv(dev); |
| |
| return dma_prepare_rcv_buf(&priv->rx_dma, packet, len); |
| } |
| |
| static int bcm6368_eth_recv(struct udevice *dev, int flags, uchar **packetp) |
| { |
| struct bcm6368_eth_priv *priv = dev_get_priv(dev); |
| |
| return dma_receive(&priv->rx_dma, (void**)packetp, NULL); |
| } |
| |
| static int bcm6368_eth_send(struct udevice *dev, void *packet, int length) |
| { |
| struct bcm6368_eth_priv *priv = dev_get_priv(dev); |
| |
| /* pad packets smaller than ETH_ZLEN */ |
| if (length < ETH_ZLEN) { |
| memset(packet + length, 0, ETH_ZLEN - length); |
| length = ETH_ZLEN; |
| } |
| |
| return dma_send(&priv->tx_dma, packet, length, NULL); |
| } |
| |
| static int bcm6368_eth_adjust_link(struct udevice *dev) |
| { |
| struct bcm6368_eth_priv *priv = dev_get_priv(dev); |
| unsigned int i; |
| |
| for (i = 0; i < priv->num_ports; i++) { |
| struct bcm_enetsw_port *port; |
| int val, j, up, adv, lpa, speed, duplex, media; |
| int external_phy = bcm_enet_port_is_rgmii(i); |
| u8 override; |
| |
| port = &priv->used_ports[i]; |
| if (!port->used) |
| continue; |
| |
| if (port->bypass_link) |
| continue; |
| |
| /* dummy read to clear */ |
| for (j = 0; j < 2; j++) |
| val = bcm6368_mdio_read(priv, external_phy, |
| port->phy_id, MII_BMSR); |
| |
| if (val == 0xffff) |
| continue; |
| |
| up = (val & BMSR_LSTATUS) ? 1 : 0; |
| if (!(up ^ priv->sw_port_link[i])) |
| continue; |
| |
| priv->sw_port_link[i] = up; |
| |
| /* link changed */ |
| if (!up) { |
| dev_info(dev, "link DOWN on %s\n", port->name); |
| writeb_be(ETH_PORTOV_ENABLE_MASK, |
| priv->base + ETH_PORTOV_REG(i)); |
| writeb_be(ETH_PTCTRL_RXDIS_MASK | |
| ETH_PTCTRL_TXDIS_MASK, |
| priv->base + ETH_PTCTRL_REG(i)); |
| continue; |
| } |
| |
| adv = bcm6368_mdio_read(priv, external_phy, |
| port->phy_id, MII_ADVERTISE); |
| |
| lpa = bcm6368_mdio_read(priv, external_phy, port->phy_id, |
| MII_LPA); |
| |
| /* figure out media and duplex from advertise and LPA values */ |
| media = mii_nway_result(lpa & adv); |
| duplex = (media & ADVERTISE_FULL) ? 1 : 0; |
| |
| if (media & (ADVERTISE_100FULL | ADVERTISE_100HALF)) |
| speed = 100; |
| else |
| speed = 10; |
| |
| if (val & BMSR_ESTATEN) { |
| adv = bcm6368_mdio_read(priv, external_phy, |
| port->phy_id, MII_CTRL1000); |
| |
| lpa = bcm6368_mdio_read(priv, external_phy, |
| port->phy_id, MII_STAT1000); |
| |
| if ((adv & (ADVERTISE_1000FULL | ADVERTISE_1000HALF)) && |
| (lpa & (LPA_1000FULL | LPA_1000HALF))) { |
| speed = 1000; |
| duplex = (lpa & LPA_1000FULL); |
| } |
| } |
| |
| pr_alert("link UP on %s, %dMbps, %s-duplex\n", |
| port->name, speed, duplex ? "full" : "half"); |
| |
| override = ETH_PORTOV_ENABLE_MASK | |
| ETH_PORTOV_LINKUP_MASK; |
| |
| if (speed == 1000) |
| override |= ETH_PORTOV_1000_MASK; |
| else if (speed == 100) |
| override |= ETH_PORTOV_100_MASK; |
| if (duplex) |
| override |= ETH_PORTOV_FDX_MASK; |
| |
| writeb_be(override, priv->base + ETH_PORTOV_REG(i)); |
| writeb_be(0, priv->base + ETH_PTCTRL_REG(i)); |
| } |
| |
| return 0; |
| } |
| |
| static int bcm6368_eth_start(struct udevice *dev) |
| { |
| struct bcm6368_eth_priv *priv = dev_get_priv(dev); |
| uint8_t i; |
| |
| /* disable all ports */ |
| for (i = 0; i < priv->num_ports; i++) { |
| setbits_8(priv->base + ETH_PORTOV_REG(i), |
| ETH_PORTOV_ENABLE_MASK); |
| setbits_8(priv->base + ETH_PTCTRL_REG(i), |
| ETH_PTCTRL_RXDIS_MASK | ETH_PTCTRL_TXDIS_MASK); |
| priv->sw_port_link[i] = 0; |
| } |
| |
| /* enable external ports */ |
| for (i = ETH_RGMII_PORT0; i < priv->num_ports; i++) { |
| u8 rgmii_ctrl = ETH_RGMII_CTRL_GMII_CLK_EN; |
| |
| if (!priv->used_ports[i].used) |
| continue; |
| |
| if (priv->rgmii_override) |
| rgmii_ctrl |= ETH_RGMII_CTRL_MII_OVERRIDE_EN; |
| if (priv->rgmii_timing) |
| rgmii_ctrl |= ETH_RGMII_CTRL_TIMING_SEL_EN; |
| |
| setbits_8(priv->base + ETH_RGMII_CTRL_REG(i), rgmii_ctrl); |
| } |
| |
| /* reset mib */ |
| setbits_8(priv->base + ETH_GMCR_REG, ETH_GMCR_RST_MIB_MASK); |
| mdelay(1); |
| clrbits_8(priv->base + ETH_GMCR_REG, ETH_GMCR_RST_MIB_MASK); |
| mdelay(1); |
| |
| /* force CPU port state */ |
| setbits_8(priv->base + ETH_IMPOV_REG, |
| ETH_IMPOV_FORCE_MASK | ETH_IMPOV_LINKUP_MASK); |
| |
| /* enable switch forward engine */ |
| setbits_8(priv->base + ETH_SWMODE_REG, ETH_SWMODE_FWD_EN_MASK); |
| |
| /* prepare rx dma buffers */ |
| for (i = 0; i < ETH_RX_DESC; i++) { |
| int ret = dma_prepare_rcv_buf(&priv->rx_dma, net_rx_packets[i], |
| PKTSIZE_ALIGN); |
| if (ret < 0) |
| break; |
| } |
| |
| /* enable dma rx channel */ |
| dma_enable(&priv->rx_dma); |
| |
| /* enable dma tx channel */ |
| dma_enable(&priv->tx_dma); |
| |
| /* apply override config for bypass_link ports here. */ |
| for (i = 0; i < priv->num_ports; i++) { |
| struct bcm_enetsw_port *port; |
| u8 override; |
| |
| port = &priv->used_ports[i]; |
| if (!port->used) |
| continue; |
| |
| if (!port->bypass_link) |
| continue; |
| |
| override = ETH_PORTOV_ENABLE_MASK | |
| ETH_PORTOV_LINKUP_MASK; |
| |
| switch (port->force_speed) { |
| case 1000: |
| override |= ETH_PORTOV_1000_MASK; |
| break; |
| case 100: |
| override |= ETH_PORTOV_100_MASK; |
| break; |
| case 10: |
| break; |
| default: |
| pr_warn("%s: invalid forced speed on port %s\n", |
| __func__, port->name); |
| break; |
| } |
| |
| if (port->force_duplex_full) |
| override |= ETH_PORTOV_FDX_MASK; |
| |
| writeb_be(override, priv->base + ETH_PORTOV_REG(i)); |
| writeb_be(0, priv->base + ETH_PTCTRL_REG(i)); |
| } |
| |
| bcm6368_eth_adjust_link(dev); |
| |
| return 0; |
| } |
| |
| static void bcm6368_eth_stop(struct udevice *dev) |
| { |
| struct bcm6368_eth_priv *priv = dev_get_priv(dev); |
| uint8_t i; |
| |
| /* disable all ports */ |
| for (i = 0; i < priv->num_ports; i++) { |
| setbits_8(priv->base + ETH_PORTOV_REG(i), |
| ETH_PORTOV_ENABLE_MASK); |
| setbits_8(priv->base + ETH_PTCTRL_REG(i), |
| ETH_PTCTRL_RXDIS_MASK | ETH_PTCTRL_TXDIS_MASK); |
| } |
| |
| /* disable external ports */ |
| for (i = ETH_RGMII_PORT0; i < priv->num_ports; i++) { |
| if (!priv->used_ports[i].used) |
| continue; |
| |
| clrbits_8(priv->base + ETH_RGMII_CTRL_REG(i), |
| ETH_RGMII_CTRL_GMII_CLK_EN); |
| } |
| |
| /* disable CPU port */ |
| clrbits_8(priv->base + ETH_IMPOV_REG, |
| ETH_IMPOV_FORCE_MASK | ETH_IMPOV_LINKUP_MASK); |
| |
| /* disable switch forward engine */ |
| clrbits_8(priv->base + ETH_SWMODE_REG, ETH_SWMODE_FWD_EN_MASK); |
| |
| /* disable dma rx channel */ |
| dma_disable(&priv->rx_dma); |
| |
| /* disable dma tx channel */ |
| dma_disable(&priv->tx_dma); |
| } |
| |
| static const struct eth_ops bcm6368_eth_ops = { |
| .free_pkt = bcm6368_eth_free_pkt, |
| .recv = bcm6368_eth_recv, |
| .send = bcm6368_eth_send, |
| .start = bcm6368_eth_start, |
| .stop = bcm6368_eth_stop, |
| }; |
| |
| static const struct udevice_id bcm6368_eth_ids[] = { |
| { .compatible = "brcm,bcm6368-enet", }, |
| { /* sentinel */ } |
| }; |
| |
| static bool bcm6368_phy_is_external(struct bcm6368_eth_priv *priv, int phy_id) |
| { |
| uint8_t i; |
| |
| for (i = 0; i < priv->num_ports; ++i) { |
| if (!priv->used_ports[i].used) |
| continue; |
| if (priv->used_ports[i].phy_id == phy_id) |
| return bcm_enet_port_is_rgmii(i); |
| } |
| |
| return true; |
| } |
| |
| static int bcm6368_mii_mdio_read(struct mii_dev *bus, int addr, int devaddr, |
| int reg) |
| { |
| struct bcm6368_eth_priv *priv = bus->priv; |
| bool ext = bcm6368_phy_is_external(priv, addr); |
| |
| return bcm6368_mdio_read(priv, ext, addr, reg); |
| } |
| |
| static int bcm6368_mii_mdio_write(struct mii_dev *bus, int addr, int devaddr, |
| int reg, u16 data) |
| { |
| struct bcm6368_eth_priv *priv = bus->priv; |
| bool ext = bcm6368_phy_is_external(priv, addr); |
| |
| return bcm6368_mdio_write(priv, ext, addr, reg, data); |
| } |
| |
| static int bcm6368_mdio_init(const char *name, struct bcm6368_eth_priv *priv) |
| { |
| struct mii_dev *bus; |
| |
| bus = mdio_alloc(); |
| if (!bus) { |
| pr_err("%s: failed to allocate MDIO bus\n", __func__); |
| return -ENOMEM; |
| } |
| |
| bus->read = bcm6368_mii_mdio_read; |
| bus->write = bcm6368_mii_mdio_write; |
| bus->priv = priv; |
| snprintf(bus->name, sizeof(bus->name), "%s", name); |
| |
| return mdio_register(bus); |
| } |
| |
| static int bcm6368_eth_probe(struct udevice *dev) |
| { |
| struct eth_pdata *pdata = dev_get_plat(dev); |
| struct bcm6368_eth_priv *priv = dev_get_priv(dev); |
| int num_ports, ret, i; |
| ofnode node; |
| |
| /* get base address */ |
| priv->base = dev_remap_addr(dev); |
| if (!priv->base) |
| return -EINVAL; |
| pdata->iobase = (phys_addr_t) priv->base; |
| |
| /* get number of ports */ |
| num_ports = dev_read_u32_default(dev, "brcm,num-ports", ETH_MAX_PORT); |
| if (!num_ports || num_ports > ETH_MAX_PORT) |
| return -EINVAL; |
| |
| /* get dma channels */ |
| ret = dma_get_by_name(dev, "tx", &priv->tx_dma); |
| if (ret) |
| return -EINVAL; |
| |
| ret = dma_get_by_name(dev, "rx", &priv->rx_dma); |
| if (ret) |
| return -EINVAL; |
| |
| /* try to enable clocks */ |
| for (i = 0; ; i++) { |
| struct clk clk; |
| int ret; |
| |
| ret = clk_get_by_index(dev, i, &clk); |
| if (ret < 0) |
| break; |
| |
| ret = clk_enable(&clk); |
| if (ret < 0) { |
| pr_err("%s: error enabling clock %d\n", __func__, i); |
| return ret; |
| } |
| } |
| |
| /* try to perform resets */ |
| for (i = 0; ; i++) { |
| struct reset_ctl reset; |
| int ret; |
| |
| ret = reset_get_by_index(dev, i, &reset); |
| if (ret < 0) |
| break; |
| |
| ret = reset_deassert(&reset); |
| if (ret < 0) { |
| pr_err("%s: error deasserting reset %d\n", __func__, i); |
| return ret; |
| } |
| |
| ret = reset_free(&reset); |
| if (ret < 0) { |
| pr_err("%s: error freeing reset %d\n", __func__, i); |
| return ret; |
| } |
| } |
| |
| /* set priv data */ |
| priv->num_ports = num_ports; |
| if (dev_read_bool(dev, "brcm,rgmii-override")) |
| priv->rgmii_override = true; |
| if (dev_read_bool(dev, "brcm,rgmii-timing")) |
| priv->rgmii_timing = true; |
| |
| /* get ports */ |
| dev_for_each_subnode(node, dev) { |
| const char *comp; |
| const char *label; |
| unsigned int p; |
| int phy_id; |
| int speed; |
| |
| comp = ofnode_read_string(node, "compatible"); |
| if (!comp || memcmp(comp, ETH_PORT_STR, sizeof(ETH_PORT_STR))) |
| continue; |
| |
| p = ofnode_read_u32_default(node, "reg", ETH_MAX_PORT); |
| if (p >= num_ports) |
| return -EINVAL; |
| |
| label = ofnode_read_string(node, "label"); |
| if (!label) { |
| debug("%s: node %s has no label\n", __func__, |
| ofnode_get_name(node)); |
| return -EINVAL; |
| } |
| |
| phy_id = ofnode_read_u32_default(node, "brcm,phy-id", -1); |
| |
| priv->used_ports[p].used = true; |
| priv->used_ports[p].name = label; |
| priv->used_ports[p].phy_id = phy_id; |
| |
| if (ofnode_read_bool(node, "full-duplex")) |
| priv->used_ports[p].force_duplex_full = true; |
| if (ofnode_read_bool(node, "bypass-link")) |
| priv->used_ports[p].bypass_link = true; |
| speed = ofnode_read_u32_default(node, "speed", 0); |
| if (speed) |
| priv->used_ports[p].force_speed = speed; |
| } |
| |
| /* init mii bus */ |
| ret = bcm6368_mdio_init(dev->name, priv); |
| if (ret) |
| return ret; |
| |
| /* enable jumbo on all ports */ |
| writel_be(0x1ff, priv->base + ETH_JMBCTL_PORT_REG); |
| writew_be(9728, priv->base + ETH_JMBCTL_MAXSIZE_REG); |
| |
| return 0; |
| } |
| |
| U_BOOT_DRIVER(bcm6368_eth) = { |
| .name = "bcm6368_eth", |
| .id = UCLASS_ETH, |
| .of_match = bcm6368_eth_ids, |
| .ops = &bcm6368_eth_ops, |
| .plat_auto = sizeof(struct eth_pdata), |
| .priv_auto = sizeof(struct bcm6368_eth_priv), |
| .probe = bcm6368_eth_probe, |
| }; |