xref: /linux/drivers/net/phy/aquantia/aquantia_leds.c (revision 8faabc041a001140564f718dabe37753e88b37fa)
1 // SPDX-License-Identifier: GPL-2.0
2 /* LED driver for Aquantia PHY
3  *
4  * Author: Daniel Golle <daniel@makrotopia.org>
5  */
6 
7 #include <linux/phy.h>
8 
9 #include "aquantia.h"
10 
aqr_phy_led_brightness_set(struct phy_device * phydev,u8 index,enum led_brightness value)11 int aqr_phy_led_brightness_set(struct phy_device *phydev,
12 			       u8 index, enum led_brightness value)
13 {
14 	if (index >= AQR_MAX_LEDS)
15 		return -EINVAL;
16 
17 	return phy_modify_mmd(phydev, MDIO_MMD_VEND1, AQR_LED_PROV(index),
18 			      VEND1_GLOBAL_LED_PROV_LINK_MASK |
19 			      VEND1_GLOBAL_LED_PROV_FORCE_ON |
20 			      VEND1_GLOBAL_LED_PROV_RX_ACT |
21 			      VEND1_GLOBAL_LED_PROV_TX_ACT,
22 			      value ? VEND1_GLOBAL_LED_PROV_FORCE_ON : 0);
23 }
24 
25 static const unsigned long supported_triggers = (BIT(TRIGGER_NETDEV_LINK) |
26 						 BIT(TRIGGER_NETDEV_LINK_100) |
27 						 BIT(TRIGGER_NETDEV_LINK_1000) |
28 						 BIT(TRIGGER_NETDEV_LINK_2500) |
29 						 BIT(TRIGGER_NETDEV_LINK_5000) |
30 						 BIT(TRIGGER_NETDEV_LINK_10000)  |
31 						 BIT(TRIGGER_NETDEV_RX) |
32 						 BIT(TRIGGER_NETDEV_TX));
33 
aqr_phy_led_hw_is_supported(struct phy_device * phydev,u8 index,unsigned long rules)34 int aqr_phy_led_hw_is_supported(struct phy_device *phydev, u8 index,
35 				unsigned long rules)
36 {
37 	if (index >= AQR_MAX_LEDS)
38 		return -EINVAL;
39 
40 	/* All combinations of the supported triggers are allowed */
41 	if (rules & ~supported_triggers)
42 		return -EOPNOTSUPP;
43 
44 	return 0;
45 }
46 
aqr_phy_led_hw_control_get(struct phy_device * phydev,u8 index,unsigned long * rules)47 int aqr_phy_led_hw_control_get(struct phy_device *phydev, u8 index,
48 			       unsigned long *rules)
49 {
50 	int val;
51 
52 	if (index >= AQR_MAX_LEDS)
53 		return -EINVAL;
54 
55 	val = phy_read_mmd(phydev, MDIO_MMD_VEND1, AQR_LED_PROV(index));
56 	if (val < 0)
57 		return val;
58 
59 	*rules = 0;
60 	if (val & VEND1_GLOBAL_LED_PROV_LINK100)
61 		*rules |= BIT(TRIGGER_NETDEV_LINK_100);
62 
63 	if (val & VEND1_GLOBAL_LED_PROV_LINK1000)
64 		*rules |= BIT(TRIGGER_NETDEV_LINK_1000);
65 
66 	if (val & VEND1_GLOBAL_LED_PROV_LINK2500)
67 		*rules |= BIT(TRIGGER_NETDEV_LINK_2500);
68 
69 	if (val & VEND1_GLOBAL_LED_PROV_LINK5000)
70 		*rules |= BIT(TRIGGER_NETDEV_LINK_5000);
71 
72 	if (val & VEND1_GLOBAL_LED_PROV_LINK10000)
73 		*rules |= BIT(TRIGGER_NETDEV_LINK_10000);
74 
75 	if (val & VEND1_GLOBAL_LED_PROV_RX_ACT)
76 		*rules |= BIT(TRIGGER_NETDEV_RX);
77 
78 	if (val & VEND1_GLOBAL_LED_PROV_TX_ACT)
79 		*rules |= BIT(TRIGGER_NETDEV_TX);
80 
81 	return 0;
82 }
83 
aqr_phy_led_hw_control_set(struct phy_device * phydev,u8 index,unsigned long rules)84 int aqr_phy_led_hw_control_set(struct phy_device *phydev, u8 index,
85 			       unsigned long rules)
86 {
87 	u16 val = 0;
88 
89 	if (index >= AQR_MAX_LEDS)
90 		return -EINVAL;
91 
92 	if (rules & (BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK)))
93 		val |= VEND1_GLOBAL_LED_PROV_LINK100;
94 
95 	if (rules & (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK)))
96 		val |= VEND1_GLOBAL_LED_PROV_LINK1000;
97 
98 	if (rules & (BIT(TRIGGER_NETDEV_LINK_2500) | BIT(TRIGGER_NETDEV_LINK)))
99 		val |= VEND1_GLOBAL_LED_PROV_LINK2500;
100 
101 	if (rules & (BIT(TRIGGER_NETDEV_LINK_5000) | BIT(TRIGGER_NETDEV_LINK)))
102 		val |= VEND1_GLOBAL_LED_PROV_LINK5000;
103 
104 	if (rules & (BIT(TRIGGER_NETDEV_LINK_10000) | BIT(TRIGGER_NETDEV_LINK)))
105 		val |= VEND1_GLOBAL_LED_PROV_LINK10000;
106 
107 	if (rules & BIT(TRIGGER_NETDEV_RX))
108 		val |= VEND1_GLOBAL_LED_PROV_RX_ACT;
109 
110 	if (rules & BIT(TRIGGER_NETDEV_TX))
111 		val |= VEND1_GLOBAL_LED_PROV_TX_ACT;
112 
113 	return phy_modify_mmd(phydev, MDIO_MMD_VEND1, AQR_LED_PROV(index),
114 			      VEND1_GLOBAL_LED_PROV_LINK_MASK |
115 			      VEND1_GLOBAL_LED_PROV_FORCE_ON |
116 			      VEND1_GLOBAL_LED_PROV_RX_ACT |
117 			      VEND1_GLOBAL_LED_PROV_TX_ACT, val);
118 }
119 
aqr_phy_led_active_low_set(struct phy_device * phydev,int index,bool enable)120 int aqr_phy_led_active_low_set(struct phy_device *phydev, int index, bool enable)
121 {
122 	return phy_modify_mmd(phydev, MDIO_MMD_VEND1, AQR_LED_DRIVE(index),
123 			      VEND1_GLOBAL_LED_DRIVE_VDD,
124 			      enable ? 0 : VEND1_GLOBAL_LED_DRIVE_VDD);
125 }
126 
aqr_phy_led_polarity_set(struct phy_device * phydev,int index,unsigned long modes)127 int aqr_phy_led_polarity_set(struct phy_device *phydev, int index, unsigned long modes)
128 {
129 	bool force_active_low = false, force_active_high = false;
130 	struct aqr107_priv *priv = phydev->priv;
131 	u32 mode;
132 
133 	if (index >= AQR_MAX_LEDS)
134 		return -EINVAL;
135 
136 	for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
137 		switch (mode) {
138 		case PHY_LED_ACTIVE_LOW:
139 			force_active_low = true;
140 			break;
141 		case PHY_LED_ACTIVE_HIGH:
142 			force_active_high = true;
143 			break;
144 		default:
145 			return -EINVAL;
146 		}
147 	}
148 
149 	/* Save LED driver vdd state to restore on SW reset */
150 	if (force_active_low)
151 		priv->leds_active_low |= BIT(index);
152 
153 	if (force_active_high)
154 		priv->leds_active_high |= BIT(index);
155 
156 	if (force_active_high || force_active_low)
157 		return aqr_phy_led_active_low_set(phydev, index, force_active_low);
158 
159 	return -EINVAL;
160 }
161