Skip to content

Commit 4ac94f7

Browse files
Frank Saedavem330
authored andcommitted
net: phy: Add driver for Motorcomm yt8531 gigabit ethernet phy
Add a driver for the motorcomm yt8531 gigabit ethernet phy. We have verified the driver on AM335x platform with yt8531 board. On the board, yt8531 gigabit ethernet phy works in utp mode, RGMII interface, supports 1000M/100M/10M speeds, and wol(magic package). Signed-off-by: Frank Sae <[email protected]> Reviewed-by: Andrew Lunn <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 36152f8 commit 4ac94f7

File tree

2 files changed

+207
-3
lines changed

2 files changed

+207
-3
lines changed

drivers/net/phy/Kconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ config MOTORCOMM_PHY
257257
tristate "Motorcomm PHYs"
258258
help
259259
Enables support for Motorcomm network PHYs.
260-
Currently supports the YT8511, YT8521, YT8531S Gigabit Ethernet PHYs.
260+
Currently supports YT85xx Gigabit Ethernet PHYs.
261261

262262
config NATIONAL_PHY
263263
tristate "National Semiconductor PHYs"

drivers/net/phy/motorcomm.c

Lines changed: 206 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// SPDX-License-Identifier: GPL-2.0+
22
/*
3-
* Motorcomm 8511/8521/8531S PHY driver.
3+
* Motorcomm 8511/8521/8531/8531S PHY driver.
44
*
55
* Author: Peter Geis <[email protected]>
66
* Author: Frank <[email protected]>
@@ -14,6 +14,7 @@
1414

1515
#define PHY_ID_YT8511 0x0000010a
1616
#define PHY_ID_YT8521 0x0000011a
17+
#define PHY_ID_YT8531 0x4f51e91b
1718
#define PHY_ID_YT8531S 0x4f51e91a
1819

1920
/* YT8521/YT8531S Register Overview
@@ -517,6 +518,61 @@ static int ytphy_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
517518
return phy_restore_page(phydev, old_page, ret);
518519
}
519520

521+
static int yt8531_set_wol(struct phy_device *phydev,
522+
struct ethtool_wolinfo *wol)
523+
{
524+
const u16 mac_addr_reg[] = {
525+
YTPHY_WOL_MACADDR2_REG,
526+
YTPHY_WOL_MACADDR1_REG,
527+
YTPHY_WOL_MACADDR0_REG,
528+
};
529+
const u8 *mac_addr;
530+
u16 mask, val;
531+
int ret;
532+
u8 i;
533+
534+
if (wol->wolopts & WAKE_MAGIC) {
535+
mac_addr = phydev->attached_dev->dev_addr;
536+
537+
/* Store the device address for the magic packet */
538+
for (i = 0; i < 3; i++) {
539+
ret = ytphy_write_ext_with_lock(phydev, mac_addr_reg[i],
540+
((mac_addr[i * 2] << 8)) |
541+
(mac_addr[i * 2 + 1]));
542+
if (ret < 0)
543+
return ret;
544+
}
545+
546+
/* Enable WOL feature */
547+
mask = YTPHY_WCR_PULSE_WIDTH_MASK | YTPHY_WCR_INTR_SEL;
548+
val = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
549+
val |= YTPHY_WCR_TYPE_PULSE | YTPHY_WCR_PULSE_WIDTH_672MS;
550+
ret = ytphy_modify_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG,
551+
mask, val);
552+
if (ret < 0)
553+
return ret;
554+
555+
/* Enable WOL interrupt */
556+
ret = phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG, 0,
557+
YTPHY_IER_WOL);
558+
if (ret < 0)
559+
return ret;
560+
} else {
561+
/* Disable WOL feature */
562+
mask = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
563+
ret = ytphy_modify_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG,
564+
mask, 0);
565+
566+
/* Disable WOL interrupt */
567+
ret = phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG,
568+
YTPHY_IER_WOL, 0);
569+
if (ret < 0)
570+
return ret;
571+
}
572+
573+
return 0;
574+
}
575+
520576
static int yt8511_read_page(struct phy_device *phydev)
521577
{
522578
return __phy_read(phydev, YT8511_PAGE_SELECT);
@@ -767,6 +823,17 @@ static int ytphy_rgmii_clk_delay_config(struct phy_device *phydev)
767823
return ytphy_modify_ext(phydev, YT8521_RGMII_CONFIG1_REG, mask, val);
768824
}
769825

