278 lines
5.8 KiB
C
278 lines
5.8 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;
|
||
|
}
|
||
|
|
||
|
int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id)
|
||
|
{
|
||
|
int path;
|
||
|
|
||
|
path = (mac_id == 0) ? MTK_ETH_PATH_GMAC1_SGMII :
|
||
|
MTK_ETH_PATH_GMAC2_SGMII;
|
||
|
|
||
|
/* Setup proper MUXes along the path */
|
||
|
return mtk_eth_mux_setup(eth, path);
|
||
|
}
|
||
|
|
||
|
int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id)
|
||
|
{
|
||
|
int path = 0;
|
||
|
|
||
|
if (mac_id == 1)
|
||
|
path = MTK_ETH_PATH_GMAC2_GEPHY;
|
||
|
|
||
|
if (!path)
|
||
|
return -EINVAL;
|
||
|
|
||
|
/* Setup proper MUXes along the path */
|
||
|
return mtk_eth_mux_setup(eth, path);
|
||
|
}
|
||
|
|
||
|
int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id)
|
||
|
{
|
||
|
int path;
|
||
|
|
||
|
path = (mac_id == 0) ? MTK_ETH_PATH_GMAC1_RGMII :
|
||
|
MTK_ETH_PATH_GMAC2_RGMII;
|
||
|
|
||
|
/* Setup proper MUXes along the path */
|
||
|
return mtk_eth_mux_setup(eth, path);
|
||
|
}
|
||
|
|