forked from coolsnowwolf/lede
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[bot] AutoMerging: merge all upstream's changes:
* https://github.com/coolsnowwolf/lede: mediatek: sync MT7988 USXGMII with SDK driver mediatek: add driver for built-in 2.5G Ethernet PHY mediatek: update pending SoC Ethernet PHY driver
- Loading branch information
Showing
8 changed files
with
2,391 additions
and
1,990 deletions.
There are no files selected for viewing
1,382 changes: 789 additions & 593 deletions
1,382
...ux/generic/pending-5.15/737-07-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch
Large diffs are not rendered by default.
Oops, something went wrong.
262 changes: 262 additions & 0 deletions
262
target/linux/mediatek/files-5.15/drivers/net/phy/mediatek-2p5ge.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,262 @@ | ||
// SPDX-License-Identifier: GPL-2.0+ | ||
#include <linux/bitfield.h> | ||
#include <linux/firmware.h> | ||
#include <linux/module.h> | ||
#include <linux/nvmem-consumer.h> | ||
#include <linux/of_address.h> | ||
#include <linux/of_platform.h> | ||
#include <linux/pinctrl/consumer.h> | ||
#include <linux/phy.h> | ||
|
||
#define MEDAITEK_2P5GE_PHY_DMB_FW "mediatek/mediatek-2p5ge-phy-dmb.bin" | ||
#define MEDIATEK_2P5GE_PHY_PMB_FW "mediatek/mediatek-2p5ge-phy-pmb.bin" | ||
|
||
#define MD32_EN_CFG 0x18 | ||
#define MD32_EN BIT(0) | ||
|
||
#define BASE100T_STATUS_EXTEND 0x10 | ||
#define BASE1000T_STATUS_EXTEND 0x11 | ||
#define EXTEND_CTRL_AND_STATUS 0x16 | ||
|
||
#define PHY_AUX_CTRL_STATUS 0x1d | ||
#define PHY_AUX_DPX_MASK GENMASK(5, 5) | ||
#define PHY_AUX_SPEED_MASK GENMASK(4, 2) | ||
|
||
/* Registers on MDIO_MMD_VEND1 */ | ||
#define MTK_PHY_LINK_STATUS_MISC 0xa2 | ||
#define MTK_PHY_FDX_ENABLE BIT(5) | ||
|
||
/* Registers on MDIO_MMD_VEND2 */ | ||
#define MTK_PHY_LED0_ON_CTRL 0x24 | ||
#define MTK_PHY_LED0_ON_LINK1000 BIT(0) | ||
#define MTK_PHY_LED0_ON_LINK100 BIT(1) | ||
#define MTK_PHY_LED0_ON_LINK10 BIT(2) | ||
#define MTK_PHY_LED0_ON_LINK2500 BIT(7) | ||
#define MTK_PHY_LED0_POLARITY BIT(14) | ||
|
||
#define MTK_PHY_LED1_ON_CTRL 0x26 | ||
#define MTK_PHY_LED1_ON_FDX BIT(4) | ||
#define MTK_PHY_LED1_ON_HDX BIT(5) | ||
#define MTK_PHY_LED1_POLARITY BIT(14) | ||
|
||
enum { | ||
PHY_AUX_SPD_10 = 0, | ||
PHY_AUX_SPD_100, | ||
PHY_AUX_SPD_1000, | ||
PHY_AUX_SPD_2500, | ||
}; | ||
|
||
static int mt798x_2p5ge_phy_config_init(struct phy_device *phydev) | ||
{ | ||
int ret; | ||
int i; | ||
const struct firmware *fw; | ||
struct device *dev = &phydev->mdio.dev; | ||
struct device_node *np; | ||
void __iomem *dmb_addr; | ||
void __iomem *pmb_addr; | ||
void __iomem *mcucsr_base; | ||
u16 reg; | ||
struct pinctrl *pinctrl; | ||
|
||
np = of_find_compatible_node(NULL, NULL, "mediatek,2p5gphy-fw"); | ||
if (!np) | ||
return -ENOENT; | ||
|
||
dmb_addr = of_iomap(np, 0); | ||
if (!dmb_addr) | ||
return -ENOMEM; | ||
pmb_addr = of_iomap(np, 1); | ||
if (!pmb_addr) | ||
return -ENOMEM; | ||
mcucsr_base = of_iomap(np, 2); | ||
if (!mcucsr_base) | ||
return -ENOMEM; | ||
|
||
ret = request_firmware(&fw, MEDAITEK_2P5GE_PHY_DMB_FW, dev); | ||
if (ret) { | ||
dev_err(dev, "failed to load firmware: %s, ret: %d\n", | ||
MEDAITEK_2P5GE_PHY_DMB_FW, ret); | ||
return ret; | ||
} | ||
for (i = 0; i < fw->size - 1; i += 4) | ||
writel(*((uint32_t *)(fw->data + i)), dmb_addr + i); | ||
release_firmware(fw); | ||
|
||
ret = request_firmware(&fw, MEDIATEK_2P5GE_PHY_PMB_FW, dev); | ||
if (ret) { | ||
dev_err(dev, "failed to load firmware: %s, ret: %d\n", | ||
MEDIATEK_2P5GE_PHY_PMB_FW, ret); | ||
return ret; | ||
} | ||
for (i = 0; i < fw->size - 1; i += 4) | ||
writel(*((uint32_t *)(fw->data + i)), pmb_addr + i); | ||
release_firmware(fw); | ||
|
||
reg = readw(mcucsr_base + MD32_EN_CFG); | ||
writew(reg | MD32_EN, mcucsr_base + MD32_EN_CFG); | ||
dev_dbg(dev, "Firmware loading/trigger ok.\n"); | ||
|
||
/* Setup LED */ | ||
phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL, | ||
MTK_PHY_LED0_POLARITY); | ||
|
||
phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL, | ||
MTK_PHY_LED0_ON_LINK10 | | ||
MTK_PHY_LED0_ON_LINK100 | | ||
MTK_PHY_LED0_ON_LINK1000 | | ||
MTK_PHY_LED0_ON_LINK2500); | ||
|
||
phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED1_ON_CTRL, | ||
MTK_PHY_LED1_ON_FDX | MTK_PHY_LED1_ON_HDX); | ||
|
||
pinctrl = devm_pinctrl_get_select(&phydev->mdio.dev, "i2p5gbe-led"); | ||
if (IS_ERR(pinctrl)) { | ||
dev_err(&phydev->mdio.dev, "Fail to set LED pins!\n"); | ||
return PTR_ERR(pinctrl); | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static int mt798x_2p5ge_phy_config_aneg(struct phy_device *phydev) | ||
{ | ||
bool changed = false; | ||
u32 adv; | ||
int ret; | ||
|
||
if (phydev->autoneg == AUTONEG_DISABLE) { | ||
/* Configure half duplex with genphy_setup_forced, | ||
* because genphy_c45_pma_setup_forced does not support. | ||
*/ | ||
return phydev->duplex != DUPLEX_FULL | ||
? genphy_setup_forced(phydev) | ||
: genphy_c45_pma_setup_forced(phydev); | ||
} | ||
|
||
ret = genphy_c45_an_config_aneg(phydev); | ||
if (ret < 0) | ||
return ret; | ||
if (ret > 0) | ||
changed = true; | ||
|
||
adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising); | ||
ret = phy_modify_changed(phydev, MII_CTRL1000, | ||
ADVERTISE_1000FULL | ADVERTISE_1000HALF, | ||
adv); | ||
if (ret < 0) | ||
return ret; | ||
if (ret > 0) | ||
changed = true; | ||
|
||
return genphy_c45_check_and_restart_aneg(phydev, changed); | ||
} | ||
|
||
static int mt798x_2p5ge_phy_get_features(struct phy_device *phydev) | ||
{ | ||
int ret; | ||
|
||
ret = genphy_read_abilities(phydev); | ||
if (ret) | ||
return ret; | ||
|
||
/* We don't support HDX at MAC layer on mt798x. | ||
* So mask phy's HDX capabilities, too. | ||
*/ | ||
linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, | ||
phydev->supported); | ||
linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, | ||
phydev->supported); | ||
linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, | ||
phydev->supported); | ||
linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, | ||
phydev->supported); | ||
linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported); | ||
|
||
return 0; | ||
} | ||
|
||
static int mt798x_2p5ge_phy_read_status(struct phy_device *phydev) | ||
{ | ||
int ret; | ||
|
||
ret = genphy_update_link(phydev); | ||
if (ret) | ||
return ret; | ||
|
||
phydev->speed = SPEED_UNKNOWN; | ||
phydev->duplex = DUPLEX_UNKNOWN; | ||
phydev->pause = 0; | ||
phydev->asym_pause = 0; | ||
|
||
if (!phydev->link) | ||
return 0; | ||
|
||
if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) { | ||
ret = genphy_c45_read_lpa(phydev); | ||
if (ret < 0) | ||
return ret; | ||
|
||
/* Read the link partner's 1G advertisement */ | ||
ret = phy_read(phydev, MII_STAT1000); | ||
if (ret < 0) | ||
return ret; | ||
mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, ret); | ||
} else if (phydev->autoneg == AUTONEG_DISABLE) { | ||
linkmode_zero(phydev->lp_advertising); | ||
} | ||
|
||
ret = phy_read(phydev, PHY_AUX_CTRL_STATUS); | ||
if (ret < 0) | ||
return ret; | ||
|
||
switch (FIELD_GET(PHY_AUX_SPEED_MASK, ret)) { | ||
case PHY_AUX_SPD_10: | ||
phydev->speed = SPEED_10; | ||
break; | ||
case PHY_AUX_SPD_100: | ||
phydev->speed = SPEED_100; | ||
break; | ||
case PHY_AUX_SPD_1000: | ||
phydev->speed = SPEED_1000; | ||
break; | ||
case PHY_AUX_SPD_2500: | ||
phydev->speed = SPEED_2500; | ||
phydev->duplex = DUPLEX_FULL; /* 2.5G must be FDX */ | ||
break; | ||
} | ||
|
||
ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LINK_STATUS_MISC); | ||
if (ret < 0) | ||
return ret; | ||
|
||
phydev->duplex = (ret & MTK_PHY_FDX_ENABLE) ? DUPLEX_FULL : DUPLEX_HALF; | ||
|
||
return 0; | ||
} | ||
|
||
static struct phy_driver mtk_gephy_driver[] = { | ||
{ | ||
PHY_ID_MATCH_EXACT(0x00339c11), | ||
.name = "MediaTek MT798x 2.5GbE PHY", | ||
.config_init = mt798x_2p5ge_phy_config_init, | ||
.config_aneg = mt798x_2p5ge_phy_config_aneg, | ||
.get_features = mt798x_2p5ge_phy_get_features, | ||
.read_status = mt798x_2p5ge_phy_read_status, | ||
}, | ||
}; | ||
|
||
module_phy_driver(mtk_gephy_driver); | ||
|
||
static struct mdio_device_id __maybe_unused mtk_2p5ge_phy_tbl[] = { | ||
{ PHY_ID_MATCH_VENDOR(0x00339c00) }, | ||
{ } | ||
}; | ||
|
||
MODULE_DESCRIPTION("MediaTek 2.5Gb Ethernet PHY driver"); | ||
MODULE_AUTHOR("SkyLake Huang <[email protected]>"); | ||
MODULE_LICENSE("GPL"); | ||
|
||
MODULE_DEVICE_TABLE(mdio, mtk_2p5ge_phy_tbl); | ||
MODULE_FIRMWARE(MEDAITEK_2P5GE_PHY_DMB_FW); | ||
MODULE_FIRMWARE(MEDIATEK_2P5GE_PHY_PMB_FW); |
Oops, something went wrong.