+// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2011 PetaLogix
* Copyright (C) 2010 Xilinx, Inc. All rights reserved.
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#include <config.h>
#include <common.h>
+#include <cpu_func.h>
#include <dm.h>
+#include <log.h>
#include <net.h>
#include <malloc.h>
#include <asm/io.h>
#include <phy.h>
#include <miiphy.h>
+#include <wait_bit.h>
+#include <linux/delay.h>
DECLARE_GLOBAL_DATA_PTR;
#define XAE_MDIO_DIV_DFT 29 /* Default MDIO clock divisor */
+#define XAXIDMA_BD_STS_ACTUAL_LEN_MASK 0x007FFFFF /* Actual len */
+
/* DMA macros */
/* Bitmasks of XAXIDMA_CR_OFFSET register */
#define XAXIDMA_CR_RUNSTOP_MASK 0x00000001 /* Start/stop DMA channel */
struct axidma_reg {
u32 control; /* DMACR */
u32 status; /* DMASR */
- u32 current; /* CURDESC */
- u32 reserved;
- u32 tail; /* TAILDESC */
+ u32 current; /* CURDESC low 32 bit */
+ u32 current_hi; /* CURDESC high 32 bit */
+ u32 tail; /* TAILDESC low 32 bit */
+ u32 tail_hi; /* TAILDESC high 32 bit */
};
/* Private driver structures */
phy_interface_t interface;
struct phy_device *phydev;
struct mii_dev *bus;
+ u8 eth_hasnobuf;
+ int phy_of_handle;
};
/* BD descriptors */
u32 timeout = 200;
/* Wait till MDIO interface is ready to accept a new transaction. */
- while (timeout && (!(in_be32(®s->mdio_mcr)
+ while (timeout && (!(readl(®s->mdio_mcr)
& XAE_MDIO_MCR_READY_MASK))) {
timeout--;
udelay(1);
return 0;
}
+/**
+ * axienet_dma_write - Memory mapped Axi DMA register Buffer Descriptor write.
+ * @bd: pointer to BD descriptor structure
+ * @desc: Address offset of DMA descriptors
+ *
+ * This function writes the value into the corresponding Axi DMA register.
+ */
+static inline void axienet_dma_write(struct axidma_bd *bd, u32 *desc)
+{
+#if defined(CONFIG_PHYS_64BIT)
+ writeq(bd, desc);
+#else
+ writel((u32)bd, desc);
+#endif
+}
+
static u32 phyread(struct axidma_priv *priv, u32 phyaddress, u32 registernum,
u16 *val)
{
XAE_MDIO_MCR_INITIATE_MASK |
XAE_MDIO_MCR_OP_READ_MASK;
- out_be32(®s->mdio_mcr, mdioctrlreg);
+ writel(mdioctrlreg, ®s->mdio_mcr);
if (mdio_wait(regs))
return 1;
/* Read data */
- *val = in_be32(®s->mdio_mrd);
+ *val = readl(®s->mdio_mrd);
return 0;
}
XAE_MDIO_MCR_OP_WRITE_MASK;
/* Write data */
- out_be32(®s->mdio_mwd, data);
+ writel(data, ®s->mdio_mwd);
- out_be32(®s->mdio_mcr, mdioctrlreg);
+ writel(mdioctrlreg, ®s->mdio_mcr);
if (mdio_wait(regs))
return 1;
SUPPORTED_1000baseT_Full;
/* Set default MDIO divisor */
- out_be32(®s->mdio_mc, XAE_MDIO_DIV_DFT | XAE_MDIO_MC_MDIOEN_MASK);
+ writel(XAE_MDIO_DIV_DFT | XAE_MDIO_MC_MDIOEN_MASK, ®s->mdio_mc);
if (priv->phyaddr == -1) {
/* Detect the PHY address */
}
/* Interface - look at tsec */
- phydev = phy_connect(priv->bus, priv->phyaddr, dev, 0);
+ phydev = phy_connect(priv->bus, priv->phyaddr, dev, priv->interface);
phydev->supported &= supported;
phydev->advertising = phydev->supported;
priv->phydev = phydev;
+ if (priv->phy_of_handle)
+ priv->phydev->node = offset_to_ofnode(priv->phy_of_handle);
phy_config(phydev);
return 0;
/* Setting axi emac and phy to proper setting */
static int setup_phy(struct udevice *dev)
{
- u32 speed, emmc_reg;
+ u16 temp;
+ u32 speed, emmc_reg, ret;
struct axidma_priv *priv = dev_get_priv(dev);
struct axi_regs *regs = priv->iobase;
struct phy_device *phydev = priv->phydev;
+ if (priv->interface == PHY_INTERFACE_MODE_SGMII) {
+ /*
+ * In SGMII cases the isolate bit might set
+ * after DMA and ethernet resets and hence
+ * check and clear if set.
+ */
+ ret = phyread(priv, priv->phyaddr, MII_BMCR, &temp);
+ if (ret)
+ return 0;
+ if (temp & BMCR_ISOLATE) {
+ temp &= ~BMCR_ISOLATE;
+ ret = phywrite(priv, priv->phyaddr, MII_BMCR, temp);
+ if (ret)
+ return 0;
+ }
+ }
+
if (phy_startup(phydev)) {
printf("axiemac: could not initialize PHY %s\n",
phydev->dev->name);
}
/* Setup the emac for the phy speed */
- emmc_reg = in_be32(®s->emmc);
+ emmc_reg = readl(®s->emmc);
emmc_reg &= ~XAE_EMMC_LINKSPEED_MASK;
emmc_reg |= speed;
/* Write new speed setting out to Axi Ethernet */
- out_be32(®s->emmc, emmc_reg);
+ writel(emmc_reg, ®s->emmc);
/*
* Setting the operating speed of the MAC needs a delay. There
u32 temp;
/* Stop the hardware */
- temp = in_be32(&priv->dmatx->control);
+ temp = readl(&priv->dmatx->control);
temp &= ~XAXIDMA_CR_RUNSTOP_MASK;
- out_be32(&priv->dmatx->control, temp);
+ writel(temp, &priv->dmatx->control);
- temp = in_be32(&priv->dmarx->control);
+ temp = readl(&priv->dmarx->control);
temp &= ~XAXIDMA_CR_RUNSTOP_MASK;
- out_be32(&priv->dmarx->control, temp);
+ writel(temp, &priv->dmarx->control);
debug("axiemac: Halted\n");
}
static int axi_ethernet_init(struct axidma_priv *priv)
{
struct axi_regs *regs = priv->iobase;
- u32 timeout = 200;
+ int err;
/*
* Check the status of the MgtRdy bit in the interrupt status
* for the Sgmii and 1000BaseX PHY interfaces. No other register reads
* will be valid until this bit is valid.
* The bit is always a 1 for all other PHY interfaces.
+ * Interrupt status and enable registers are not available in non
+ * processor mode and hence bypass in this mode
*/
- while (timeout && (!(in_be32(®s->is) & XAE_INT_MGTRDY_MASK))) {
- timeout--;
- udelay(1);
- }
- if (!timeout) {
- printf("%s: Timeout\n", __func__);
- return 1;
- }
+ if (!priv->eth_hasnobuf) {
+ err = wait_for_bit_le32(®s->is, XAE_INT_MGTRDY_MASK,
+ true, 200, false);
+ if (err) {
+ printf("%s: Timeout\n", __func__);
+ return 1;
+ }
- /* Stop the device and reset HW */
- /* Disable interrupts */
- out_be32(®s->ie, 0);
+ /*
+ * Stop the device and reset HW
+ * Disable interrupts
+ */
+ writel(0, ®s->ie);
+ }
/* Disable the receiver */
- out_be32(®s->rcw1, in_be32(®s->rcw1) & ~XAE_RCW1_RX_MASK);
+ writel(readl(®s->rcw1) & ~XAE_RCW1_RX_MASK, ®s->rcw1);
/*
* Stopping the receiver in mid-packet causes a dropped packet
* indication from HW. Clear it.
*/
- /* Set the interrupt status register to clear the interrupt */
- out_be32(®s->is, XAE_INT_RXRJECT_MASK);
+ if (!priv->eth_hasnobuf) {
+ /* Set the interrupt status register to clear the interrupt */
+ writel(XAE_INT_RXRJECT_MASK, ®s->is);
+ }
/* Setup HW */
/* Set default MDIO divisor */
- out_be32(®s->mdio_mc, XAE_MDIO_DIV_DFT | XAE_MDIO_MC_MDIOEN_MASK);
+ writel(XAE_MDIO_DIV_DFT | XAE_MDIO_MC_MDIOEN_MASK, ®s->mdio_mc);
debug("axiemac: InitHw done\n");
return 0;
/* Set the MAC address */
int val = ((pdata->enetaddr[3] << 24) | (pdata->enetaddr[2] << 16) |
(pdata->enetaddr[1] << 8) | (pdata->enetaddr[0]));
- out_be32(®s->uaw0, val);
+ writel(val, ®s->uaw0);
val = (pdata->enetaddr[5] << 8) | pdata->enetaddr[4];
- val |= in_be32(®s->uaw1) & ~XAE_UAW1_UNICASTADDR_MASK;
- out_be32(®s->uaw1, val);
+ val |= readl(®s->uaw1) & ~XAE_UAW1_UNICASTADDR_MASK;
+ writel(val, ®s->uaw1);
return 0;
}
u32 timeout = 500;
/* Reset the engine so the hardware starts from a known state */
- out_be32(&priv->dmatx->control, XAXIDMA_CR_RESET_MASK);
- out_be32(&priv->dmarx->control, XAXIDMA_CR_RESET_MASK);
+ writel(XAXIDMA_CR_RESET_MASK, &priv->dmatx->control);
+ writel(XAXIDMA_CR_RESET_MASK, &priv->dmarx->control);
/* At the initialization time, hardware should finish reset quickly */
while (timeout--) {
/* Check transmit/receive channel */
/* Reset is done when the reset bit is low */
- if (!((in_be32(&priv->dmatx->control) |
- in_be32(&priv->dmarx->control))
+ if (!((readl(&priv->dmatx->control) |
+ readl(&priv->dmarx->control))
& XAXIDMA_CR_RESET_MASK)) {
break;
}
return -1;
/* Disable all RX interrupts before RxBD space setup */
- temp = in_be32(&priv->dmarx->control);
+ temp = readl(&priv->dmarx->control);
temp &= ~XAXIDMA_IRQ_ALL_MASK;
- out_be32(&priv->dmarx->control, temp);
+ writel(temp, &priv->dmarx->control);
/* Start DMA RX channel. Now it's ready to receive data.*/
- out_be32(&priv->dmarx->current, (u32)&rx_bd);
+ axienet_dma_write(&rx_bd, &priv->dmarx->current);
/* Setup the BD. */
memset(&rx_bd, 0, sizeof(rx_bd));
flush_cache((u32)&rxframe, sizeof(rxframe));
/* Start the hardware */
- temp = in_be32(&priv->dmarx->control);
+ temp = readl(&priv->dmarx->control);
temp |= XAXIDMA_CR_RUNSTOP_MASK;
- out_be32(&priv->dmarx->control, temp);
+ writel(temp, &priv->dmarx->control);
/* Rx BD is ready - start */
- out_be32(&priv->dmarx->tail, (u32)&rx_bd);
+ axienet_dma_write(&rx_bd, &priv->dmarx->tail);
/* Enable TX */
- out_be32(®s->tc, XAE_TC_TX_MASK);
+ writel(XAE_TC_TX_MASK, ®s->tc);
/* Enable RX */
- out_be32(®s->rcw1, XAE_RCW1_RX_MASK);
+ writel(XAE_RCW1_RX_MASK, ®s->rcw1);
/* PHY setup */
if (!setup_phy(dev)) {
/* Flush the last BD so DMA core could see the updates */
flush_cache((u32)&tx_bd, sizeof(tx_bd));
- if (in_be32(&priv->dmatx->status) & XAXIDMA_HALTED_MASK) {
+ if (readl(&priv->dmatx->status) & XAXIDMA_HALTED_MASK) {
u32 temp;
- out_be32(&priv->dmatx->current, (u32)&tx_bd);
+ axienet_dma_write(&tx_bd, &priv->dmatx->current);
/* Start the hardware */
- temp = in_be32(&priv->dmatx->control);
+ temp = readl(&priv->dmatx->control);
temp |= XAXIDMA_CR_RUNSTOP_MASK;
- out_be32(&priv->dmatx->control, temp);
+ writel(temp, &priv->dmatx->control);
}
/* Start transfer */
- out_be32(&priv->dmatx->tail, (u32)&tx_bd);
+ axienet_dma_write(&tx_bd, &priv->dmatx->tail);
/* Wait for transmission to complete */
debug("axiemac: Waiting for tx to be done\n");
timeout = 200;
- while (timeout && (!(in_be32(&priv->dmatx->status) &
+ while (timeout && (!(readl(&priv->dmatx->status) &
(XAXIDMA_IRQ_DELAY_MASK | XAXIDMA_IRQ_IOC_MASK)))) {
timeout--;
udelay(1);
u32 status;
/* Read pending interrupts */
- status = in_be32(&priv->dmarx->status);
+ status = readl(&priv->dmarx->status);
/* Acknowledge pending interrupts */
- out_be32(&priv->dmarx->status, status & XAXIDMA_IRQ_ALL_MASK);
+ writel(status & XAXIDMA_IRQ_ALL_MASK, &priv->dmarx->status);
/*
* If Reception done interrupt is asserted, call RX call back function
debug("axiemac: RX data ready\n");
/* Disable IRQ for a moment till packet is handled */
- temp = in_be32(&priv->dmarx->control);
+ temp = readl(&priv->dmarx->control);
temp &= ~XAXIDMA_IRQ_ALL_MASK;
- out_be32(&priv->dmarx->control, temp);
+ writel(temp, &priv->dmarx->control);
+ if (!priv->eth_hasnobuf)
+ length = rx_bd.app4 & 0xFFFF; /* max length mask */
+ else
+ length = rx_bd.status & XAXIDMA_BD_STS_ACTUAL_LEN_MASK;
- length = rx_bd.app4 & 0xFFFF; /* max length mask */
#ifdef DEBUG
print_buffer(&rxframe, &rxframe[0], 1, length, 16);
#endif
flush_cache((u32)&rxframe, sizeof(rxframe));
/* Rx BD is ready - start again */
- out_be32(&priv->dmarx->tail, (u32)&rx_bd);
+ axienet_dma_write(&rx_bd, &priv->dmarx->tail);
debug("axiemac: RX completed, framelength = %d\n", length);
priv->bus->read = axiemac_miiphy_read;
priv->bus->write = axiemac_miiphy_write;
priv->bus->priv = priv;
- strcpy(priv->bus->name, "axi_emac");
- ret = mdio_register(priv->bus);
+ ret = mdio_register_seq(priv->bus, dev->seq);
if (ret)
return ret;
{
struct eth_pdata *pdata = dev_get_platdata(dev);
struct axidma_priv *priv = dev_get_priv(dev);
+ int node = dev_of_offset(dev);
int offset = 0;
const char *phy_mode;
- pdata->iobase = (phys_addr_t)dev_get_addr(dev);
+ pdata->iobase = (phys_addr_t)devfdt_get_addr(dev);
priv->iobase = (struct axi_regs *)pdata->iobase;
- offset = fdtdec_lookup_phandle(gd->fdt_blob, dev->of_offset,
+ offset = fdtdec_lookup_phandle(gd->fdt_blob, node,
"axistream-connected");
if (offset <= 0) {
printf("%s: axistream is not found\n", __func__);
return -EINVAL;
}
- priv->dmatx = (struct axidma_reg *)fdtdec_get_int(gd->fdt_blob,
- offset, "reg", 0);
+ priv->dmatx = (struct axidma_reg *)fdtdec_get_addr(gd->fdt_blob,
+ offset, "reg");
if (!priv->dmatx) {
printf("%s: axi_dma register space not found\n", __func__);
return -EINVAL;
priv->phyaddr = -1;
- offset = fdtdec_lookup_phandle(gd->fdt_blob, dev->of_offset,
- "phy-handle");
- if (offset > 0)
+ offset = fdtdec_lookup_phandle(gd->fdt_blob, node, "phy-handle");
+ if (offset > 0) {
priv->phyaddr = fdtdec_get_int(gd->fdt_blob, offset, "reg", -1);
+ priv->phy_of_handle = offset;
+ }
- phy_mode = fdt_getprop(gd->fdt_blob, dev->of_offset, "phy-mode", NULL);
+ phy_mode = fdt_getprop(gd->fdt_blob, node, "phy-mode", NULL);
if (phy_mode)
pdata->phy_interface = phy_get_interface_by_name(phy_mode);
if (pdata->phy_interface == -1) {
- debug("%s: Invalid PHY interface '%s'\n", __func__, phy_mode);
+ printf("%s: Invalid PHY interface '%s'\n", __func__, phy_mode);
return -EINVAL;
}
priv->interface = pdata->phy_interface;
+ priv->eth_hasnobuf = fdtdec_get_bool(gd->fdt_blob, node,
+ "xlnx,eth-hasnobuf");
+
printf("AXI EMAC: %lx, phyaddr %d, interface %s\n", (ulong)priv->iobase,
priv->phyaddr, phy_string_for_interface(priv->interface));