| // SPDX-License-Identifier: GPL-2.0+ | |
| /* | |
| * (C) Copyright 2007-2009 Michal Simek | |
| * (C) Copyright 2003 Xilinx Inc. | |
| * | |
| * Michal SIMEK <monstr@monstr.eu> | |
| */ | |
| #include <log.h> | |
| #include <net.h> | |
| #include <config.h> | |
| #include <dm.h> | |
| #include <console.h> | |
| #include <malloc.h> | |
| #include <asm/global_data.h> | |
| #include <phy.h> | |
| #include <miiphy.h> | |
| #include <fdtdec.h> | |
| #include <linux/delay.h> | |
| #include <linux/errno.h> | |
| #include <linux/io.h> | |
| #include <linux/kernel.h> | |
| #include <eth_phy.h> | |
| DECLARE_GLOBAL_DATA_PTR; | |
| #define ENET_ADDR_LENGTH 6 | |
| #define ETH_FCS_LEN 4 /* Octets in the FCS */ | |
| /* Xmit complete */ | |
| #define XEL_TSR_XMIT_BUSY_MASK 0x00000001UL | |
| /* Xmit interrupt enable bit */ | |
| #define XEL_TSR_XMIT_IE_MASK 0x00000008UL | |
| /* Program the MAC address */ | |
| #define XEL_TSR_PROGRAM_MASK 0x00000002UL | |
| /* define for programming the MAC address into the EMAC Lite */ | |
| #define XEL_TSR_PROG_MAC_ADDR (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_PROGRAM_MASK) | |
| /* Transmit packet length upper byte */ | |
| #define XEL_TPLR_LENGTH_MASK_HI 0x0000FF00UL | |
| /* Transmit packet length lower byte */ | |
| #define XEL_TPLR_LENGTH_MASK_LO 0x000000FFUL | |
| /* Recv complete */ | |
| #define XEL_RSR_RECV_DONE_MASK 0x00000001UL | |
| /* Recv interrupt enable bit */ | |
| #define XEL_RSR_RECV_IE_MASK 0x00000008UL | |
| /* MDIO Address Register Bit Masks */ | |
| #define XEL_MDIOADDR_REGADR_MASK 0x0000001F /* Register Address */ | |
| #define XEL_MDIOADDR_PHYADR_MASK 0x000003E0 /* PHY Address */ | |
| #define XEL_MDIOADDR_PHYADR_SHIFT 5 | |
| #define XEL_MDIOADDR_OP_MASK 0x00000400 /* RD/WR Operation */ | |
| /* MDIO Write Data Register Bit Masks */ | |
| #define XEL_MDIOWR_WRDATA_MASK 0x0000FFFF /* Data to be Written */ | |
| /* MDIO Read Data Register Bit Masks */ | |
| #define XEL_MDIORD_RDDATA_MASK 0x0000FFFF /* Data to be Read */ | |
| /* MDIO Control Register Bit Masks */ | |
| #define XEL_MDIOCTRL_MDIOSTS_MASK 0x00000001 /* MDIO Status Mask */ | |
| #define XEL_MDIOCTRL_MDIOEN_MASK 0x00000008 /* MDIO Enable */ | |
| struct emaclite_regs { | |
| u32 tx_ping; /* 0x0 - TX Ping buffer */ | |
| u32 reserved1[504]; | |
| u32 mdioaddr; /* 0x7e4 - MDIO Address Register */ | |
| u32 mdiowr; /* 0x7e8 - MDIO Write Data Register */ | |
| u32 mdiord;/* 0x7ec - MDIO Read Data Register */ | |
| u32 mdioctrl; /* 0x7f0 - MDIO Control Register */ | |
| u32 tx_ping_tplr; /* 0x7f4 - Tx packet length */ | |
| u32 global_interrupt; /* 0x7f8 - Global interrupt enable */ | |
| u32 tx_ping_tsr; /* 0x7fc - Tx status */ | |
| u32 tx_pong; /* 0x800 - TX Pong buffer */ | |
| u32 reserved2[508]; | |
| u32 tx_pong_tplr; /* 0xff4 - Tx packet length */ | |
| u32 reserved3; /* 0xff8 */ | |
| u32 tx_pong_tsr; /* 0xffc - Tx status */ | |
| u32 rx_ping; /* 0x1000 - Receive Buffer */ | |
| u32 reserved4[510]; | |
| u32 rx_ping_rsr; /* 0x17fc - Rx status */ | |
| u32 rx_pong; /* 0x1800 - Receive Buffer */ | |
| u32 reserved5[510]; | |
| u32 rx_pong_rsr; /* 0x1ffc - Rx status */ | |
| }; | |
| struct xemaclite { | |
| bool use_rx_pong_buffer_next; /* Next RX buffer to read from */ | |
| u32 txpp; /* TX ping pong buffer */ | |
| u32 rxpp; /* RX ping pong buffer */ | |
| int phyaddr; | |
| struct emaclite_regs *regs; | |
| struct phy_device *phydev; | |
| struct mii_dev *bus; | |
| }; | |
| static uchar etherrxbuff[PKTSIZE_ALIGN]; /* Receive buffer */ | |
| static void xemaclite_alignedread(u32 *srcptr, void *destptr, u32 bytecount) | |
| { | |
| u32 i; | |
| u32 alignbuffer; | |
| u32 *to32ptr; | |
| u32 *from32ptr; | |
| u8 *to8ptr; | |
| u8 *from8ptr; | |
| from32ptr = (u32 *) srcptr; | |
| /* Word aligned buffer, no correction needed. */ | |
| to32ptr = (u32 *) destptr; | |
| while (bytecount > 3) { | |
| *to32ptr++ = __raw_readl(from32ptr++); | |
| bytecount -= 4; | |
| } | |
| to8ptr = (u8 *) to32ptr; | |
| alignbuffer = __raw_readl(from32ptr++); | |
| from8ptr = (u8 *) &alignbuffer; | |
| for (i = 0; i < bytecount; i++) | |
| *to8ptr++ = *from8ptr++; | |
| } | |
| static void xemaclite_alignedwrite(void *srcptr, u32 *destptr, u32 bytecount) | |
| { | |
| u32 i; | |
| u32 alignbuffer; | |
| u32 *to32ptr = (u32 *) destptr; | |
| u32 *from32ptr; | |
| u8 *to8ptr; | |
| u8 *from8ptr; | |
| from32ptr = (u32 *) srcptr; | |
| while (bytecount > 3) { | |
| __raw_writel(*from32ptr++, to32ptr++); | |
| bytecount -= 4; | |
| } | |
| alignbuffer = 0; | |
| to8ptr = (u8 *) &alignbuffer; | |
| from8ptr = (u8 *) from32ptr; | |
| for (i = 0; i < bytecount; i++) | |
| *to8ptr++ = *from8ptr++; | |
| __raw_writel(alignbuffer, to32ptr++); | |
| } | |
| static int wait_for_bit(const char *func, u32 *reg, const u32 mask, | |
| bool set, unsigned int timeout) | |
| { | |
| u32 val; | |
| unsigned long start = get_timer(0); | |
| while (1) { | |
| val = __raw_readl(reg); | |
| if (!set) | |
| val = ~val; | |
| if ((val & mask) == mask) | |
| return 0; | |
| if (get_timer(start) > timeout) | |
| break; | |
| if (ctrlc()) { | |
| puts("Abort\n"); | |
| return -EINTR; | |
| } | |
| udelay(1); | |
| } | |
| debug("%s: Timeout (reg=%p mask=%08x wait_set=%i)\n", | |
| func, reg, mask, set); | |
| return -ETIMEDOUT; | |
| } | |
| static int mdio_wait(struct emaclite_regs *regs) | |
| { | |
| return wait_for_bit(__func__, ®s->mdioctrl, | |
| XEL_MDIOCTRL_MDIOSTS_MASK, false, 2000); | |
| } | |
| static u32 phyread(struct xemaclite *emaclite, u32 phyaddress, u32 registernum, | |
| u16 *data) | |
| { | |
| struct emaclite_regs *regs = emaclite->regs; | |
| if (mdio_wait(regs)) | |
| return 1; | |
| u32 ctrl_reg = __raw_readl(®s->mdioctrl); | |
| __raw_writel(XEL_MDIOADDR_OP_MASK | |
| | ((phyaddress << XEL_MDIOADDR_PHYADR_SHIFT) | |
| | registernum), ®s->mdioaddr); | |
| __raw_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK, ®s->mdioctrl); | |
| if (mdio_wait(regs)) | |
| return 1; | |
| /* Read data */ | |
| *data = __raw_readl(®s->mdiord); | |
| return 0; | |
| } | |
| static u32 phywrite(struct xemaclite *emaclite, u32 phyaddress, u32 registernum, | |
| u16 data) | |
| { | |
| struct emaclite_regs *regs = emaclite->regs; | |
| if (mdio_wait(regs)) | |
| return 1; | |
| /* | |
| * Write the PHY address, register number and clear the OP bit in the | |
| * MDIO Address register and then write the value into the MDIO Write | |
| * Data register. Finally, set the Status bit in the MDIO Control | |
| * register to start a MDIO write transaction. | |
| */ | |
| u32 ctrl_reg = __raw_readl(®s->mdioctrl); | |
| __raw_writel(~XEL_MDIOADDR_OP_MASK | |
| & ((phyaddress << XEL_MDIOADDR_PHYADR_SHIFT) | |
| | registernum), ®s->mdioaddr); | |
| __raw_writel(data, ®s->mdiowr); | |
| __raw_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK, ®s->mdioctrl); | |
| if (mdio_wait(regs)) | |
| return 1; | |
| return 0; | |
| } | |
| static void emaclite_stop(struct udevice *dev) | |
| { | |
| debug("eth_stop\n"); | |
| } | |
| /* Use MII register 1 (MII status register) to detect PHY */ | |
| #define PHY_DETECT_REG 1 | |
| /* Mask used to verify certain PHY features (or register contents) | |
| * in the register above: | |
| * 0x1000: 10Mbps full duplex support | |
| * 0x0800: 10Mbps half duplex support | |
| * 0x0008: Auto-negotiation support | |
| */ | |
| #define PHY_DETECT_MASK 0x1808 | |
| static int setup_phy(struct udevice *dev) | |
| { | |
| int i, ret; | |
| u16 phyreg; | |
| struct xemaclite *emaclite = dev_get_priv(dev); | |
| struct phy_device *phydev; | |
| u32 supported = SUPPORTED_10baseT_Half | | |
| SUPPORTED_10baseT_Full | | |
| SUPPORTED_100baseT_Half | | |
| SUPPORTED_100baseT_Full; | |
| if (emaclite->phyaddr != -1) { | |
| phyread(emaclite, emaclite->phyaddr, PHY_DETECT_REG, &phyreg); | |
| if ((phyreg != 0xFFFF) && | |
| ((phyreg & PHY_DETECT_MASK) == PHY_DETECT_MASK)) { | |
| /* Found a valid PHY address */ | |
| debug("Default phy address %d is valid\n", | |
| emaclite->phyaddr); | |
| } else { | |
| debug("PHY address is not setup correctly %d\n", | |
| emaclite->phyaddr); | |
| emaclite->phyaddr = -1; | |
| } | |
| } | |
| if (emaclite->phyaddr == -1) { | |
| /* detect the PHY address */ | |
| for (i = 31; i >= 0; i--) { | |
| phyread(emaclite, i, PHY_DETECT_REG, &phyreg); | |
| if ((phyreg != 0xFFFF) && | |
| ((phyreg & PHY_DETECT_MASK) == PHY_DETECT_MASK)) { | |
| /* Found a valid PHY address */ | |
| emaclite->phyaddr = i; | |
| debug("emaclite: Found valid phy address, %d\n", | |
| i); | |
| break; | |
| } | |
| } | |
| } | |
| /* interface - look at tsec */ | |
| phydev = phy_connect(emaclite->bus, emaclite->phyaddr, dev, | |
| PHY_INTERFACE_MODE_MII); | |
| /* | |
| * Phy can support 1000baseT but device NOT that's why phydev->supported | |
| * must be setup for 1000baseT. phydev->advertising setups what speeds | |
| * will be used for autonegotiation where 1000baseT must be disabled. | |
| */ | |
| phydev->supported = supported | SUPPORTED_1000baseT_Half | | |
| SUPPORTED_1000baseT_Full; | |
| phydev->advertising = supported; | |
| emaclite->phydev = phydev; | |
| phy_config(phydev); | |
| ret = phy_startup(phydev); | |
| if (ret) | |
| return ret; | |
| if (!phydev->link) { | |
| printf("%s: No link.\n", phydev->dev->name); | |
| return 0; | |
| } | |
| /* Do not setup anything */ | |
| return 1; | |
| } | |
| static int emaclite_start(struct udevice *dev) | |
| { | |
| struct xemaclite *emaclite = dev_get_priv(dev); | |
| struct eth_pdata *pdata = dev_get_plat(dev); | |
| struct emaclite_regs *regs = emaclite->regs; | |
| debug("EmacLite Initialization Started\n"); | |
| /* | |
| * TX - TX_PING & TX_PONG initialization | |
| */ | |
| /* Restart PING TX */ | |
| __raw_writel(0, ®s->tx_ping_tsr); | |
| /* Copy MAC address */ | |
| xemaclite_alignedwrite(pdata->enetaddr, ®s->tx_ping, | |
| ENET_ADDR_LENGTH); | |
| /* Set the length */ | |
| __raw_writel(ENET_ADDR_LENGTH, ®s->tx_ping_tplr); | |
| /* Update the MAC address in the EMAC Lite */ | |
| __raw_writel(XEL_TSR_PROG_MAC_ADDR, ®s->tx_ping_tsr); | |
| /* Wait for EMAC Lite to finish with the MAC address update */ | |
| while ((__raw_readl(®s->tx_ping_tsr) & | |
| XEL_TSR_PROG_MAC_ADDR) != 0) | |
| ; | |
| if (emaclite->txpp) { | |
| /* The same operation with PONG TX */ | |
| __raw_writel(0, ®s->tx_pong_tsr); | |
| xemaclite_alignedwrite(pdata->enetaddr, ®s->tx_pong, | |
| ENET_ADDR_LENGTH); | |
| __raw_writel(ENET_ADDR_LENGTH, ®s->tx_pong_tplr); | |
| __raw_writel(XEL_TSR_PROG_MAC_ADDR, ®s->tx_pong_tsr); | |
| while ((__raw_readl(®s->tx_pong_tsr) & | |
| XEL_TSR_PROG_MAC_ADDR) != 0) | |
| ; | |
| } | |
| /* | |
| * RX - RX_PING & RX_PONG initialization | |
| */ | |
| /* Write out the value to flush the RX buffer */ | |
| __raw_writel(XEL_RSR_RECV_IE_MASK, ®s->rx_ping_rsr); | |
| if (emaclite->rxpp) | |
| __raw_writel(XEL_RSR_RECV_IE_MASK, ®s->rx_pong_rsr); | |
| __raw_writel(XEL_MDIOCTRL_MDIOEN_MASK, ®s->mdioctrl); | |
| if (__raw_readl(®s->mdioctrl) & XEL_MDIOCTRL_MDIOEN_MASK) | |
| if (!setup_phy(dev)) | |
| return -1; | |
| debug("EmacLite Initialization complete\n"); | |
| return 0; | |
| } | |
| static int xemaclite_txbufferavailable(struct xemaclite *emaclite) | |
| { | |
| u32 tmp; | |
| struct emaclite_regs *regs = emaclite->regs; | |
| /* | |
| * Read the other buffer register | |
| * and determine if the other buffer is available | |
| */ | |
| tmp = ~__raw_readl(®s->tx_ping_tsr); | |
| if (emaclite->txpp) | |
| tmp |= ~__raw_readl(®s->tx_pong_tsr); | |
| return !(tmp & XEL_TSR_XMIT_BUSY_MASK); | |
| } | |
| static int emaclite_send(struct udevice *dev, void *ptr, int len) | |
| { | |
| u32 reg; | |
| struct xemaclite *emaclite = dev_get_priv(dev); | |
| struct emaclite_regs *regs = emaclite->regs; | |
| u32 maxtry = 1000; | |
| if (len > PKTSIZE) | |
| len = PKTSIZE; | |
| while (xemaclite_txbufferavailable(emaclite) && maxtry) { | |
| udelay(10); | |
| maxtry--; | |
| } | |
| if (!maxtry) { | |
| printf("Error: Timeout waiting for ethernet TX buffer\n"); | |
| /* Restart PING TX */ | |
| __raw_writel(0, ®s->tx_ping_tsr); | |
| if (emaclite->txpp) { | |
| __raw_writel(0, ®s->tx_pong_tsr); | |
| } | |
| return -1; | |
| } | |
| /* Determine if the expected buffer address is empty */ | |
| reg = __raw_readl(®s->tx_ping_tsr); | |
| if ((reg & XEL_TSR_XMIT_BUSY_MASK) == 0) { | |
| debug("Send packet from tx_ping buffer\n"); | |
| /* Write the frame to the buffer */ | |
| xemaclite_alignedwrite(ptr, ®s->tx_ping, len); | |
| __raw_writel(len | |
| & (XEL_TPLR_LENGTH_MASK_HI | XEL_TPLR_LENGTH_MASK_LO), | |
| ®s->tx_ping_tplr); | |
| reg = __raw_readl(®s->tx_ping_tsr); | |
| reg |= XEL_TSR_XMIT_BUSY_MASK; | |
| __raw_writel(reg, ®s->tx_ping_tsr); | |
| return 0; | |
| } | |
| if (emaclite->txpp) { | |
| /* Determine if the expected buffer address is empty */ | |
| reg = __raw_readl(®s->tx_pong_tsr); | |
| if ((reg & XEL_TSR_XMIT_BUSY_MASK) == 0) { | |
| debug("Send packet from tx_pong buffer\n"); | |
| /* Write the frame to the buffer */ | |
| xemaclite_alignedwrite(ptr, ®s->tx_pong, len); | |
| __raw_writel(len & | |
| (XEL_TPLR_LENGTH_MASK_HI | | |
| XEL_TPLR_LENGTH_MASK_LO), | |
| ®s->tx_pong_tplr); | |
| reg = __raw_readl(®s->tx_pong_tsr); | |
| reg |= XEL_TSR_XMIT_BUSY_MASK; | |
| __raw_writel(reg, ®s->tx_pong_tsr); | |
| return 0; | |
| } | |
| } | |
| puts("Error while sending frame\n"); | |
| return -1; | |
| } | |
| static int emaclite_recv(struct udevice *dev, int flags, uchar **packetp) | |
| { | |
| u32 length, first_read, reg, attempt = 0; | |
| void *addr, *ack; | |
| struct xemaclite *emaclite = dev_get_priv(dev); | |
| struct emaclite_regs *regs = emaclite->regs; | |
| struct ethernet_hdr *eth; | |
| struct ip_udp_hdr *ip; | |
| try_again: | |
| if (!emaclite->use_rx_pong_buffer_next) { | |
| reg = __raw_readl(®s->rx_ping_rsr); | |
| debug("Testing data at rx_ping\n"); | |
| if ((reg & XEL_RSR_RECV_DONE_MASK) == XEL_RSR_RECV_DONE_MASK) { | |
| debug("Data found in rx_ping buffer\n"); | |
| addr = ®s->rx_ping; | |
| ack = ®s->rx_ping_rsr; | |
| } else { | |
| debug("Data not found in rx_ping buffer\n"); | |
| /* Pong buffer is not available - return immediately */ | |
| if (!emaclite->rxpp) | |
| return -1; | |
| /* Try pong buffer if this is first attempt */ | |
| if (attempt++) | |
| return -1; | |
| emaclite->use_rx_pong_buffer_next = | |
| !emaclite->use_rx_pong_buffer_next; | |
| goto try_again; | |
| } | |
| } else { | |
| reg = __raw_readl(®s->rx_pong_rsr); | |
| debug("Testing data at rx_pong\n"); | |
| if ((reg & XEL_RSR_RECV_DONE_MASK) == XEL_RSR_RECV_DONE_MASK) { | |
| debug("Data found in rx_pong buffer\n"); | |
| addr = ®s->rx_pong; | |
| ack = ®s->rx_pong_rsr; | |
| } else { | |
| debug("Data not found in rx_pong buffer\n"); | |
| /* Try ping buffer if this is first attempt */ | |
| if (attempt++) | |
| return -1; | |
| emaclite->use_rx_pong_buffer_next = | |
| !emaclite->use_rx_pong_buffer_next; | |
| goto try_again; | |
| } | |
| } | |
| /* Read all bytes for ARP packet with 32bit alignment - 48bytes */ | |
| first_read = ALIGN(ETHER_HDR_SIZE + ARP_HDR_SIZE + ETH_FCS_LEN, 4); | |
| xemaclite_alignedread(addr, etherrxbuff, first_read); | |
| /* Detect real packet size */ | |
| eth = (struct ethernet_hdr *)etherrxbuff; | |
| switch (ntohs(eth->et_protlen)) { | |
| case PROT_ARP: | |
| length = first_read; | |
| debug("ARP Packet %x\n", length); | |
| break; | |
| case PROT_IP: | |
| ip = (struct ip_udp_hdr *)(etherrxbuff + ETHER_HDR_SIZE); | |
| length = ntohs(ip->ip_len); | |
| length += ETHER_HDR_SIZE + ETH_FCS_LEN; | |
| debug("IP Packet %x\n", length); | |
| if (length > PKTSIZE) | |
| length = PKTSIZE; | |
| break; | |
| default: | |
| debug("Other Packet\n"); | |
| length = PKTSIZE; | |
| break; | |
| } | |
| /* Read the rest of the packet which is longer then first read */ | |
| if (length > first_read) | |
| xemaclite_alignedread(addr + first_read, | |
| etherrxbuff + first_read, | |
| length - first_read); | |
| /* Acknowledge the frame */ | |
| reg = __raw_readl(ack); | |
| reg &= ~XEL_RSR_RECV_DONE_MASK; | |
| __raw_writel(reg, ack); | |
| debug("Packet receive from 0x%p, length %dB\n", addr, length); | |
| *packetp = etherrxbuff; | |
| return length; | |
| } | |
| static int emaclite_miiphy_read(struct mii_dev *bus, int addr, | |
| int devad, int reg) | |
| { | |
| u32 ret; | |
| u16 val = 0; | |
| ret = phyread(bus->priv, addr, reg, &val); | |
| debug("emaclite: Read MII 0x%x, 0x%x, 0x%x, %d\n", addr, reg, val, ret); | |
| return val; | |
| } | |
| static int emaclite_miiphy_write(struct mii_dev *bus, int addr, int devad, | |
| int reg, u16 value) | |
| { | |
| debug("emaclite: Write MII 0x%x, 0x%x, 0x%x\n", addr, reg, value); | |
| return phywrite(bus->priv, addr, reg, value); | |
| } | |
| static int emaclite_probe(struct udevice *dev) | |
| { | |
| struct xemaclite *emaclite = dev_get_priv(dev); | |
| int ret; | |
| if (IS_ENABLED(CONFIG_DM_ETH_PHY)) | |
| emaclite->bus = eth_phy_get_mdio_bus(dev); | |
| if (!emaclite->bus) { | |
| emaclite->bus = mdio_alloc(); | |
| emaclite->bus->read = emaclite_miiphy_read; | |
| emaclite->bus->write = emaclite_miiphy_write; | |
| emaclite->bus->priv = emaclite; | |
| ret = mdio_register_seq(emaclite->bus, dev_seq(dev)); | |
| if (ret) | |
| return ret; | |
| } | |
| if (IS_ENABLED(CONFIG_DM_ETH_PHY)) { | |
| eth_phy_set_mdio_bus(dev, emaclite->bus); | |
| emaclite->phyaddr = eth_phy_get_addr(dev); | |
| } | |
| printf("EMACLITE: %lx, phyaddr %d, %d/%d\n", (ulong)emaclite->regs, | |
| emaclite->phyaddr, emaclite->txpp, emaclite->rxpp); | |
| return 0; | |
| } | |
| static int emaclite_remove(struct udevice *dev) | |
| { | |
| struct xemaclite *emaclite = dev_get_priv(dev); | |
| free(emaclite->phydev); | |
| mdio_unregister(emaclite->bus); | |
| mdio_free(emaclite->bus); | |
| return 0; | |
| } | |
| static const struct eth_ops emaclite_ops = { | |
| .start = emaclite_start, | |
| .send = emaclite_send, | |
| .recv = emaclite_recv, | |
| .stop = emaclite_stop, | |
| }; | |
| static int emaclite_of_to_plat(struct udevice *dev) | |
| { | |
| struct eth_pdata *pdata = dev_get_plat(dev); | |
| struct xemaclite *emaclite = dev_get_priv(dev); | |
| int offset = 0; | |
| pdata->iobase = dev_read_addr(dev); | |
| emaclite->regs = (struct emaclite_regs *)ioremap(pdata->iobase, | |
| 0x10000); | |
| emaclite->phyaddr = -1; | |
| if (!(IS_ENABLED(CONFIG_DM_ETH_PHY))) { | |
| offset = fdtdec_lookup_phandle(gd->fdt_blob, dev_of_offset(dev), | |
| "phy-handle"); | |
| if (offset > 0) | |
| emaclite->phyaddr = fdtdec_get_int(gd->fdt_blob, | |
| offset, "reg", -1); | |
| } | |
| emaclite->txpp = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), | |
| "xlnx,tx-ping-pong", 0); | |
| emaclite->rxpp = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), | |
| "xlnx,rx-ping-pong", 0); | |
| return 0; | |
| } | |
| static const struct udevice_id emaclite_ids[] = { | |
| { .compatible = "xlnx,xps-ethernetlite-1.00.a" }, | |
| { } | |
| }; | |
| U_BOOT_DRIVER(emaclite) = { | |
| .name = "emaclite", | |
| .id = UCLASS_ETH, | |
| .of_match = emaclite_ids, | |
| .of_to_plat = emaclite_of_to_plat, | |
| .probe = emaclite_probe, | |
| .remove = emaclite_remove, | |
| .ops = &emaclite_ops, | |
| .priv_auto = sizeof(struct xemaclite), | |
| .plat_auto = sizeof(struct eth_pdata), | |
| }; |