diff options
Diffstat (limited to 'drivers/net/pfe_eth/pfe_eth.c')
-rw-r--r-- | drivers/net/pfe_eth/pfe_eth.c | 545 |
1 files changed, 545 insertions, 0 deletions
diff --git a/drivers/net/pfe_eth/pfe_eth.c b/drivers/net/pfe_eth/pfe_eth.c new file mode 100644 index 0000000..8d8de40 --- /dev/null +++ b/drivers/net/pfe_eth/pfe_eth.c @@ -0,0 +1,545 @@ +/* + * Copyright 2015-2016 Freescale Semiconductor, Inc. + * Copyright 2017 NXP + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <pfe_eth/pfe_eth.h> + +struct gemac_s gem_info[] = { + /* PORT_0 configuration */ { + /* GEMAC config */ + .gemac_mode = GMII, + .gemac_speed = SPEED_1000M, + .gemac_duplex = DUPLEX_FULL, + + /* phy iface */ + .phy_address = EMAC1_PHY_ADDR, + .phy_mode = PHY_INTERFACE_MODE_SGMII, + }, + /* PORT_1 configuration */ { + /* GEMAC config */ + .gemac_mode = GMII, + .gemac_speed = SPEED_1000M, + .gemac_duplex = DUPLEX_FULL, + + /* phy iface */ + .phy_address = EMAC2_PHY_ADDR, + .phy_mode = PHY_INTERFACE_MODE_RGMII, + }, +}; + +#define MAX_GEMACS 2 + +static struct ls1012a_eth_dev *gemac_list[MAX_GEMACS]; + +#define MDIO_TIMEOUT 5000 + +static inline void ls1012a_gemac_enable(void *gemac_base) +{ + writel(readl(gemac_base + EMAC_ECNTRL_REG) | + EMAC_ECNTRL_ETHER_EN, gemac_base + EMAC_ECNTRL_REG); +} + +static inline void ls1012a_gemac_disable(void *gemac_base) +{ + writel(readl(gemac_base + EMAC_ECNTRL_REG) & + ~EMAC_ECNTRL_ETHER_EN, gemac_base + EMAC_ECNTRL_REG); +} + +static inline void ls1012a_gemac_set_speed(void *gemac_base, u32 speed) +{ + struct ccsr_scfg *scfg = (struct ccsr_scfg *)CONFIG_SYS_FSL_SCFG_ADDR; + u32 ecr = readl(gemac_base + EMAC_ECNTRL_REG) & ~EMAC_ECNTRL_SPEED; + u32 rcr = readl(gemac_base + EMAC_RCNTRL_REG) & ~EMAC_RCNTRL_RMII_10T; + u32 rgmii_pcr = in_be32(&scfg->rgmiipcr) & + ~(SCFG_RGMIIPCR_SETSP_1000M|SCFG_RGMIIPCR_SETSP_10M); + + if (speed == _1000BASET) { + ecr |= EMAC_ECNTRL_SPEED; + rgmii_pcr |= SCFG_RGMIIPCR_SETSP_1000M; + } else if (speed != _100BASET) { + rcr |= EMAC_RCNTRL_RMII_10T; + rgmii_pcr |= SCFG_RGMIIPCR_SETSP_10M; + } + + writel(ecr, gemac_base + EMAC_ECNTRL_REG); + out_be32(&scfg->rgmiipcr, rgmii_pcr | SCFG_RGMIIPCR_SETFD); + + /* remove loop back */ + rcr &= ~EMAC_RCNTRL_LOOP; + /* enable flow control */ + rcr |= EMAC_RCNTRL_FCE; + + /* Enable MII mode */ + rcr |= EMAC_RCNTRL_MII_MODE; + + writel(rcr, gemac_base + EMAC_RCNTRL_REG); + + /* Enable Tx full duplex */ + writel(readl(gemac_base + EMAC_TCNTRL_REG) | EMAC_TCNTRL_FDEN, + gemac_base + EMAC_TCNTRL_REG); +} + +static inline void ls1012a_gemac_set_ethaddr(void *gemac_base, uchar *mac) +{ + writel((mac[0] << 24) + (mac[1] << 16) + (mac[2] << 8) + mac[3], + gemac_base + EMAC_PHY_ADDR_LOW); + writel((mac[4] << 24) + (mac[5] << 16) + 0x8808, gemac_base + + EMAC_PHY_ADDR_HIGH); +} + +/** Stops or Disables GEMAC pointing to this eth iface. + * + * @param[in] edev Pointer to eth device structure. + * + * @return none + */ +static inline void ls1012a_eth_halt(struct eth_device *edev) +{ + struct ls1012a_eth_dev *priv = (struct ls1012a_eth_dev *)edev->priv; + + ls1012a_gemac_disable(priv->gem->gemac_base); + + gpi_disable(priv->gem->egpi_base); +} + +static int ls1012a_eth_init(struct eth_device *dev, bd_t *bd) +{ + struct ls1012a_eth_dev *priv = (struct ls1012a_eth_dev *)dev->priv; + struct gemac_s *gem = priv->gem; + int speed; + + /* set ethernet mac address */ + ls1012a_gemac_set_ethaddr(gem->gemac_base, dev->enetaddr); + + writel(0x00000004, gem->gemac_base + EMAC_TFWR_STR_FWD); + writel(0x00000005, gem->gemac_base + EMAC_RX_SECTIOM_FULL); + writel(0x00003fff, gem->gemac_base + EMAC_TRUNC_FL); + writel(0x00000030, gem->gemac_base + EMAC_TX_SECTION_EMPTY); + writel(0x00000000, gem->gemac_base + EMAC_MIB_CTRL_STS_REG); + +#ifdef CONFIG_PHYLIB + /* Start up the PHY */ + if (phy_startup(priv->phydev)) { + printf("Could not initialize PHY %s\n", + priv->phydev->dev->name); + return -1; + } + speed = priv->phydev->speed; + printf("Speed detected %x\n", speed); + if (priv->phydev->duplex == DUPLEX_HALF) { + printf("Half duplex not supported\n"); + return -1; + } +#endif + + ls1012a_gemac_set_speed(gem->gemac_base, speed); + + /* Enable GPI */ + gpi_enable(gem->egpi_base); + + /* Enable GEMAC */ + ls1012a_gemac_enable(gem->gemac_base); + + return 0; +} + +static int ls1012a_eth_send(struct eth_device *dev, void *data, int length) +{ + struct ls1012a_eth_dev *priv = (struct ls1012a_eth_dev *)dev->priv; + + int rc; + int i = 0; + + rc = pfe_send(priv->gemac_port, data, length); + + if (rc < 0) { + printf("Tx Q full\n"); + return 0; + } + + while (1) { + rc = pfe_tx_done(); + if (rc == 0) + break; + + udelay(100); + i++; + if (i == 30000) + printf("Tx timeout, send failed\n"); + break; + } + + return 0; +} + +static int ls1012a_eth_recv(struct eth_device *dev) +{ + struct ls1012a_eth_dev *priv = (struct ls1012a_eth_dev *)dev->priv; + u32 pkt_buf; + int len; + int phy_port; + + len = pfe_recv(&pkt_buf, &phy_port); + + if (len < 0) + return 0; /* no packet in rx */ + + debug("Rx pkt: pkt_buf(%08x), phy_port(%d), len(%d)\n", pkt_buf, + phy_port, len); + if (phy_port != priv->gemac_port) { + printf("Rx pkt not on expected port\n"); + return 0; + } + + /* Pass the packet up to the protocol layers. */ + net_process_received_packet((void *)(long int)pkt_buf, len); + + return 0; +} + +#if defined(CONFIG_PHYLIB) + +#define MDIO_TIMEOUT 5000 +static int ls1012a_write_addr(struct mii_dev *bus, int phy_addr, int dev_addr, + int reg_addr) +{ + void *reg_base = bus->priv; + u32 devadr; + u32 phy; + u32 reg_data; + int timeout = MDIO_TIMEOUT; + + devadr = ((dev_addr & EMAC_MII_DATA_RA_MASK) << EMAC_MII_DATA_RA_SHIFT); + phy = ((phy_addr & EMAC_MII_DATA_PA_MASK) << EMAC_MII_DATA_PA_SHIFT); + + reg_data = (EMAC_MII_DATA_TA | phy | devadr | reg_addr); + + + writel(reg_data, reg_base + EMAC_MII_DATA_REG); + + /* + * wait for the MII interrupt + */ + while (!(readl(reg_base + EMAC_IEVENT_REG) & EMAC_IEVENT_MII)) { + if (timeout-- <= 0) { + printf("Phy MDIO read/write timeout\n"); + return -1; + } + } + + /* + * clear MII interrupt + */ + writel(EMAC_IEVENT_MII, reg_base + EMAC_IEVENT_REG); + + + return 0; +} + +static int ls1012a_phy_read(struct mii_dev *bus, int phy_addr, int dev_addr, int + reg_addr) +{ + void *reg_base = bus->priv; + u32 reg; + u32 phy; + u32 reg_data; + u16 val; + int timeout = MDIO_TIMEOUT; + + if (dev_addr == MDIO_DEVAD_NONE) { + reg = ((reg_addr & EMAC_MII_DATA_RA_MASK) << + EMAC_MII_DATA_RA_SHIFT); + } else { + ls1012a_write_addr(bus, phy_addr, dev_addr, reg_addr); + reg = ((dev_addr & EMAC_MII_DATA_RA_MASK) << + EMAC_MII_DATA_RA_SHIFT); + } + + phy = ((phy_addr & EMAC_MII_DATA_PA_MASK) << EMAC_MII_DATA_PA_SHIFT); + + if (dev_addr == MDIO_DEVAD_NONE) + reg_data = (EMAC_MII_DATA_ST | EMAC_MII_DATA_OP_RD | + EMAC_MII_DATA_TA | phy | reg); + else + reg_data = (EMAC_MII_DATA_OP_CL45_RD | EMAC_MII_DATA_TA | + phy | reg); + + writel(reg_data, reg_base + EMAC_MII_DATA_REG); + + /* + * wait for the MII interrupt + */ + while (!(readl(reg_base + EMAC_IEVENT_REG) & EMAC_IEVENT_MII)) { + if (timeout-- <= 0) { + printf("Phy MDIO read/write timeout\n"); + return -1; + } + } + + /* + * clear MII interrupt + */ + writel(EMAC_IEVENT_MII, reg_base + EMAC_IEVENT_REG); + + /* + * it's now safe to read the PHY's register + */ + val = (u16)readl(reg_base + EMAC_MII_DATA_REG); + debug("%s: %p phy: 0x%x reg:0x%08x val:%#x\n", __func__, reg_base, + phy_addr, reg_addr, val); + + return val; +} + +static int ls1012a_phy_write(struct mii_dev *bus, int phy_addr, int dev_addr, + int reg_addr, u16 data) +{ + void *reg_base = bus->priv; + u32 reg; + u32 phy; + u32 reg_data; + int timeout = MDIO_TIMEOUT; + int val; + + if (dev_addr == MDIO_DEVAD_NONE) { + reg = ((reg_addr & EMAC_MII_DATA_RA_MASK) << + EMAC_MII_DATA_RA_SHIFT); + } else { + ls1012a_write_addr(bus, phy_addr, dev_addr, reg_addr); + reg = ((dev_addr & EMAC_MII_DATA_RA_MASK) << + EMAC_MII_DATA_RA_SHIFT); + } + + phy = ((phy_addr & EMAC_MII_DATA_PA_MASK) << EMAC_MII_DATA_PA_SHIFT); + + if (dev_addr == MDIO_DEVAD_NONE) + reg_data = (EMAC_MII_DATA_ST | EMAC_MII_DATA_OP_WR | + EMAC_MII_DATA_TA | phy | reg | data); + else + reg_data = (EMAC_MII_DATA_OP_CL45_WR | EMAC_MII_DATA_TA | + phy | reg | data); + + writel(reg_data, reg_base + EMAC_MII_DATA_REG); + + /* + * wait for the MII interrupt + */ + while (!(readl(reg_base + EMAC_IEVENT_REG) & EMAC_IEVENT_MII)) { + if (timeout-- <= 0) { + printf("Phy MDIO read/write timeout\n"); + return -1; + } + } + + /* + * clear MII interrupt + */ + writel(EMAC_IEVENT_MII, reg_base + EMAC_IEVENT_REG); + + debug("%s: phy: %02x reg:%02x val:%#x\n", __func__, phy_addr, + reg_addr, data); + + return val; +} + +struct mii_dev *ls1012a_mdio_init(struct mdio_info *mdio_info) +{ + struct mii_dev *bus; + int ret; + u32 mdio_speed; + u32 pclk = 250000000; + + bus = mdio_alloc(); + if (!bus) { + printf("mdio_alloc failed\n"); + return NULL; + } + bus->read = ls1012a_phy_read; + bus->write = ls1012a_phy_write; + /* MAC1 MDIO used to communicate with external PHYS */ + bus->priv = mdio_info->reg_base; + sprintf(bus->name, mdio_info->name); + + /* configure mdio speed */ + mdio_speed = (DIV_ROUND_UP(pclk, 4000000) << EMAC_MII_SPEED_SHIFT); + mdio_speed |= EMAC_HOLDTIME(0x5); + writel(mdio_speed, mdio_info->reg_base + EMAC_MII_CTRL_REG); + + ret = mdio_register(bus); + if (ret) { + printf("mdio_register failed\n"); + free(bus); + return NULL; + } + return bus; +} + +static void ls1012a_configure_serdes(struct ls1012a_eth_dev *priv) +{ + struct mii_dev bus; + int value, sgmii_2500 = 0; + struct gemac_s *gem = priv->gem; + + if (gem->phy_mode == PHY_INTERFACE_MODE_SGMII_2500) + sgmii_2500 = 1; + + printf("%s %d\n", __func__, priv->gemac_port); + + /* PCS configuration done with corresponding GEMAC */ + bus.priv = gem_info[priv->gemac_port].gemac_base; + + ls1012a_phy_read(&bus, 0, MDIO_DEVAD_NONE, 0x0); + ls1012a_phy_read(&bus, 0, MDIO_DEVAD_NONE, 0x1); + ls1012a_phy_read(&bus, 0, MDIO_DEVAD_NONE, 0x2); + ls1012a_phy_read(&bus, 0, MDIO_DEVAD_NONE, 0x3); + + /* Reset serdes */ + ls1012a_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x0, 0x8000); + + /* SGMII IF mode + AN enable only for 1G SGMII, not for 2.5G */ + value = PHY_SGMII_IF_MODE_SGMII; + if (!sgmii_2500) + value |= PHY_SGMII_IF_MODE_AN; + else + value |= PHY_SGMII_IF_MODE_SGMII_GBT; + + ls1012a_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x14, value); + + /* Dev ability according to SGMII specification */ + value = PHY_SGMII_DEV_ABILITY_SGMII; + ls1012a_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x4, value); + + /* These values taken from validation team */ + if (!sgmii_2500) { + ls1012a_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x13, 0x0); + ls1012a_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x12, 0x400); + } else { + ls1012a_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x13, 0x7); + ls1012a_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x12, 0xa120); + } + + /* Restart AN */ + value = PHY_SGMII_CR_DEF_VAL; + if (!sgmii_2500) + value |= PHY_SGMII_CR_RESET_AN; + ls1012a_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0, value); +} + +void ls1012a_set_mdio(int dev_id, struct mii_dev *bus) +{ + gem_info[dev_id].bus = bus; +} + +void ls1012a_set_phy_address_mode(int dev_id, int phy_id, int phy_mode) +{ + gem_info[dev_id].phy_address = phy_id; + gem_info[dev_id].phy_mode = phy_mode; +} + +int ls1012a_phy_configure(struct ls1012a_eth_dev *priv, int dev_id, int phy_id) +{ + struct phy_device *phydev = NULL; + struct eth_device *dev = priv->dev; + struct gemac_s *gem = priv->gem; + struct ccsr_scfg *scfg = (struct ccsr_scfg *)CONFIG_SYS_FSL_SCFG_ADDR; + + /* Configure SGMII PCS */ + if (gem->phy_mode == PHY_INTERFACE_MODE_SGMII || + gem->phy_mode == PHY_INTERFACE_MODE_SGMII_2500) { + out_be32(&scfg->mdioselcr, 0x00000000); + ls1012a_configure_serdes(priv); + } + + /* By this time on-chip SGMII initialization is done + * we can switch mdio interface to external PHYs + */ + out_be32(&scfg->mdioselcr, 0x80000000); + + if (!gem->bus) + return -1; + phydev = phy_connect(gem->bus, phy_id, dev, gem->phy_mode); + if (!phydev) { + printf("phy_connect failed\n"); + return -1; + } + + phy_config(phydev); + + priv->phydev = phydev; + + return 0; +} +#endif + +int gemac_initialize(bd_t *bis, int dev_id, char *devname) +{ + struct eth_device *dev; + struct ls1012a_eth_dev *priv; + struct pfe *pfe; + int i; + + if (dev_id > 1) { + printf("Invalid port\n"); + return -1; + } + + dev = (struct eth_device *)malloc(sizeof(struct eth_device)); + if (!dev) + return -1; + + memset(dev, 0, sizeof(struct eth_device)); + + priv = (struct ls1012a_eth_dev *)malloc(sizeof(struct ls1012a_eth_dev)); + if (!priv) + return -1; + + gemac_list[dev_id] = priv; + priv->gemac_port = dev_id; + priv->gem = &gem_info[priv->gemac_port]; + priv->dev = dev; + + pfe = &priv->pfe; + + pfe->cbus_baseaddr = (void *)CONFIG_SYS_FSL_PFE_ADDR; + pfe->ddr_baseaddr = (void *)CONFIG_DDR_PFE_BASEADDR; + pfe->ddr_phys_baseaddr = (unsigned long)CONFIG_DDR_PFE_PHYS_BASEADDR; + + sprintf(dev->name, devname); + dev->priv = priv; + dev->init = ls1012a_eth_init; + dev->halt = ls1012a_eth_halt; + dev->send = ls1012a_eth_send; + dev->recv = ls1012a_eth_recv; + + /* Tell u-boot to get the addr from the env */ + for (i = 0; i < 6; i++) + dev->enetaddr[i] = 0; + + pfe_probe(pfe); + + switch (priv->gemac_port) { + case EMAC_PORT_0: + default: + priv->gem->gemac_base = EMAC1_BASE_ADDR; + priv->gem->egpi_base = EGPI1_BASE_ADDR; + break; + case EMAC_PORT_1: + priv->gem->gemac_base = EMAC2_BASE_ADDR; + priv->gem->egpi_base = EGPI2_BASE_ADDR; + break; + } + +#if defined(CONFIG_PHYLIB) + if (ls1012a_phy_configure(priv, dev_id, + gem_info[priv->gemac_port].phy_address)) + return -1; +#endif + + eth_register(dev); + + return 0; +} |