1010#include <linux/mii.h>
1111#include <linux/ethtool.h>
1212#include <linux/phy.h>
13+ #include <linux/bitfield.h>
1314
1415/* Vitesse Extended Page Magic Register(s) */
16+ #define MII_VSC73XX_EXT_PAGE_1E 0x01
1517#define MII_VSC82X4_EXT_PAGE_16E 0x10
1618#define MII_VSC82X4_EXT_PAGE_17E 0x11
1719#define MII_VSC82X4_EXT_PAGE_18E 0x12
6062/* Vitesse Extended Page Access Register */
6163#define MII_VSC82X4_EXT_PAGE_ACCESS 0x1f
6264
65+ /* Vitesse VSC73XX Extended Control Register */
66+ #define MII_VSC73XX_PHY_CTRL_EXT3 0x14
67+
68+ #define MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_EN BIT(4)
69+ #define MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_CNT GENMASK(3, 2)
70+ #define MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_STA BIT(1)
71+ #define MII_VSC73XX_DOWNSHIFT_MAX 5
72+ #define MII_VSC73XX_DOWNSHIFT_INVAL 1
73+
6374/* Vitesse VSC8601 Extended PHY Control Register 1 */
6475#define MII_VSC8601_EPHY_CTL 0x17
6576#define MII_VSC8601_EPHY_CTL_RGMII_SKEW (1 << 8)
@@ -128,6 +139,74 @@ static int vsc73xx_write_page(struct phy_device *phydev, int page)
128139 return __phy_write (phydev , VSC73XX_EXT_PAGE_ACCESS , page );
129140}
130141
142+ static int vsc73xx_get_downshift (struct phy_device * phydev , u8 * data )
143+ {
144+ int val , enable , cnt ;
145+
146+ val = phy_read_paged (phydev , MII_VSC73XX_EXT_PAGE_1E ,
147+ MII_VSC73XX_PHY_CTRL_EXT3 );
148+ if (val < 0 )
149+ return val ;
150+
151+ enable = FIELD_GET (MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_EN , val );
152+ cnt = FIELD_GET (MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_CNT , val ) + 2 ;
153+
154+ * data = enable ? cnt : DOWNSHIFT_DEV_DISABLE ;
155+
156+ return 0 ;
157+ }
158+
159+ static int vsc73xx_set_downshift (struct phy_device * phydev , u8 cnt )
160+ {
161+ u16 mask , val ;
162+ int ret ;
163+
164+ if (cnt > MII_VSC73XX_DOWNSHIFT_MAX )
165+ return - E2BIG ;
166+ else if (cnt == MII_VSC73XX_DOWNSHIFT_INVAL )
167+ return - EINVAL ;
168+
169+ mask = MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_EN ;
170+
171+ if (!cnt ) {
172+ val = 0 ;
173+ } else {
174+ mask |= MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_CNT ;
175+ val = MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_EN |
176+ FIELD_PREP (MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_CNT ,
177+ cnt - 2 );
178+ }
179+
180+ ret = phy_modify_paged (phydev , MII_VSC73XX_EXT_PAGE_1E ,
181+ MII_VSC73XX_PHY_CTRL_EXT3 , mask , val );
182+ if (ret < 0 )
183+ return ret ;
184+
185+ return genphy_soft_reset (phydev );
186+ }
187+
188+ static int vsc73xx_get_tunable (struct phy_device * phydev ,
189+ struct ethtool_tunable * tuna , void * data )
190+ {
191+ switch (tuna -> id ) {
192+ case ETHTOOL_PHY_DOWNSHIFT :
193+ return vsc73xx_get_downshift (phydev , data );
194+ default :
195+ return - EOPNOTSUPP ;
196+ }
197+ }
198+
199+ static int vsc73xx_set_tunable (struct phy_device * phydev ,
200+ struct ethtool_tunable * tuna , const void * data )
201+ {
202+ switch (tuna -> id ) {
203+ case ETHTOOL_PHY_DOWNSHIFT :
204+ return vsc73xx_set_downshift (phydev , * (const u8 * )data );
205+ default :
206+ return - EOPNOTSUPP ;
207+ }
208+ }
209+
131210static void vsc73xx_config_init (struct phy_device * phydev )
132211{
133212 /* Receiver init */
@@ -137,6 +216,9 @@ static void vsc73xx_config_init(struct phy_device *phydev)
137216
138217 /* Config LEDs 0x61 */
139218 phy_modify (phydev , MII_TPISTATUS , 0xff00 , 0x0061 );
219+
220+ /* Enable downshift by default */
221+ vsc73xx_set_downshift (phydev , MII_VSC73XX_DOWNSHIFT_MAX );
140222}
141223
142224static int vsc738x_config_init (struct phy_device * phydev )
@@ -447,6 +529,8 @@ static struct phy_driver vsc82xx_driver[] = {
447529 .config_aneg = vsc73xx_config_aneg ,
448530 .read_page = vsc73xx_read_page ,
449531 .write_page = vsc73xx_write_page ,
532+ .get_tunable = vsc73xx_get_tunable ,
533+ .set_tunable = vsc73xx_set_tunable ,
450534}, {
451535 .phy_id = PHY_ID_VSC7388 ,
452536 .name = "Vitesse VSC7388" ,
@@ -456,6 +540,8 @@ static struct phy_driver vsc82xx_driver[] = {
456540 .config_aneg = vsc73xx_config_aneg ,
457541 .read_page = vsc73xx_read_page ,
458542 .write_page = vsc73xx_write_page ,
543+ .get_tunable = vsc73xx_get_tunable ,
544+ .set_tunable = vsc73xx_set_tunable ,
459545}, {
460546 .phy_id = PHY_ID_VSC7395 ,
461547 .name = "Vitesse VSC7395" ,
@@ -465,6 +551,8 @@ static struct phy_driver vsc82xx_driver[] = {
465551 .config_aneg = vsc73xx_config_aneg ,
466552 .read_page = vsc73xx_read_page ,
467553 .write_page = vsc73xx_write_page ,
554+ .get_tunable = vsc73xx_get_tunable ,
555+ .set_tunable = vsc73xx_set_tunable ,
468556}, {
469557 .phy_id = PHY_ID_VSC7398 ,
470558 .name = "Vitesse VSC7398" ,
@@ -474,6 +562,8 @@ static struct phy_driver vsc82xx_driver[] = {
474562 .config_aneg = vsc73xx_config_aneg ,
475563 .read_page = vsc73xx_read_page ,
476564 .write_page = vsc73xx_write_page ,
565+ .get_tunable = vsc73xx_get_tunable ,
566+ .set_tunable = vsc73xx_set_tunable ,
477567}, {
478568 .phy_id = PHY_ID_VSC8662 ,
479569 .name = "Vitesse VSC8662" ,
0 commit comments