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 ®); 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 ®); 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 ®); 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, ®); 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