summaryrefslogtreecommitdiff
path: root/drivers/net/pfe_eth/pfe_eth.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/pfe_eth/pfe_eth.c')
-rw-r--r--drivers/net/pfe_eth/pfe_eth.c545
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;
+}