mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2025-08-14 03:06:59 +02:00
mtk_eth.c contains not only the ethernet GMAC/DMA driver, but also some ethernet switch initialization code. As we may add more switch support in the future, it's better to move them out of mtk_eth.c to avoid increasing the code complexity. Since not all switches are supported for a particular board, Kconfig options are added to allow user to select which switch should be built into u-boot. If multiple switches are selected, auto-detecting can also be enabled. Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
263 lines
5.8 KiB
C
263 lines
5.8 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2025 MediaTek Inc.
|
|
*
|
|
* Author: Weijie Gao <weijie.gao@mediatek.com>
|
|
* Author: Mark Lee <mark-mc.lee@mediatek.com>
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <time.h>
|
|
#include "mtk_eth.h"
|
|
#include "mt753x.h"
|
|
|
|
/*
|
|
* MT753x Internal Register Address Bits
|
|
* -------------------------------------------------------------------
|
|
* | 15 14 13 12 11 10 9 8 7 6 | 5 4 3 2 | 1 0 |
|
|
* |----------------------------------------|---------------|--------|
|
|
* | Page Address | Reg Address | Unused |
|
|
* -------------------------------------------------------------------
|
|
*/
|
|
|
|
int __mt753x_mdio_reg_read(struct mtk_eth_priv *priv, u32 smi_addr, u32 reg,
|
|
u32 *data)
|
|
{
|
|
int ret, low_word, high_word;
|
|
|
|
/* Write page address */
|
|
ret = mtk_mii_write(priv, smi_addr, 0x1f, reg >> 6);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Read low word */
|
|
low_word = mtk_mii_read(priv, smi_addr, (reg >> 2) & 0xf);
|
|
if (low_word < 0)
|
|
return low_word;
|
|
|
|
/* Read high word */
|
|
high_word = mtk_mii_read(priv, smi_addr, 0x10);
|
|
if (high_word < 0)
|
|
return high_word;
|
|
|
|
if (data)
|
|
*data = ((u32)high_word << 16) | (low_word & 0xffff);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mt753x_mdio_reg_read(struct mt753x_switch_priv *priv, u32 reg, u32 *data)
|
|
{
|
|
return __mt753x_mdio_reg_read(priv->epriv.eth, priv->smi_addr, reg,
|
|
data);
|
|
}
|
|
|
|
int mt753x_mdio_reg_write(struct mt753x_switch_priv *priv, u32 reg, u32 data)
|
|
{
|
|
int ret;
|
|
|
|
/* Write page address */
|
|
ret = mtk_mii_write(priv->epriv.eth, priv->smi_addr, 0x1f, reg >> 6);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Write low word */
|
|
ret = mtk_mii_write(priv->epriv.eth, priv->smi_addr, (reg >> 2) & 0xf,
|
|
data & 0xffff);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Write high word */
|
|
return mtk_mii_write(priv->epriv.eth, priv->smi_addr, 0x10, data >> 16);
|
|
}
|
|
|
|
int mt753x_reg_read(struct mt753x_switch_priv *priv, u32 reg, u32 *data)
|
|
{
|
|
return priv->reg_read(priv, reg, data);
|
|
}
|
|
|
|
int mt753x_reg_write(struct mt753x_switch_priv *priv, u32 reg, u32 data)
|
|
{
|
|
return priv->reg_write(priv, reg, data);
|
|
}
|
|
|
|
void mt753x_reg_rmw(struct mt753x_switch_priv *priv, u32 reg, u32 clr, u32 set)
|
|
{
|
|
u32 val;
|
|
|
|
priv->reg_read(priv, reg, &val);
|
|
val &= ~clr;
|
|
val |= set;
|
|
priv->reg_write(priv, reg, val);
|
|
}
|
|
|
|
/* Indirect MDIO clause 22/45 access */
|
|
static int mt7531_mii_rw(struct mt753x_switch_priv *priv, int phy, int reg,
|
|
u16 data, u32 cmd, u32 st)
|
|
{
|
|
u32 val, timeout_ms;
|
|
ulong timeout;
|
|
int ret = 0;
|
|
|
|
val = (st << MDIO_ST_S) |
|
|
((cmd << MDIO_CMD_S) & MDIO_CMD_M) |
|
|
((phy << MDIO_PHY_ADDR_S) & MDIO_PHY_ADDR_M) |
|
|
((reg << MDIO_REG_ADDR_S) & MDIO_REG_ADDR_M);
|
|
|
|
if (cmd == MDIO_CMD_WRITE || cmd == MDIO_CMD_ADDR)
|
|
val |= data & MDIO_RW_DATA_M;
|
|
|
|
mt753x_reg_write(priv, MT7531_PHY_IAC, val | PHY_ACS_ST);
|
|
|
|
timeout_ms = 100;
|
|
timeout = get_timer(0);
|
|
while (1) {
|
|
mt753x_reg_read(priv, MT7531_PHY_IAC, &val);
|
|
|
|
if ((val & PHY_ACS_ST) == 0)
|
|
break;
|
|
|
|
if (get_timer(timeout) > timeout_ms)
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
if (cmd == MDIO_CMD_READ || cmd == MDIO_CMD_READ_C45) {
|
|
mt753x_reg_read(priv, MT7531_PHY_IAC, &val);
|
|
ret = val & MDIO_RW_DATA_M;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int mt7531_mii_read(struct mt753x_switch_priv *priv, u8 phy, u8 reg)
|
|
{
|
|
u8 phy_addr;
|
|
|
|
if (phy >= MT753X_NUM_PHYS)
|
|
return -EINVAL;
|
|
|
|
phy_addr = MT753X_PHY_ADDR(priv->phy_base, phy);
|
|
|
|
return mt7531_mii_rw(priv, phy_addr, reg, 0, MDIO_CMD_READ,
|
|
MDIO_ST_C22);
|
|
}
|
|
|
|
int mt7531_mii_write(struct mt753x_switch_priv *priv, u8 phy, u8 reg, u16 val)
|
|
{
|
|
u8 phy_addr;
|
|
|
|
if (phy >= MT753X_NUM_PHYS)
|
|
return -EINVAL;
|
|
|
|
phy_addr = MT753X_PHY_ADDR(priv->phy_base, phy);
|
|
|
|
return mt7531_mii_rw(priv, phy_addr, reg, val, MDIO_CMD_WRITE,
|
|
MDIO_ST_C22);
|
|
}
|
|
|
|
int mt7531_mmd_read(struct mt753x_switch_priv *priv, u8 addr, u8 devad,
|
|
u16 reg)
|
|
{
|
|
u8 phy_addr;
|
|
int ret;
|
|
|
|
if (addr >= MT753X_NUM_PHYS)
|
|
return -EINVAL;
|
|
|
|
phy_addr = MT753X_PHY_ADDR(priv->phy_base, addr);
|
|
|
|
ret = mt7531_mii_rw(priv, phy_addr, devad, reg, MDIO_CMD_ADDR,
|
|
MDIO_ST_C45);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return mt7531_mii_rw(priv, phy_addr, devad, 0, MDIO_CMD_READ_C45,
|
|
MDIO_ST_C45);
|
|
}
|
|
|
|
int mt7531_mmd_write(struct mt753x_switch_priv *priv, u8 addr, u8 devad,
|
|
u16 reg, u16 val)
|
|
{
|
|
u8 phy_addr;
|
|
int ret;
|
|
|
|
if (addr >= MT753X_NUM_PHYS)
|
|
return 0;
|
|
|
|
phy_addr = MT753X_PHY_ADDR(priv->phy_base, addr);
|
|
|
|
ret = mt7531_mii_rw(priv, phy_addr, devad, reg, MDIO_CMD_ADDR,
|
|
MDIO_ST_C45);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return mt7531_mii_rw(priv, phy_addr, devad, val, MDIO_CMD_WRITE,
|
|
MDIO_ST_C45);
|
|
}
|
|
|
|
static int mt7531_mdio_read(struct mii_dev *bus, int addr, int devad, int reg)
|
|
{
|
|
struct mt753x_switch_priv *priv = bus->priv;
|
|
|
|
if (devad < 0)
|
|
return mt7531_mii_read(priv, addr, reg);
|
|
|
|
return mt7531_mmd_read(priv, addr, devad, reg);
|
|
}
|
|
|
|
static int mt7531_mdio_write(struct mii_dev *bus, int addr, int devad, int reg,
|
|
u16 val)
|
|
{
|
|
struct mt753x_switch_priv *priv = bus->priv;
|
|
|
|
if (devad < 0)
|
|
return mt7531_mii_write(priv, addr, reg, val);
|
|
|
|
return mt7531_mmd_write(priv, addr, devad, reg, val);
|
|
}
|
|
|
|
int mt7531_mdio_register(struct mt753x_switch_priv *priv)
|
|
{
|
|
struct mii_dev *mdio_bus = mdio_alloc();
|
|
int ret;
|
|
|
|
if (!mdio_bus)
|
|
return -ENOMEM;
|
|
|
|
mdio_bus->read = mt7531_mdio_read;
|
|
mdio_bus->write = mt7531_mdio_write;
|
|
snprintf(mdio_bus->name, sizeof(mdio_bus->name), priv->epriv.sw->name);
|
|
|
|
mdio_bus->priv = priv;
|
|
|
|
ret = mdio_register(mdio_bus);
|
|
if (ret) {
|
|
mdio_free(mdio_bus);
|
|
return ret;
|
|
}
|
|
|
|
priv->mdio_bus = mdio_bus;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void mt753x_port_isolation(struct mt753x_switch_priv *priv)
|
|
{
|
|
u32 i;
|
|
|
|
for (i = 0; i < MT753X_NUM_PORTS; i++) {
|
|
/* Set port matrix mode */
|
|
if (i != 6)
|
|
mt753x_reg_write(priv, PCR_REG(i),
|
|
(0x40 << PORT_MATRIX_S));
|
|
else
|
|
mt753x_reg_write(priv, PCR_REG(i),
|
|
(0x3f << PORT_MATRIX_S));
|
|
|
|
/* Set port mode to user port */
|
|
mt753x_reg_write(priv, PVC_REG(i),
|
|
(0x8100 << STAG_VPID_S) |
|
|
(VLAN_ATTR_USER << VLAN_ATTR_S));
|
|
}
|
|
}
|