diff options
Diffstat (limited to 'target/linux/generic/backport-5.15/818-v6.5-12-net-dsa-qca8k-implement-hw_control-ops.patch')
-rw-r--r-- | target/linux/generic/backport-5.15/818-v6.5-12-net-dsa-qca8k-implement-hw_control-ops.patch | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/target/linux/generic/backport-5.15/818-v6.5-12-net-dsa-qca8k-implement-hw_control-ops.patch b/target/linux/generic/backport-5.15/818-v6.5-12-net-dsa-qca8k-implement-hw_control-ops.patch new file mode 100644 index 0000000000..ad76d89b7b --- /dev/null +++ b/target/linux/generic/backport-5.15/818-v6.5-12-net-dsa-qca8k-implement-hw_control-ops.patch @@ -0,0 +1,200 @@ +From e0256648c831af13cbfe4a1787327fcec01c2807 Mon Sep 17 00:00:00 2001 +From: Christian Marangi <ansuelsmth@gmail.com> +Date: Mon, 29 May 2023 18:32:42 +0200 +Subject: [PATCH 12/13] net: dsa: qca8k: implement hw_control ops + +Implement hw_control ops to drive Switch LEDs based on hardware events. + +Netdev trigger is the declared supported trigger for hw control +operation and supports the following mode: +- tx +- rx + +When hw_control_set is called, LEDs are set to follow the requested +mode. +Each LEDs will blink at 4Hz by default. + +Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/qca/qca8k-leds.c | 154 +++++++++++++++++++++++++++++++ + 1 file changed, 154 insertions(+) + +--- a/drivers/net/dsa/qca/qca8k-leds.c ++++ b/drivers/net/dsa/qca/qca8k-leds.c +@@ -32,6 +32,43 @@ qca8k_get_enable_led_reg(int port_num, i + } + + static int ++qca8k_get_control_led_reg(int port_num, int led_num, struct qca8k_led_pattern_en *reg_info) ++{ ++ reg_info->reg = QCA8K_LED_CTRL_REG(led_num); ++ ++ /* 6 total control rule: ++ * 3 control rules for phy0-3 that applies to all their leds ++ * 3 control rules for phy4 ++ */ ++ if (port_num == 4) ++ reg_info->shift = QCA8K_LED_PHY4_CONTROL_RULE_SHIFT; ++ else ++ reg_info->shift = QCA8K_LED_PHY0123_CONTROL_RULE_SHIFT; ++ ++ return 0; ++} ++ ++static int ++qca8k_parse_netdev(unsigned long rules, u32 *offload_trigger) ++{ ++ /* Parsing specific to netdev trigger */ ++ if (test_bit(TRIGGER_NETDEV_TX, &rules)) ++ *offload_trigger |= QCA8K_LED_TX_BLINK_MASK; ++ if (test_bit(TRIGGER_NETDEV_RX, &rules)) ++ *offload_trigger |= QCA8K_LED_RX_BLINK_MASK; ++ ++ if (rules && !*offload_trigger) ++ return -EOPNOTSUPP; ++ ++ /* Enable some default rule by default to the requested mode: ++ * - Blink at 4Hz by default ++ */ ++ *offload_trigger |= QCA8K_LED_BLINK_4HZ; ++ ++ return 0; ++} ++ ++static int + qca8k_led_brightness_set(struct qca8k_led *led, + enum led_brightness brightness) + { +@@ -165,6 +202,119 @@ qca8k_cled_blink_set(struct led_classdev + } + + static int ++qca8k_cled_trigger_offload(struct led_classdev *ldev, bool enable) ++{ ++ struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev); ++ ++ struct qca8k_led_pattern_en reg_info; ++ struct qca8k_priv *priv = led->priv; ++ u32 mask, val = QCA8K_LED_ALWAYS_OFF; ++ ++ qca8k_get_enable_led_reg(led->port_num, led->led_num, ®_info); ++ ++ if (enable) ++ val = QCA8K_LED_RULE_CONTROLLED; ++ ++ 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 bool ++qca8k_cled_hw_control_status(struct led_classdev *ldev) ++{ ++ struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev); ++ ++ struct qca8k_led_pattern_en reg_info; ++ struct qca8k_priv *priv = led->priv; ++ u32 val; ++ ++ qca8k_get_enable_led_reg(led->port_num, led->led_num, ®_info); ++ ++ regmap_read(priv->regmap, reg_info.reg, &val); ++ ++ 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; ++ } ++ ++ return val == QCA8K_LED_RULE_CONTROLLED; ++} ++ ++static int ++qca8k_cled_hw_control_is_supported(struct led_classdev *ldev, unsigned long rules) ++{ ++ u32 offload_trigger = 0; ++ ++ return qca8k_parse_netdev(rules, &offload_trigger); ++} ++ ++static int ++qca8k_cled_hw_control_set(struct led_classdev *ldev, unsigned long rules) ++{ ++ struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev); ++ struct qca8k_led_pattern_en reg_info; ++ struct qca8k_priv *priv = led->priv; ++ u32 offload_trigger = 0; ++ int ret; ++ ++ ret = qca8k_parse_netdev(rules, &offload_trigger); ++ if (ret) ++ return ret; ++ ++ ret = qca8k_cled_trigger_offload(ldev, true); ++ if (ret) ++ return ret; ++ ++ qca8k_get_control_led_reg(led->port_num, led->led_num, ®_info); ++ ++ return regmap_update_bits(priv->regmap, reg_info.reg, ++ QCA8K_LED_RULE_MASK << reg_info.shift, ++ offload_trigger << reg_info.shift); ++} ++ ++static int ++qca8k_cled_hw_control_get(struct led_classdev *ldev, unsigned long *rules) ++{ ++ struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev); ++ struct qca8k_led_pattern_en reg_info; ++ struct qca8k_priv *priv = led->priv; ++ u32 val; ++ int ret; ++ ++ /* With hw control not active return err */ ++ if (!qca8k_cled_hw_control_status(ldev)) ++ return -EINVAL; ++ ++ qca8k_get_control_led_reg(led->port_num, led->led_num, ®_info); ++ ++ ret = regmap_read(priv->regmap, reg_info.reg, &val); ++ if (ret) ++ return ret; ++ ++ val >>= reg_info.shift; ++ val &= QCA8K_LED_RULE_MASK; ++ ++ /* Parsing specific to netdev trigger */ ++ if (val & QCA8K_LED_TX_BLINK_MASK) ++ set_bit(TRIGGER_NETDEV_TX, rules); ++ if (val & QCA8K_LED_RX_BLINK_MASK) ++ set_bit(TRIGGER_NETDEV_RX, rules); ++ ++ return 0; ++} ++ ++static int + qca8k_parse_port_leds(struct qca8k_priv *priv, struct fwnode_handle *port, int port_num) + { + struct fwnode_handle *led = NULL, *leds = NULL; +@@ -224,6 +374,10 @@ qca8k_parse_port_leds(struct qca8k_priv + port_led->cdev.max_brightness = 1; + port_led->cdev.brightness_set_blocking = qca8k_cled_brightness_set_blocking; + port_led->cdev.blink_set = qca8k_cled_blink_set; ++ port_led->cdev.hw_control_is_supported = qca8k_cled_hw_control_is_supported; ++ port_led->cdev.hw_control_set = qca8k_cled_hw_control_set; ++ port_led->cdev.hw_control_get = qca8k_cled_hw_control_get; ++ port_led->cdev.hw_control_trigger = "netdev"; + init_data.default_label = ":port"; + init_data.fwnode = led; + init_data.devname_mandatory = true; |