162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci// Copyright (c) 2018-2019 MediaTek Inc.
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci/* A library for configuring path from GMAC/GDM to target PHY
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Author: Sean Wang <sean.wang@mediatek.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/phy.h>
1162306a36Sopenharmony_ci#include <linux/regmap.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "mtk_eth_soc.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistruct mtk_eth_muxc {
1662306a36Sopenharmony_ci	const char	*name;
1762306a36Sopenharmony_ci	int		cap_bit;
1862306a36Sopenharmony_ci	int		(*set_path)(struct mtk_eth *eth, u64 path);
1962306a36Sopenharmony_ci};
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic const char *mtk_eth_path_name(u64 path)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	switch (path) {
2462306a36Sopenharmony_ci	case MTK_ETH_PATH_GMAC1_RGMII:
2562306a36Sopenharmony_ci		return "gmac1_rgmii";
2662306a36Sopenharmony_ci	case MTK_ETH_PATH_GMAC1_TRGMII:
2762306a36Sopenharmony_ci		return "gmac1_trgmii";
2862306a36Sopenharmony_ci	case MTK_ETH_PATH_GMAC1_SGMII:
2962306a36Sopenharmony_ci		return "gmac1_sgmii";
3062306a36Sopenharmony_ci	case MTK_ETH_PATH_GMAC2_RGMII:
3162306a36Sopenharmony_ci		return "gmac2_rgmii";
3262306a36Sopenharmony_ci	case MTK_ETH_PATH_GMAC2_SGMII:
3362306a36Sopenharmony_ci		return "gmac2_sgmii";
3462306a36Sopenharmony_ci	case MTK_ETH_PATH_GMAC2_GEPHY:
3562306a36Sopenharmony_ci		return "gmac2_gephy";
3662306a36Sopenharmony_ci	case MTK_ETH_PATH_GDM1_ESW:
3762306a36Sopenharmony_ci		return "gdm1_esw";
3862306a36Sopenharmony_ci	default:
3962306a36Sopenharmony_ci		return "unknown path";
4062306a36Sopenharmony_ci	}
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic int set_mux_gdm1_to_gmac1_esw(struct mtk_eth *eth, u64 path)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	bool updated = true;
4662306a36Sopenharmony_ci	u32 mask, set, reg;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	switch (path) {
4962306a36Sopenharmony_ci	case MTK_ETH_PATH_GMAC1_SGMII:
5062306a36Sopenharmony_ci		mask = ~(u32)MTK_MUX_TO_ESW;
5162306a36Sopenharmony_ci		set = 0;
5262306a36Sopenharmony_ci		break;
5362306a36Sopenharmony_ci	case MTK_ETH_PATH_GDM1_ESW:
5462306a36Sopenharmony_ci		mask = ~(u32)MTK_MUX_TO_ESW;
5562306a36Sopenharmony_ci		set = MTK_MUX_TO_ESW;
5662306a36Sopenharmony_ci		break;
5762306a36Sopenharmony_ci	default:
5862306a36Sopenharmony_ci		updated = false;
5962306a36Sopenharmony_ci		break;
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	if (mtk_is_netsys_v3_or_greater(eth))
6362306a36Sopenharmony_ci		reg = MTK_MAC_MISC_V3;
6462306a36Sopenharmony_ci	else
6562306a36Sopenharmony_ci		reg = MTK_MAC_MISC;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	if (updated)
6862306a36Sopenharmony_ci		mtk_m32(eth, mask, set, reg);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	dev_dbg(eth->dev, "path %s in %s updated = %d\n",
7162306a36Sopenharmony_ci		mtk_eth_path_name(path), __func__, updated);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	return 0;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic int set_mux_gmac2_gmac0_to_gephy(struct mtk_eth *eth, u64 path)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	unsigned int val = 0;
7962306a36Sopenharmony_ci	bool updated = true;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	switch (path) {
8262306a36Sopenharmony_ci	case MTK_ETH_PATH_GMAC2_GEPHY:
8362306a36Sopenharmony_ci		val = ~(u32)GEPHY_MAC_SEL;
8462306a36Sopenharmony_ci		break;
8562306a36Sopenharmony_ci	default:
8662306a36Sopenharmony_ci		updated = false;
8762306a36Sopenharmony_ci		break;
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	if (updated)
9162306a36Sopenharmony_ci		regmap_update_bits(eth->infra, INFRA_MISC2, GEPHY_MAC_SEL, val);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	dev_dbg(eth->dev, "path %s in %s updated = %d\n",
9462306a36Sopenharmony_ci		mtk_eth_path_name(path), __func__, updated);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	return 0;
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic int set_mux_u3_gmac2_to_qphy(struct mtk_eth *eth, u64 path)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	unsigned int val = 0, mask = 0, reg = 0;
10262306a36Sopenharmony_ci	bool updated = true;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	switch (path) {
10562306a36Sopenharmony_ci	case MTK_ETH_PATH_GMAC2_SGMII:
10662306a36Sopenharmony_ci		if (MTK_HAS_CAPS(eth->soc->caps, MTK_U3_COPHY_V2)) {
10762306a36Sopenharmony_ci			reg = USB_PHY_SWITCH_REG;
10862306a36Sopenharmony_ci			val = SGMII_QPHY_SEL;
10962306a36Sopenharmony_ci			mask = QPHY_SEL_MASK;
11062306a36Sopenharmony_ci		} else {
11162306a36Sopenharmony_ci			reg = INFRA_MISC2;
11262306a36Sopenharmony_ci			val = CO_QPHY_SEL;
11362306a36Sopenharmony_ci			mask = val;
11462306a36Sopenharmony_ci		}
11562306a36Sopenharmony_ci		break;
11662306a36Sopenharmony_ci	default:
11762306a36Sopenharmony_ci		updated = false;
11862306a36Sopenharmony_ci		break;
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	if (updated)
12262306a36Sopenharmony_ci		regmap_update_bits(eth->infra, reg, mask, val);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	dev_dbg(eth->dev, "path %s in %s updated = %d\n",
12562306a36Sopenharmony_ci		mtk_eth_path_name(path), __func__, updated);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	return 0;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic int set_mux_gmac1_gmac2_to_sgmii_rgmii(struct mtk_eth *eth, u64 path)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	unsigned int val = 0;
13362306a36Sopenharmony_ci	bool updated = true;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	switch (path) {
13662306a36Sopenharmony_ci	case MTK_ETH_PATH_GMAC1_SGMII:
13762306a36Sopenharmony_ci		val = SYSCFG0_SGMII_GMAC1;
13862306a36Sopenharmony_ci		break;
13962306a36Sopenharmony_ci	case MTK_ETH_PATH_GMAC2_SGMII:
14062306a36Sopenharmony_ci		val = SYSCFG0_SGMII_GMAC2;
14162306a36Sopenharmony_ci		break;
14262306a36Sopenharmony_ci	case MTK_ETH_PATH_GMAC1_RGMII:
14362306a36Sopenharmony_ci	case MTK_ETH_PATH_GMAC2_RGMII:
14462306a36Sopenharmony_ci		regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val);
14562306a36Sopenharmony_ci		val &= SYSCFG0_SGMII_MASK;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci		if ((path == MTK_GMAC1_RGMII && val == SYSCFG0_SGMII_GMAC1) ||
14862306a36Sopenharmony_ci		    (path == MTK_GMAC2_RGMII && val == SYSCFG0_SGMII_GMAC2))
14962306a36Sopenharmony_ci			val = 0;
15062306a36Sopenharmony_ci		else
15162306a36Sopenharmony_ci			updated = false;
15262306a36Sopenharmony_ci		break;
15362306a36Sopenharmony_ci	default:
15462306a36Sopenharmony_ci		updated = false;
15562306a36Sopenharmony_ci		break;
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	if (updated)
15962306a36Sopenharmony_ci		regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0,
16062306a36Sopenharmony_ci				   SYSCFG0_SGMII_MASK, val);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	dev_dbg(eth->dev, "path %s in %s updated = %d\n",
16362306a36Sopenharmony_ci		mtk_eth_path_name(path), __func__, updated);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	return 0;
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic int set_mux_gmac12_to_gephy_sgmii(struct mtk_eth *eth, u64 path)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	unsigned int val = 0;
17162306a36Sopenharmony_ci	bool updated = true;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	switch (path) {
17662306a36Sopenharmony_ci	case MTK_ETH_PATH_GMAC1_SGMII:
17762306a36Sopenharmony_ci		val |= SYSCFG0_SGMII_GMAC1_V2;
17862306a36Sopenharmony_ci		break;
17962306a36Sopenharmony_ci	case MTK_ETH_PATH_GMAC2_GEPHY:
18062306a36Sopenharmony_ci		val &= ~(u32)SYSCFG0_SGMII_GMAC2_V2;
18162306a36Sopenharmony_ci		break;
18262306a36Sopenharmony_ci	case MTK_ETH_PATH_GMAC2_SGMII:
18362306a36Sopenharmony_ci		val |= SYSCFG0_SGMII_GMAC2_V2;
18462306a36Sopenharmony_ci		break;
18562306a36Sopenharmony_ci	default:
18662306a36Sopenharmony_ci		updated = false;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	if (updated)
19062306a36Sopenharmony_ci		regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0,
19162306a36Sopenharmony_ci				   SYSCFG0_SGMII_MASK, val);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	dev_dbg(eth->dev, "path %s in %s updated = %d\n",
19462306a36Sopenharmony_ci		mtk_eth_path_name(path), __func__, updated);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	return 0;
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic const struct mtk_eth_muxc mtk_eth_muxc[] = {
20062306a36Sopenharmony_ci	{
20162306a36Sopenharmony_ci		.name = "mux_gdm1_to_gmac1_esw",
20262306a36Sopenharmony_ci		.cap_bit = MTK_ETH_MUX_GDM1_TO_GMAC1_ESW,
20362306a36Sopenharmony_ci		.set_path = set_mux_gdm1_to_gmac1_esw,
20462306a36Sopenharmony_ci	}, {
20562306a36Sopenharmony_ci		.name = "mux_gmac2_gmac0_to_gephy",
20662306a36Sopenharmony_ci		.cap_bit = MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY,
20762306a36Sopenharmony_ci		.set_path = set_mux_gmac2_gmac0_to_gephy,
20862306a36Sopenharmony_ci	}, {
20962306a36Sopenharmony_ci		.name = "mux_u3_gmac2_to_qphy",
21062306a36Sopenharmony_ci		.cap_bit = MTK_ETH_MUX_U3_GMAC2_TO_QPHY,
21162306a36Sopenharmony_ci		.set_path = set_mux_u3_gmac2_to_qphy,
21262306a36Sopenharmony_ci	}, {
21362306a36Sopenharmony_ci		.name = "mux_gmac1_gmac2_to_sgmii_rgmii",
21462306a36Sopenharmony_ci		.cap_bit = MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII,
21562306a36Sopenharmony_ci		.set_path = set_mux_gmac1_gmac2_to_sgmii_rgmii,
21662306a36Sopenharmony_ci	}, {
21762306a36Sopenharmony_ci		.name = "mux_gmac12_to_gephy_sgmii",
21862306a36Sopenharmony_ci		.cap_bit = MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII,
21962306a36Sopenharmony_ci		.set_path = set_mux_gmac12_to_gephy_sgmii,
22062306a36Sopenharmony_ci	},
22162306a36Sopenharmony_ci};
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic int mtk_eth_mux_setup(struct mtk_eth *eth, u64 path)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	int i, err = 0;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	if (!MTK_HAS_CAPS(eth->soc->caps, path)) {
22862306a36Sopenharmony_ci		dev_err(eth->dev, "path %s isn't support on the SoC\n",
22962306a36Sopenharmony_ci			mtk_eth_path_name(path));
23062306a36Sopenharmony_ci		return -EINVAL;
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	if (!MTK_HAS_CAPS(eth->soc->caps, MTK_MUX))
23462306a36Sopenharmony_ci		return 0;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	/* Setup MUX in path fabric */
23762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mtk_eth_muxc); i++) {
23862306a36Sopenharmony_ci		if (MTK_HAS_CAPS(eth->soc->caps, mtk_eth_muxc[i].cap_bit)) {
23962306a36Sopenharmony_ci			err = mtk_eth_muxc[i].set_path(eth, path);
24062306a36Sopenharmony_ci			if (err)
24162306a36Sopenharmony_ci				goto out;
24262306a36Sopenharmony_ci		} else {
24362306a36Sopenharmony_ci			dev_dbg(eth->dev, "mux %s isn't present on the SoC\n",
24462306a36Sopenharmony_ci				mtk_eth_muxc[i].name);
24562306a36Sopenharmony_ci		}
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ciout:
24962306a36Sopenharmony_ci	return err;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ciint mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	u64 path;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	path = (mac_id == 0) ?  MTK_ETH_PATH_GMAC1_SGMII :
25762306a36Sopenharmony_ci				MTK_ETH_PATH_GMAC2_SGMII;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	/* Setup proper MUXes along the path */
26062306a36Sopenharmony_ci	return mtk_eth_mux_setup(eth, path);
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ciint mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	u64 path = 0;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	if (mac_id == 1)
26862306a36Sopenharmony_ci		path = MTK_ETH_PATH_GMAC2_GEPHY;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	if (!path)
27162306a36Sopenharmony_ci		return -EINVAL;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	/* Setup proper MUXes along the path */
27462306a36Sopenharmony_ci	return mtk_eth_mux_setup(eth, path);
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ciint mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	u64 path;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	path = (mac_id == 0) ?  MTK_ETH_PATH_GMAC1_RGMII :
28262306a36Sopenharmony_ci				MTK_ETH_PATH_GMAC2_RGMII;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	/* Setup proper MUXes along the path */
28562306a36Sopenharmony_ci	return mtk_eth_mux_setup(eth, path);
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
288