Skip to content

Commit 165cd04

Browse files
oleremdavem330
authored andcommitted
net: phy: dp83td510: Add support for the DP83TD510 Ethernet PHY
The DP83TD510E is an ultra-low power Ethernet physical layer transceiver that supports 10M single pair cable. This driver was tested with NXP SJA1105, STMMAC and ASIX AX88772B USB Ethernet controller. Signed-off-by: Oleksij Rempel <[email protected]> Reviewed-by: Andrew Lunn <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 2013ad8 commit 165cd04

File tree

3 files changed

+216
-0
lines changed

3 files changed

+216
-0
lines changed

drivers/net/phy/Kconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,12 @@ config DP83869_PHY
342342
Currently supports the DP83869 PHY. This PHY supports copper and
343343
fiber connections.
344344

345+
config DP83TD510_PHY
346+
tristate "Texas Instruments DP83TD510 Ethernet 10Base-T1L PHY"
347+
help
348+
Support for the DP83TD510 Ethernet 10Base-T1L PHY. This PHY supports
349+
a 10M single pair Ethernet connection for up to 1000 meter cable.
350+
345351
config VITESSE_PHY
346352
tristate "Vitesse PHYs"
347353
help

drivers/net/phy/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ obj-$(CONFIG_DP83848_PHY) += dp83848.o
5757
obj-$(CONFIG_DP83867_PHY) += dp83867.o
5858
obj-$(CONFIG_DP83869_PHY) += dp83869.o
5959
obj-$(CONFIG_DP83TC811_PHY) += dp83tc811.o
60+
obj-$(CONFIG_DP83TD510_PHY) += dp83td510.o
6061
obj-$(CONFIG_FIXED_PHY) += fixed_phy.o
6162
obj-$(CONFIG_ICPLUS_PHY) += icplus.o
6263
obj-$(CONFIG_INTEL_XWAY_PHY) += intel-xway.o

