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 <pgwipeout@gmail.com>
66 * Author: Frank <Frank.Sae@motor-comm.com>
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+
520576static 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
19912194module_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" );
19942197MODULE_AUTHOR ("Peter Geis" );
19952198MODULE_AUTHOR ("Frank" );
19962199MODULE_LICENSE ("GPL" );
19972200
19982201static 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