Skip to content

Commit a3783db

Browse files
Dimitri FedrauPaolo Abeni
Dimitri Fedrau
authored and
Paolo Abeni
committed
net: phy: marvell-88q2xxx: Add support for PHY LEDs on 88q2xxx
Marvell 88Q2XXX devices support up to two configurable Light Emitting Diode (LED). Add minimal LED controller driver supporting the most common uses with the 'netdev' trigger. Reviewed-by: Stefan Eichenberger <[email protected]> Reviewed-by: Andrew Lunn <[email protected]> Signed-off-by: Dimitri Fedrau <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Paolo Abeni <[email protected]>
1 parent 8dbf0c7 commit a3783db

File tree

1 file changed

+185
-4
lines changed

1 file changed

+185
-4
lines changed

drivers/net/phy/marvell-88q2xxx.c

Lines changed: 185 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
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

@@ -27,6 +28,9 @@
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
@@ -40,6 +44,22 @@
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
@@ -95,8 +115,12 @@
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+
98121
struct mv88q2xxx_priv {
99122
bool enable_temp;
123+
bool enable_led0;
100124
};
101125

102126
struct mmd_val {
@@ -460,6 +484,9 @@ static int mv88q2xxx_config_aneg(struct phy_device *phydev)
460484

461485
static 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+
743825
static 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+
9211099
static 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

Comments
 (0)