linux_dsm_epyc7002/drivers/net/ethernet/mediatek/mtk_eth_path.c
René van Dorst e2c746944e net: ethernet: mediatek: Fix overlapping capability bits.
Both MTK_TRGMII_MT7621_CLK and MTK_PATH_BIT are defined as bit 10.

This can causes issues on non-MT7621 devices which has the
MTK_PATH_BIT(MTK_ETH_PATH_GMAC1_RGMII) and MTK_TRGMII capability set.
The wrong TRGMII setup code can be executed. The current wrongly executed
code doesn’t do any harm on MT7623 and the TRGMII setup for the MT7623
SOC side is done in MT7530 driver So it wasn’t noticed in the test.

Move all capability bits in one enum so that they are all unique and easy
to expand in the future.

Because mtk_eth_path enum is merged in to mkt_eth_capabilities, the
variable path value is no longer between 0 to number of paths,
mtk_eth_path_name can’t be used anymore in this form. Convert the
mtk_eth_path_name array to a function to lookup the pathname.

The old code walked thru the mtk_eth_path enum, which is also merged
with mkt_eth_capabilities. Expand array mtk_eth_muxc so it can store the
name and capability bit of the mux. Convert the code so it can walk thru
the mtk_eth_muxc array.

Fixes: 8efaa653a8 ("net: ethernet: mediatek: Add MT7621 TRGMII mode support")
Signed-off-by: René van Dorst <opensource@vdorst.com>

v1->v2:
- Move all capability bits in one enum, suggested by Willem de Bruijn
- Convert the mtk_eth_path_name array to a function to lookup the pathname
- Expand array mtk_eth_muxc so it can also store the name and capability
  bit of the mux
- Updated commit message

Signed-off-by: David S. Miller <davem@davemloft.net>
2019-07-04 12:37:10 -07:00

353 lines
7.5 KiB
C

// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2018-2019 MediaTek Inc.
/* A library for configuring path from GMAC/GDM to target PHY
*
* Author: Sean Wang <sean.wang@mediatek.com>
*
*/
#include <linux/phy.h>
#include <linux/regmap.h>
#include "mtk_eth_soc.h"
struct mtk_eth_muxc {
const char *name;
int cap_bit;
int (*set_path)(struct mtk_eth *eth, int path);
};
static const char *mtk_eth_path_name(int path)
{
switch (path) {
case MTK_ETH_PATH_GMAC1_RGMII:
return "gmac1_rgmii";
case MTK_ETH_PATH_GMAC1_TRGMII:
return "gmac1_trgmii";
case MTK_ETH_PATH_GMAC1_SGMII:
return "gmac1_sgmii";
case MTK_ETH_PATH_GMAC2_RGMII:
return "gmac2_rgmii";
case MTK_ETH_PATH_GMAC2_SGMII:
return "gmac2_sgmii";
case MTK_ETH_PATH_GMAC2_GEPHY:
return "gmac2_gephy";
case MTK_ETH_PATH_GDM1_ESW:
return "gdm1_esw";
default:
return "unknown path";
}
}
static int set_mux_gdm1_to_gmac1_esw(struct mtk_eth *eth, int path)
{
bool updated = true;
u32 val, mask, set;
switch (path) {
case MTK_ETH_PATH_GMAC1_SGMII:
mask = ~(u32)MTK_MUX_TO_ESW;
set = 0;
break;
case MTK_ETH_PATH_GDM1_ESW:
mask = ~(u32)MTK_MUX_TO_ESW;
set = MTK_MUX_TO_ESW;
break;
default:
updated = false;
break;
};
if (updated) {
val = mtk_r32(eth, MTK_MAC_MISC);
val = (val & mask) | set;
mtk_w32(eth, val, MTK_MAC_MISC);
}
dev_dbg(eth->dev, "path %s in %s updated = %d\n",
mtk_eth_path_name(path), __func__, updated);
return 0;
}
static int set_mux_gmac2_gmac0_to_gephy(struct mtk_eth *eth, int path)
{
unsigned int val = 0;
bool updated = true;
switch (path) {
case MTK_ETH_PATH_GMAC2_GEPHY:
val = ~(u32)GEPHY_MAC_SEL;
break;
default:
updated = false;
break;
}
if (updated)
regmap_update_bits(eth->infra, INFRA_MISC2, GEPHY_MAC_SEL, val);
dev_dbg(eth->dev, "path %s in %s updated = %d\n",
mtk_eth_path_name(path), __func__, updated);
return 0;
}
static int set_mux_u3_gmac2_to_qphy(struct mtk_eth *eth, int path)
{
unsigned int val = 0;
bool updated = true;
switch (path) {
case MTK_ETH_PATH_GMAC2_SGMII:
val = CO_QPHY_SEL;
break;
default:
updated = false;
break;
}
if (updated)
regmap_update_bits(eth->infra, INFRA_MISC2, CO_QPHY_SEL, val);
dev_dbg(eth->dev, "path %s in %s updated = %d\n",
mtk_eth_path_name(path), __func__, updated);
return 0;
}
static int set_mux_gmac1_gmac2_to_sgmii_rgmii(struct mtk_eth *eth, int path)
{
unsigned int val = 0;
bool updated = true;
switch (path) {
case MTK_ETH_PATH_GMAC1_SGMII:
val = SYSCFG0_SGMII_GMAC1;
break;
case MTK_ETH_PATH_GMAC2_SGMII:
val = SYSCFG0_SGMII_GMAC2;
break;
case MTK_ETH_PATH_GMAC1_RGMII:
case MTK_ETH_PATH_GMAC2_RGMII:
regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val);
val &= SYSCFG0_SGMII_MASK;
if ((path == MTK_GMAC1_RGMII && val == SYSCFG0_SGMII_GMAC1) ||
(path == MTK_GMAC2_RGMII && val == SYSCFG0_SGMII_GMAC2))
val = 0;
else
updated = false;
break;
default:
updated = false;
break;
};
if (updated)
regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0,
SYSCFG0_SGMII_MASK, val);
dev_dbg(eth->dev, "path %s in %s updated = %d\n",
mtk_eth_path_name(path), __func__, updated);
return 0;
}
static int set_mux_gmac12_to_gephy_sgmii(struct mtk_eth *eth, int path)
{
unsigned int val = 0;
bool updated = true;
regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val);
switch (path) {
case MTK_ETH_PATH_GMAC1_SGMII:
val |= SYSCFG0_SGMII_GMAC1_V2;
break;
case MTK_ETH_PATH_GMAC2_GEPHY:
val &= ~(u32)SYSCFG0_SGMII_GMAC2_V2;
break;
case MTK_ETH_PATH_GMAC2_SGMII:
val |= SYSCFG0_SGMII_GMAC2_V2;
break;
default:
updated = false;
};
if (updated)
regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0,
SYSCFG0_SGMII_MASK, val);
dev_dbg(eth->dev, "path %s in %s updated = %d\n",
mtk_eth_path_name(path), __func__, updated);
return 0;
}
static const struct mtk_eth_muxc mtk_eth_muxc[] = {
{
.name = "mux_gdm1_to_gmac1_esw",
.cap_bit = MTK_ETH_MUX_GDM1_TO_GMAC1_ESW,
.set_path = set_mux_gdm1_to_gmac1_esw,
}, {
.name = "mux_gmac2_gmac0_to_gephy",
.cap_bit = MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY,
.set_path = set_mux_gmac2_gmac0_to_gephy,
}, {
.name = "mux_u3_gmac2_to_qphy",
.cap_bit = MTK_ETH_MUX_U3_GMAC2_TO_QPHY,
.set_path = set_mux_u3_gmac2_to_qphy,
}, {
.name = "mux_gmac1_gmac2_to_sgmii_rgmii",
.cap_bit = MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII,
.set_path = set_mux_gmac1_gmac2_to_sgmii_rgmii,
}, {
.name = "mux_gmac12_to_gephy_sgmii",
.cap_bit = MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII,
.set_path = set_mux_gmac12_to_gephy_sgmii,
},
};
static int mtk_eth_mux_setup(struct mtk_eth *eth, int path)
{
int i, err = 0;
if (!MTK_HAS_CAPS(eth->soc->caps, path)) {
dev_err(eth->dev, "path %s isn't support on the SoC\n",
mtk_eth_path_name(path));
return -EINVAL;
}
if (!MTK_HAS_CAPS(eth->soc->caps, MTK_MUX))
return 0;
/* Setup MUX in path fabric */
for (i = 0; i < ARRAY_SIZE(mtk_eth_muxc); i++) {
if (MTK_HAS_CAPS(eth->soc->caps, mtk_eth_muxc[i].cap_bit)) {
err = mtk_eth_muxc[i].set_path(eth, path);
if (err)
goto out;
} else {
dev_dbg(eth->dev, "mux %s isn't present on the SoC\n",
mtk_eth_muxc[i].name);
}
}
out:
return err;
}
static int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id)
{
unsigned int val = 0;
int sid, err, path;
path = (mac_id == 0) ? MTK_ETH_PATH_GMAC1_SGMII :
MTK_ETH_PATH_GMAC2_SGMII;
/* Setup proper MUXes along the path */
err = mtk_eth_mux_setup(eth, path);
if (err)
return err;
/* The path GMAC to SGMII will be enabled once the SGMIISYS is being
* setup done.
*/
regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val);
regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0,
SYSCFG0_SGMII_MASK, ~(u32)SYSCFG0_SGMII_MASK);
/* Decide how GMAC and SGMIISYS be mapped */
sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ? 0 : mac_id;
/* Setup SGMIISYS with the determined property */
if (MTK_HAS_FLAGS(eth->sgmii->flags[sid], MTK_SGMII_PHYSPEED_AN))
err = mtk_sgmii_setup_mode_an(eth->sgmii, sid);
else
err = mtk_sgmii_setup_mode_force(eth->sgmii, sid);
if (err)
return err;
regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0,
SYSCFG0_SGMII_MASK, val);
return 0;
}
static int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id)
{
int err, path = 0;
if (mac_id == 1)
path = MTK_ETH_PATH_GMAC2_GEPHY;
if (!path)
return -EINVAL;
/* Setup proper MUXes along the path */
err = mtk_eth_mux_setup(eth, path);
if (err)
return err;
return 0;
}
static int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id)
{
int err, path;
path = (mac_id == 0) ? MTK_ETH_PATH_GMAC1_RGMII :
MTK_ETH_PATH_GMAC2_RGMII;
/* Setup proper MUXes along the path */
err = mtk_eth_mux_setup(eth, path);
if (err)
return err;
return 0;
}
int mtk_setup_hw_path(struct mtk_eth *eth, int mac_id, int phymode)
{
int err;
switch (phymode) {
case PHY_INTERFACE_MODE_TRGMII:
case PHY_INTERFACE_MODE_RGMII_TXID:
case PHY_INTERFACE_MODE_RGMII_RXID:
case PHY_INTERFACE_MODE_RGMII_ID:
case PHY_INTERFACE_MODE_RGMII:
case PHY_INTERFACE_MODE_MII:
case PHY_INTERFACE_MODE_REVMII:
case PHY_INTERFACE_MODE_RMII:
if (MTK_HAS_CAPS(eth->soc->caps, MTK_RGMII)) {
err = mtk_gmac_rgmii_path_setup(eth, mac_id);
if (err)
return err;
}
break;
case PHY_INTERFACE_MODE_SGMII:
if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) {
err = mtk_gmac_sgmii_path_setup(eth, mac_id);
if (err)
return err;
}
break;
case PHY_INTERFACE_MODE_GMII:
if (MTK_HAS_CAPS(eth->soc->caps, MTK_GEPHY)) {
err = mtk_gmac_gephy_path_setup(eth, mac_id);
if (err)
return err;
}
break;
default:
break;
}
return 0;
}