xref: /linux/drivers/net/dsa/mv88e6xxx/leds.c (revision 25768de50b1f2dbb6ea44bd5148a87fe2c9c3688)
1*94a2a84fSLinus Walleij // SPDX-License-Identifier: GPL-2.0-or-later
2*94a2a84fSLinus Walleij #include <linux/bitfield.h>
3*94a2a84fSLinus Walleij #include <linux/leds.h>
4*94a2a84fSLinus Walleij #include <linux/property.h>
5*94a2a84fSLinus Walleij 
6*94a2a84fSLinus Walleij #include "chip.h"
7*94a2a84fSLinus Walleij #include "global2.h"
8*94a2a84fSLinus Walleij #include "port.h"
9*94a2a84fSLinus Walleij 
10*94a2a84fSLinus Walleij /* Offset 0x16: LED control */
11*94a2a84fSLinus Walleij 
12*94a2a84fSLinus Walleij static int mv88e6xxx_port_led_write(struct mv88e6xxx_chip *chip, int port, u16 reg)
13*94a2a84fSLinus Walleij {
14*94a2a84fSLinus Walleij 	reg |= MV88E6XXX_PORT_LED_CONTROL_UPDATE;
15*94a2a84fSLinus Walleij 
16*94a2a84fSLinus Walleij 	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_LED_CONTROL, reg);
17*94a2a84fSLinus Walleij }
18*94a2a84fSLinus Walleij 
19*94a2a84fSLinus Walleij static int mv88e6xxx_port_led_read(struct mv88e6xxx_chip *chip, int port,
20*94a2a84fSLinus Walleij 				   u16 ptr, u16 *val)
21*94a2a84fSLinus Walleij {
22*94a2a84fSLinus Walleij 	int err;
23*94a2a84fSLinus Walleij 
24*94a2a84fSLinus Walleij 	err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_LED_CONTROL, ptr);
25*94a2a84fSLinus Walleij 	if (err)
26*94a2a84fSLinus Walleij 		return err;
27*94a2a84fSLinus Walleij 
28*94a2a84fSLinus Walleij 	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_LED_CONTROL, val);
29*94a2a84fSLinus Walleij 	*val &= 0x3ff;
30*94a2a84fSLinus Walleij 
31*94a2a84fSLinus Walleij 	return err;
32*94a2a84fSLinus Walleij }
33*94a2a84fSLinus Walleij 
34*94a2a84fSLinus Walleij static int mv88e6xxx_led_brightness_set(struct mv88e6xxx_port *p, int led,
35*94a2a84fSLinus Walleij 					int brightness)
36*94a2a84fSLinus Walleij {
37*94a2a84fSLinus Walleij 	u16 reg;
38*94a2a84fSLinus Walleij 	int err;
39*94a2a84fSLinus Walleij 
40*94a2a84fSLinus Walleij 	err = mv88e6xxx_port_led_read(p->chip, p->port,
41*94a2a84fSLinus Walleij 				      MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL,
42*94a2a84fSLinus Walleij 				      &reg);
43*94a2a84fSLinus Walleij 	if (err)
44*94a2a84fSLinus Walleij 		return err;
45*94a2a84fSLinus Walleij 
46*94a2a84fSLinus Walleij 	if (led == 1)
47*94a2a84fSLinus Walleij 		reg &= ~MV88E6XXX_PORT_LED_CONTROL_LED1_SEL_MASK;
48*94a2a84fSLinus Walleij 	else
49*94a2a84fSLinus Walleij 		reg &= ~MV88E6XXX_PORT_LED_CONTROL_LED0_SEL_MASK;
50*94a2a84fSLinus Walleij 
51*94a2a84fSLinus Walleij 	if (brightness) {
52*94a2a84fSLinus Walleij 		/* Selector 0x0f == Force LED ON */
53*94a2a84fSLinus Walleij 		if (led == 1)
54*94a2a84fSLinus Walleij 			reg |= MV88E6XXX_PORT_LED_CONTROL_LED1_SELF;
55*94a2a84fSLinus Walleij 		else
56*94a2a84fSLinus Walleij 			reg |= MV88E6XXX_PORT_LED_CONTROL_LED0_SELF;
57*94a2a84fSLinus Walleij 	} else {
58*94a2a84fSLinus Walleij 		/* Selector 0x0e == Force LED OFF */
59*94a2a84fSLinus Walleij 		if (led == 1)
60*94a2a84fSLinus Walleij 			reg |= MV88E6XXX_PORT_LED_CONTROL_LED1_SELE;
61*94a2a84fSLinus Walleij 		else
62*94a2a84fSLinus Walleij 			reg |= MV88E6XXX_PORT_LED_CONTROL_LED0_SELE;
63*94a2a84fSLinus Walleij 	}
64*94a2a84fSLinus Walleij 
65*94a2a84fSLinus Walleij 	reg |= MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL;
66*94a2a84fSLinus Walleij 
67*94a2a84fSLinus Walleij 	return mv88e6xxx_port_led_write(p->chip, p->port, reg);
68*94a2a84fSLinus Walleij }
69*94a2a84fSLinus Walleij 
70*94a2a84fSLinus Walleij static int mv88e6xxx_led0_brightness_set_blocking(struct led_classdev *ldev,
71*94a2a84fSLinus Walleij 						  enum led_brightness brightness)
72*94a2a84fSLinus Walleij {
73*94a2a84fSLinus Walleij 	struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led0);
74*94a2a84fSLinus Walleij 	int err;
75*94a2a84fSLinus Walleij 
76*94a2a84fSLinus Walleij 	mv88e6xxx_reg_lock(p->chip);
77*94a2a84fSLinus Walleij 	err = mv88e6xxx_led_brightness_set(p, 0, brightness);
78*94a2a84fSLinus Walleij 	mv88e6xxx_reg_unlock(p->chip);
79*94a2a84fSLinus Walleij 
80*94a2a84fSLinus Walleij 	return err;
81*94a2a84fSLinus Walleij }
82*94a2a84fSLinus Walleij 
83*94a2a84fSLinus Walleij static int mv88e6xxx_led1_brightness_set_blocking(struct led_classdev *ldev,
84*94a2a84fSLinus Walleij 						  enum led_brightness brightness)
85*94a2a84fSLinus Walleij {
86*94a2a84fSLinus Walleij 	struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led1);
87*94a2a84fSLinus Walleij 	int err;
88*94a2a84fSLinus Walleij 
89*94a2a84fSLinus Walleij 	mv88e6xxx_reg_lock(p->chip);
90*94a2a84fSLinus Walleij 	err = mv88e6xxx_led_brightness_set(p, 1, brightness);
91*94a2a84fSLinus Walleij 	mv88e6xxx_reg_unlock(p->chip);
92*94a2a84fSLinus Walleij 
93*94a2a84fSLinus Walleij 	return err;
94*94a2a84fSLinus Walleij }
95*94a2a84fSLinus Walleij 
96*94a2a84fSLinus Walleij struct mv88e6xxx_led_hwconfig {
97*94a2a84fSLinus Walleij 	int led;
98*94a2a84fSLinus Walleij 	u8 portmask;
99*94a2a84fSLinus Walleij 	unsigned long rules;
100*94a2a84fSLinus Walleij 	bool fiber;
101*94a2a84fSLinus Walleij 	bool blink_activity;
102*94a2a84fSLinus Walleij 	u16 selector;
103*94a2a84fSLinus Walleij };
104*94a2a84fSLinus Walleij 
105*94a2a84fSLinus Walleij /* The following is a lookup table to check what rules we can support on a
106*94a2a84fSLinus Walleij  * certain LED given restrictions such as that some rules only work with fiber
107*94a2a84fSLinus Walleij  * (SFP) connections and some blink on activity by default.
108*94a2a84fSLinus Walleij  */
109*94a2a84fSLinus Walleij #define MV88E6XXX_PORTS_0_3 (BIT(0) | BIT(1) | BIT(2) | BIT(3))
110*94a2a84fSLinus Walleij #define MV88E6XXX_PORTS_4_5 (BIT(4) | BIT(5))
111*94a2a84fSLinus Walleij #define MV88E6XXX_PORT_4 BIT(4)
112*94a2a84fSLinus Walleij #define MV88E6XXX_PORT_5 BIT(5)
113*94a2a84fSLinus Walleij 
114*94a2a84fSLinus Walleij /* Entries are listed in selector order.
115*94a2a84fSLinus Walleij  *
116*94a2a84fSLinus Walleij  * These configurations vary across different switch families, list
117*94a2a84fSLinus Walleij  * different tables per-family here.
118*94a2a84fSLinus Walleij  */
119*94a2a84fSLinus Walleij static const struct mv88e6xxx_led_hwconfig mv88e6352_led_hwconfigs[] = {
120*94a2a84fSLinus Walleij 	{
121*94a2a84fSLinus Walleij 		.led = 0,
122*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORT_4,
123*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_LINK),
124*94a2a84fSLinus Walleij 		.blink_activity = true,
125*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL0,
126*94a2a84fSLinus Walleij 	},
127*94a2a84fSLinus Walleij 	{
128*94a2a84fSLinus Walleij 		.led = 1,
129*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORT_5,
130*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_LINK_1000),
131*94a2a84fSLinus Walleij 		.blink_activity = true,
132*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL0,
133*94a2a84fSLinus Walleij 	},
134*94a2a84fSLinus Walleij 	{
135*94a2a84fSLinus Walleij 		.led = 0,
136*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORTS_0_3,
137*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK_1000),
138*94a2a84fSLinus Walleij 		.blink_activity = true,
139*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL1,
140*94a2a84fSLinus Walleij 	},
141*94a2a84fSLinus Walleij 	{
142*94a2a84fSLinus Walleij 		.led = 1,
143*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORTS_0_3,
144*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK_100),
145*94a2a84fSLinus Walleij 		.blink_activity = true,
146*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL1,
147*94a2a84fSLinus Walleij 	},
148*94a2a84fSLinus Walleij 	{
149*94a2a84fSLinus Walleij 		.led = 0,
150*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORTS_4_5,
151*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_LINK_100),
152*94a2a84fSLinus Walleij 		.blink_activity = true,
153*94a2a84fSLinus Walleij 		.fiber = true,
154*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL1,
155*94a2a84fSLinus Walleij 	},
156*94a2a84fSLinus Walleij 	{
157*94a2a84fSLinus Walleij 		.led = 1,
158*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORTS_4_5,
159*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_LINK_1000),
160*94a2a84fSLinus Walleij 		.blink_activity = true,
161*94a2a84fSLinus Walleij 		.fiber = true,
162*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL1,
163*94a2a84fSLinus Walleij 	},
164*94a2a84fSLinus Walleij 	{
165*94a2a84fSLinus Walleij 		.led = 0,
166*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORTS_0_3,
167*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_LINK_1000),
168*94a2a84fSLinus Walleij 		.blink_activity = true,
169*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL2,
170*94a2a84fSLinus Walleij 	},
171*94a2a84fSLinus Walleij 	{
172*94a2a84fSLinus Walleij 		.led = 1,
173*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORTS_0_3,
174*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK_100),
175*94a2a84fSLinus Walleij 		.blink_activity = true,
176*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL2,
177*94a2a84fSLinus Walleij 	},
178*94a2a84fSLinus Walleij 	{
179*94a2a84fSLinus Walleij 		.led = 0,
180*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORTS_4_5,
181*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_LINK_1000),
182*94a2a84fSLinus Walleij 		.blink_activity = true,
183*94a2a84fSLinus Walleij 		.fiber = true,
184*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL2,
185*94a2a84fSLinus Walleij 	},
186*94a2a84fSLinus Walleij 	{
187*94a2a84fSLinus Walleij 		.led = 1,
188*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORTS_4_5,
189*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_LINK_100),
190*94a2a84fSLinus Walleij 		.blink_activity = true,
191*94a2a84fSLinus Walleij 		.fiber = true,
192*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL2,
193*94a2a84fSLinus Walleij 	},
194*94a2a84fSLinus Walleij 	{
195*94a2a84fSLinus Walleij 		.led = 0,
196*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORTS_0_3,
197*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_LINK),
198*94a2a84fSLinus Walleij 		.blink_activity = true,
199*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL3,
200*94a2a84fSLinus Walleij 	},
201*94a2a84fSLinus Walleij 	{
202*94a2a84fSLinus Walleij 		.led = 1,
203*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORTS_0_3,
204*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_LINK_1000),
205*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL3,
206*94a2a84fSLinus Walleij 	},
207*94a2a84fSLinus Walleij 	{
208*94a2a84fSLinus Walleij 		.led = 1,
209*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORTS_4_5,
210*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_LINK),
211*94a2a84fSLinus Walleij 		.fiber = true,
212*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL3,
213*94a2a84fSLinus Walleij 	},
214*94a2a84fSLinus Walleij 	{
215*94a2a84fSLinus Walleij 		.led = 1,
216*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORT_4,
217*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_LINK),
218*94a2a84fSLinus Walleij 		.blink_activity = true,
219*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL4,
220*94a2a84fSLinus Walleij 	},
221*94a2a84fSLinus Walleij 	{
222*94a2a84fSLinus Walleij 		.led = 1,
223*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORT_5,
224*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_LINK),
225*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL5,
226*94a2a84fSLinus Walleij 	},
227*94a2a84fSLinus Walleij 	{
228*94a2a84fSLinus Walleij 		.led = 0,
229*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORTS_0_3,
230*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_FULL_DUPLEX),
231*94a2a84fSLinus Walleij 		.blink_activity = true,
232*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL6,
233*94a2a84fSLinus Walleij 	},
234*94a2a84fSLinus Walleij 	{
235*94a2a84fSLinus Walleij 		.led = 1,
236*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORTS_0_3,
237*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK_1000),
238*94a2a84fSLinus Walleij 		.blink_activity = true,
239*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL6,
240*94a2a84fSLinus Walleij 	},
241*94a2a84fSLinus Walleij 	{
242*94a2a84fSLinus Walleij 		.led = 0,
243*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORT_4,
244*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_FULL_DUPLEX),
245*94a2a84fSLinus Walleij 		.blink_activity = true,
246*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL6,
247*94a2a84fSLinus Walleij 	},
248*94a2a84fSLinus Walleij 	{
249*94a2a84fSLinus Walleij 		.led = 1,
250*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORT_5,
251*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_FULL_DUPLEX),
252*94a2a84fSLinus Walleij 		.blink_activity = true,
253*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL6,
254*94a2a84fSLinus Walleij 	},
255*94a2a84fSLinus Walleij 	{
256*94a2a84fSLinus Walleij 		.led = 0,
257*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORTS_0_3,
258*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK_1000),
259*94a2a84fSLinus Walleij 		.blink_activity = true,
260*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL7,
261*94a2a84fSLinus Walleij 	},
262*94a2a84fSLinus Walleij 	{
263*94a2a84fSLinus Walleij 		.led = 1,
264*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORTS_0_3,
265*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK_1000),
266*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL7,
267*94a2a84fSLinus Walleij 	},
268*94a2a84fSLinus Walleij 	{
269*94a2a84fSLinus Walleij 		.led = 0,
270*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORTS_0_3,
271*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_LINK),
272*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL8,
273*94a2a84fSLinus Walleij 	},
274*94a2a84fSLinus Walleij 	{
275*94a2a84fSLinus Walleij 		.led = 1,
276*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORTS_0_3,
277*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_LINK),
278*94a2a84fSLinus Walleij 		.blink_activity = true,
279*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL8,
280*94a2a84fSLinus Walleij 	},
281*94a2a84fSLinus Walleij 	{
282*94a2a84fSLinus Walleij 		.led = 0,
283*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORT_5,
284*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_LINK),
285*94a2a84fSLinus Walleij 		.blink_activity = true,
286*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL8,
287*94a2a84fSLinus Walleij 	},
288*94a2a84fSLinus Walleij 	{
289*94a2a84fSLinus Walleij 		.led = 0,
290*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORTS_0_3,
291*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_LINK_10),
292*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL9,
293*94a2a84fSLinus Walleij 	},
294*94a2a84fSLinus Walleij 	{
295*94a2a84fSLinus Walleij 		.led = 1,
296*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORTS_0_3,
297*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_LINK_100),
298*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL9,
299*94a2a84fSLinus Walleij 	},
300*94a2a84fSLinus Walleij 	{
301*94a2a84fSLinus Walleij 		.led = 0,
302*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORTS_0_3,
303*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_LINK_10),
304*94a2a84fSLinus Walleij 		.blink_activity = true,
305*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SELA,
306*94a2a84fSLinus Walleij 	},
307*94a2a84fSLinus Walleij 	{
308*94a2a84fSLinus Walleij 		.led = 1,
309*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORTS_0_3,
310*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_LINK_100),
311*94a2a84fSLinus Walleij 		.blink_activity = true,
312*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SELA,
313*94a2a84fSLinus Walleij 	},
314*94a2a84fSLinus Walleij 	{
315*94a2a84fSLinus Walleij 		.led = 0,
316*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORTS_0_3,
317*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK_1000),
318*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SELB,
319*94a2a84fSLinus Walleij 	},
320*94a2a84fSLinus Walleij 	{
321*94a2a84fSLinus Walleij 		.led = 1,
322*94a2a84fSLinus Walleij 		.portmask = MV88E6XXX_PORTS_0_3,
323*94a2a84fSLinus Walleij 		.rules = BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK_1000),
324*94a2a84fSLinus Walleij 		.blink_activity = true,
325*94a2a84fSLinus Walleij 		.selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SELB,
326*94a2a84fSLinus Walleij 	},
327*94a2a84fSLinus Walleij };
328*94a2a84fSLinus Walleij 
329*94a2a84fSLinus Walleij /* mv88e6xxx_led_match_selector() - look up the appropriate LED mode selector
330*94a2a84fSLinus Walleij  * @p: port state container
331*94a2a84fSLinus Walleij  * @led: LED number, 0 or 1
332*94a2a84fSLinus Walleij  * @blink_activity: blink the LED (usually blink on indicated activity)
333*94a2a84fSLinus Walleij  * @fiber: the link is connected to fiber such as SFP
334*94a2a84fSLinus Walleij  * @rules: LED status flags from the LED classdev core
335*94a2a84fSLinus Walleij  * @selector: fill in the selector in this parameter with an OR operation
336*94a2a84fSLinus Walleij  */
337*94a2a84fSLinus Walleij static int mv88e6xxx_led_match_selector(struct mv88e6xxx_port *p, int led, bool blink_activity,
338*94a2a84fSLinus Walleij 					bool fiber, unsigned long rules, u16 *selector)
339*94a2a84fSLinus Walleij {
340*94a2a84fSLinus Walleij 	const struct mv88e6xxx_led_hwconfig *conf;
341*94a2a84fSLinus Walleij 	int i;
342*94a2a84fSLinus Walleij 
343*94a2a84fSLinus Walleij 	/* No rules means we turn the LED off */
344*94a2a84fSLinus Walleij 	if (!rules) {
345*94a2a84fSLinus Walleij 		if (led == 1)
346*94a2a84fSLinus Walleij 			*selector |= MV88E6XXX_PORT_LED_CONTROL_LED1_SELE;
347*94a2a84fSLinus Walleij 		else
348*94a2a84fSLinus Walleij 			*selector |= MV88E6XXX_PORT_LED_CONTROL_LED0_SELE;
349*94a2a84fSLinus Walleij 		return 0;
350*94a2a84fSLinus Walleij 	}
351*94a2a84fSLinus Walleij 
352*94a2a84fSLinus Walleij 	/* TODO: these rules are for MV88E6352, when adding other families,
353*94a2a84fSLinus Walleij 	 * think about making sure you select the table that match the
354*94a2a84fSLinus Walleij 	 * specific switch family.
355*94a2a84fSLinus Walleij 	 */
356*94a2a84fSLinus Walleij 	for (i = 0; i < ARRAY_SIZE(mv88e6352_led_hwconfigs); i++) {
357*94a2a84fSLinus Walleij 		conf = &mv88e6352_led_hwconfigs[i];
358*94a2a84fSLinus Walleij 
359*94a2a84fSLinus Walleij 		if (conf->led != led)
360*94a2a84fSLinus Walleij 			continue;
361*94a2a84fSLinus Walleij 
362*94a2a84fSLinus Walleij 		if (!(conf->portmask & BIT(p->port)))
363*94a2a84fSLinus Walleij 			continue;
364*94a2a84fSLinus Walleij 
365*94a2a84fSLinus Walleij 		if (conf->blink_activity != blink_activity)
366*94a2a84fSLinus Walleij 			continue;
367*94a2a84fSLinus Walleij 
368*94a2a84fSLinus Walleij 		if (conf->fiber != fiber)
369*94a2a84fSLinus Walleij 			continue;
370*94a2a84fSLinus Walleij 
371*94a2a84fSLinus Walleij 		if (conf->rules == rules) {
372*94a2a84fSLinus Walleij 			dev_dbg(p->chip->dev, "port%d LED %d set selector %04x for rules %08lx\n",
373*94a2a84fSLinus Walleij 				p->port, led, conf->selector, rules);
374*94a2a84fSLinus Walleij 			*selector |= conf->selector;
375*94a2a84fSLinus Walleij 			return 0;
376*94a2a84fSLinus Walleij 		}
377*94a2a84fSLinus Walleij 	}
378*94a2a84fSLinus Walleij 
379*94a2a84fSLinus Walleij 	return -EOPNOTSUPP;
380*94a2a84fSLinus Walleij }
381*94a2a84fSLinus Walleij 
382*94a2a84fSLinus Walleij /* mv88e6xxx_led_match_selector() - find Linux netdev rules from a selector value
383*94a2a84fSLinus Walleij  * @p: port state container
384*94a2a84fSLinus Walleij  * @selector: the selector value from the LED actity register
385*94a2a84fSLinus Walleij  * @led: LED number, 0 or 1
386*94a2a84fSLinus Walleij  * @rules: Linux netdev activity rules found from selector
387*94a2a84fSLinus Walleij  */
388*94a2a84fSLinus Walleij static int
389*94a2a84fSLinus Walleij mv88e6xxx_led_match_rule(struct mv88e6xxx_port *p, u16 selector, int led, unsigned long *rules)
390*94a2a84fSLinus Walleij {
391*94a2a84fSLinus Walleij 	const struct mv88e6xxx_led_hwconfig *conf;
392*94a2a84fSLinus Walleij 	int i;
393*94a2a84fSLinus Walleij 
394*94a2a84fSLinus Walleij 	/* Find the selector in the table, we just look for the right selector
395*94a2a84fSLinus Walleij 	 * and ignore if the activity has special properties such as blinking
396*94a2a84fSLinus Walleij 	 * or is fiber-only.
397*94a2a84fSLinus Walleij 	 */
398*94a2a84fSLinus Walleij 	for (i = 0; i < ARRAY_SIZE(mv88e6352_led_hwconfigs); i++) {
399*94a2a84fSLinus Walleij 		conf = &mv88e6352_led_hwconfigs[i];
400*94a2a84fSLinus Walleij 
401*94a2a84fSLinus Walleij 		if (conf->led != led)
402*94a2a84fSLinus Walleij 			continue;
403*94a2a84fSLinus Walleij 
404*94a2a84fSLinus Walleij 		if (!(conf->portmask & BIT(p->port)))
405*94a2a84fSLinus Walleij 			continue;
406*94a2a84fSLinus Walleij 
407*94a2a84fSLinus Walleij 		if (conf->selector == selector) {
408*94a2a84fSLinus Walleij 			dev_dbg(p->chip->dev, "port%d LED %d has selector %04x, rules %08lx\n",
409*94a2a84fSLinus Walleij 				p->port, led, selector, conf->rules);
410*94a2a84fSLinus Walleij 			*rules = conf->rules;
411*94a2a84fSLinus Walleij 			return 0;
412*94a2a84fSLinus Walleij 		}
413*94a2a84fSLinus Walleij 	}
414*94a2a84fSLinus Walleij 
415*94a2a84fSLinus Walleij 	return -EINVAL;
416*94a2a84fSLinus Walleij }
417*94a2a84fSLinus Walleij 
418*94a2a84fSLinus Walleij /* mv88e6xxx_led_get_selector() - get the appropriate LED mode selector
419*94a2a84fSLinus Walleij  * @p: port state container
420*94a2a84fSLinus Walleij  * @led: LED number, 0 or 1
421*94a2a84fSLinus Walleij  * @fiber: the link is connected to fiber such as SFP
422*94a2a84fSLinus Walleij  * @rules: LED status flags from the LED classdev core
423*94a2a84fSLinus Walleij  * @selector: fill in the selector in this parameter with an OR operation
424*94a2a84fSLinus Walleij  */
425*94a2a84fSLinus Walleij static int mv88e6xxx_led_get_selector(struct mv88e6xxx_port *p, int led,
426*94a2a84fSLinus Walleij 				      bool fiber, unsigned long rules, u16 *selector)
427*94a2a84fSLinus Walleij {
428*94a2a84fSLinus Walleij 	int err;
429*94a2a84fSLinus Walleij 
430*94a2a84fSLinus Walleij 	/* What happens here is that we first try to locate a trigger with solid
431*94a2a84fSLinus Walleij 	 * indicator (such as LED is on for a 1000 link) else we try a second
432*94a2a84fSLinus Walleij 	 * sweep to find something suitable with a trigger that will blink on
433*94a2a84fSLinus Walleij 	 * activity.
434*94a2a84fSLinus Walleij 	 */
435*94a2a84fSLinus Walleij 	err = mv88e6xxx_led_match_selector(p, led, false, fiber, rules, selector);
436*94a2a84fSLinus Walleij 	if (err)
437*94a2a84fSLinus Walleij 		return mv88e6xxx_led_match_selector(p, led, true, fiber, rules, selector);
438*94a2a84fSLinus Walleij 
439*94a2a84fSLinus Walleij 	return 0;
440*94a2a84fSLinus Walleij }
441*94a2a84fSLinus Walleij 
442*94a2a84fSLinus Walleij /* Sets up the hardware blinking period */
443*94a2a84fSLinus Walleij static int mv88e6xxx_led_set_blinking_period(struct mv88e6xxx_port *p, int led,
444*94a2a84fSLinus Walleij 					     unsigned long delay_on, unsigned long delay_off)
445*94a2a84fSLinus Walleij {
446*94a2a84fSLinus Walleij 	unsigned long period;
447*94a2a84fSLinus Walleij 	u16 reg;
448*94a2a84fSLinus Walleij 
449*94a2a84fSLinus Walleij 	period = delay_on + delay_off;
450*94a2a84fSLinus Walleij 
451*94a2a84fSLinus Walleij 	reg = 0;
452*94a2a84fSLinus Walleij 
453*94a2a84fSLinus Walleij 	switch (period) {
454*94a2a84fSLinus Walleij 	case 21:
455*94a2a84fSLinus Walleij 		reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_21MS;
456*94a2a84fSLinus Walleij 		break;
457*94a2a84fSLinus Walleij 	case 42:
458*94a2a84fSLinus Walleij 		reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_42MS;
459*94a2a84fSLinus Walleij 		break;
460*94a2a84fSLinus Walleij 	case 84:
461*94a2a84fSLinus Walleij 		reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_84MS;
462*94a2a84fSLinus Walleij 		break;
463*94a2a84fSLinus Walleij 	case 168:
464*94a2a84fSLinus Walleij 		reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_168MS;
465*94a2a84fSLinus Walleij 		break;
466*94a2a84fSLinus Walleij 	case 336:
467*94a2a84fSLinus Walleij 		reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_336MS;
468*94a2a84fSLinus Walleij 		break;
469*94a2a84fSLinus Walleij 	case 672:
470*94a2a84fSLinus Walleij 		reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_672MS;
471*94a2a84fSLinus Walleij 		break;
472*94a2a84fSLinus Walleij 	default:
473*94a2a84fSLinus Walleij 		/* Fall back to software blinking */
474*94a2a84fSLinus Walleij 		return -EINVAL;
475*94a2a84fSLinus Walleij 	}
476*94a2a84fSLinus Walleij 
477*94a2a84fSLinus Walleij 	/* This is essentially PWM duty cycle: how long time of the period
478*94a2a84fSLinus Walleij 	 * will the LED be on. Zero isn't great in most cases.
479*94a2a84fSLinus Walleij 	 */
480*94a2a84fSLinus Walleij 	switch (delay_on) {
481*94a2a84fSLinus Walleij 	case 0:
482*94a2a84fSLinus Walleij 		/* This is usually pretty useless and will make the LED look OFF */
483*94a2a84fSLinus Walleij 		reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_NONE;
484*94a2a84fSLinus Walleij 		break;
485*94a2a84fSLinus Walleij 	case 21:
486*94a2a84fSLinus Walleij 		reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_21MS;
487*94a2a84fSLinus Walleij 		break;
488*94a2a84fSLinus Walleij 	case 42:
489*94a2a84fSLinus Walleij 		reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_42MS;
490*94a2a84fSLinus Walleij 		break;
491*94a2a84fSLinus Walleij 	case 84:
492*94a2a84fSLinus Walleij 		reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_84MS;
493*94a2a84fSLinus Walleij 		break;
494*94a2a84fSLinus Walleij 	case 168:
495*94a2a84fSLinus Walleij 		reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_168MS;
496*94a2a84fSLinus Walleij 		break;
497*94a2a84fSLinus Walleij 	default:
498*94a2a84fSLinus Walleij 		/* Just use something non-zero */
499*94a2a84fSLinus Walleij 		reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_21MS;
500*94a2a84fSLinus Walleij 		break;
501*94a2a84fSLinus Walleij 	}
502*94a2a84fSLinus Walleij 
503*94a2a84fSLinus Walleij 	/* Set up blink rate */
504*94a2a84fSLinus Walleij 	reg |= MV88E6XXX_PORT_LED_CONTROL_POINTER_STRETCH_BLINK;
505*94a2a84fSLinus Walleij 
506*94a2a84fSLinus Walleij 	return mv88e6xxx_port_led_write(p->chip, p->port, reg);
507*94a2a84fSLinus Walleij }
508*94a2a84fSLinus Walleij 
509*94a2a84fSLinus Walleij static int mv88e6xxx_led_blink_set(struct mv88e6xxx_port *p, int led,
510*94a2a84fSLinus Walleij 				   unsigned long *delay_on, unsigned long *delay_off)
511*94a2a84fSLinus Walleij {
512*94a2a84fSLinus Walleij 	u16 reg;
513*94a2a84fSLinus Walleij 	int err;
514*94a2a84fSLinus Walleij 
515*94a2a84fSLinus Walleij 	/* Choose a sensible default 336 ms (~3 Hz) */
516*94a2a84fSLinus Walleij 	if ((*delay_on == 0) && (*delay_off == 0)) {
517*94a2a84fSLinus Walleij 		*delay_on = 168;
518*94a2a84fSLinus Walleij 		*delay_off = 168;
519*94a2a84fSLinus Walleij 	}
520*94a2a84fSLinus Walleij 
521*94a2a84fSLinus Walleij 	/* No off delay is just on */
522*94a2a84fSLinus Walleij 	if (*delay_off == 0)
523*94a2a84fSLinus Walleij 		return mv88e6xxx_led_brightness_set(p, led, 1);
524*94a2a84fSLinus Walleij 
525*94a2a84fSLinus Walleij 	err = mv88e6xxx_led_set_blinking_period(p, led, *delay_on, *delay_off);
526*94a2a84fSLinus Walleij 	if (err)
527*94a2a84fSLinus Walleij 		return err;
528*94a2a84fSLinus Walleij 
529*94a2a84fSLinus Walleij 	err = mv88e6xxx_port_led_read(p->chip, p->port,
530*94a2a84fSLinus Walleij 				      MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL,
531*94a2a84fSLinus Walleij 				      &reg);
532*94a2a84fSLinus Walleij 	if (err)
533*94a2a84fSLinus Walleij 		return err;
534*94a2a84fSLinus Walleij 
535*94a2a84fSLinus Walleij 	if (led == 1)
536*94a2a84fSLinus Walleij 		reg &= ~MV88E6XXX_PORT_LED_CONTROL_LED1_SEL_MASK;
537*94a2a84fSLinus Walleij 	else
538*94a2a84fSLinus Walleij 		reg &= ~MV88E6XXX_PORT_LED_CONTROL_LED0_SEL_MASK;
539*94a2a84fSLinus Walleij 
540*94a2a84fSLinus Walleij 	/* This will select the forced blinking status */
541*94a2a84fSLinus Walleij 	if (led == 1)
542*94a2a84fSLinus Walleij 		reg |= MV88E6XXX_PORT_LED_CONTROL_LED1_SELD;
543*94a2a84fSLinus Walleij 	else
544*94a2a84fSLinus Walleij 		reg |= MV88E6XXX_PORT_LED_CONTROL_LED0_SELD;
545*94a2a84fSLinus Walleij 
546*94a2a84fSLinus Walleij 	reg |= MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL;
547*94a2a84fSLinus Walleij 
548*94a2a84fSLinus Walleij 	return mv88e6xxx_port_led_write(p->chip, p->port, reg);
549*94a2a84fSLinus Walleij }
550*94a2a84fSLinus Walleij 
551*94a2a84fSLinus Walleij static int mv88e6xxx_led0_blink_set(struct led_classdev *ldev,
552*94a2a84fSLinus Walleij 				    unsigned long *delay_on,
553*94a2a84fSLinus Walleij 				    unsigned long *delay_off)
554*94a2a84fSLinus Walleij {
555*94a2a84fSLinus Walleij 	struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led0);
556*94a2a84fSLinus Walleij 	int err;
557*94a2a84fSLinus Walleij 
558*94a2a84fSLinus Walleij 	mv88e6xxx_reg_lock(p->chip);
559*94a2a84fSLinus Walleij 	err = mv88e6xxx_led_blink_set(p, 0, delay_on, delay_off);
560*94a2a84fSLinus Walleij 	mv88e6xxx_reg_unlock(p->chip);
561*94a2a84fSLinus Walleij 
562*94a2a84fSLinus Walleij 	return err;
563*94a2a84fSLinus Walleij }
564*94a2a84fSLinus Walleij 
565*94a2a84fSLinus Walleij static int mv88e6xxx_led1_blink_set(struct led_classdev *ldev,
566*94a2a84fSLinus Walleij 				    unsigned long *delay_on,
567*94a2a84fSLinus Walleij 				    unsigned long *delay_off)
568*94a2a84fSLinus Walleij {
569*94a2a84fSLinus Walleij 	struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led1);
570*94a2a84fSLinus Walleij 	int err;
571*94a2a84fSLinus Walleij 
572*94a2a84fSLinus Walleij 	mv88e6xxx_reg_lock(p->chip);
573*94a2a84fSLinus Walleij 	err = mv88e6xxx_led_blink_set(p, 1, delay_on, delay_off);
574*94a2a84fSLinus Walleij 	mv88e6xxx_reg_unlock(p->chip);
575*94a2a84fSLinus Walleij 
576*94a2a84fSLinus Walleij 	return err;
577*94a2a84fSLinus Walleij }
578*94a2a84fSLinus Walleij 
579*94a2a84fSLinus Walleij static int
580*94a2a84fSLinus Walleij mv88e6xxx_led0_hw_control_is_supported(struct led_classdev *ldev, unsigned long rules)
581*94a2a84fSLinus Walleij {
582*94a2a84fSLinus Walleij 	struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led0);
583*94a2a84fSLinus Walleij 	u16 selector = 0;
584*94a2a84fSLinus Walleij 
585*94a2a84fSLinus Walleij 	return mv88e6xxx_led_get_selector(p, 0, p->fiber, rules, &selector);
586*94a2a84fSLinus Walleij }
587*94a2a84fSLinus Walleij 
588*94a2a84fSLinus Walleij static int
589*94a2a84fSLinus Walleij mv88e6xxx_led1_hw_control_is_supported(struct led_classdev *ldev, unsigned long rules)
590*94a2a84fSLinus Walleij {
591*94a2a84fSLinus Walleij 	struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led1);
592*94a2a84fSLinus Walleij 	u16 selector = 0;
593*94a2a84fSLinus Walleij 
594*94a2a84fSLinus Walleij 	return mv88e6xxx_led_get_selector(p, 1, p->fiber, rules, &selector);
595*94a2a84fSLinus Walleij }
596*94a2a84fSLinus Walleij 
597*94a2a84fSLinus Walleij static int mv88e6xxx_led_hw_control_set(struct mv88e6xxx_port *p,
598*94a2a84fSLinus Walleij 					int led, unsigned long rules)
599*94a2a84fSLinus Walleij {
600*94a2a84fSLinus Walleij 	u16 reg;
601*94a2a84fSLinus Walleij 	int err;
602*94a2a84fSLinus Walleij 
603*94a2a84fSLinus Walleij 	err = mv88e6xxx_port_led_read(p->chip, p->port,
604*94a2a84fSLinus Walleij 				      MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL,
605*94a2a84fSLinus Walleij 				      &reg);
606*94a2a84fSLinus Walleij 	if (err)
607*94a2a84fSLinus Walleij 		return err;
608*94a2a84fSLinus Walleij 
609*94a2a84fSLinus Walleij 	if (led == 1)
610*94a2a84fSLinus Walleij 		reg &= ~MV88E6XXX_PORT_LED_CONTROL_LED1_SEL_MASK;
611*94a2a84fSLinus Walleij 	else
612*94a2a84fSLinus Walleij 		reg &= ~MV88E6XXX_PORT_LED_CONTROL_LED0_SEL_MASK;
613*94a2a84fSLinus Walleij 
614*94a2a84fSLinus Walleij 	err = mv88e6xxx_led_get_selector(p, led, p->fiber, rules, &reg);
615*94a2a84fSLinus Walleij 	if (err)
616*94a2a84fSLinus Walleij 		return err;
617*94a2a84fSLinus Walleij 
618*94a2a84fSLinus Walleij 	reg |= MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL;
619*94a2a84fSLinus Walleij 
620*94a2a84fSLinus Walleij 	if (led == 0)
621*94a2a84fSLinus Walleij 		dev_dbg(p->chip->dev, "LED 0 hw control on port %d trigger selector 0x%02x\n",
622*94a2a84fSLinus Walleij 			p->port,
623*94a2a84fSLinus Walleij 			(unsigned int)(reg & MV88E6XXX_PORT_LED_CONTROL_LED0_SEL_MASK));
624*94a2a84fSLinus Walleij 	else
625*94a2a84fSLinus Walleij 		dev_dbg(p->chip->dev, "LED 1 hw control on port %d trigger selector 0x%02x\n",
626*94a2a84fSLinus Walleij 			p->port,
627*94a2a84fSLinus Walleij 			(unsigned int)(reg & MV88E6XXX_PORT_LED_CONTROL_LED1_SEL_MASK) >> 4);
628*94a2a84fSLinus Walleij 
629*94a2a84fSLinus Walleij 	return mv88e6xxx_port_led_write(p->chip, p->port, reg);
630*94a2a84fSLinus Walleij }
631*94a2a84fSLinus Walleij 
632*94a2a84fSLinus Walleij static int
633*94a2a84fSLinus Walleij mv88e6xxx_led_hw_control_get(struct mv88e6xxx_port *p, int led, unsigned long *rules)
634*94a2a84fSLinus Walleij {
635*94a2a84fSLinus Walleij 	u16 val;
636*94a2a84fSLinus Walleij 	int err;
637*94a2a84fSLinus Walleij 
638*94a2a84fSLinus Walleij 	mv88e6xxx_reg_lock(p->chip);
639*94a2a84fSLinus Walleij 	err = mv88e6xxx_port_led_read(p->chip, p->port,
640*94a2a84fSLinus Walleij 				      MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL, &val);
641*94a2a84fSLinus Walleij 	mv88e6xxx_reg_unlock(p->chip);
642*94a2a84fSLinus Walleij 	if (err)
643*94a2a84fSLinus Walleij 		return err;
644*94a2a84fSLinus Walleij 
645*94a2a84fSLinus Walleij 	/* Mask out the selector bits for this port */
646*94a2a84fSLinus Walleij 	if (led == 1) {
647*94a2a84fSLinus Walleij 		val &= MV88E6XXX_PORT_LED_CONTROL_LED1_SEL_MASK;
648*94a2a84fSLinus Walleij 		/* It's forced blinking/OFF/ON */
649*94a2a84fSLinus Walleij 		if (val == MV88E6XXX_PORT_LED_CONTROL_LED1_SELD ||
650*94a2a84fSLinus Walleij 		    val == MV88E6XXX_PORT_LED_CONTROL_LED1_SELE ||
651*94a2a84fSLinus Walleij 		    val == MV88E6XXX_PORT_LED_CONTROL_LED1_SELF) {
652*94a2a84fSLinus Walleij 			*rules = 0;
653*94a2a84fSLinus Walleij 			return 0;
654*94a2a84fSLinus Walleij 		}
655*94a2a84fSLinus Walleij 	} else {
656*94a2a84fSLinus Walleij 		val &= MV88E6XXX_PORT_LED_CONTROL_LED0_SEL_MASK;
657*94a2a84fSLinus Walleij 		/* It's forced blinking/OFF/ON */
658*94a2a84fSLinus Walleij 		if (val == MV88E6XXX_PORT_LED_CONTROL_LED0_SELD ||
659*94a2a84fSLinus Walleij 		    val == MV88E6XXX_PORT_LED_CONTROL_LED0_SELE ||
660*94a2a84fSLinus Walleij 		    val == MV88E6XXX_PORT_LED_CONTROL_LED0_SELF) {
661*94a2a84fSLinus Walleij 			*rules = 0;
662*94a2a84fSLinus Walleij 			return 0;
663*94a2a84fSLinus Walleij 		}
664*94a2a84fSLinus Walleij 	}
665*94a2a84fSLinus Walleij 
666*94a2a84fSLinus Walleij 	err = mv88e6xxx_led_match_rule(p, val, led, rules);
667*94a2a84fSLinus Walleij 	if (!err)
668*94a2a84fSLinus Walleij 		return 0;
669*94a2a84fSLinus Walleij 
670*94a2a84fSLinus Walleij 	dev_dbg(p->chip->dev, "couldn't find matching selector for %04x\n", val);
671*94a2a84fSLinus Walleij 	*rules = 0;
672*94a2a84fSLinus Walleij 	return 0;
673*94a2a84fSLinus Walleij }
674*94a2a84fSLinus Walleij 
675*94a2a84fSLinus Walleij static int
676*94a2a84fSLinus Walleij mv88e6xxx_led0_hw_control_set(struct led_classdev *ldev, unsigned long rules)
677*94a2a84fSLinus Walleij {
678*94a2a84fSLinus Walleij 	struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led0);
679*94a2a84fSLinus Walleij 	int err;
680*94a2a84fSLinus Walleij 
681*94a2a84fSLinus Walleij 	mv88e6xxx_reg_lock(p->chip);
682*94a2a84fSLinus Walleij 	err = mv88e6xxx_led_hw_control_set(p, 0, rules);
683*94a2a84fSLinus Walleij 	mv88e6xxx_reg_unlock(p->chip);
684*94a2a84fSLinus Walleij 
685*94a2a84fSLinus Walleij 	return err;
686*94a2a84fSLinus Walleij }
687*94a2a84fSLinus Walleij 
688*94a2a84fSLinus Walleij static int
689*94a2a84fSLinus Walleij mv88e6xxx_led1_hw_control_set(struct led_classdev *ldev, unsigned long rules)
690*94a2a84fSLinus Walleij {
691*94a2a84fSLinus Walleij 	struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led1);
692*94a2a84fSLinus Walleij 	int err;
693*94a2a84fSLinus Walleij 
694*94a2a84fSLinus Walleij 	mv88e6xxx_reg_lock(p->chip);
695*94a2a84fSLinus Walleij 	err = mv88e6xxx_led_hw_control_set(p, 1, rules);
696*94a2a84fSLinus Walleij 	mv88e6xxx_reg_unlock(p->chip);
697*94a2a84fSLinus Walleij 
698*94a2a84fSLinus Walleij 	return err;
699*94a2a84fSLinus Walleij }
700*94a2a84fSLinus Walleij 
701*94a2a84fSLinus Walleij static int
702*94a2a84fSLinus Walleij mv88e6xxx_led0_hw_control_get(struct led_classdev *ldev, unsigned long *rules)
703*94a2a84fSLinus Walleij {
704*94a2a84fSLinus Walleij 	struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led0);
705*94a2a84fSLinus Walleij 
706*94a2a84fSLinus Walleij 	return mv88e6xxx_led_hw_control_get(p, 0, rules);
707*94a2a84fSLinus Walleij }
708*94a2a84fSLinus Walleij 
709*94a2a84fSLinus Walleij static int
710*94a2a84fSLinus Walleij mv88e6xxx_led1_hw_control_get(struct led_classdev *ldev, unsigned long *rules)
711*94a2a84fSLinus Walleij {
712*94a2a84fSLinus Walleij 	struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led1);
713*94a2a84fSLinus Walleij 
714*94a2a84fSLinus Walleij 	return mv88e6xxx_led_hw_control_get(p, 1, rules);
715*94a2a84fSLinus Walleij }
716*94a2a84fSLinus Walleij 
717*94a2a84fSLinus Walleij static struct device *mv88e6xxx_led_hw_control_get_device(struct mv88e6xxx_port *p)
718*94a2a84fSLinus Walleij {
719*94a2a84fSLinus Walleij 	struct dsa_port *dp;
720*94a2a84fSLinus Walleij 
721*94a2a84fSLinus Walleij 	dp = dsa_to_port(p->chip->ds, p->port);
722*94a2a84fSLinus Walleij 	if (!dp)
723*94a2a84fSLinus Walleij 		return NULL;
724*94a2a84fSLinus Walleij 	if (dp->user)
725*94a2a84fSLinus Walleij 		return &dp->user->dev;
726*94a2a84fSLinus Walleij 	return NULL;
727*94a2a84fSLinus Walleij }
728*94a2a84fSLinus Walleij 
729*94a2a84fSLinus Walleij static struct device *
730*94a2a84fSLinus Walleij mv88e6xxx_led0_hw_control_get_device(struct led_classdev *ldev)
731*94a2a84fSLinus Walleij {
732*94a2a84fSLinus Walleij 	struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led0);
733*94a2a84fSLinus Walleij 
734*94a2a84fSLinus Walleij 	return mv88e6xxx_led_hw_control_get_device(p);
735*94a2a84fSLinus Walleij }
736*94a2a84fSLinus Walleij 
737*94a2a84fSLinus Walleij static struct device *
738*94a2a84fSLinus Walleij mv88e6xxx_led1_hw_control_get_device(struct led_classdev *ldev)
739*94a2a84fSLinus Walleij {
740*94a2a84fSLinus Walleij 	struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led1);
741*94a2a84fSLinus Walleij 
742*94a2a84fSLinus Walleij 	return mv88e6xxx_led_hw_control_get_device(p);
743*94a2a84fSLinus Walleij }
744*94a2a84fSLinus Walleij 
745*94a2a84fSLinus Walleij int mv88e6xxx_port_setup_leds(struct mv88e6xxx_chip *chip, int port)
746*94a2a84fSLinus Walleij {
747*94a2a84fSLinus Walleij 	struct fwnode_handle *led = NULL, *leds = NULL;
748*94a2a84fSLinus Walleij 	struct led_init_data init_data = { };
749*94a2a84fSLinus Walleij 	enum led_default_state state;
750*94a2a84fSLinus Walleij 	struct mv88e6xxx_port *p;
751*94a2a84fSLinus Walleij 	struct led_classdev *l;
752*94a2a84fSLinus Walleij 	struct device *dev;
753*94a2a84fSLinus Walleij 	u32 led_num;
754*94a2a84fSLinus Walleij 	int ret;
755*94a2a84fSLinus Walleij 
756*94a2a84fSLinus Walleij 	/* LEDs are on ports 1,2,3,4, 5 and 6 (index 0..5), no more */
757*94a2a84fSLinus Walleij 	if (port > 5)
758*94a2a84fSLinus Walleij 		return -EOPNOTSUPP;
759*94a2a84fSLinus Walleij 
760*94a2a84fSLinus Walleij 	p = &chip->ports[port];
761*94a2a84fSLinus Walleij 	if (!p->fwnode)
762*94a2a84fSLinus Walleij 		return 0;
763*94a2a84fSLinus Walleij 
764*94a2a84fSLinus Walleij 	dev = chip->dev;
765*94a2a84fSLinus Walleij 
766*94a2a84fSLinus Walleij 	leds = fwnode_get_named_child_node(p->fwnode, "leds");
767*94a2a84fSLinus Walleij 	if (!leds) {
768*94a2a84fSLinus Walleij 		dev_dbg(dev, "No Leds node specified in device tree for port %d!\n",
769*94a2a84fSLinus Walleij 			port);
770*94a2a84fSLinus Walleij 		return 0;
771*94a2a84fSLinus Walleij 	}
772*94a2a84fSLinus Walleij 
773*94a2a84fSLinus Walleij 	fwnode_for_each_child_node(leds, led) {
774*94a2a84fSLinus Walleij 		/* Reg represent the led number of the port, max 2
775*94a2a84fSLinus Walleij 		 * LEDs can be connected to each port, in some designs
776*94a2a84fSLinus Walleij 		 * only one LED is connected.
777*94a2a84fSLinus Walleij 		 */
778*94a2a84fSLinus Walleij 		if (fwnode_property_read_u32(led, "reg", &led_num))
779*94a2a84fSLinus Walleij 			continue;
780*94a2a84fSLinus Walleij 		if (led_num > 1) {
781*94a2a84fSLinus Walleij 			dev_err(dev, "invalid LED specified port %d\n", port);
782*94a2a84fSLinus Walleij 			return -EINVAL;
783*94a2a84fSLinus Walleij 		}
784*94a2a84fSLinus Walleij 
785*94a2a84fSLinus Walleij 		if (led_num == 0)
786*94a2a84fSLinus Walleij 			l = &p->led0;
787*94a2a84fSLinus Walleij 		else
788*94a2a84fSLinus Walleij 			l = &p->led1;
789*94a2a84fSLinus Walleij 
790*94a2a84fSLinus Walleij 		state = led_init_default_state_get(led);
791*94a2a84fSLinus Walleij 		switch (state) {
792*94a2a84fSLinus Walleij 		case LEDS_DEFSTATE_ON:
793*94a2a84fSLinus Walleij 			l->brightness = 1;
794*94a2a84fSLinus Walleij 			mv88e6xxx_led_brightness_set(p, led_num, 1);
795*94a2a84fSLinus Walleij 			break;
796*94a2a84fSLinus Walleij 		case LEDS_DEFSTATE_KEEP:
797*94a2a84fSLinus Walleij 			break;
798*94a2a84fSLinus Walleij 		default:
799*94a2a84fSLinus Walleij 			l->brightness = 0;
800*94a2a84fSLinus Walleij 			mv88e6xxx_led_brightness_set(p, led_num, 0);
801*94a2a84fSLinus Walleij 		}
802*94a2a84fSLinus Walleij 
803*94a2a84fSLinus Walleij 		l->max_brightness = 1;
804*94a2a84fSLinus Walleij 		if (led_num == 0) {
805*94a2a84fSLinus Walleij 			l->brightness_set_blocking = mv88e6xxx_led0_brightness_set_blocking;
806*94a2a84fSLinus Walleij 			l->blink_set = mv88e6xxx_led0_blink_set;
807*94a2a84fSLinus Walleij 			l->hw_control_is_supported = mv88e6xxx_led0_hw_control_is_supported;
808*94a2a84fSLinus Walleij 			l->hw_control_set = mv88e6xxx_led0_hw_control_set;
809*94a2a84fSLinus Walleij 			l->hw_control_get = mv88e6xxx_led0_hw_control_get;
810*94a2a84fSLinus Walleij 			l->hw_control_get_device = mv88e6xxx_led0_hw_control_get_device;
811*94a2a84fSLinus Walleij 		} else {
812*94a2a84fSLinus Walleij 			l->brightness_set_blocking = mv88e6xxx_led1_brightness_set_blocking;
813*94a2a84fSLinus Walleij 			l->blink_set = mv88e6xxx_led1_blink_set;
814*94a2a84fSLinus Walleij 			l->hw_control_is_supported = mv88e6xxx_led1_hw_control_is_supported;
815*94a2a84fSLinus Walleij 			l->hw_control_set = mv88e6xxx_led1_hw_control_set;
816*94a2a84fSLinus Walleij 			l->hw_control_get = mv88e6xxx_led1_hw_control_get;
817*94a2a84fSLinus Walleij 			l->hw_control_get_device = mv88e6xxx_led1_hw_control_get_device;
818*94a2a84fSLinus Walleij 		}
819*94a2a84fSLinus Walleij 		l->hw_control_trigger = "netdev";
820*94a2a84fSLinus Walleij 
821*94a2a84fSLinus Walleij 		init_data.default_label = ":port";
822*94a2a84fSLinus Walleij 		init_data.fwnode = led;
823*94a2a84fSLinus Walleij 		init_data.devname_mandatory = true;
824*94a2a84fSLinus Walleij 		init_data.devicename = kasprintf(GFP_KERNEL, "%s:0%d:0%d", chip->info->name,
825*94a2a84fSLinus Walleij 						 port, led_num);
826*94a2a84fSLinus Walleij 		if (!init_data.devicename)
827*94a2a84fSLinus Walleij 			return -ENOMEM;
828*94a2a84fSLinus Walleij 
829*94a2a84fSLinus Walleij 		ret = devm_led_classdev_register_ext(dev, l, &init_data);
830*94a2a84fSLinus Walleij 		kfree(init_data.devicename);
831*94a2a84fSLinus Walleij 
832*94a2a84fSLinus Walleij 		if (ret) {
833*94a2a84fSLinus Walleij 			dev_err(dev, "Failed to init LED %d for port %d", led_num, port);
834*94a2a84fSLinus Walleij 			return ret;
835*94a2a84fSLinus Walleij 		}
836*94a2a84fSLinus Walleij 	}
837*94a2a84fSLinus Walleij 
838*94a2a84fSLinus Walleij 	return 0;
839*94a2a84fSLinus Walleij }
840