2018-05-06 21:58:06 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
2018-03-08 10:00:25 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2015-2016 Freescale Semiconductor, Inc.
|
|
|
|
* Copyright 2017 NXP
|
|
|
|
*/
|
|
|
|
#include <common.h>
|
|
|
|
#include <dm.h>
|
2020-05-10 17:40:05 +00:00
|
|
|
#include <log.h>
|
2020-02-03 14:36:16 +00:00
|
|
|
#include <malloc.h>
|
2018-03-08 10:00:25 +00:00
|
|
|
#include <dm/platform_data/pfe_dm_eth.h>
|
|
|
|
#include <net.h>
|
2020-05-10 17:40:11 +00:00
|
|
|
#include <linux/delay.h>
|
2018-03-08 10:00:25 +00:00
|
|
|
#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;
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2019-07-30 22:00:51 +00:00
|
|
|
return 0;
|
2018-03-08 10:00:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|