123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435 |
- From 1e264f9d2918b5737023c44a23ae04def1095210 Mon Sep 17 00:00:00 2001
- From: Christian Marangi <[email protected]>
- Date: Mon, 17 Apr 2023 17:17:24 +0200
- Subject: [PATCH 2/9] net: dsa: qca8k: add LEDs basic support
- Add LEDs basic support for qca8k Switch Family by adding basic
- brightness_set() support.
- Since these LEDs refelect port status, the default label is set to
- ":port". DT binding should describe the color and function of the
- LEDs using standard LEDs api.
- Each LED always have the device name as prefix. The device name is
- composed from the mii bus id and the PHY addr resulting in example
- names like:
- - qca8k-0.0:00:amber:lan
- - qca8k-0.0:00:white:lan
- - qca8k-0.0:01:amber:lan
- - qca8k-0.0:01:white:lan
- These LEDs supports only blocking variant of the brightness_set()
- function since they can sleep during access of the switch leds to set
- the brightness.
- While at it add to the qca8k header file each mode defined by the Switch
- Documentation for future use.
- Signed-off-by: Christian Marangi <[email protected]>
- Reviewed-by: Florian Fainelli <[email protected]>
- Signed-off-by: David S. Miller <[email protected]>
- ---
- drivers/net/dsa/qca/Kconfig | 8 ++
- drivers/net/dsa/qca/Makefile | 3 +
- drivers/net/dsa/qca/qca8k-8xxx.c | 5 +
- drivers/net/dsa/qca/qca8k-leds.c | 239 +++++++++++++++++++++++++++++++
- drivers/net/dsa/qca/qca8k.h | 60 ++++++++
- drivers/net/dsa/qca/qca8k_leds.h | 16 +++
- 6 files changed, 331 insertions(+)
- create mode 100644 drivers/net/dsa/qca/qca8k-leds.c
- create mode 100644 drivers/net/dsa/qca/qca8k_leds.h
- --- a/drivers/net/dsa/qca/Kconfig
- +++ b/drivers/net/dsa/qca/Kconfig
- @@ -15,3 +15,11 @@ config NET_DSA_QCA8K
- help
- This enables support for the Qualcomm Atheros QCA8K Ethernet
- switch chips.
- +
- +config NET_DSA_QCA8K_LEDS_SUPPORT
- + bool "Qualcomm Atheros QCA8K Ethernet switch family LEDs support"
- + depends on NET_DSA_QCA8K
- + depends on LEDS_CLASS
- + help
- + This enabled support for LEDs present on the Qualcomm Atheros
- + QCA8K Ethernet switch chips.
- --- a/drivers/net/dsa/qca/Makefile
- +++ b/drivers/net/dsa/qca/Makefile
- @@ -2,3 +2,6 @@
- obj-$(CONFIG_NET_DSA_AR9331) += ar9331.o
- obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o
- qca8k-y += qca8k-common.o qca8k-8xxx.o
- +ifdef CONFIG_NET_DSA_QCA8K_LEDS_SUPPORT
- +qca8k-y += qca8k-leds.o
- +endif
- --- a/drivers/net/dsa/qca/qca8k-8xxx.c
- +++ b/drivers/net/dsa/qca/qca8k-8xxx.c
- @@ -22,6 +22,7 @@
- #include <linux/dsa/tag_qca.h>
-
- #include "qca8k.h"
- +#include "qca8k_leds.h"
-
- static void
- qca8k_split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page)
- @@ -1851,6 +1852,10 @@ qca8k_setup(struct dsa_switch *ds)
- if (ret)
- return ret;
-
- + ret = qca8k_setup_led_ctrl(priv);
- + if (ret)
- + return ret;
- +
- qca8k_setup_pcs(priv, &priv->pcs_port_0, 0);
- qca8k_setup_pcs(priv, &priv->pcs_port_6, 6);
-
- --- /dev/null
- +++ b/drivers/net/dsa/qca/qca8k-leds.c
- @@ -0,0 +1,239 @@
- +// SPDX-License-Identifier: GPL-2.0
- +#include <linux/regmap.h>
- +#include <net/dsa.h>
- +
- +#include "qca8k.h"
- +#include "qca8k_leds.h"
- +
- +static int
- +qca8k_get_enable_led_reg(int port_num, int led_num, struct qca8k_led_pattern_en *reg_info)
- +{
- + switch (port_num) {
- + case 0:
- + reg_info->reg = QCA8K_LED_CTRL_REG(led_num);
- + reg_info->shift = QCA8K_LED_PHY0123_CONTROL_RULE_SHIFT;
- + break;
- + case 1:
- + case 2:
- + case 3:
- + /* Port 123 are controlled on a different reg */
- + reg_info->reg = QCA8K_LED_CTRL3_REG;
- + reg_info->shift = QCA8K_LED_PHY123_PATTERN_EN_SHIFT(port_num, led_num);
- + break;
- + case 4:
- + reg_info->reg = QCA8K_LED_CTRL_REG(led_num);
- + reg_info->shift = QCA8K_LED_PHY4_CONTROL_RULE_SHIFT;
- + break;
- + default:
- + return -EINVAL;
- + }
- +
- + return 0;
- +}
- +
- +static int
- +qca8k_led_brightness_set(struct qca8k_led *led,
- + enum led_brightness brightness)
- +{
- + struct qca8k_led_pattern_en reg_info;
- + struct qca8k_priv *priv = led->priv;
- + u32 mask, val;
- +
- + qca8k_get_enable_led_reg(led->port_num, led->led_num, ®_info);
- +
- + val = QCA8K_LED_ALWAYS_OFF;
- + if (brightness)
- + val = QCA8K_LED_ALWAYS_ON;
- +
- + /* HW regs to control brightness is special and port 1-2-3
- + * are placed in a different reg.
- + *
- + * To control port 0 brightness:
- + * - the 2 bit (15, 14) of:
- + * - QCA8K_LED_CTRL0_REG for led1
- + * - QCA8K_LED_CTRL1_REG for led2
- + * - QCA8K_LED_CTRL2_REG for led3
- + *
- + * To control port 4:
- + * - the 2 bit (31, 30) of:
- + * - QCA8K_LED_CTRL0_REG for led1
- + * - QCA8K_LED_CTRL1_REG for led2
- + * - QCA8K_LED_CTRL2_REG for led3
- + *
- + * To control port 1:
- + * - the 2 bit at (9, 8) of QCA8K_LED_CTRL3_REG are used for led1
- + * - the 2 bit at (11, 10) of QCA8K_LED_CTRL3_REG are used for led2
- + * - the 2 bit at (13, 12) of QCA8K_LED_CTRL3_REG are used for led3
- + *
- + * To control port 2:
- + * - the 2 bit at (15, 14) of QCA8K_LED_CTRL3_REG are used for led1
- + * - the 2 bit at (17, 16) of QCA8K_LED_CTRL3_REG are used for led2
- + * - the 2 bit at (19, 18) of QCA8K_LED_CTRL3_REG are used for led3
- + *
- + * To control port 3:
- + * - the 2 bit at (21, 20) of QCA8K_LED_CTRL3_REG are used for led1
- + * - the 2 bit at (23, 22) of QCA8K_LED_CTRL3_REG are used for led2
- + * - the 2 bit at (25, 24) of QCA8K_LED_CTRL3_REG are used for led3
- + *
- + * To abstract this and have less code, we use the port and led numm
- + * to calculate the shift and the correct reg due to this problem of
- + * not having a 1:1 map of LED with the regs.
- + */
- + if (led->port_num == 0 || led->port_num == 4) {
- + mask = QCA8K_LED_PATTERN_EN_MASK;
- + val <<= QCA8K_LED_PATTERN_EN_SHIFT;
- + } else {
- + mask = QCA8K_LED_PHY123_PATTERN_EN_MASK;
- + }
- +
- + return regmap_update_bits(priv->regmap, reg_info.reg,
- + mask << reg_info.shift,
- + val << reg_info.shift);
- +}
- +
- +static int
- +qca8k_cled_brightness_set_blocking(struct led_classdev *ldev,
- + enum led_brightness brightness)
- +{
- + struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev);
- +
- + return qca8k_led_brightness_set(led, brightness);
- +}
- +
- +static enum led_brightness
- +qca8k_led_brightness_get(struct qca8k_led *led)
- +{
- + struct qca8k_led_pattern_en reg_info;
- + struct qca8k_priv *priv = led->priv;
- + u32 val;
- + int ret;
- +
- + qca8k_get_enable_led_reg(led->port_num, led->led_num, ®_info);
- +
- + ret = regmap_read(priv->regmap, reg_info.reg, &val);
- + if (ret)
- + return 0;
- +
- + val >>= reg_info.shift;
- +
- + if (led->port_num == 0 || led->port_num == 4) {
- + val &= QCA8K_LED_PATTERN_EN_MASK;
- + val >>= QCA8K_LED_PATTERN_EN_SHIFT;
- + } else {
- + val &= QCA8K_LED_PHY123_PATTERN_EN_MASK;
- + }
- +
- + /* Assume brightness ON only when the LED is set to always ON */
- + return val == QCA8K_LED_ALWAYS_ON;
- +}
- +
- +static int
- +qca8k_parse_port_leds(struct qca8k_priv *priv, struct fwnode_handle *port, int port_num)
- +{
- + struct fwnode_handle *led = NULL, *leds = NULL;
- + struct led_init_data init_data = { };
- + struct dsa_switch *ds = priv->ds;
- + enum led_default_state state;
- + struct qca8k_led *port_led;
- + int led_num, led_index;
- + int ret;
- +
- + leds = fwnode_get_named_child_node(port, "leds");
- + if (!leds) {
- + dev_dbg(priv->dev, "No Leds node specified in device tree for port %d!\n",
- + port_num);
- + return 0;
- + }
- +
- + fwnode_for_each_child_node(leds, led) {
- + /* Reg represent the led number of the port.
- + * Each port can have at most 3 leds attached
- + * Commonly:
- + * 1. is gigabit led
- + * 2. is mbit led
- + * 3. additional status led
- + */
- + if (fwnode_property_read_u32(led, "reg", &led_num))
- + continue;
- +
- + if (led_num >= QCA8K_LED_PORT_COUNT) {
- + dev_warn(priv->dev, "Invalid LED reg %d defined for port %d",
- + led_num, port_num);
- + continue;
- + }
- +
- + led_index = QCA8K_LED_PORT_INDEX(port_num, led_num);
- +
- + port_led = &priv->ports_led[led_index];
- + port_led->port_num = port_num;
- + port_led->led_num = led_num;
- + port_led->priv = priv;
- +
- + state = led_init_default_state_get(led);
- + switch (state) {
- + case LEDS_DEFSTATE_ON:
- + port_led->cdev.brightness = 1;
- + qca8k_led_brightness_set(port_led, 1);
- + break;
- + case LEDS_DEFSTATE_KEEP:
- + port_led->cdev.brightness =
- + qca8k_led_brightness_get(port_led);
- + break;
- + default:
- + port_led->cdev.brightness = 0;
- + qca8k_led_brightness_set(port_led, 0);
- + }
- +
- + port_led->cdev.max_brightness = 1;
- + port_led->cdev.brightness_set_blocking = qca8k_cled_brightness_set_blocking;
- + init_data.default_label = ":port";
- + init_data.fwnode = led;
- + init_data.devname_mandatory = true;
- + init_data.devicename = kasprintf(GFP_KERNEL, "%s:0%d", ds->slave_mii_bus->id,
- + port_num);
- + if (!init_data.devicename)
- + return -ENOMEM;
- +
- + ret = devm_led_classdev_register_ext(priv->dev, &port_led->cdev, &init_data);
- + if (ret)
- + dev_warn(priv->dev, "Failed to init LED %d for port %d", led_num, port_num);
- +
- + kfree(init_data.devicename);
- + }
- +
- + return 0;
- +}
- +
- +int
- +qca8k_setup_led_ctrl(struct qca8k_priv *priv)
- +{
- + struct fwnode_handle *ports, *port;
- + int port_num;
- + int ret;
- +
- + ports = device_get_named_child_node(priv->dev, "ports");
- + if (!ports) {
- + dev_info(priv->dev, "No ports node specified in device tree!");
- + return 0;
- + }
- +
- + fwnode_for_each_child_node(ports, port) {
- + if (fwnode_property_read_u32(port, "reg", &port_num))
- + continue;
- +
- + /* Skip checking for CPU port 0 and CPU port 6 as not supported */
- + if (port_num == 0 || port_num == 6)
- + continue;
- +
- + /* Each port can have at most 3 different leds attached.
- + * Switch port starts from 0 to 6, but port 0 and 6 are CPU
- + * port. The port index needs to be decreased by one to identify
- + * the correct port for LED setup.
- + */
- + ret = qca8k_parse_port_leds(priv, port, qca8k_port_to_phy(port_num));
- + if (ret)
- + return ret;
- + }
- +
- + return 0;
- +}
- --- a/drivers/net/dsa/qca/qca8k.h
- +++ b/drivers/net/dsa/qca/qca8k.h
- @@ -11,6 +11,7 @@
- #include <linux/delay.h>
- #include <linux/regmap.h>
- #include <linux/gpio.h>
- +#include <linux/leds.h>
- #include <linux/dsa/tag_qca.h>
-
- #define QCA8K_ETHERNET_MDIO_PRIORITY 7
- @@ -85,6 +86,51 @@
- #define QCA8K_MDIO_MASTER_DATA(x) FIELD_PREP(QCA8K_MDIO_MASTER_DATA_MASK, x)
- #define QCA8K_MDIO_MASTER_MAX_PORTS 5
- #define QCA8K_MDIO_MASTER_MAX_REG 32
- +
- +/* LED control register */
- +#define QCA8K_LED_PORT_COUNT 3
- +#define QCA8K_LED_COUNT ((QCA8K_NUM_PORTS - QCA8K_NUM_CPU_PORTS) * QCA8K_LED_PORT_COUNT)
- +#define QCA8K_LED_RULE_COUNT 6
- +#define QCA8K_LED_RULE_MAX 11
- +#define QCA8K_LED_PORT_INDEX(_phy, _led) (((_phy) * QCA8K_LED_PORT_COUNT) + (_led))
- +
- +#define QCA8K_LED_PHY123_PATTERN_EN_SHIFT(_phy, _led) ((((_phy) - 1) * 6) + 8 + (2 * (_led)))
- +#define QCA8K_LED_PHY123_PATTERN_EN_MASK GENMASK(1, 0)
- +
- +#define QCA8K_LED_PHY0123_CONTROL_RULE_SHIFT 0
- +#define QCA8K_LED_PHY4_CONTROL_RULE_SHIFT 16
- +
- +#define QCA8K_LED_CTRL_REG(_i) (0x050 + (_i) * 4)
- +#define QCA8K_LED_CTRL0_REG 0x50
- +#define QCA8K_LED_CTRL1_REG 0x54
- +#define QCA8K_LED_CTRL2_REG 0x58
- +#define QCA8K_LED_CTRL3_REG 0x5C
- +#define QCA8K_LED_CTRL_SHIFT(_i) (((_i) % 2) * 16)
- +#define QCA8K_LED_CTRL_MASK GENMASK(15, 0)
- +#define QCA8K_LED_RULE_MASK GENMASK(13, 0)
- +#define QCA8K_LED_BLINK_FREQ_MASK GENMASK(1, 0)
- +#define QCA8K_LED_BLINK_FREQ_SHITF 0
- +#define QCA8K_LED_BLINK_2HZ 0
- +#define QCA8K_LED_BLINK_4HZ 1
- +#define QCA8K_LED_BLINK_8HZ 2
- +#define QCA8K_LED_BLINK_AUTO 3
- +#define QCA8K_LED_LINKUP_OVER_MASK BIT(2)
- +#define QCA8K_LED_TX_BLINK_MASK BIT(4)
- +#define QCA8K_LED_RX_BLINK_MASK BIT(5)
- +#define QCA8K_LED_COL_BLINK_MASK BIT(7)
- +#define QCA8K_LED_LINK_10M_EN_MASK BIT(8)
- +#define QCA8K_LED_LINK_100M_EN_MASK BIT(9)
- +#define QCA8K_LED_LINK_1000M_EN_MASK BIT(10)
- +#define QCA8K_LED_POWER_ON_LIGHT_MASK BIT(11)
- +#define QCA8K_LED_HALF_DUPLEX_MASK BIT(12)
- +#define QCA8K_LED_FULL_DUPLEX_MASK BIT(13)
- +#define QCA8K_LED_PATTERN_EN_MASK GENMASK(15, 14)
- +#define QCA8K_LED_PATTERN_EN_SHIFT 14
- +#define QCA8K_LED_ALWAYS_OFF 0
- +#define QCA8K_LED_ALWAYS_BLINK_4HZ 1
- +#define QCA8K_LED_ALWAYS_ON 2
- +#define QCA8K_LED_RULE_CONTROLLED 3
- +
- #define QCA8K_GOL_MAC_ADDR0 0x60
- #define QCA8K_GOL_MAC_ADDR1 0x64
- #define QCA8K_MAX_FRAME_SIZE 0x78
- @@ -382,6 +428,19 @@ struct qca8k_pcs {
- int port;
- };
-
- +struct qca8k_led_pattern_en {
- + u32 reg;
- + u8 shift;
- +};
- +
- +struct qca8k_led {
- + u8 port_num;
- + u8 led_num;
- + u16 old_rule;
- + struct qca8k_priv *priv;
- + struct led_classdev cdev;
- +};
- +
- struct qca8k_priv {
- u8 switch_id;
- u8 switch_revision;
- @@ -406,6 +465,7 @@ struct qca8k_priv {
- struct qca8k_pcs pcs_port_0;
- struct qca8k_pcs pcs_port_6;
- const struct qca8k_match_data *info;
- + struct qca8k_led ports_led[QCA8K_LED_COUNT];
- };
-
- struct qca8k_mib_desc {
- --- /dev/null
- +++ b/drivers/net/dsa/qca/qca8k_leds.h
- @@ -0,0 +1,16 @@
- +/* SPDX-License-Identifier: GPL-2.0-only */
- +
- +#ifndef __QCA8K_LEDS_H
- +#define __QCA8K_LEDS_H
- +
- +/* Leds Support function */
- +#ifdef CONFIG_NET_DSA_QCA8K_LEDS_SUPPORT
- +int qca8k_setup_led_ctrl(struct qca8k_priv *priv);
- +#else
- +static inline int qca8k_setup_led_ctrl(struct qca8k_priv *priv)
- +{
- + return 0;
- +}
- +#endif
- +
- +#endif /* __QCA8K_LEDS_H */
|