Skip to content

Commit 4b44ca4

Browse files
committed
mediatek: add driver for built-in 2.5G Ethernet PHY
Add driver for the built-in 2.5G Ethernet PHY found in the MT7988 SoC. To function the PHY also needs firmware files which have not yet been published via linux-firmware. Signed-off-by: Daniel Golle <[email protected]>
1 parent cce9d68 commit 4b44ca4

File tree

4 files changed

+303
-0
lines changed

4 files changed

+303
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
// SPDX-License-Identifier: GPL-2.0+
2+
#include <linux/bitfield.h>
3+
#include <linux/firmware.h>
4+
#include <linux/module.h>
5+
#include <linux/nvmem-consumer.h>
6+
#include <linux/of_address.h>
7+
#include <linux/of_platform.h>
8+
#include <linux/pinctrl/consumer.h>
9+
#include <linux/phy.h>
10+
11+
#define MEDAITEK_2P5GE_PHY_DMB_FW "mediatek/mediatek-2p5ge-phy-dmb.bin"
12+
#define MEDIATEK_2P5GE_PHY_PMB_FW "mediatek/mediatek-2p5ge-phy-pmb.bin"
13+
14+
#define MD32_EN_CFG 0x18
15+
#define MD32_EN BIT(0)
16+
17+
#define BASE100T_STATUS_EXTEND 0x10
18+
#define BASE1000T_STATUS_EXTEND 0x11
19+
#define EXTEND_CTRL_AND_STATUS 0x16
20+
21+
#define PHY_AUX_CTRL_STATUS 0x1d
22+
#define PHY_AUX_DPX_MASK GENMASK(5, 5)
23+
#define PHY_AUX_SPEED_MASK GENMASK(4, 2)
24+
25+
/* Registers on MDIO_MMD_VEND1 */
26+
#define MTK_PHY_LINK_STATUS_MISC 0xa2
27+
#define MTK_PHY_FDX_ENABLE BIT(5)
28+
29+
/* Registers on MDIO_MMD_VEND2 */
30+
#define MTK_PHY_LED0_ON_CTRL 0x24
31+
#define MTK_PHY_LED0_ON_LINK1000 BIT(0)
32+
#define MTK_PHY_LED0_ON_LINK100 BIT(1)
33+
#define MTK_PHY_LED0_ON_LINK10 BIT(2)
34+
#define MTK_PHY_LED0_ON_LINK2500 BIT(7)
35+
#define MTK_PHY_LED0_POLARITY BIT(14)
36+
37+
#define MTK_PHY_LED1_ON_CTRL 0x26
38+
#define MTK_PHY_LED1_ON_FDX BIT(4)
39+
#define MTK_PHY_LED1_ON_HDX BIT(5)
40+
#define MTK_PHY_LED1_POLARITY BIT(14)
41+
42+
enum {
43+
PHY_AUX_SPD_10 = 0,
44+
PHY_AUX_SPD_100,
45+
PHY_AUX_SPD_1000,
46+
PHY_AUX_SPD_2500,
47+
};
48+
49+
static int mt798x_2p5ge_phy_config_init(struct phy_device *phydev)
50+
{
51+
int ret;
52+
int i;
53+
const struct firmware *fw;
54+
struct device *dev = &phydev->mdio.dev;
55+
struct device_node *np;
56+
void __iomem *dmb_addr;
57+
void __iomem *pmb_addr;
58+
void __iomem *mcucsr_base;
59+
u16 reg;
60+
struct pinctrl *pinctrl;
61+
62+
np = of_find_compatible_node(NULL, NULL, "mediatek,2p5gphy-fw");
63+
if (!np)
64+
return -ENOENT;
65+
66+
dmb_addr = of_iomap(np, 0);
67+
if (!dmb_addr)
68+
return -ENOMEM;
69+
pmb_addr = of_iomap(np, 1);
70+
if (!pmb_addr)
71+
return -ENOMEM;
72+
mcucsr_base = of_iomap(np, 2);
73+
if (!mcucsr_base)
74+
return -ENOMEM;
75+
76+
ret = request_firmware(&fw, MEDAITEK_2P5GE_PHY_DMB_FW, dev);
77+
if (ret) {
78+
dev_err(dev, "failed to load firmware: %s, ret: %d\n",
79+
MEDAITEK_2P5GE_PHY_DMB_FW, ret);
80+
return ret;
81+
}
82+
for (i = 0; i < fw->size - 1; i += 4)
83+
writel(*((uint32_t *)(fw->data + i)), dmb_addr + i);
84+
release_firmware(fw);
85+
86+
ret = request_firmware(&fw, MEDIATEK_2P5GE_PHY_PMB_FW, dev);
87+
if (ret) {
88+
dev_err(dev, "failed to load firmware: %s, ret: %d\n",
89+
MEDIATEK_2P5GE_PHY_PMB_FW, ret);
90+
return ret;
91+
}
92+
for (i = 0; i < fw->size - 1; i += 4)
93+
writel(*((uint32_t *)(fw->data + i)), pmb_addr + i);
94+
release_firmware(fw);
95+
96+
reg = readw(mcucsr_base + MD32_EN_CFG);
97+
writew(reg | MD32_EN, mcucsr_base + MD32_EN_CFG);
98+
dev_dbg(dev, "Firmware loading/trigger ok.\n");
99+
100+
/* Setup LED */
101+
phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL,
102+
MTK_PHY_LED0_POLARITY);
103+
104+
phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL,
105+
MTK_PHY_LED0_ON_LINK10 |
106+
MTK_PHY_LED0_ON_LINK100 |
107+
MTK_PHY_LED0_ON_LINK1000 |
108+
MTK_PHY_LED0_ON_LINK2500);
109+
110+
phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED1_ON_CTRL,
111+
MTK_PHY_LED1_ON_FDX | MTK_PHY_LED1_ON_HDX);
112+
113+
pinctrl = devm_pinctrl_get_select(&phydev->mdio.dev, "i2p5gbe-led");
114+
if (IS_ERR(pinctrl)) {
115+
dev_err(&phydev->mdio.dev, "Fail to set LED pins!\n");
116+
return PTR_ERR(pinctrl);
117+
}
118+
119+
return 0;
120+
}
121+
122+
static int mt798x_2p5ge_phy_config_aneg(struct phy_device *phydev)
123+
{
124+
bool changed = false;
125+
u32 adv;
126+
int ret;
127+
128+
if (phydev->autoneg == AUTONEG_DISABLE) {
129+
/* Configure half duplex with genphy_setup_forced,
130+
* because genphy_c45_pma_setup_forced does not support.
131+
*/
132+
return phydev->duplex != DUPLEX_FULL
133+
? genphy_setup_forced(phydev)
134+
: genphy_c45_pma_setup_forced(phydev);
135+
}
136+
137+
ret = genphy_c45_an_config_aneg(phydev);
138+
if (ret < 0)
139+
return ret;
140+
if (ret > 0)
141+
changed = true;
142+
143+
adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
144+
ret = phy_modify_changed(phydev, MII_CTRL1000,
145+
ADVERTISE_1000FULL | ADVERTISE_1000HALF,
146+
adv);
147+
if (ret < 0)
148+
return ret;
149+
if (ret > 0)
150+
changed = true;
151+
152+
return genphy_c45_check_and_restart_aneg(phydev, changed);
153+
}
154+
155+
static int mt798x_2p5ge_phy_get_features(struct phy_device *phydev)
156+
{
157+
int ret;
158+
159+
ret = genphy_read_abilities(phydev);
160+
if (ret)
161+
return ret;
162+
163+
/* We don't support HDX at MAC layer on mt798x.
164+
* So mask phy's HDX capabilities, too.
165+
*/
166+
linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
167+
phydev->supported);
168+
linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
169+
phydev->supported);
170+
linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
171+
phydev->supported);
172+
linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
173+
phydev->supported);
174+
linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
175+
176+
return 0;
177+
}
178+
179+
static int mt798x_2p5ge_phy_read_status(struct phy_device *phydev)
180+
{
181+
int ret;
182+
183+
ret = genphy_update_link(phydev);
184+
if (ret)
185+
return ret;
186+
187+
phydev->speed = SPEED_UNKNOWN;
188+
phydev->duplex = DUPLEX_UNKNOWN;
189+
phydev->pause = 0;
190+
phydev->asym_pause = 0;
191+
192+
if (!phydev->link)
193+
return 0;
194+
195+
if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) {
196+
ret = genphy_c45_read_lpa(phydev);
197+
if (ret < 0)
198+
return ret;
199+
200+
/* Read the link partner's 1G advertisement */
201+
ret = phy_read(phydev, MII_STAT1000);
202+
if (ret < 0)
203+
return ret;
204+
mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, ret);
205+
} else if (phydev->autoneg == AUTONEG_DISABLE) {
206+
linkmode_zero(phydev->lp_advertising);
207+
}
208+
209+
ret = phy_read(phydev, PHY_AUX_CTRL_STATUS);
210+
if (ret < 0)
211+
return ret;
212+
213+
switch (FIELD_GET(PHY_AUX_SPEED_MASK, ret)) {
214+
case PHY_AUX_SPD_10:
215+
phydev->speed = SPEED_10;
216+
break;
217+
case PHY_AUX_SPD_100:
218+
phydev->speed = SPEED_100;
219+
break;
220+
case PHY_AUX_SPD_1000:
221+
phydev->speed = SPEED_1000;
222+
break;
223+
case PHY_AUX_SPD_2500:
224+
phydev->speed = SPEED_2500;
225+
phydev->duplex = DUPLEX_FULL; /* 2.5G must be FDX */
226+
break;
227+
}
228+
229+
ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LINK_STATUS_MISC);
230+
if (ret < 0)
231+
return ret;
232+
233+
phydev->duplex = (ret & MTK_PHY_FDX_ENABLE) ? DUPLEX_FULL : DUPLEX_HALF;
234+
235+
return 0;
236+
}
237+
238+
static struct phy_driver mtk_gephy_driver[] = {
239+
{
240+
PHY_ID_MATCH_EXACT(0x00339c11),
241+
.name = "MediaTek MT798x 2.5GbE PHY",
242+
.config_init = mt798x_2p5ge_phy_config_init,
243+
.config_aneg = mt798x_2p5ge_phy_config_aneg,
244+
.get_features = mt798x_2p5ge_phy_get_features,
245+
.read_status = mt798x_2p5ge_phy_read_status,
246+
},
247+
};
248+
249+
module_phy_driver(mtk_gephy_driver);
250+
251+
static struct mdio_device_id __maybe_unused mtk_2p5ge_phy_tbl[] = {
252+
{ PHY_ID_MATCH_VENDOR(0x00339c00) },
253+
{ }
254+
};
255+
256+
MODULE_DESCRIPTION("MediaTek 2.5Gb Ethernet PHY driver");
257+
MODULE_AUTHOR("SkyLake Huang <[email protected]>");
258+
MODULE_LICENSE("GPL");
259+
260+
MODULE_DEVICE_TABLE(mdio, mtk_2p5ge_phy_tbl);
261+
MODULE_FIRMWARE(MEDAITEK_2P5GE_PHY_DMB_FW);
262+
MODULE_FIRMWARE(MEDIATEK_2P5GE_PHY_PMB_FW);

