@@ -52,6 +52,8 @@ extern int phy_port_read_paged(struct phy_device *phydev, int port, int page, u3
5252#define RTL821XINT_MEDIA_PAGE_SELECT 0x1d
5353/* external RTL821X PHY uses register 0x1e to select media page */
5454#define RTL821XEXT_MEDIA_PAGE_SELECT 0x1e
55+ #define RTL821X_PHYCR2 0x19
56+ #define RTL821X_PHYCR2_PHY_EEE_ENABLE BIT(5)
5557
5658#define RTL821X_CHIP_ID 0x6276
5759
@@ -1160,231 +1162,61 @@ static int rtl8214fc_config_aneg(struct phy_device *phydev)
11601162 return ret ;
11611163}
11621164
1163- /* Enable EEE on the RTL8218B PHYs
1164- * The method used is not the preferred way (which would be based on the MAC-EEE state,
1165- * but the only way that works since the kernel first enables EEE in the MAC
1166- * and then sets up the PHY. The MAC-based approach would require the oppsite.
1167- */
1168- static void rtl8218d_eee_set (struct phy_device * phydev , bool enable )
1169- {
1170- u32 val ;
1171- bool an_enabled ;
1172-
1173- pr_debug ("In %s %d, enable %d\n" , __func__ , phydev -> mdio .addr , enable );
1174- /* Set GPHY page to copper */
1175- phy_write_paged (phydev , RTL821X_PAGE_GPHY , RTL821XEXT_MEDIA_PAGE_SELECT , RTL821X_MEDIA_PAGE_COPPER );
1176-
1177- val = phy_read (phydev , MII_BMCR );
1178- an_enabled = val & BMCR_ANENABLE ;
1179-
1180- val = phy_read_mmd (phydev , MDIO_MMD_AN , MDIO_AN_EEE_ADV );
1181- val |= MDIO_EEE_1000T | MDIO_EEE_100TX ;
1182- phy_write_mmd (phydev , MDIO_MMD_AN , MDIO_AN_EEE_ADV , enable ? (MDIO_EEE_100TX | MDIO_EEE_1000T ) : 0 );
1183-
1184- /* 500M EEE ability */
1185- val = phy_read_paged (phydev , RTL821X_PAGE_GPHY , 20 );
1186- if (enable )
1187- val |= BIT (7 );
1188- else
1189- val &= ~BIT (7 );
1190- phy_write_paged (phydev , RTL821X_PAGE_GPHY , 20 , val );
1191-
1192- /* Restart AN if enabled */
1193- if (an_enabled ) {
1194- val = phy_read (phydev , MII_BMCR );
1195- val |= BMCR_ANRESTART ;
1196- phy_write (phydev , MII_BMCR , val );
1197- }
1198-
1199- /* GPHY page back to auto */
1200- phy_write_paged (phydev , RTL821X_PAGE_GPHY , RTL821XEXT_MEDIA_PAGE_SELECT , RTL821X_MEDIA_PAGE_AUTO );
1201- }
1202-
1203- static int rtl8218b_get_eee (struct phy_device * phydev , struct ethtool_keee * e )
1204- {
1205- u32 val ;
1206- int addr = phydev -> mdio .addr ;
1207-
1208- pr_debug ("In %s, port %d, was enabled: %d\n" , __func__ , addr , e -> eee_enabled );
1209-
1210- /* Set GPHY page to copper */
1211- phy_write_paged (phydev , RTL821X_PAGE_GPHY , RTL821XINT_MEDIA_PAGE_SELECT , RTL821X_MEDIA_PAGE_COPPER );
1212-
1213- val = phy_read_paged (phydev , 7 , MDIO_AN_EEE_ADV );
1214- if (e -> eee_enabled ) {
1215- /* Verify vs MAC-based EEE */
1216- e -> eee_enabled = !!(val & BIT (7 ));
1217- if (!e -> eee_enabled ) {
1218- val = phy_read_paged (phydev , RTL821X_PAGE_MAC , 25 );
1219- e -> eee_enabled = !!(val & BIT (4 ));
1220- }
1221- }
1222- pr_debug ("%s: enabled: %d\n" , __func__ , e -> eee_enabled );
1223-
1224- /* GPHY page to auto */
1225- phy_write_paged (phydev , RTL821X_PAGE_GPHY , RTL821XINT_MEDIA_PAGE_SELECT , RTL821X_MEDIA_PAGE_AUTO );
1226-
1227- return 0 ;
1228- }
1229-
1230- static int rtl8218d_get_eee (struct phy_device * phydev , struct ethtool_keee * e )
1165+ static int rtl821x_read_mmd (struct phy_device * phydev , int devnum , u16 regnum )
12311166{
1232- u32 val ;
1167+ struct mii_bus * bus = phydev -> mdio . bus ;
12331168 int addr = phydev -> mdio .addr ;
1169+ int ret ;
12341170
1235- pr_debug ("In %s, port %d, was enabled: %d\n" , __func__ , addr , e -> eee_enabled );
1236-
1237- /* Set GPHY page to copper */
1238- phy_write_paged (phydev , RTL821X_PAGE_GPHY , RTL821XEXT_MEDIA_PAGE_SELECT , RTL821X_MEDIA_PAGE_COPPER );
1239-
1240- val = phy_read_paged (phydev , 7 , MDIO_AN_EEE_ADV );
1241- if (e -> eee_enabled )
1242- e -> eee_enabled = !!(val & BIT (7 ));
1243- pr_debug ("%s: enabled: %d\n" , __func__ , e -> eee_enabled );
1244-
1245- /* GPHY page to auto */
1246- phy_write_paged (phydev , RTL821X_PAGE_GPHY , RTL821XEXT_MEDIA_PAGE_SELECT , RTL821X_MEDIA_PAGE_AUTO );
1247-
1248- return 0 ;
1249- }
1250-
1251- static int rtl8214fc_set_eee (struct phy_device * phydev , struct ethtool_keee * e )
1252- {
1253- u32 poll_state ;
1254- int port = phydev -> mdio .addr ;
1255- bool an_enabled ;
1256- u32 val ;
1257-
1258- pr_debug ("In %s port %d, enabled %d\n" , __func__ , port , e -> eee_enabled );
1259-
1260- if (rtl8214fc_media_is_fibre (phydev )) {
1261- netdev_err (phydev -> attached_dev , "Port %d configured for FIBRE" , port );
1262- return - ENOTSUPP ;
1263- }
1264-
1265- poll_state = disable_polling (port );
1266-
1267- /* Set GPHY page to copper */
1268- phy_write_paged (phydev , RTL821X_PAGE_GPHY , RTL821XINT_MEDIA_PAGE_SELECT , RTL821X_MEDIA_PAGE_COPPER );
1269-
1270- /* Get auto-negotiation status */
1271- val = phy_read (phydev , MII_BMCR );
1272- an_enabled = val & BMCR_ANENABLE ;
1273-
1274- pr_info ("%s: aneg: %d\n" , __func__ , an_enabled );
1275- val = phy_read_paged (phydev , RTL821X_PAGE_MAC , 25 );
1276- val &= ~BIT (5 ); /* Use MAC-based EEE */
1277- phy_write_paged (phydev , RTL821X_PAGE_MAC , 25 , val );
1278-
1279- /* Enable 100M (bit 1) / 1000M (bit 2) EEE */
1280- phy_write_paged (phydev , 7 , MDIO_AN_EEE_ADV , e -> eee_enabled ? (MDIO_EEE_100TX | MDIO_EEE_1000T ) : 0 );
1171+ /*
1172+ * The RTL821x PHYs are usually only C22 capable and are defined accordingly in DTS.
1173+ * Nevertheless GPL source drops clearly indicate that EEE features can be accessed
1174+ * directly via C45. Testing shows that C45 over C22 (as used in kernel EEE framework)
1175+ * works as well but only as long as PHY polling is disabled in the SOC. To avoid ugly
1176+ * hacks pass through C45 accesses for important EEE registers. Maybe some day the mdio
1177+ * bus can intercept these patterns and switch off/on polling on demand. That way this
1178+ * phy device driver can avoid handling special cases on its own.
1179+ */
12811180
1282- /* 500M EEE ability */
1283- val = phy_read_paged ( phydev , RTL821X_PAGE_GPHY , 20 );
1284- if ( e -> eee_enabled )
1285- val |= BIT ( 7 );
1181+ if (( devnum == MDIO_MMD_PCS && regnum == MDIO_PCS_EEE_ABLE ) ||
1182+ ( devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV ) ||
1183+ ( devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_LPABLE ) )
1184+ ret = __mdiobus_c45_read ( bus , addr , devnum , regnum );
12861185 else
1287- val &= ~BIT (7 );
1288-
1289- phy_write_paged (phydev , RTL821X_PAGE_GPHY , 20 , val );
1290-
1291- /* Restart AN if enabled */
1292- if (an_enabled ) {
1293- pr_info ("%s: doing aneg\n" , __func__ );
1294- val = phy_read (phydev , MII_BMCR );
1295- val |= BMCR_ANRESTART ;
1296- phy_write (phydev , MII_BMCR , val );
1297- }
1298-
1299- /* GPHY page back to auto */
1300- phy_write_paged (phydev , RTL821X_PAGE_GPHY , RTL821XINT_MEDIA_PAGE_SELECT , RTL821X_MEDIA_PAGE_AUTO );
1301-
1302- resume_polling (poll_state );
1186+ ret = - EOPNOTSUPP ;
13031187
1304- return 0 ;
1188+ return ret ;
13051189}
13061190
1307- static int rtl8214fc_get_eee (struct phy_device * phydev , struct ethtool_keee * e )
1191+ static int rtl821x_write_mmd (struct phy_device * phydev , int devnum , u16 regnum , u16 val )
13081192{
1193+ struct mii_bus * bus = phydev -> mdio .bus ;
13091194 int addr = phydev -> mdio .addr ;
1195+ int ret ;
13101196
1311- pr_debug ( "In %s port %d, enabled %d\n" , __func__ , addr , e -> eee_enabled );
1312- if (rtl8214fc_media_is_fibre ( phydev )) {
1313- netdev_err ( phydev -> attached_dev , "Port %d configured for FIBRE" , addr );
1314- return - ENOTSUPP ;
1315- }
1197+ /* see rtl821x_read_mmd() */
1198+ if (devnum == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV )
1199+ ret = __mdiobus_c45_write ( bus , addr , devnum , regnum , val );
1200+ else
1201+ ret = - EOPNOTSUPP ;
13161202
1317- return rtl8218b_get_eee ( phydev , e ) ;
1203+ return ret ;
13181204}
13191205
1320- static int rtl8218b_set_eee (struct phy_device * phydev , struct ethtool_keee * e )
1206+ static int rtl8214fc_read_mmd (struct phy_device * phydev , int devnum , u16 regnum )
13211207{
1322- int port = phydev -> mdio .addr ;
1323- u64 poll_state ;
1324- u32 val ;
1325- bool an_enabled ;
1326-
1327- pr_info ("In %s, port %d, enabled %d\n" , __func__ , port , e -> eee_enabled );
1328-
1329- poll_state = disable_polling (port );
1330-
1331- /* Set GPHY page to copper */
1332- phy_write (phydev , RTL821XEXT_MEDIA_PAGE_SELECT , RTL821X_MEDIA_PAGE_COPPER );
1333- val = phy_read (phydev , MII_BMCR );
1334- an_enabled = val & BMCR_ANENABLE ;
1335-
1336- if (e -> eee_enabled ) {
1337- /* 100/1000M EEE Capability */
1338- phy_write (phydev , 13 , 0x0007 );
1339- phy_write (phydev , 14 , 0x003C );
1340- phy_write (phydev , 13 , 0x4007 );
1341- phy_write (phydev , 14 , 0x0006 );
1342-
1343- val = phy_read_paged (phydev , RTL821X_PAGE_MAC , 25 );
1344- val |= BIT (4 );
1345- phy_write_paged (phydev , RTL821X_PAGE_MAC , 25 , val );
1346- } else {
1347- /* 100/1000M EEE Capability */
1348- phy_write (phydev , 13 , 0x0007 );
1349- phy_write (phydev , 14 , 0x003C );
1350- phy_write (phydev , 13 , 0x0007 );
1351- phy_write (phydev , 14 , 0x0000 );
1352-
1353- val = phy_read_paged (phydev , RTL821X_PAGE_MAC , 25 );
1354- val &= ~BIT (4 );
1355- phy_write_paged (phydev , RTL821X_PAGE_MAC , 25 , val );
1356- }
1357-
1358- /* Restart AN if enabled */
1359- if (an_enabled ) {
1360- val = phy_read (phydev , MII_BMCR );
1361- val |= BMCR_ANRESTART ;
1362- phy_write (phydev , MII_BMCR , val );
1363- }
1364-
1365- /* GPHY page back to auto */
1366- phy_write_paged (phydev , RTL821X_PAGE_GPHY , RTL821XEXT_MEDIA_PAGE_SELECT , RTL821X_MEDIA_PAGE_AUTO );
1367-
1368- pr_info ("%s done\n" , __func__ );
1369- resume_polling (poll_state );
1208+ if (__rtl8214fc_media_is_fibre (phydev ))
1209+ return - EOPNOTSUPP ;
13701210
1371- return 0 ;
1211+ return rtl821x_read_mmd ( phydev , devnum , regnum ) ;
13721212}
13731213
1374- static int rtl8218d_set_eee (struct phy_device * phydev , struct ethtool_keee * e )
1214+ static int rtl8214fc_write_mmd (struct phy_device * phydev , int devnum , u16 regnum , u16 val )
13751215{
1376- int addr = phydev -> mdio .addr ;
1377- u64 poll_state ;
1378-
1379- pr_info ("In %s, port %d, enabled %d\n" , __func__ , addr , e -> eee_enabled );
1380-
1381- poll_state = disable_polling (addr );
1382-
1383- rtl8218d_eee_set (phydev , (bool ) e -> eee_enabled );
1384-
1385- resume_polling (poll_state );
1216+ if (__rtl8214fc_media_is_fibre (phydev ))
1217+ return - EOPNOTSUPP ;
13861218
1387- return 0 ;
1219+ return rtl821x_write_mmd ( phydev , devnum , regnum , val ) ;
13881220}
13891221
13901222static int rtl8380_configure_rtl8214c (struct phy_device * phydev )
@@ -3884,6 +3716,15 @@ static int rtl8218d_phy_probe(struct phy_device *phydev)
38843716 return 0 ;
38853717}
38863718
3719+ static int rtl821x_config_init (struct phy_device * phydev )
3720+ {
3721+ /* Disable PHY-mode EEE so LPI is passed to the MAC */
3722+ phy_modify_paged (phydev , RTL821X_PAGE_MAC , RTL821X_PHYCR2 ,
3723+ RTL821X_PHYCR2_PHY_EEE_ENABLE , 0 );
3724+
3725+ return 0 ;
3726+ }
3727+
38873728static int rtl838x_serdes_probe (struct phy_device * phydev )
38883729{
38893730 int addr = phydev -> mdio .addr ;
@@ -3955,41 +3796,57 @@ static struct phy_driver rtl83xx_phy_driver[] = {
39553796 .match_phy_device = rtl8214fc_match_phy_device ,
39563797 .name = "Realtek RTL8214FC" ,
39573798 .config_aneg = rtl8214fc_config_aneg ,
3958- .get_eee = rtl8214fc_get_eee ,
3799+ .config_init = rtl821x_config_init ,
39593800 .get_features = rtl8214fc_get_features ,
39603801 .get_tunable = rtl8214fc_get_tunable ,
39613802 .probe = rtl8214fc_phy_probe ,
3803+ .read_mmd = rtl8214fc_read_mmd ,
39623804 .read_page = rtl821x_read_page ,
39633805 .read_status = rtl8214fc_read_status ,
39643806 .resume = rtl8214fc_resume ,
3965- .set_eee = rtl8214fc_set_eee ,
39663807 .set_tunable = rtl8214fc_set_tunable ,
39673808 .suspend = rtl8214fc_suspend ,
3809+ .write_mmd = rtl8214fc_write_mmd ,
39683810 .write_page = rtl821x_write_page ,
39693811 },
39703812 {
39713813 .match_phy_device = rtl8218b_ext_match_phy_device ,
39723814 .name = "Realtek RTL8218B (external)" ,
3815+ .config_init = rtl821x_config_init ,
39733816 .features = PHY_GBIT_FEATURES ,
39743817 .probe = rtl8218b_ext_phy_probe ,
3818+ .read_mmd = rtl821x_read_mmd ,
39753819 .read_page = rtl821x_read_page ,
3976- .write_page = rtl821x_write_page ,
3820+ .resume = genphy_resume ,
39773821 .suspend = genphy_suspend ,
3822+ .write_mmd = rtl821x_write_mmd ,
3823+ .write_page = rtl821x_write_page ,
3824+ },
3825+ {
3826+ PHY_ID_MATCH_MODEL (PHY_ID_RTL8218B_I ),
3827+ .name = "Realtek RTL8218B (internal)" ,
3828+ .config_init = rtl821x_config_init ,
3829+ .features = PHY_GBIT_FEATURES ,
3830+ .probe = rtl8218b_int_phy_probe ,
3831+ .read_mmd = rtl821x_read_mmd ,
3832+ .read_page = rtl821x_read_page ,
39783833 .resume = genphy_resume ,
3979- .set_eee = rtl8218b_set_eee ,
3980- .get_eee = rtl8218b_get_eee ,
3834+ .suspend = genphy_suspend ,
3835+ .write_mmd = rtl821x_write_mmd ,
3836+ .write_page = rtl821x_write_page ,
39813837 },
39823838 {
39833839 PHY_ID_MATCH_EXACT (PHY_ID_RTL8218D ),
39843840 .name = "REALTEK RTL8218D" ,
3841+ .config_init = rtl821x_config_init ,
39853842 .features = PHY_GBIT_FEATURES ,
39863843 .probe = rtl8218d_phy_probe ,
3844+ .read_mmd = rtl821x_read_mmd ,
39873845 .read_page = rtl821x_read_page ,
3988- .write_page = rtl821x_write_page ,
3989- .suspend = genphy_suspend ,
39903846 .resume = genphy_resume ,
3991- .set_eee = rtl8218d_set_eee ,
3992- .get_eee = rtl8218d_get_eee ,
3847+ .suspend = genphy_suspend ,
3848+ .write_mmd = rtl821x_write_mmd ,
3849+ .write_page = rtl821x_write_page ,
39933850 },
39943851 {
39953852 PHY_ID_MATCH_MODEL (PHY_ID_RTL8221B ),
@@ -4017,18 +3874,6 @@ static struct phy_driver rtl83xx_phy_driver[] = {
40173874 .set_eee = rtl8226_set_eee ,
40183875 .get_eee = rtl8226_get_eee ,
40193876 },
4020- {
4021- PHY_ID_MATCH_MODEL (PHY_ID_RTL8218B_I ),
4022- .name = "Realtek RTL8218B (internal)" ,
4023- .features = PHY_GBIT_FEATURES ,
4024- .probe = rtl8218b_int_phy_probe ,
4025- .read_page = rtl821x_read_page ,
4026- .write_page = rtl821x_write_page ,
4027- .suspend = genphy_suspend ,
4028- .resume = genphy_resume ,
4029- .set_eee = rtl8218b_set_eee ,
4030- .get_eee = rtl8218b_get_eee ,
4031- },
40323877 {
40333878 PHY_ID_MATCH_MODEL (PHY_ID_RTL8218B_I ),
40343879 .name = "Realtek RTL8380 SERDES" ,
0 commit comments