From nobody Wed Dec 17 17:26:08 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id AF10AC5AD4C for ; Thu, 23 Nov 2023 09:38:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232396AbjKWJhz (ORCPT ); Thu, 23 Nov 2023 04:37:55 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47550 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229464AbjKWJhw (ORCPT ); Thu, 23 Nov 2023 04:37:52 -0500 Received: from mail-lf1-x129.google.com (mail-lf1-x129.google.com [IPv6:2a00:1450:4864:20::129]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 65F241A4 for ; Thu, 23 Nov 2023 01:37:55 -0800 (PST) Received: by mail-lf1-x129.google.com with SMTP id 2adb3069b0e04-50aab20e828so897589e87.2 for ; Thu, 23 Nov 2023 01:37:55 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1700732273; x=1701337073; darn=vger.kernel.org; h=cc:to:message-id:content-transfer-encoding:mime-version:subject :date:from:from:to:cc:subject:date:message-id:reply-to; bh=OZAfhO4KgnkBDD08sObhUKJ1ppADzDCKAjigAvfm3O0=; b=DbijjyPkLqHEO6O8Ji2sXSRoSSI91FFz/MSRxedJ4FLJllrfEhahLUsNAnZYNBy1F4 fHpNWuNINNpdh079gf7+8CBYZrux36vtygRo1jXLd6PiLh6yKdqPQ2VwjIIOhOvAMS8r hM3SuPVB5u+lSqB4oqlDB+N9gR5F52F61EwqVvJg7b1ZblSsemiaEUcMCH3W+fPUDuS4 BRZJgxSdH3om2oYZi6YRFVxqKO5yJMEsvrDzo47qcCe9W2EYiDs1PNJv/T62y/x+14W7 z8YHnLHtBuhJyMZpzvi11+3yNJkGc7K6ynMcrIOcqp+I7o5lu8c98DO0oluiPo0uEjvf 1jmQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700732273; x=1701337073; h=cc:to:message-id:content-transfer-encoding:mime-version:subject :date:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=OZAfhO4KgnkBDD08sObhUKJ1ppADzDCKAjigAvfm3O0=; b=Rkzd0u0nvahRGSbXTzt3H6Uh+JFAglPHHUDllWpzgwmc14eJJ/CH6cJcbqiaJJ2a/S HYHmehqr74kbnV0ooJNMJ/GYHd/I0vA+PZv+U2JzqUryk4qDsu7poyg8TUKWhWdzUxg8 IoZEMBlTcfH37FyN6sSUAdi5dGzttirre5Pngu1sibi2tMo3cd+JmVv9ds9DcAwFEfos uTcGo0BD6QirgFzB8xoKdMqLtVSnDNj0KK0xqcc82q1xAA9cs6PMX6J4yzX1hdLZzJlJ WCXXRnJbZVPNYM7Zu4G7FTAKMcWA4zSkB6SfYwFmsS27nXJ5fEUjHfWLnx/jJ4KjXcRD OM3g== X-Gm-Message-State: AOJu0YyNG3hZRhFtrb2cH2R2P+9BW7m5zCcY/4BT+7YPvKzz5qHxdy6t p4KbrPGjXp+nIaZoxnBkvjW3uDep6pzkaidioNU= X-Google-Smtp-Source: AGHT+IE4KFNPj71t7LzUMLrsmrNTA9KI3MQlDM/l0UKBOvx+4gac9BZROsN8X7lgp4kEk8jSXY1Oow== X-Received: by 2002:a19:2d43:0:b0:503:1913:ed8e with SMTP id t3-20020a192d43000000b005031913ed8emr3313563lft.61.1700732273009; Thu, 23 Nov 2023 01:37:53 -0800 (PST) Received: from [127.0.1.1] ([85.235.12.238]) by smtp.gmail.com with ESMTPSA id i14-20020a056512006e00b0050a711c197dsm138014lfo.232.2023.11.23.01.37.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Nov 2023 01:37:52 -0800 (PST) From: Linus Walleij Date: Thu, 23 Nov 2023 10:37:48 +0100 Subject: [PATCH RFC] net: dsa: mv88e6xxx: Support LED control MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20231123-mv88e6xxx-leds-v1-1-3c379b3d23fb@linaro.org> X-B4-Tracking: v=1; b=H4sIAGsdX2UC/6tWKk4tykwtVrJSqFYqSi3LLM7MzwNyDHUUlJIzE vPSU3UzU4B8JSMDI2NDQyNj3dwyC4tUs4qKCt2c1JRi3SSLJMskY0OztDRLCyWgpoKi1LTMCrC B0UpBbs5KsbW1AOZY/aBlAAAA To: Andrew Lunn , Florian Fainelli , Vladimir Oltean , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Christian Marangi , Tim Harvey Cc: linux-kernel@vger.kernel.org, netdev@vger.kernel.org, Linus Walleij X-Mailer: b4 0.12.4 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This adds control over the hardware LEDs in the Marvell MV88E6xxx DSA switch and enables it for MV88E6352. This fixes an imminent problem on the Inteno XG6846 which has a WAN LED that simply do not work with hardware defaults: driver amendment is necessary. The patch is modeled after Christian Marangis LED support code for the QCA8k DSA switch, I got help with the register definitions from Tim Harvey. After this patch it is possible to activate hardware link indication like this (or with a similar script): cd /sys/class/leds/Marvell\ 88E6352:05:00:green:wan/ echo netdev > trigger echo 1 > link This makes the green link indicator come up on any link speed. It is also possible to be more elaborate, like this: cd /sys/class/leds/Marvell\ 88E6352:05:00:green:wan/ echo netdev > trigger echo 1 > link_1000 cd /sys/class/leds/Marvell\ 88E6352:05:01:amber:wan/ echo netdev > trigger echo 1 > link_100 Making the green LED come on for a gigabit link and the amber LED come on for a 100 mbit link. After the previous series rewriting the MV88E6xxx DT bindings to use YAML a "leds" subnode is already valid for each port, in my scratch device tree it looks like this: leds { #address-cells =3D <1>; #size-cells =3D <0>; led@0 { reg =3D <0>; color =3D ; function =3D LED_FUNCTION_LAN; default-state =3D "off"; linux,default-trigger =3D "netdev"; }; led@1 { reg =3D <1>; color =3D ; function =3D LED_FUNCTION_LAN; default-state =3D "off"; }; }; This DT config is not yet configuring everything: the netdev default trigger is assigned by the hw acceleration callbacks are not called, and there is no way to set the netdev sub-trigger type from the device tree, such as if you want a gigabit link indicator. This has to be done from userspace at this point. Signed-off-by: Linus Walleij --- drivers/net/dsa/mv88e6xxx/Makefile | 1 + drivers/net/dsa/mv88e6xxx/chip.c | 36 +- drivers/net/dsa/mv88e6xxx/chip.h | 12 + drivers/net/dsa/mv88e6xxx/leds.c | 741 +++++++++++++++++++++++++++++++++= ++++ drivers/net/dsa/mv88e6xxx/port.c | 1 + drivers/net/dsa/mv88e6xxx/port.h | 125 +++++++ 6 files changed, 914 insertions(+), 2 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/Makefile b/drivers/net/dsa/mv88e6xxx= /Makefile index a9a9651187db..6720d9303914 100644 --- a/drivers/net/dsa/mv88e6xxx/Makefile +++ b/drivers/net/dsa/mv88e6xxx/Makefile @@ -9,6 +9,7 @@ mv88e6xxx-objs +=3D global2.o mv88e6xxx-objs +=3D global2_avb.o mv88e6xxx-objs +=3D global2_scratch.o mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) +=3D hwtstamp.o +mv88e6xxx-objs +=3D leds.o mv88e6xxx-objs +=3D pcs-6185.o mv88e6xxx-objs +=3D pcs-6352.o mv88e6xxx-objs +=3D pcs-639x.o diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/c= hip.c index 42b1acaca33a..a09d3a214469 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -3153,14 +3154,44 @@ static int mv88e6xxx_setup_upstream_port(struct mv8= 8e6xxx_chip *chip, int port) static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) { struct device_node *phy_handle =3D NULL; + struct fwnode_handle *ports_fwnode; + struct fwnode_handle *port_fwnode; struct dsa_switch *ds =3D chip->ds; + struct mv88e6xxx_port *p; struct dsa_port *dp; int tx_amp; int err; u16 reg; + u32 val; + + p =3D &chip->ports[port]; + p->chip =3D chip; + p->port =3D port; + + /* Look up corresponding fwnode if any */ + ports_fwnode =3D device_get_named_child_node(chip->dev, "ethernet-ports"); + if (!ports_fwnode) + ports_fwnode =3D device_get_named_child_node(chip->dev, "ports"); + if (ports_fwnode) { + fwnode_for_each_child_node(ports_fwnode, port_fwnode) { + if (fwnode_property_read_u32(port_fwnode, "reg", &val)) + continue; + if (val =3D=3D port) { + p->fwnode =3D port_fwnode; + p->fiber =3D fwnode_property_present(port_fwnode, "sfp"); + break; + } + } + } else { + dev_info(chip->dev, + "no ethernet ports node defined for the device\n"); + } =20 - chip->ports[port].chip =3D chip; - chip->ports[port].port =3D port; + if (chip->info->ops->port_setup_leds) { + err =3D chip->info->ops->port_setup_leds(chip, port); + if (err && err !=3D -EOPNOTSUPP) + return err; + } =20 err =3D mv88e6xxx_port_setup_mac(chip, port, LINK_UNFORCED, SPEED_UNFORCED, DUPLEX_UNFORCED, @@ -5148,6 +5179,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops =3D { .port_disable_learn_limit =3D mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override =3D mv88e6xxx_port_disable_pri_override, .port_get_cmode =3D mv88e6352_port_get_cmode, + .port_setup_leds =3D mv88e6xxx_port_setup_leds, .port_setup_message_port =3D mv88e6xxx_setup_message_port, .stats_snapshot =3D mv88e6320_g1_stats_snapshot, .stats_set_histogram =3D mv88e6095_g1_stats_set_histogram, diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/c= hip.h index 44383a03ef2f..9de6a57e8ba9 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -13,7 +13,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -275,6 +277,7 @@ struct mv88e6xxx_vlan { struct mv88e6xxx_port { struct mv88e6xxx_chip *chip; int port; + struct fwnode_handle *fwnode; struct mv88e6xxx_vlan bridge_pvid; u64 serdes_stats[2]; u64 atu_member_violation; @@ -289,6 +292,12 @@ struct mv88e6xxx_port { struct devlink_region *region; void *pcs_private; =20 + /* LED related information */ + bool fiber; + struct led_classdev led0; + struct led_classdev led1; + u16 ledreg; + /* MacAuth Bypass control flag */ bool mab; }; @@ -555,6 +564,9 @@ struct mv88e6xxx_ops { phy_interface_t mode); int (*port_get_cmode)(struct mv88e6xxx_chip *chip, int port, u8 *cmode); =20 + /* LED control */ + int (*port_setup_leds)(struct mv88e6xxx_chip *chip, int port); + /* Some devices have a per port register indicating what is * the upstream port this port should forward to. */ diff --git a/drivers/net/dsa/mv88e6xxx/leds.c b/drivers/net/dsa/mv88e6xxx/l= eds.c new file mode 100644 index 000000000000..cc11773bdf9f --- /dev/null +++ b/drivers/net/dsa/mv88e6xxx/leds.c @@ -0,0 +1,741 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include + +#include "chip.h" +#include "global2.h" +#include "port.h" + +/* Offset 0x16: LED control */ + +static int mv88e6xxx_led_brightness_set(struct mv88e6xxx_port *p, int led, + int brightness) +{ + u16 reg; + + reg =3D p->ledreg; + + if (led =3D=3D 1) + reg &=3D ~MV88E6XXX_PORT_LED_CONTROL_LED1_SEL_MASK; + else + reg &=3D ~MV88E6XXX_PORT_LED_CONTROL_LED0_SEL_MASK; + + if (brightness) { + /* Selector 0x0f =3D=3D Force LED ON */ + if (led =3D=3D 1) + reg |=3D MV88E6XXX_PORT_LED_CONTROL_LED1_SELF; + else + reg |=3D MV88E6XXX_PORT_LED_CONTROL_LED0_SELF; + } else { + /* Selector 0x0e =3D=3D Force LED OFF */ + if (led =3D=3D 1) + reg |=3D MV88E6XXX_PORT_LED_CONTROL_LED1_SELE; + else + reg |=3D MV88E6XXX_PORT_LED_CONTROL_LED0_SELE; + } + + p->ledreg =3D reg; + + reg |=3D MV88E6XXX_PORT_LED_CONTROL_UPDATE; + reg |=3D MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL; + + return mv88e6xxx_port_write(p->chip, p->port, MV88E6XXX_PORT_LED_CONTROL,= reg); +} + +static int mv88e6xxx_led0_brightness_set_blocking(struct led_classdev *lde= v, + enum led_brightness brightness) +{ + struct mv88e6xxx_port *p =3D container_of(ldev, struct mv88e6xxx_port, le= d0); + int err; + + mv88e6xxx_reg_lock(p->chip); + err =3D mv88e6xxx_led_brightness_set(p, 0, brightness); + mv88e6xxx_reg_unlock(p->chip); + + return err; +} + +static int mv88e6xxx_led1_brightness_set_blocking(struct led_classdev *lde= v, + enum led_brightness brightness) +{ + struct mv88e6xxx_port *p =3D container_of(ldev, struct mv88e6xxx_port, le= d1); + int err; + + mv88e6xxx_reg_lock(p->chip); + err =3D mv88e6xxx_led_brightness_set(p, 1, brightness); + mv88e6xxx_reg_unlock(p->chip); + + return err; +} + +struct mv88e6xxx_led_hwconfig { + int led; + u8 portmask; + unsigned long rules; + bool fiber; + bool blink_activity; + u16 selector; +}; + +/* The following is a lookup table to check what rules we can support on a + * certain LED given restrictions such as that some rules only work with f= iber + * (SFP) connections and some blink on activity by default. + */ +#define MV88E6XXX_PORTS_0_3 (BIT(0)|BIT(1)|BIT(2)|BIT(3)) +#define MV88E6XXX_PORTS_4_5 (BIT(4)|BIT(5)) +#define MV88E6XXX_PORT_4 BIT(4) +#define MV88E6XXX_PORT_5 BIT(5) + +/* Entries are listed in selector order */ +static const struct mv88e6xxx_led_hwconfig mv88e6xxx_led_hwconfigs[] =3D { + { + .led =3D 0, + .portmask =3D MV88E6XXX_PORT_4, + .rules =3D BIT(TRIGGER_NETDEV_LINK), + .blink_activity =3D true, + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED0_SEL0, + }, + { + .led =3D 1, + .portmask =3D MV88E6XXX_PORT_5, + .rules =3D BIT(TRIGGER_NETDEV_LINK_1000), + .blink_activity =3D true, + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED1_SEL0, + }, + { + .led =3D 0, + .portmask =3D MV88E6XXX_PORTS_0_3, + .rules =3D BIT(TRIGGER_NETDEV_LINK_100)|BIT(TRIGGER_NETDEV_LINK_1000), + .blink_activity =3D true, + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED0_SEL1, + }, + { + .led =3D 1, + .portmask =3D MV88E6XXX_PORTS_0_3, + .rules =3D BIT(TRIGGER_NETDEV_LINK_10)|BIT(TRIGGER_NETDEV_LINK_100), + .blink_activity =3D true, + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED1_SEL1, + }, + { + .led =3D 0, + .portmask =3D MV88E6XXX_PORTS_4_5, + .rules =3D BIT(TRIGGER_NETDEV_LINK_100), + .blink_activity =3D true, + .fiber =3D true, + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED0_SEL1, + }, + { + .led =3D 1, + .portmask =3D MV88E6XXX_PORTS_4_5, + .rules =3D BIT(TRIGGER_NETDEV_LINK_1000), + .blink_activity =3D true, + .fiber =3D true, + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED1_SEL1, + }, + { + .led =3D 0, + .portmask =3D MV88E6XXX_PORTS_0_3, + .rules =3D BIT(TRIGGER_NETDEV_LINK_1000), + .blink_activity =3D true, + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED0_SEL2, + }, + { + .led =3D 1, + .portmask =3D MV88E6XXX_PORTS_0_3, + .rules =3D BIT(TRIGGER_NETDEV_LINK_10)|BIT(TRIGGER_NETDEV_LINK_100), + .blink_activity =3D true, + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED1_SEL2, + }, + { + .led =3D 0, + .portmask =3D MV88E6XXX_PORTS_4_5, + .rules =3D BIT(TRIGGER_NETDEV_LINK_1000), + .blink_activity =3D true, + .fiber =3D true, + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED0_SEL2, + }, + { + .led =3D 1, + .portmask =3D MV88E6XXX_PORTS_4_5, + .rules =3D BIT(TRIGGER_NETDEV_LINK_100), + .blink_activity =3D true, + .fiber =3D true, + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED1_SEL2, + }, + { + .led =3D 0, + .portmask =3D MV88E6XXX_PORTS_0_3, + .rules =3D BIT(TRIGGER_NETDEV_LINK), + .blink_activity =3D true, + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED0_SEL3, + }, + { + .led =3D 1, + .portmask =3D MV88E6XXX_PORTS_0_3, + .rules =3D BIT(TRIGGER_NETDEV_LINK_1000), + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED1_SEL3, + }, + { + .led =3D 1, + .portmask =3D MV88E6XXX_PORTS_4_5, + .rules =3D BIT(TRIGGER_NETDEV_LINK), + .fiber =3D true, + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED1_SEL3, + }, + { + .led =3D 1, + .portmask =3D MV88E6XXX_PORT_4, + .rules =3D BIT(TRIGGER_NETDEV_LINK), + .blink_activity =3D true, + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED1_SEL4, + }, + { + .led =3D 1, + .portmask =3D MV88E6XXX_PORT_5, + .rules =3D BIT(TRIGGER_NETDEV_LINK), + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED1_SEL5, + }, + { + .led =3D 0, + .portmask =3D MV88E6XXX_PORTS_0_3, + .rules =3D BIT(TRIGGER_NETDEV_FULL_DUPLEX), + .blink_activity =3D true, + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED0_SEL6, + }, + { + .led =3D 1, + .portmask =3D MV88E6XXX_PORTS_0_3, + .rules =3D BIT(TRIGGER_NETDEV_LINK_10)|BIT(TRIGGER_NETDEV_LINK_1000), + .blink_activity =3D true, + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED1_SEL6, + }, + { + .led =3D 0, + .portmask =3D MV88E6XXX_PORT_4, + .rules =3D BIT(TRIGGER_NETDEV_FULL_DUPLEX), + .blink_activity =3D true, + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED0_SEL6, + }, + { + .led =3D 1, + .portmask =3D MV88E6XXX_PORT_5, + .rules =3D BIT(TRIGGER_NETDEV_FULL_DUPLEX), + .blink_activity =3D true, + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED1_SEL6, + }, + { + .led =3D 0, + .portmask =3D MV88E6XXX_PORTS_0_3, + .rules =3D BIT(TRIGGER_NETDEV_LINK_10)|BIT(TRIGGER_NETDEV_LINK_1000), + .blink_activity =3D true, + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED0_SEL7, + }, + { + .led =3D 1, + .portmask =3D MV88E6XXX_PORTS_0_3, + .rules =3D BIT(TRIGGER_NETDEV_LINK_10)|BIT(TRIGGER_NETDEV_LINK_1000), + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED1_SEL7, + }, + { + .led =3D 0, + .portmask =3D MV88E6XXX_PORTS_0_3, + .rules =3D BIT(TRIGGER_NETDEV_LINK), + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED0_SEL8, + }, + { + .led =3D 1, + .portmask =3D MV88E6XXX_PORTS_0_3, + .rules =3D BIT(TRIGGER_NETDEV_LINK), + .blink_activity =3D true, + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED1_SEL8, + }, + { + .led =3D 0, + .portmask =3D MV88E6XXX_PORT_5, + .rules =3D BIT(TRIGGER_NETDEV_LINK), + .blink_activity =3D true, + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED0_SEL8, + }, + { + .led =3D 0, + .portmask =3D MV88E6XXX_PORTS_0_3, + .rules =3D BIT(TRIGGER_NETDEV_LINK_10), + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED0_SEL9, + }, + { + .led =3D 1, + .portmask =3D MV88E6XXX_PORTS_0_3, + .rules =3D BIT(TRIGGER_NETDEV_LINK_100), + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED1_SEL9, + }, + { + .led =3D 0, + .portmask =3D MV88E6XXX_PORTS_0_3, + .rules =3D BIT(TRIGGER_NETDEV_LINK_10), + .blink_activity =3D true, + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED0_SELA, + }, + { + .led =3D 1, + .portmask =3D MV88E6XXX_PORTS_0_3, + .rules =3D BIT(TRIGGER_NETDEV_LINK_100), + .blink_activity =3D true, + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED1_SELA, + }, + { + .led =3D 0, + .portmask =3D MV88E6XXX_PORTS_0_3, + .rules =3D BIT(TRIGGER_NETDEV_LINK_100)|BIT(TRIGGER_NETDEV_LINK_1000), + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED0_SELB, + }, + { + .led =3D 1, + .portmask =3D MV88E6XXX_PORTS_0_3, + .rules =3D BIT(TRIGGER_NETDEV_LINK_100)|BIT(TRIGGER_NETDEV_LINK_1000), + .blink_activity =3D true, + .selector =3D MV88E6XXX_PORT_LED_CONTROL_LED1_SELB, + }, +}; + +/* mv88e6xxx_led_match_selector() - look up the appropriate LED mode selec= tor + * @p: port state container + * @led: LED number, 0 or 1 + * @blink_activity: blink the LED (usually blink on indicated activity) + * @fiber: the link is connected to fiber such as SFP + * @rules: LED status flags from the LED classdev core + * @selector: fill in the selector in this parameter with an OR operation + */ +static int mv88e6xxx_led_match_selector(struct mv88e6xxx_port *p, int led,= bool blink_activity, + bool fiber, unsigned long rules, u16 *selector) +{ + const struct mv88e6xxx_led_hwconfig *conf; + int i; + + /* No rules means we turn the LED off */ + if (!rules) { + if (led =3D=3D 1) + *selector |=3D MV88E6XXX_PORT_LED_CONTROL_LED1_SELE; + else + *selector |=3D MV88E6XXX_PORT_LED_CONTROL_LED0_SELE; + return 0; + } + + for (i =3D 0; i < ARRAY_SIZE(mv88e6xxx_led_hwconfigs); i++) { + conf =3D &mv88e6xxx_led_hwconfigs[i]; + + if (conf->led !=3D led) + continue; + + if (!(conf->portmask & BIT(p->port))) + continue; + + if (conf->blink_activity !=3D blink_activity) + continue; + + if (conf->fiber !=3D fiber) + continue; + + if (conf->rules =3D=3D rules) { + *selector |=3D conf->selector; + return 0; + } + } + + return -EOPNOTSUPP; +} + +/* mv88e6xxx_led_get_selector() - get the appropriate LED mode selector + * @p: port state container + * @led: LED number, 0 or 1 + * @fiber: the link is connected to fiber such as SFP + * @rules: LED status flags from the LED classdev core + * @selector: fill in the selector in this parameter with an OR operation + */ +static int mv88e6xxx_led_get_selector(struct mv88e6xxx_port *p, int led, + bool fiber, unsigned long rules, u16 *selector) +{ + int err; + + /* What happens here is that we first try to locate a trigger with solid + * indicator (such as LED is on for a 1000 link) else we try a second + * sweep to find something suitable with a trigger that will blink on + * activity. + */ + err =3D mv88e6xxx_led_match_selector(p, led, false, fiber, rules, selecto= r); + if (err) + return mv88e6xxx_led_match_selector(p, led, true, fiber, rules, selector= ); + + return 0; +} + +/* Sets up the hardware blinking period */ +static int mv88e6xxx_led_set_blinking_period(struct mv88e6xxx_port *p, int= led, + unsigned long *delay_on, unsigned long *delay_off) +{ + unsigned long period; + u16 reg; + + period =3D *delay_on + *delay_off; + + reg =3D 0; + + switch (period) { + case 21: + reg |=3D MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_21MS; + break; + case 42: + reg |=3D MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_42MS; + break; + case 84: + reg |=3D MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_84MS; + break; + case 168: + reg |=3D MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_168MS; + break; + case 336: + reg |=3D MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_336MS; + break; + case 672: + reg |=3D MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_672MS; + break; + default: + /* Fall back to software blinking */ + return -EINVAL; + } + + /* This is essentially PWM duty cycle: how long time of the period + * will the LED be on. Zero isn't great in most cases. + */ + switch (*delay_on) { + case 0: + /* This is usually pretty useless and will make the LED look OFF */ + reg |=3D MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_NONE; + break; + case 21: + reg |=3D MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_21MS; + break; + case 42: + reg |=3D MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_42MS; + break; + case 84: + reg |=3D MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_84MS; + break; + case 168: + reg |=3D MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_168MS; + break; + default: + /* Just use something non-zero */ + reg |=3D MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_21MS; + *delay_on =3D 21; + break; + } + + reg |=3D MV88E6XXX_PORT_LED_CONTROL_UPDATE; + /* Set up blink rate */ + reg |=3D MV88E6XXX_PORT_LED_CONTROL_POINTER_STRETCH_BLINK; + + return mv88e6xxx_port_write(p->chip, p->port, MV88E6XXX_PORT_LED_CONTROL,= reg); +} + +static int mv88e6xxx_led_blink_set(struct mv88e6xxx_port *p, int led, + unsigned long *delay_on, unsigned long *delay_off) +{ + u16 reg; + int err; + + /* Choose a sensible default 336 ms (~3 Hz) */ + if ((*delay_on =3D=3D 0) && (*delay_off =3D=3D 0)) { + *delay_on =3D 168; + *delay_off =3D 168; + } + + /* No off delay is just on */ + if (*delay_off =3D=3D 0) + return mv88e6xxx_led_brightness_set(p, led, 1); + + err =3D mv88e6xxx_led_set_blinking_period(p, led, delay_on, delay_off); + if (err) + return err; + + reg =3D p->ledreg; + + if (led =3D=3D 1) + reg &=3D ~MV88E6XXX_PORT_LED_CONTROL_LED1_SEL_MASK; + else + reg &=3D ~MV88E6XXX_PORT_LED_CONTROL_LED0_SEL_MASK; + + /* This will select the forced blinking status */ + if (led =3D=3D 1) + reg |=3D MV88E6XXX_PORT_LED_CONTROL_LED1_SELD; + else + reg |=3D MV88E6XXX_PORT_LED_CONTROL_LED0_SELD; + + p->ledreg =3D reg; + + reg |=3D MV88E6XXX_PORT_LED_CONTROL_UPDATE; + reg |=3D MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL; + + return mv88e6xxx_port_write(p->chip, p->port, MV88E6XXX_PORT_LED_CONTROL,= reg); +} + +static int mv88e6xxx_led0_blink_set(struct led_classdev *ldev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct mv88e6xxx_port *p =3D container_of(ldev, struct mv88e6xxx_port, le= d0); + int err; + + mv88e6xxx_reg_lock(p->chip); + err =3D mv88e6xxx_led_blink_set(p, 0, delay_on, delay_off); + mv88e6xxx_reg_unlock(p->chip); + + return err; +} + +static int mv88e6xxx_led1_blink_set(struct led_classdev *ldev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct mv88e6xxx_port *p =3D container_of(ldev, struct mv88e6xxx_port, le= d1); + int err; + + mv88e6xxx_reg_lock(p->chip); + err =3D mv88e6xxx_led_blink_set(p, 1, delay_on, delay_off); + mv88e6xxx_reg_unlock(p->chip); + + return err; +} + +static int +mv88e6xxx_led0_hw_control_is_supported(struct led_classdev *ldev, unsigned= long rules) +{ + struct mv88e6xxx_port *p =3D container_of(ldev, struct mv88e6xxx_port, le= d0); + u16 selector =3D 0; + + return mv88e6xxx_led_get_selector(p, 0, p->fiber, rules, &selector); +} + +static int +mv88e6xxx_led1_hw_control_is_supported(struct led_classdev *ldev, unsigned= long rules) +{ + struct mv88e6xxx_port *p =3D container_of(ldev, struct mv88e6xxx_port, le= d1); + u16 selector =3D 0; + + return mv88e6xxx_led_get_selector(p, 1, p->fiber, rules, &selector); +} + +static int mv88e6xxx_led_hw_control_set(struct mv88e6xxx_port *p, + int led, unsigned long rules) +{ + u16 reg; + int err; + + reg =3D p->ledreg; + + if (led =3D=3D 1) + reg &=3D ~MV88E6XXX_PORT_LED_CONTROL_LED1_SEL_MASK; + else + reg &=3D ~MV88E6XXX_PORT_LED_CONTROL_LED0_SEL_MASK; + + err =3D mv88e6xxx_led_get_selector(p, led, p->fiber, rules, ®); + if (err) + return err; + + p->ledreg =3D reg; + + reg |=3D MV88E6XXX_PORT_LED_CONTROL_UPDATE; + reg |=3D MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL; + + if (led =3D=3D 0) + dev_dbg(p->chip->dev, "LED 0 hw control on port %d trigger selector 0x%0= 2x\n", + p->port, + (unsigned int)(p->ledreg & MV88E6XXX_PORT_LED_CONTROL_LED0_SEL_MASK)); + else + dev_dbg(p->chip->dev, "LED 1 hw control on port %d trigger selector 0x%0= 2x\n", + p->port, + (unsigned int)(p->ledreg & MV88E6XXX_PORT_LED_CONTROL_LED1_SEL_MASK) >>= 4); + + err =3D mv88e6xxx_port_write(p->chip, p->port, MV88E6XXX_PORT_LED_CONTROL= , reg); + if (err) + return err; + + return 0; +} + +static int +mv88e6xxx_led_hw_control_get(struct mv88e6xxx_port *p, int led, unsigned l= ong *rules) +{ + /* The hardware register cannot be read: no inital state determined */ + return -EINVAL; +} + +static int +mv88e6xxx_led0_hw_control_set(struct led_classdev *ldev, unsigned long rul= es) +{ + struct mv88e6xxx_port *p =3D container_of(ldev, struct mv88e6xxx_port, le= d0); + int err; + + mv88e6xxx_reg_lock(p->chip); + err =3D mv88e6xxx_led_hw_control_set(p, 0, rules); + mv88e6xxx_reg_unlock(p->chip); + + return err; +} + +static int +mv88e6xxx_led1_hw_control_set(struct led_classdev *ldev, unsigned long rul= es) +{ + struct mv88e6xxx_port *p =3D container_of(ldev, struct mv88e6xxx_port, le= d1); + int err; + + mv88e6xxx_reg_lock(p->chip); + err =3D mv88e6xxx_led_hw_control_set(p, 1, rules); + mv88e6xxx_reg_unlock(p->chip); + + return err; +} + +static int +mv88e6xxx_led0_hw_control_get(struct led_classdev *ldev, unsigned long *ru= les) +{ + struct mv88e6xxx_port *p =3D container_of(ldev, struct mv88e6xxx_port, le= d0); + + return mv88e6xxx_led_hw_control_get(p, 0, rules); +} + +static int +mv88e6xxx_led1_hw_control_get(struct led_classdev *ldev, unsigned long *ru= les) +{ + struct mv88e6xxx_port *p =3D container_of(ldev, struct mv88e6xxx_port, le= d1); + + return mv88e6xxx_led_hw_control_get(p, 1, rules); +} + +static struct device *mv88e6xxx_led_hw_control_get_device(struct mv88e6xxx= _port *p) +{ + struct dsa_port *dp; + + dp =3D dsa_to_port(p->chip->ds, p->port); + if (!dp) + return NULL; + if (dp->user) + return &dp->user->dev; + return NULL; +} + +static struct device * +mv88e6xxx_led0_hw_control_get_device(struct led_classdev *ldev) +{ + struct mv88e6xxx_port *p =3D container_of(ldev, struct mv88e6xxx_port, le= d0); + + return mv88e6xxx_led_hw_control_get_device(p); +} + +static struct device * +mv88e6xxx_led1_hw_control_get_device(struct led_classdev *ldev) +{ + struct mv88e6xxx_port *p =3D container_of(ldev, struct mv88e6xxx_port, le= d1); + + return mv88e6xxx_led_hw_control_get_device(p); +} + +int mv88e6xxx_port_setup_leds(struct mv88e6xxx_chip *chip, int port) +{ + struct fwnode_handle *led =3D NULL, *leds =3D NULL; + struct led_init_data init_data =3D { }; + unsigned long delay_off =3D 168; + unsigned long delay_on =3D 168; + enum led_default_state state; + struct mv88e6xxx_port *p; + struct led_classdev *l; + struct device *dev; + u32 led_num; + int ret; + + /* LEDs are on ports 1,2,3,4, 5 and 6 (index 0..5), no more */ + if (port > 5) + return -EOPNOTSUPP; + + p =3D &chip->ports[port]; + if (!p->fwnode) + return 0; + p->ledreg =3D 0; + + dev =3D chip->dev; + + leds =3D fwnode_get_named_child_node(p->fwnode, "leds"); + if (!leds) { + dev_info(dev, "No Leds node specified in device tree for port %d!\n", + port); + return 0; + } + + fwnode_for_each_child_node(leds, led) { + /* Reg represent the led number of the port, max 2 + * LEDs can be connected to each port, in some designs + * only one LED is connected. + */ + if (fwnode_property_read_u32(led, "reg", &led_num)) + continue; + if (led_num > 1) { + dev_err(dev, "invalid LED specified port %d\n", port); + continue; + } + + if (led_num =3D=3D 0) + l =3D &p->led0; + else + l =3D &p->led1; + + state =3D led_init_default_state_get(led); + switch (state) { + case LEDS_DEFSTATE_ON: + l->brightness =3D 1; + mv88e6xxx_led_brightness_set(p, led_num, 1); + break; + case LEDS_DEFSTATE_KEEP: + break; + default: + l->brightness =3D 0; + mv88e6xxx_led_brightness_set(p, led_num, 0); + } + + /* Default blinking period for LEDs */ + mv88e6xxx_led_set_blinking_period(p, led_num, &delay_on, &delay_off); + + l->max_brightness =3D 1; + if (led_num =3D=3D 0) { + l->brightness_set_blocking =3D mv88e6xxx_led0_brightness_set_blocking; + l->blink_set =3D mv88e6xxx_led0_blink_set; + l->hw_control_is_supported =3D mv88e6xxx_led0_hw_control_is_supported; + l->hw_control_set =3D mv88e6xxx_led0_hw_control_set; + l->hw_control_get =3D mv88e6xxx_led0_hw_control_get; + l->hw_control_get_device =3D mv88e6xxx_led0_hw_control_get_device; + } else { + l->brightness_set_blocking =3D mv88e6xxx_led1_brightness_set_blocking; + l->blink_set =3D mv88e6xxx_led1_blink_set; + l->hw_control_is_supported =3D mv88e6xxx_led1_hw_control_is_supported; + l->hw_control_set =3D mv88e6xxx_led1_hw_control_set; + l->hw_control_get =3D mv88e6xxx_led1_hw_control_get; + l->hw_control_get_device =3D mv88e6xxx_led1_hw_control_get_device; + } + l->hw_control_trigger =3D "netdev"; + + init_data.default_label =3D ":port"; + init_data.fwnode =3D led; + init_data.devname_mandatory =3D true; + init_data.devicename =3D kasprintf(GFP_KERNEL, "%s:0%d:0%d", chip->info-= >name, + port, led_num); + if (!init_data.devicename) + return -ENOMEM; + + ret =3D devm_led_classdev_register_ext(dev, l, &init_data); + if (ret) + dev_err(dev, "Failed to init LED %d for port %d", led_num, port); + + kfree(init_data.devicename); + } + + return 0; +} diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/p= ort.c index 5394a8cf7bf1..d72bba1969f7 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -12,6 +12,7 @@ #include #include #include +#include =20 #include "chip.h" #include "global2.h" diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/p= ort.h index 86deeb347cbc..723e5f46c7e1 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -294,6 +294,130 @@ /* Offset 0x13: OutFiltered Counter */ #define MV88E6XXX_PORT_OUT_FILTERED 0x13 =20 +/* Offset 0x16: LED Control */ +#define MV88E6XXX_PORT_LED_CONTROL 0x16 +#define MV88E6XXX_PORT_LED_CONTROL_UPDATE BIT(15) +#define MV88E6XXX_PORT_LED_CONTROL_POINTER_MASK GENMASK(14, 12) +#define MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL (0x00 << 12) /* Con= trol for LED 0 and 1 */ +#define MV88E6XXX_PORT_LED_CONTROL_POINTER_STRETCH_BLINK (0x06 << 12) /* S= tetch and Blink Rate */ +#define MV88E6XXX_PORT_LED_CONTROL_POINTER_CNTL_SPECIAL (0x07 << 12) /* C= ontrol for the Port's Special LED */ +#define MV88E6XXX_PORT_LED_CONTROL_DATA_MASK GENMASK(10, 0) +/* Selection masks valid for either port 1,2,3,4 or 5 */ +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL_MASK GENMASK(3, 0) +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL_MASK GENMASK(7, 4) +/* Selection control for LED 0 and 1, ports 5 and 6 only has LED 0 + * Bits Function + * 0..3 LED 0 control selector on ports 1-5 + * 4..7 LED 1 control selector on ports 1-4 on port 5 this controls LED 0= of port 6 + * + * Sel Port LED Function + * 0 1-4 0 Link/Act/Speed by Blink Rate (off=3Dno link, on=3Dlink, bl= ink=3Dactivity, blink speed=3Dlink speed) + * 1-4 1 Port 2's Special LED + * 5-6 0 Port 5 Link/Act (off=3Dno link, on=3Dlink, blink=3Dactivit= y) + * 5-6 1 Port 6 Link/Act (off=3Dno link, on=3Dlink 1000, blink=3Dac= tivity) + * 1 1-4 0 100/1000 Link/Act (off=3Dno link, on=3D100 or 1000 link, b= link=3Dactivity) + * 1-4 1 10/100 Link Act (off=3Dno link, on=3D10 or 100 link, blink= =3Dactivity) + * 5-6 0 Fiber 100 Link/Act (off=3Dno link, on=3Dlink 100, blink=3D= activity) + * 5-6 1 Fiber 1000 Link/Act (off=3Dno link, on=3Dlink 1000, blink= =3Dactivity) + * 2 1-4 0 1000 Link/Act (off=3Dno link, on=3Dlink 1000, blink=3Dacti= vity) + * 1-4 1 10/100 Link/Act (off=3Dno link, on=3D10 or 100 link, blink= =3Dactivity) + * 5-6 0 Fiber 1000 Link/Act (off=3Dno link, on=3Dlink 1000, blink= =3Dactivity) + * 5-6 1 Fiber 100 Link/Act (off=3Dno link, on=3Dlink 100, blink=3D= activity) + * 3 1-4 0 Link/Act (off=3Dno link, on=3Dlink, blink=3Dactivity) + * 1-4 1 1000 Link (off=3Dno link, on=3D1000 link) + * 5-6 0 Port 0's Special LED + * 5-6 1 Fiber Link (off=3Dno link, on=3Dlink) + * 4 1-4 0 Port 0's Special LED + * 1-4 1 Port 1's Special LED + * 5-6 0 Port 1's Special LED + * 5-6 1 Port 5 Link/Act (off=3Dno link, on=3Dlink, blink=3Dactivit= y) + * 5 1-4 0 Reserved + * 1-4 1 Reserved + * 5-6 0 Port 2's Special LED + * 5-6 1 Port 6 Link (off=3Dno link, on=3Dlink) + * 6 1-4 0 Duplex/Collision (off=3Dhalf-duplex,on=3Dfull-duplex,blink= =3Dcollision) + * 1-4 1 10/1000 Link/Act (off=3Dno link, on=3D10 or 1000 link, bli= nk=3Dactivity) + * 5-6 0 Port 5 Duplex/Collision (off=3Dhalf-duplex, on=3Dfull-dupl= ex, blink=3Dcol) + * 5-6 1 Port 6 Duplex/Collision (off=3Dhalf-duplex, on=3Dfull-dupl= ex, blink=3Dcol) + * 7 1-4 0 10/1000 Link/Act (off=3Dno link, on=3D10 or 1000 link, bli= nk=3Dactivity) + * 1-4 1 10/1000 Link (off=3Dno link, on=3D10 or 1000 link) + * 5-6 0 Port 5 Link/Act/Speed by Blink rate (off=3Dno link, on=3Dl= ink, blink=3Dactivity, blink speed=3Dlink speed) + * 5-6 1 Port 6 Link/Act/Speed by Blink rate (off=3Dno link, on=3Dl= ink, blink=3Dactivity, blink speed=3Dlink speed) + * 8 1-4 0 Link (off=3Dno link, on=3Dlink) + * 1-4 1 Activity (off=3Dno link, blink on=3Dactivity) + * 5-6 0 Port 6 Link/Act (off=3Dno link, on=3Dlink, blink=3Dactivit= y) + * 5-6 1 Port 0's Special LED + * 9 1-4 0 10 Link (off=3Dno link, on=3D10 link) + * 1-4 1 100 Link (off=3Dno link, on=3D100 link) + * 5-6 0 Reserved + * 5-6 1 Port 1's Special LED + * a 1-4 0 10 Link/Act (off=3Dno link, on=3D10 link, blink=3Dactivity) + * 1-4 1 100 Link/Act (off=3Dno link, on=3D100 link, blink=3Dactivi= ty) + * 5-6 0 Reserved + * 5-6 1 Port 2's Special LED + * b 1-4 0 100/1000 Link (off=3Dno link, on=3D100 or 1000 link) + * 1-4 1 10/100 Link (off=3Dno link, on=3D100 link, blink=3Dactivit= y) + * 5-6 0 Reserved + * 5-6 1 Reserved + * c * * PTP Act (blink on=3DPTP activity) + * d * * Force Blink + * e * * Force Off + * f * * Force On + */ +/* Select LED0 output */ +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL0 0x0 +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL1 0x1 +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL2 0x2 +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL3 0x3 +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL4 0x4 +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL5 0x5 +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL6 0x6 +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL7 0x7 +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL8 0x8 +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL9 0x9 +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SELA 0xa +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SELB 0xb +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SELC 0xc +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SELD 0xd +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SELE 0xe +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SELF 0xf +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL0 (0x0 << 4) +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL1 (0x1 << 4) +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL2 (0x2 << 4) +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL3 (0x3 << 4) +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL4 (0x4 << 4) +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL5 (0x5 << 4) +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL6 (0x6 << 4) +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL7 (0x7 << 4) +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL8 (0x8 << 4) +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL9 (0x9 << 4) +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SELA (0xa << 4) +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SELB (0xb << 4) +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SELC (0xc << 4) +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SELD (0xd << 4) +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SELE (0xe << 4) +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SELF (0xf << 4) +/* Stretch and Blink Rate Control (Index 0x06 of LED Control) */ +/* Pulse Stretch Selection for all LED's on this port */ +#define MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_NONE (0 << 4) +#define MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_21MS (1 << 4) +#define MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_42MS (2 << 4) +#define MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_84MS (3 << 4) +#define MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_168MS (4 << 4) +/* Blink Rate Selection for all LEDs on this port */ +#define MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_21MS 0 +#define MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_42MS 1 +#define MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_84MS 2 +#define MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_168MS 3 +#define MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_336MS 4 +#define MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_672MS 5 + /* Control for Special LED (Index 0x7 of LED Control on Port0) */ +#define MV88E6XXX_PORT_LED_CONTROL_0x07_P0_LAN_LINKACT_SHIFT 0 /* bits 6:0= LAN Link Activity LED */ +/* Control for Special LED (Index 0x7 of LED Control on Port 1) */ +#define MV88E6XXX_PORT_LED_CONTROL_0x07_P1_WAN_LINKACT_SHIFT 0 /* bits 6:0= WAN Link Activity LED */ +/* Control for Special LED (Index 0x7 of LED Control on Port 2) */ +#define MV88E6XXX_PORT_LED_CONTROL_0x07_P2_PTP_ACT 0 /* bits 6:0 PTP Acti= vity */ + /* Offset 0x18: IEEE Priority Mapping Table */ #define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE 0x18 #define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_UPDATE 0x8000 @@ -442,6 +566,7 @@ int mv88e6393x_port_set_cmode(struct mv88e6xxx_chip *ch= ip, int port, phy_interface_t mode); int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cm= ode); int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cm= ode); +int mv88e6xxx_port_setup_leds(struct mv88e6xxx_chip *chip, int port); int mv88e6xxx_port_drop_untagged(struct mv88e6xxx_chip *chip, int port, bool drop_untagged); int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port, bool = map); --- base-commit: ae2dae528d50e7968ece0a00246e21c1d083d15f change-id: 20231123-mv88e6xxx-leds-b8b9b316ff98 Best regards, --=20 Linus Walleij