88 */
99#include <linux/ethtool_netlink.h>
1010#include <linux/marvell_phy.h>
11+ #include <linux/of.h>
1112#include <linux/phy.h>
1213#include <linux/hwmon.h>
1314
2728#define MDIO_MMD_AN_MV_STAT2_100BT1 0x2000
2829#define MDIO_MMD_AN_MV_STAT2_1000BT1 0x4000
2930
31+ #define MDIO_MMD_PCS_MV_RESET_CTRL 32768
32+ #define MDIO_MMD_PCS_MV_RESET_CTRL_TX_DISABLE 0x8
33+
3034#define MDIO_MMD_PCS_MV_INT_EN 32784
3135#define MDIO_MMD_PCS_MV_INT_EN_LINK_UP 0x0040
3236#define MDIO_MMD_PCS_MV_INT_EN_LINK_DOWN 0x0080
4044#define MDIO_MMD_PCS_MV_GPIO_INT_CTRL 32787
4145#define MDIO_MMD_PCS_MV_GPIO_INT_CTRL_TRI_DIS 0x0800
4246
47+ #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL 32790
48+ #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LED_1_MASK GENMASK(7, 4)
49+ #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LED_0_MASK GENMASK(3, 0)
50+ #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LINK 0x0 /* Link established */
51+ #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LINK_RX_TX 0x1 /* Link established, blink for rx or tx activity */
52+ #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LINK_1000BT1 0x2 /* Blink 3x for 1000BT1 link established */
53+ #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL_RX_TX_ON 0x3 /* Receive or transmit activity */
54+ #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL_RX_TX 0x4 /* Blink on receive or transmit activity */
55+ #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL_TX 0x5 /* Transmit activity */
56+ #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LINK_COPPER 0x6 /* Copper Link established */
57+ #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LINK_1000BT1_ON 0x7 /* 1000BT1 link established */
58+ #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL_FORCE_OFF 0x8 /* Force off */
59+ #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL_FORCE_ON 0x9 /* Force on */
60+ #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL_FORCE_HIGHZ 0xa /* Force Hi-Z */
61+ #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL_FORCE_BLINK 0xb /* Force blink */
62+
4363#define MDIO_MMD_PCS_MV_TEMP_SENSOR1 32833
4464#define MDIO_MMD_PCS_MV_TEMP_SENSOR1_RAW_INT 0x0001
4565#define MDIO_MMD_PCS_MV_TEMP_SENSOR1_INT 0x0040
95115
96116#define MDIO_MMD_PCS_MV_TDR_OFF_CUTOFF 65246
97117
118+ #define MV88Q2XXX_LED_INDEX_TX_ENABLE 0
119+ #define MV88Q2XXX_LED_INDEX_GPIO 1
120+
98121struct mv88q2xxx_priv {
99122 bool enable_temp ;
123+ bool enable_led0 ;
100124};
101125
102126struct mmd_val {
@@ -460,6 +484,9 @@ static int mv88q2xxx_config_aneg(struct phy_device *phydev)
460484
461485static int mv88q2xxx_config_init (struct phy_device * phydev )
462486{
487+ struct mv88q2xxx_priv * priv = phydev -> priv ;
488+ int ret ;
489+
463490 /* The 88Q2XXX PHYs do have the extended ability register available, but
464491 * register MDIO_PMA_EXTABLE where they should signalize it does not
465492 * work according to specification. Therefore, we force it here.
@@ -469,10 +496,22 @@ static int mv88q2xxx_config_init(struct phy_device *phydev)
469496 /* Configure interrupt with default settings, output is driven low for
470497 * active interrupt and high for inactive.
471498 */
472- if (phy_interrupt_is_valid (phydev ))
473- return phy_set_bits_mmd (phydev , MDIO_MMD_PCS ,
474- MDIO_MMD_PCS_MV_GPIO_INT_CTRL ,
475- MDIO_MMD_PCS_MV_GPIO_INT_CTRL_TRI_DIS );
499+ if (phy_interrupt_is_valid (phydev )) {
500+ ret = phy_set_bits_mmd (phydev , MDIO_MMD_PCS ,
501+ MDIO_MMD_PCS_MV_GPIO_INT_CTRL ,
502+ MDIO_MMD_PCS_MV_GPIO_INT_CTRL_TRI_DIS );
503+ if (ret < 0 )
504+ return ret ;
505+ }
506+
507+ /* Enable LED function and disable TX disable feature on LED/TX_ENABLE */
508+ if (priv -> enable_led0 ) {
509+ ret = phy_clear_bits_mmd (phydev , MDIO_MMD_PCS ,
510+ MDIO_MMD_PCS_MV_RESET_CTRL ,
511+ MDIO_MMD_PCS_MV_RESET_CTRL_TX_DISABLE );
512+ if (ret < 0 )
513+ return ret ;
514+ }
476515
477516 return 0 ;
478517}
@@ -740,15 +779,62 @@ static int mv88q2xxx_hwmon_probe(struct phy_device *phydev)
740779}
741780#endif
742781
782+ #if IS_ENABLED (CONFIG_OF_MDIO )
783+ static int mv88q2xxx_leds_probe (struct phy_device * phydev )
784+ {
785+ struct device_node * node = phydev -> mdio .dev .of_node ;
786+ struct mv88q2xxx_priv * priv = phydev -> priv ;
787+ struct device_node * leds ;
788+ int ret = 0 ;
789+ u32 index ;
790+
791+ if (!node )
792+ return 0 ;
793+
794+ leds = of_get_child_by_name (node , "leds" );
795+ if (!leds )
796+ return 0 ;
797+
798+ for_each_available_child_of_node_scoped (leds , led ) {
799+ ret = of_property_read_u32 (led , "reg" , & index );
800+ if (ret )
801+ goto exit ;
802+
803+ if (index > MV88Q2XXX_LED_INDEX_GPIO ) {
804+ ret = - EINVAL ;
805+ goto exit ;
806+ }
807+
808+ if (index == MV88Q2XXX_LED_INDEX_TX_ENABLE )
809+ priv -> enable_led0 = true;
810+ }
811+
812+ exit :
813+ of_node_put (leds );
814+
815+ return ret ;
816+ }
817+
818+ #else
819+ static int mv88q2xxx_leds_probe (struct phy_device * phydev )
820+ {
821+ return 0 ;
822+ }
823+ #endif
824+
743825static int mv88q2xxx_probe (struct phy_device * phydev )
744826{
745827 struct mv88q2xxx_priv * priv ;
828+ int ret ;
746829
747830 priv = devm_kzalloc (& phydev -> mdio .dev , sizeof (* priv ), GFP_KERNEL );
748831 if (!priv )
749832 return - ENOMEM ;
750833
751834 phydev -> priv = priv ;
835+ ret = mv88q2xxx_leds_probe (phydev );
836+ if (ret )
837+ return ret ;
752838
753839 return mv88q2xxx_hwmon_probe (phydev );
754840}
@@ -918,6 +1004,98 @@ static int mv88q222x_cable_test_get_status(struct phy_device *phydev,
9181004 return 0 ;
9191005}
9201006
1007+ static int mv88q2xxx_led_mode (u8 index , unsigned long rules )
1008+ {
1009+ switch (rules ) {
1010+ case BIT (TRIGGER_NETDEV_LINK ):
1011+ return MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LINK ;
1012+ case BIT (TRIGGER_NETDEV_LINK_1000 ):
1013+ return MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LINK_1000BT1_ON ;
1014+ case BIT (TRIGGER_NETDEV_TX ):
1015+ return MDIO_MMD_PCS_MV_LED_FUNC_CTRL_TX ;
1016+ case BIT (TRIGGER_NETDEV_TX ) | BIT (TRIGGER_NETDEV_RX ):
1017+ return MDIO_MMD_PCS_MV_LED_FUNC_CTRL_RX_TX ;
1018+ case BIT (TRIGGER_NETDEV_LINK ) | BIT (TRIGGER_NETDEV_TX ) | BIT (TRIGGER_NETDEV_RX ):
1019+ return MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LINK_RX_TX ;
1020+ default :
1021+ return - EOPNOTSUPP ;
1022+ }
1023+ }
1024+
1025+ static int mv88q2xxx_led_hw_is_supported (struct phy_device * phydev , u8 index ,
1026+ unsigned long rules )
1027+ {
1028+ int mode ;
1029+
1030+ mode = mv88q2xxx_led_mode (index , rules );
1031+ if (mode < 0 )
1032+ return mode ;
1033+
1034+ return 0 ;
1035+ }
1036+
1037+ static int mv88q2xxx_led_hw_control_set (struct phy_device * phydev , u8 index ,
1038+ unsigned long rules )
1039+ {
1040+ int mode ;
1041+
1042+ mode = mv88q2xxx_led_mode (index , rules );
1043+ if (mode < 0 )
1044+ return mode ;
1045+
1046+ if (index == MV88Q2XXX_LED_INDEX_TX_ENABLE )
1047+ return phy_modify_mmd (phydev , MDIO_MMD_PCS ,
1048+ MDIO_MMD_PCS_MV_LED_FUNC_CTRL ,
1049+ MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LED_0_MASK ,
1050+ FIELD_PREP (MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LED_0_MASK ,
1051+ mode ));
1052+ else
1053+ return phy_modify_mmd (phydev , MDIO_MMD_PCS ,
1054+ MDIO_MMD_PCS_MV_LED_FUNC_CTRL ,
1055+ MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LED_1_MASK ,
1056+ FIELD_PREP (MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LED_1_MASK ,
1057+ mode ));
1058+ }
1059+
1060+ static int mv88q2xxx_led_hw_control_get (struct phy_device * phydev , u8 index ,
1061+ unsigned long * rules )
1062+ {
1063+ int val ;
1064+
1065+ val = phy_read_mmd (phydev , MDIO_MMD_PCS , MDIO_MMD_PCS_MV_LED_FUNC_CTRL );
1066+ if (val < 0 )
1067+ return val ;
1068+
1069+ if (index == MV88Q2XXX_LED_INDEX_TX_ENABLE )
1070+ val = FIELD_GET (MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LED_0_MASK , val );
1071+ else
1072+ val = FIELD_GET (MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LED_1_MASK , val );
1073+
1074+ switch (val ) {
1075+ case MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LINK :
1076+ * rules = BIT (TRIGGER_NETDEV_LINK );
1077+ break ;
1078+ case MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LINK_1000BT1_ON :
1079+ * rules = BIT (TRIGGER_NETDEV_LINK_1000 );
1080+ break ;
1081+ case MDIO_MMD_PCS_MV_LED_FUNC_CTRL_TX :
1082+ * rules = BIT (TRIGGER_NETDEV_TX );
1083+ break ;
1084+ case MDIO_MMD_PCS_MV_LED_FUNC_CTRL_RX_TX :
1085+ * rules = BIT (TRIGGER_NETDEV_TX ) | BIT (TRIGGER_NETDEV_RX );
1086+ break ;
1087+ case MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LINK_RX_TX :
1088+ * rules = BIT (TRIGGER_NETDEV_LINK ) | BIT (TRIGGER_NETDEV_TX ) |
1089+ BIT (TRIGGER_NETDEV_RX );
1090+ break ;
1091+ default :
1092+ * rules = 0 ;
1093+ break ;
1094+ }
1095+
1096+ return 0 ;
1097+ }
1098+
9211099static struct phy_driver mv88q2xxx_driver [] = {
9221100 {
9231101 .phy_id = MARVELL_PHY_ID_88Q2110 ,
@@ -953,6 +1131,9 @@ static struct phy_driver mv88q2xxx_driver[] = {
9531131 .get_sqi_max = mv88q2xxx_get_sqi_max ,
9541132 .suspend = mv88q2xxx_suspend ,
9551133 .resume = mv88q2xxx_resume ,
1134+ .led_hw_is_supported = mv88q2xxx_led_hw_is_supported ,
1135+ .led_hw_control_set = mv88q2xxx_led_hw_control_set ,
1136+ .led_hw_control_get = mv88q2xxx_led_hw_control_get ,
9561137 },
9571138};
9581139
0 commit comments