drivers/net/phy/dp83td510.c

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Driver for the Texas Instruments DP83TD510 PHY
3+
* Copyright (c) 2022 Pengutronix, Oleksij Rempel <[email protected]>
4+
*/
5+
6+
#include <linux/bitfield.h>
7+
#include <linux/kernel.h>
8+
#include <linux/module.h>
9+
#include <linux/phy.h>
10+
11+
#define DP83TD510E_PHY_ID 0x20000181
12+
13+
/* MDIO_MMD_VEND2 registers */
14+
#define DP83TD510E_PHY_STS 0x10
15+
#define DP83TD510E_STS_MII_INT BIT(7)
16+
#define DP83TD510E_LINK_STATUS BIT(0)
17+
18+
#define DP83TD510E_GEN_CFG 0x11
19+
#define DP83TD510E_GENCFG_INT_POLARITY BIT(3)
20+
#define DP83TD510E_GENCFG_INT_EN BIT(1)
21+
#define DP83TD510E_GENCFG_INT_OE BIT(0)
22+
23+
#define DP83TD510E_INTERRUPT_REG_1 0x12
24+
#define DP83TD510E_INT1_LINK BIT(13)
25+
#define DP83TD510E_INT1_LINK_EN BIT(5)
26+
27+
#define DP83TD510E_AN_STAT_1 0x60c
28+
#define DP83TD510E_MASTER_SLAVE_RESOL_FAIL BIT(15)
29+
30+
static int dp83td510_config_intr(struct phy_device *phydev)
31+
{
32+
int ret;
33+
34+
if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
35+
/* Clear any pending interrupts */
36+
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PHY_STS,
37+
0x0);
38+
if (ret)
39+
return ret;
40+
41+
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
42+
DP83TD510E_INTERRUPT_REG_1,
43+
DP83TD510E_INT1_LINK_EN);
44+
if (ret)
45+
return ret;
46+
47+
ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
48+
DP83TD510E_GEN_CFG,
49+
DP83TD510E_GENCFG_INT_POLARITY |
50+
DP83TD510E_GENCFG_INT_EN |
51+
DP83TD510E_GENCFG_INT_OE);
52+
} else {
53+
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
54+
DP83TD510E_INTERRUPT_REG_1, 0x0);
55+
if (ret)
56+
return ret;
57+
58+
ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2,
59+
DP83TD510E_GEN_CFG,
60+
DP83TD510E_GENCFG_INT_EN);
61+
if (ret)
62+
return ret;
63+
64+
/* Clear any pending interrupts */
65+
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PHY_STS,
66+
0x0);
67+
}
68+
69+
return ret;
70+
}
71+
72+
static irqreturn_t dp83td510_handle_interrupt(struct phy_device *phydev)
73+
{
74+
int ret;
75+
76+
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PHY_STS);
77+
if (ret < 0) {
78+
phy_error(phydev);
79+
return IRQ_NONE;
80+
} else if (!(ret & DP83TD510E_STS_MII_INT)) {
81+
return IRQ_NONE;
82+
}
83+
84+
/* Read the current enabled interrupts */
85+
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_INTERRUPT_REG_1);
86+
if (ret < 0) {
87+
phy_error(phydev);
88+
return IRQ_NONE;
89+
} else if (!(ret & DP83TD510E_INT1_LINK_EN) ||
90+
!(ret & DP83TD510E_INT1_LINK)) {
91+
return IRQ_NONE;
92+
}
93+
94+
phy_trigger_machine(phydev);
95+
96+
return IRQ_HANDLED;
97+
}
98+
99+
static int dp83td510_read_status(struct phy_device *phydev)
100+
{
101+
u16 phy_sts;
102+
int ret;
103+
104+
phydev->speed = SPEED_UNKNOWN;
105+
phydev->duplex = DUPLEX_UNKNOWN;
106+
phydev->pause = 0;
107+
phydev->asym_pause = 0;
108+
linkmode_zero(phydev->lp_advertising);
109+
110+
phy_sts = phy_read(phydev, DP83TD510E_PHY_STS);
111+
112+
phydev->link = !!(phy_sts & DP83TD510E_LINK_STATUS);
113+
if (phydev->link) {
114+
/* This PHY supports only one link mode: 10BaseT1L_Full */
115+
phydev->duplex = DUPLEX_FULL;
116+
phydev->speed = SPEED_10;
117+
118+
if (phydev->autoneg == AUTONEG_ENABLE) {
119+
ret = genphy_c45_read_lpa(phydev);
120+
if (ret)
121+
return ret;
122+
123+
phy_resolve_aneg_linkmode(phydev);
124+
}
125+
}
126+
127+
if (phydev->autoneg == AUTONEG_ENABLE) {
128+
ret = genphy_c45_baset1_read_status(phydev);
129+
if (ret < 0)
130+
return ret;
131+
132+
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2,
133+
DP83TD510E_AN_STAT_1);
134+
if (ret < 0)
135+
return ret;
136+
137+
if (ret & DP83TD510E_MASTER_SLAVE_RESOL_FAIL)
138+
phydev->master_slave_state = MASTER_SLAVE_STATE_ERR;
139+
} else {
140+
return genphy_c45_pma_baset1_read_master_slave(phydev);
141+
}
142+
143+
return 0;
144+
}
145+
146+
static int dp83td510_config_aneg(struct phy_device *phydev)
147+
{
148+
bool changed = false;
149+
int ret;
150+
151+
ret = genphy_c45_pma_baset1_setup_master_slave(phydev);
152+
if (ret < 0)
153+
return ret;
154+
155+
if (phydev->autoneg == AUTONEG_DISABLE)
156+
return genphy_c45_an_disable_aneg(phydev);
157+
158+
ret = genphy_c45_an_config_aneg(phydev);
159+
if (ret < 0)
160+
return ret;
161+
if (ret > 0)
162+
changed = true;
163+
164+
return genphy_c45_check_and_restart_aneg(phydev, changed);
165+
}
166+
167+
static int dp83td510_get_features(struct phy_device *phydev)
168+
{
169+
/* This PHY can't respond on MDIO bus if no RMII clock is enabled.
170+
* In case RMII mode is used (most meaningful mode for this PHY) and
171+
* the PHY do not have own XTAL, and CLK providing MAC is not probed,
172+
* we won't be able to read all needed ability registers.
173+
* So provide it manually.
174+
*/
175+
176+
linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
177+
linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported);
178+
linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported);
179+
linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
180+
phydev->supported);
181+
182+
return 0;
183+
}
184+
185+
static struct phy_driver dp83td510_driver[] = {
186+
{
187+
PHY_ID_MATCH_MODEL(DP83TD510E_PHY_ID),
188+
.name = "TI DP83TD510E",
189+
190+
.config_aneg = dp83td510_config_aneg,
191+
.read_status = dp83td510_read_status,
192+
.get_features = dp83td510_get_features,
193+
.config_intr = dp83td510_config_intr,
194+
.handle_interrupt = dp83td510_handle_interrupt,
195+
196+
.suspend = genphy_suspend,
197+
.resume = genphy_resume,
198+
} };
199+
module_phy_driver(dp83td510_driver);
200+
201+
static struct mdio_device_id __maybe_unused dp83td510_tbl[] = {
202+
{ PHY_ID_MATCH_MODEL(DP83TD510E_PHY_ID) },
203+
{ }
204+
};
205+
MODULE_DEVICE_TABLE(mdio, dp83td510_tbl);
206+
207+
MODULE_DESCRIPTION("Texas Instruments DP83TD510E PHY driver");
208+
MODULE_AUTHOR("Oleksij Rempel <[email protected]>");
209+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)