summaryrefslogtreecommitdiff
path: root/drivers/net/pfe_eth/pfe_mdio.c
diff options
context:
space:
mode:
authorCalvin Johnson <calvin.johnson@nxp.com>2018-03-08 13:00:25 +0300
committerJoe Hershberger <joe.hershberger@ni.com>2018-03-22 23:05:28 +0300
commita4a40437496f3df28dffb3d7b63b9dae79ec0777 (patch)
tree869efbd91b6056c4704d3dc11d9e6350c9cc1034 /drivers/net/pfe_eth/pfe_mdio.c
parent6b1373f2452a26b0c48ba8797d28e8c005016caf (diff)
downloadu-boot-a4a40437496f3df28dffb3d7b63b9dae79ec0777.tar.xz
drivers: net: pfe_eth: LS1012A PFE driver introduction
This patch adds PFE driver to U-Boot Following are the main driver files:- pfe_hw.c: provides low level helper functions to initialize PFE internal processor engines and other hardware blocks pfe_driver.c: provides initialization functions and packet send and receive functions pfe_eth.c: provides high level gemac initialization functions pfe_firmware.c: provides functions to load firmware into PFE internal processor engines. pfe_mdio.c: provides functions to initialize phy and mdio. Signed-off-by: Calvin Johnson <calvin.johnson@nxp.com> Signed-off-by: Anjaneyulu Jagarlmudi <anji.jagarlmudi@nxp.com> Acked-by: Joe Hershberger <joe.hershberger@ni.com>
Diffstat (limited to 'drivers/net/pfe_eth/pfe_mdio.c')
-rw-r--r--drivers/net/pfe_eth/pfe_mdio.c291
1 files changed, 291 insertions, 0 deletions
diff --git a/drivers/net/pfe_eth/pfe_mdio.c b/drivers/net/pfe_eth/pfe_mdio.c
new file mode 100644
index 0000000000..a78a4d63f3
--- /dev/null
+++ b/drivers/net/pfe_eth/pfe_mdio.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright 2015-2016 Freescale Semiconductor, Inc.
+ * Copyright 2017 NXP
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+#include <common.h>
+#include <dm.h>
+#include <dm/platform_data/pfe_dm_eth.h>
+#include <net.h>
+#include <net/pfe_eth/pfe_eth.h>
+
+extern struct gemac_s gem_info[];
+#if defined(CONFIG_PHYLIB)
+
+#define MDIO_TIMEOUT 5000
+static int pfe_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 pfe_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 {
+ pfe_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 pfe_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 {
+ pfe_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;
+}
+
+static void pfe_configure_serdes(struct pfe_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;
+
+ pfe_phy_read(&bus, 0, MDIO_DEVAD_NONE, 0x0);
+ pfe_phy_read(&bus, 0, MDIO_DEVAD_NONE, 0x1);
+ pfe_phy_read(&bus, 0, MDIO_DEVAD_NONE, 0x2);
+ pfe_phy_read(&bus, 0, MDIO_DEVAD_NONE, 0x3);
+
+ /* Reset serdes */
+ pfe_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;
+
+ pfe_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x14, value);
+
+ /* Dev ability according to SGMII specification */
+ value = PHY_SGMII_DEV_ABILITY_SGMII;
+ pfe_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x4, value);
+
+ /* These values taken from validation team */
+ if (!sgmii_2500) {
+ pfe_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x13, 0x0);
+ pfe_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x12, 0x400);
+ } else {
+ pfe_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x13, 0x7);
+ pfe_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;
+ /* Disable Auto neg for 2.5G SGMII as it doesn't support auto neg*/
+ if (sgmii_2500)
+ value &= ~PHY_SGMII_ENABLE_AN;
+ pfe_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0, value);
+}
+
+int pfe_phy_configure(struct pfe_eth_dev *priv, int dev_id, int phy_id)
+{
+ struct phy_device *phydev = NULL;
+ struct udevice *dev = priv->dev;
+ struct gemac_s *gem = priv->gem;
+ struct ccsr_scfg *scfg = (struct ccsr_scfg *)CONFIG_SYS_FSL_SCFG_ADDR;
+
+ if (!gem->bus)
+ return -1;
+
+ /* Configure SGMII PCS */
+ if (gem->phy_mode == PHY_INTERFACE_MODE_SGMII ||
+ gem->phy_mode == PHY_INTERFACE_MODE_SGMII_2500) {
+ out_be32(&scfg->mdioselcr, 0x00000000);
+ pfe_configure_serdes(priv);
+ }
+
+ mdelay(100);
+
+ /* By this time on-chip SGMII initialization is done
+ * we can switch mdio interface to external PHYs
+ */
+ out_be32(&scfg->mdioselcr, 0x80000000);
+
+ phydev = phy_connect(gem->bus, phy_id, dev, gem->phy_mode);
+ if (!phydev) {
+ printf("phy_connect failed\n");
+ return -ENODEV;
+ }
+
+ phy_config(phydev);
+
+ priv->phydev = phydev;
+
+ return 0;
+}
+#endif
+
+struct mii_dev *pfe_mdio_init(struct pfe_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 = pfe_phy_read;
+ bus->write = pfe_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;
+}
+
+void pfe_set_mdio(int dev_id, struct mii_dev *bus)
+{
+ gem_info[dev_id].bus = bus;
+}
+
+void pfe_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;
+}