diff options
author | Michal Simek <michal.simek@xilinx.com> | 2015-12-10 13:33:20 +0100 |
---|---|---|
committer | Michal Simek <michal.simek@xilinx.com> | 2016-01-27 15:55:51 +0100 |
commit | d722e8641b664d129238bef361c847a8b5504b5c (patch) | |
tree | 9cccd6a5ad41bfb343f7fce1d59eadfb0379a552 /drivers | |
parent | 8ce6947831ba6ba5a54de004be3a8c107af73166 (diff) |
net: emaclite: Add MDIO support to driver
Add MDIO support before move to DM.
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
Acked-by: Joe Hershberger <joe.hershberger@ni.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/xilinx_emaclite.c | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/drivers/net/xilinx_emaclite.c b/drivers/net/xilinx_emaclite.c index d3d40b12ad..f5e4c029ed 100644 --- a/drivers/net/xilinx_emaclite.c +++ b/drivers/net/xilinx_emaclite.c @@ -10,9 +10,13 @@ #include <common.h> #include <net.h> #include <config.h> +#include <console.h> #include <malloc.h> #include <asm/io.h> +#include <phy.h> +#include <miiphy.h> #include <fdtdec.h> +#include <asm-generic/errno.h> #undef DEBUG @@ -46,11 +50,36 @@ /* Recv interrupt enable bit */ #define XEL_RSR_RECV_IE_MASK 0x00000008UL +/* MDIO */ +#define XEL_MDIOADDR_OFFSET 0x07E4 /* MDIO Address Register */ +#define XEL_MDIOWR_OFFSET 0x07E8 /* MDIO Write Data Register */ +#define XEL_MDIORD_OFFSET 0x07EC /* MDIO Read Data Register */ +#define XEL_MDIOCTRL_OFFSET 0x07F0 /* MDIO Control Register */ + +/* 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 xemaclite { u32 nexttxbuffertouse; /* Next TX buffer to write to */ u32 nextrxbuffertouse; /* Next RX buffer to read from */ u32 txpp; /* TX ping pong buffer */ u32 rxpp; /* RX ping pong buffer */ + int phyaddr; + struct phy_device *phydev; + struct mii_dev *bus; }; static u32 etherrxbuff[PKTSIZE_ALIGN/4]; /* Receive buffer */ @@ -107,11 +136,177 @@ static void xemaclite_alignedwrite(void *srcptr, u32 destptr, u32 bytecount) *to32ptr++ = alignbuffer; } +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB) +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 = 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 eth_device *dev) +{ + return wait_for_bit(__func__, + (u32 *)(dev->iobase + XEL_MDIOCTRL_OFFSET), + XEL_MDIOCTRL_MDIOSTS_MASK, false, 2000); +} + +static u32 phyread(struct eth_device *dev, u32 phyaddress, u32 registernum, + u16 *data) +{ + if (mdio_wait(dev)) + return 1; + + u32 ctrl_reg = in_be32(dev->iobase + XEL_MDIOCTRL_OFFSET); + out_be32(dev->iobase + XEL_MDIOADDR_OFFSET, + XEL_MDIOADDR_OP_MASK | + ((phyaddress << XEL_MDIOADDR_PHYADR_SHIFT) | registernum)); + out_be32(dev->iobase + XEL_MDIOCTRL_OFFSET, + ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK); + + if (mdio_wait(dev)) + return 1; + + /* Read data */ + *data = in_be32(dev->iobase + XEL_MDIORD_OFFSET); + return 0; +} + +static u32 phywrite(struct eth_device *dev, u32 phyaddress, u32 registernum, + u16 data) +{ + if (mdio_wait(dev)) + 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 = in_be32(dev->iobase + XEL_MDIOCTRL_OFFSET); + out_be32(dev->iobase + XEL_MDIOADDR_OFFSET, + ~XEL_MDIOADDR_OP_MASK & + ((phyaddress << XEL_MDIOADDR_PHYADR_SHIFT) | registernum)); + out_be32(dev->iobase + XEL_MDIOWR_OFFSET, data); + out_be32(dev->iobase + XEL_MDIOCTRL_OFFSET, + ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK); + + if (mdio_wait(dev)) + return 1; + + return 0; +} +#endif + static void emaclite_halt(struct eth_device *dev) { debug("eth_halt\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 + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB) +static int setup_phy(struct eth_device *dev) +{ + int i; + u16 phyreg; + struct xemaclite *emaclite = dev->priv; + struct phy_device *phydev; + + u32 supported = SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full; + + if (emaclite->phyaddr != -1) { + phyread(dev, 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(dev, 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); + phy_startup(phydev); + + if (!phydev->link) { + printf("%s: No link.\n", phydev->dev->name); + return 0; + } + + /* Do not setup anything */ + return 1; +} +#endif + static int emaclite_init(struct eth_device *dev, bd_t *bis) { struct xemaclite *emaclite = dev->priv; @@ -156,6 +351,13 @@ static int emaclite_init(struct eth_device *dev, bd_t *bis) out_be32 (dev->iobase + XEL_RSR_OFFSET + XEL_BUFFER_OFFSET, XEL_RSR_RECV_IE_MASK); +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB) + out_be32(dev->iobase + XEL_MDIOCTRL_OFFSET, XEL_MDIOCTRL_MDIOEN_MASK); + if (in_be32(dev->iobase + XEL_MDIOCTRL_OFFSET) & + XEL_MDIOCTRL_MDIOEN_MASK) + if (!setup_phy(dev)) + return -1; +#endif debug("EmacLite Initialization complete\n"); return 0; } @@ -327,6 +529,28 @@ static int emaclite_recv(struct eth_device *dev) } +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB) +static int emaclite_miiphy_read(const char *devname, uchar addr, + uchar reg, ushort *val) +{ + u32 ret; + struct eth_device *dev = eth_get_dev(); + + ret = phyread(dev, addr, reg, val); + debug("emaclite: Read MII 0x%x, 0x%x, 0x%x\n", addr, reg, *val); + return ret; +} + +static int emaclite_miiphy_write(const char *devname, uchar addr, + uchar reg, ushort val) +{ + struct eth_device *dev = eth_get_dev(); + + debug("emaclite: Write MII 0x%x, 0x%x, 0x%x\n", addr, reg, val); + return phywrite(dev, addr, reg, val); +} +#endif + int xilinx_emaclite_initialize(bd_t *bis, unsigned long base_addr, int txpp, int rxpp) { @@ -356,7 +580,21 @@ int xilinx_emaclite_initialize(bd_t *bis, unsigned long base_addr, dev->send = emaclite_send; dev->recv = emaclite_recv; +#ifdef CONFIG_PHY_ADDR + emaclite->phyaddr = CONFIG_PHY_ADDR; +#else + emaclite->phyaddr = -1; +#endif + eth_register(dev); +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB) + miiphy_register(dev->name, emaclite_miiphy_read, emaclite_miiphy_write); + emaclite->bus = miiphy_get_dev_by_name(dev->name); + + out_be32(dev->iobase + XEL_MDIOCTRL_OFFSET, + XEL_MDIOCTRL_MDIOEN_MASK); +#endif + return 1; } |