target/linux/mediatek/filogic/config-5.15

+1
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ CONFIG_MAXLINEAR_GPHY=y
205205
CONFIG_MDIO_BUS=y
206206
CONFIG_MDIO_DEVICE=y
207207
CONFIG_MDIO_DEVRES=y
208+
CONFIG_MEDIATEK_2P5G_PHY=y
208209
CONFIG_MEDIATEK_GE_PHY=y
209210
CONFIG_MEDIATEK_GE_SOC_PHY=y
210211
CONFIG_MEDIATEK_WATCHDOG=y

target/linux/mediatek/mt7622/config-5.15

+1
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ CONFIG_MAGIC_SYSRQ=y
230230
CONFIG_MDIO_BUS=y
231231
CONFIG_MDIO_DEVICE=y
232232
CONFIG_MDIO_DEVRES=y
233+
# CONFIG_MEDIATEK_2P5G_PHY is not set
233234
CONFIG_MEDIATEK_GE_PHY=y
234235
# CONFIG_MEDIATEK_GE_SOC_PHY is not set
235236
CONFIG_MEDIATEK_WATCHDOG=y
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
From 128dc09b0af36772062142ce9e85b19c84ac789a Mon Sep 17 00:00:00 2001
2+
From: Daniel Golle <[email protected]>
3+
Date: Tue, 28 Feb 2023 17:53:37 +0000
4+
Subject: [PATCH] net: phy: add driver for MediaTek 2.5G PHY
5+
6+
Signed-off-by: Daniel Golle <[email protected]>
7+
---
8+
drivers/net/phy/Kconfig | 7 ++
9+
drivers/net/phy/Makefile | 1 +
10+
drivers/net/phy/mediatek-2p5ge.c | 220 +++++++++++++++++++++++++++++++
11+
3 files changed, 226 insertions(+)
12+
create mode 100644 drivers/net/phy/mediatek-2p5ge.c
13+
14+
--- a/drivers/net/phy/Kconfig
15+
+++ b/drivers/net/phy/Kconfig
16+
@@ -304,6 +304,13 @@ config MEDIATEK_GE_SOC_PHY
17+
present in the SoCs efuse and will dynamically calibrate VCM
18+
(common-mode voltage) during startup.
19+
20+
+config MEDIATEK_2P5G_PHY
21+
+ tristate "MediaTek 2.5G Ethernet PHY"
22+
+ depends on (ARCH_MEDIATEK && ARM64) || COMPILE_TEST
23+
+ default NET_MEDIATEK_SOC
24+
+ help
25+
+ Supports the MediaTek 2.5G Ethernet PHY.
26+
+
27+
config MICREL_PHY
28+
tristate "Micrel PHYs"
29+
help
30+
--- a/drivers/net/phy/Makefile
31+
+++ b/drivers/net/phy/Makefile
32+
@@ -80,6 +80,7 @@ obj-$(CONFIG_MARVELL_10G_PHY) += marvell
33+
obj-$(CONFIG_MARVELL_PHY) += marvell.o
34+
obj-$(CONFIG_MARVELL_88X2222_PHY) += marvell-88x2222.o
35+
obj-$(CONFIG_MAXLINEAR_GPHY) += mxl-gpy.o
36+
+obj-$(CONFIG_MEDIATEK_2P5G_PHY) += mediatek-2p5ge.o
37+
obj-$(CONFIG_MEDIATEK_GE_PHY) += mediatek-ge.o
38+
obj-$(CONFIG_MEDIATEK_GE_SOC_PHY) += mediatek-ge-soc.o
39+
obj-$(CONFIG_MESON_GXL_PHY) += meson-gxl.o

0 commit comments

Comments
 (0)