826+
static int ytphy_rgmii_clk_delay_config_with_lock(struct phy_device *phydev)
827+
{
828+
int ret;
829+
830+
phy_lock_mdio_bus(phydev);
831+
ret = ytphy_rgmii_clk_delay_config(phydev);
832+
phy_unlock_mdio_bus(phydev);
833+
834+
return ret;
835+
}
836+
770837
/**
771838
* yt8521_probe() - read chip config then set suitable polling_mode
772839
* @phydev: a pointer to a &struct phy_device
@@ -891,6 +958,43 @@ static int yt8521_probe(struct phy_device *phydev)
891958
val);
892959
}
893960

961+
static int yt8531_probe(struct phy_device *phydev)
962+
{
963+
struct device_node *node = phydev->mdio.dev.of_node;
964+
u16 mask, val;
965+
u32 freq;
966+
967+
if (of_property_read_u32(node, "motorcomm,clk-out-frequency-hz", &freq))
968+
freq = YTPHY_DTS_OUTPUT_CLK_DIS;
969+
970+
switch (freq) {
971+
case YTPHY_DTS_OUTPUT_CLK_DIS:
972+
mask = YT8531_SCR_SYNCE_ENABLE;
973+
val = 0;
974+
break;
975+
case YTPHY_DTS_OUTPUT_CLK_25M:
976+
mask = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_SRC_MASK |
977+
YT8531_SCR_CLK_FRE_SEL_125M;
978+
val = YT8531_SCR_SYNCE_ENABLE |
979+
FIELD_PREP(YT8531_SCR_CLK_SRC_MASK,
980+
YT8531_SCR_CLK_SRC_REF_25M);
981+
break;
982+
case YTPHY_DTS_OUTPUT_CLK_125M:
983+
mask = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_SRC_MASK |
984+
YT8531_SCR_CLK_FRE_SEL_125M;
985+
val = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_FRE_SEL_125M |
986+
FIELD_PREP(YT8531_SCR_CLK_SRC_MASK,
987+
YT8531_SCR_CLK_SRC_PLL_125M);
988+
break;
989+
default:
990+
phydev_warn(phydev, "Freq err:%u\n", freq);
991+
return -EINVAL;
992+
}
993+
994+
return ytphy_modify_ext_with_lock(phydev, YTPHY_SYNCE_CFG_REG, mask,
995+
val);
996+
}
997+
894998
/**
895999
* ytphy_utp_read_lpa() - read LPA then setup lp_advertising for utp
8961000
* @phydev: a pointer to a &struct phy_device
@@ -1387,6 +1491,94 @@ static int yt8521_config_init(struct phy_device *phydev)
13871491
return phy_restore_page(phydev, old_page, ret);
13881492
}
13891493

1494+
static int yt8531_config_init(struct phy_device *phydev)
1495+
{
1496+
struct device_node *node = phydev->mdio.dev.of_node;
1497+
int ret;
1498+
1499+
ret = ytphy_rgmii_clk_delay_config_with_lock(phydev);
1500+
if (ret < 0)
1501+
return ret;
1502+
1503+
if (of_property_read_bool(node, "motorcomm,auto-sleep-disabled")) {
1504+
/* disable auto sleep */
1505+
ret = ytphy_modify_ext_with_lock(phydev,
1506+
YT8521_EXTREG_SLEEP_CONTROL1_REG,
1507+
YT8521_ESC1R_SLEEP_SW, 0);
1508+
if (ret < 0)
1509+
return ret;
1510+
}
1511+
1512+
if (of_property_read_bool(node, "motorcomm,keep-pll-enabled")) {
1513+
/* enable RXC clock when no wire plug */
1514+
ret = ytphy_modify_ext_with_lock(phydev,
1515+
YT8521_CLOCK_GATING_REG,
1516+
YT8521_CGR_RX_CLK_EN, 0);
1517+
if (ret < 0)
1518+
return ret;
1519+
}
1520+
1521+
return 0;
1522+
}
1523+
1524+
/**
1525+
* yt8531_link_change_notify() - Adjust the tx clock direction according to
1526+
* the current speed and dts config.
1527+
* @phydev: a pointer to a &struct phy_device
1528+
*
1529+
* NOTE: This function is only used to adapt to VF2 with JH7110 SoC. Please
1530+
* keep "motorcomm,tx-clk-adj-enabled" not exist in dts when the soc is not
1531+
* JH7110.
1532+
*/
1533+
static void yt8531_link_change_notify(struct phy_device *phydev)
1534+
{
1535+
struct device_node *node = phydev->mdio.dev.of_node;
1536+
bool tx_clk_adj_enabled = false;
1537+
bool tx_clk_1000_inverted;
1538+
bool tx_clk_100_inverted;
1539+
bool tx_clk_10_inverted;
1540+
u16 val = 0;
1541+
int ret;
1542+
1543+
if (of_property_read_bool(node, "motorcomm,tx-clk-adj-enabled"))
1544+
tx_clk_adj_enabled = true;
1545+
1546+
if (!tx_clk_adj_enabled)
1547+
return;
1548+
1549+
if (of_property_read_bool(node, "motorcomm,tx-clk-10-inverted"))
1550+
tx_clk_10_inverted = true;
1551+
if (of_property_read_bool(node, "motorcomm,tx-clk-100-inverted"))
1552+
tx_clk_100_inverted = true;
1553+
if (of_property_read_bool(node, "motorcomm,tx-clk-1000-inverted"))
1554+
tx_clk_1000_inverted = true;
1555+
1556+
if (phydev->speed < 0)
1557+
return;
1558+
1559+
switch (phydev->speed) {
1560+
case SPEED_1000:
1561+
if (tx_clk_1000_inverted)
1562+
val = YT8521_RC1R_TX_CLK_SEL_INVERTED;
1563+
break;
1564+
case SPEED_100:
1565+
if (tx_clk_100_inverted)
1566+
val = YT8521_RC1R_TX_CLK_SEL_INVERTED;
1567+
break;
1568+
case SPEED_10:
1569+
if (tx_clk_10_inverted)
1570+
val = YT8521_RC1R_TX_CLK_SEL_INVERTED;
1571+
break;
1572+
default:
1573+
return;
1574+
}
1575+
1576+
ret = ytphy_modify_ext_with_lock(phydev, YT8521_RGMII_CONFIG1_REG,
1577+
YT8521_RC1R_TX_CLK_SEL_INVERTED, val);
1578+
if (ret < 0)
1579+
phydev_warn(phydev, "Modify TX_CLK_SEL err:%d\n", ret);
1580+
}
1581+
13901582
/**
13911583
* yt8521_prepare_fiber_features() - A small helper function that setup
13921584
* fiber's features.
@@ -1969,6 +2161,17 @@ static struct phy_driver motorcomm_phy_drvs[] = {
19692161
.suspend = yt8521_suspend,
19702162
.resume = yt8521_resume,
19712163
},
2164+
{
2165+
PHY_ID_MATCH_EXACT(PHY_ID_YT8531),
2166+
.name = "YT8531 Gigabit Ethernet",
2167+
.probe = yt8531_probe,
2168+
.config_init = yt8531_config_init,
2169+
.suspend = genphy_suspend,
2170+
.resume = genphy_resume,
2171+
.get_wol = ytphy_get_wol,
2172+
.set_wol = yt8531_set_wol,
2173+
.link_change_notify = yt8531_link_change_notify,
2174+
},
19722175
{
19732176
PHY_ID_MATCH_EXACT(PHY_ID_YT8531S),
19742177
.name = "YT8531S Gigabit Ethernet",
@@ -1990,14 +2193,15 @@ static struct phy_driver motorcomm_phy_drvs[] = {
19902193

19912194
module_phy_driver(motorcomm_phy_drvs);
19922195

1993-
MODULE_DESCRIPTION("Motorcomm 8511/8521/8531S PHY driver");
2196+
MODULE_DESCRIPTION("Motorcomm 8511/8521/8531/8531S PHY driver");
19942197
MODULE_AUTHOR("Peter Geis");
19952198
MODULE_AUTHOR("Frank");
19962199
MODULE_LICENSE("GPL");
19972200

19982201
static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
19992202
{ PHY_ID_MATCH_EXACT(PHY_ID_YT8511) },
20002203
{ PHY_ID_MATCH_EXACT(PHY_ID_YT8521) },
2204+
{ PHY_ID_MATCH_EXACT(PHY_ID_YT8531) },
20012205
{ PHY_ID_MATCH_EXACT(PHY_ID_YT8531S) },
20022206
{ /* sentinel */ }
20032207
};

0 commit comments

Comments
 (0)