818-v6.5-12-net-dsa-qca8k-implement-hw_control-ops.patch 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. From e0256648c831af13cbfe4a1787327fcec01c2807 Mon Sep 17 00:00:00 2001
  2. From: Christian Marangi <[email protected]>
  3. Date: Mon, 29 May 2023 18:32:42 +0200
  4. Subject: [PATCH 12/13] net: dsa: qca8k: implement hw_control ops
  5. Implement hw_control ops to drive Switch LEDs based on hardware events.
  6. Netdev trigger is the declared supported trigger for hw control
  7. operation and supports the following mode:
  8. - tx
  9. - rx
  10. When hw_control_set is called, LEDs are set to follow the requested
  11. mode.
  12. Each LEDs will blink at 4Hz by default.
  13. Signed-off-by: Christian Marangi <[email protected]>
  14. Reviewed-by: Andrew Lunn <[email protected]>
  15. Signed-off-by: David S. Miller <[email protected]>
  16. ---
  17. drivers/net/dsa/qca/qca8k-leds.c | 154 +++++++++++++++++++++++++++++++
  18. 1 file changed, 154 insertions(+)
  19. --- a/drivers/net/dsa/qca/qca8k-leds.c
  20. +++ b/drivers/net/dsa/qca/qca8k-leds.c
  21. @@ -32,6 +32,43 @@ qca8k_get_enable_led_reg(int port_num, i
  22. }
  23. static int
  24. +qca8k_get_control_led_reg(int port_num, int led_num, struct qca8k_led_pattern_en *reg_info)
  25. +{
  26. + reg_info->reg = QCA8K_LED_CTRL_REG(led_num);
  27. +
  28. + /* 6 total control rule:
  29. + * 3 control rules for phy0-3 that applies to all their leds
  30. + * 3 control rules for phy4
  31. + */
  32. + if (port_num == 4)
  33. + reg_info->shift = QCA8K_LED_PHY4_CONTROL_RULE_SHIFT;
  34. + else
  35. + reg_info->shift = QCA8K_LED_PHY0123_CONTROL_RULE_SHIFT;
  36. +
  37. + return 0;
  38. +}
  39. +
  40. +static int
  41. +qca8k_parse_netdev(unsigned long rules, u32 *offload_trigger)
  42. +{
  43. + /* Parsing specific to netdev trigger */
  44. + if (test_bit(TRIGGER_NETDEV_TX, &rules))
  45. + *offload_trigger |= QCA8K_LED_TX_BLINK_MASK;
  46. + if (test_bit(TRIGGER_NETDEV_RX, &rules))
  47. + *offload_trigger |= QCA8K_LED_RX_BLINK_MASK;
  48. +
  49. + if (rules && !*offload_trigger)
  50. + return -EOPNOTSUPP;
  51. +
  52. + /* Enable some default rule by default to the requested mode:
  53. + * - Blink at 4Hz by default
  54. + */
  55. + *offload_trigger |= QCA8K_LED_BLINK_4HZ;
  56. +
  57. + return 0;
  58. +}
  59. +
  60. +static int
  61. qca8k_led_brightness_set(struct qca8k_led *led,
  62. enum led_brightness brightness)
  63. {
  64. @@ -165,6 +202,119 @@ qca8k_cled_blink_set(struct led_classdev
  65. }
  66. static int
  67. +qca8k_cled_trigger_offload(struct led_classdev *ldev, bool enable)
  68. +{
  69. + struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev);
  70. +
  71. + struct qca8k_led_pattern_en reg_info;
  72. + struct qca8k_priv *priv = led->priv;
  73. + u32 mask, val = QCA8K_LED_ALWAYS_OFF;
  74. +
  75. + qca8k_get_enable_led_reg(led->port_num, led->led_num, &reg_info);
  76. +
  77. + if (enable)
  78. + val = QCA8K_LED_RULE_CONTROLLED;
  79. +
  80. + if (led->port_num == 0 || led->port_num == 4) {
  81. + mask = QCA8K_LED_PATTERN_EN_MASK;
  82. + val <<= QCA8K_LED_PATTERN_EN_SHIFT;
  83. + } else {
  84. + mask = QCA8K_LED_PHY123_PATTERN_EN_MASK;
  85. + }
  86. +
  87. + return regmap_update_bits(priv->regmap, reg_info.reg, mask << reg_info.shift,
  88. + val << reg_info.shift);
  89. +}
  90. +
  91. +static bool
  92. +qca8k_cled_hw_control_status(struct led_classdev *ldev)
  93. +{
  94. + struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev);
  95. +
  96. + struct qca8k_led_pattern_en reg_info;
  97. + struct qca8k_priv *priv = led->priv;
  98. + u32 val;
  99. +
  100. + qca8k_get_enable_led_reg(led->port_num, led->led_num, &reg_info);
  101. +
  102. + regmap_read(priv->regmap, reg_info.reg, &val);
  103. +
  104. + val >>= reg_info.shift;
  105. +
  106. + if (led->port_num == 0 || led->port_num == 4) {
  107. + val &= QCA8K_LED_PATTERN_EN_MASK;
  108. + val >>= QCA8K_LED_PATTERN_EN_SHIFT;
  109. + } else {
  110. + val &= QCA8K_LED_PHY123_PATTERN_EN_MASK;
  111. + }
  112. +
  113. + return val == QCA8K_LED_RULE_CONTROLLED;
  114. +}
  115. +
  116. +static int
  117. +qca8k_cled_hw_control_is_supported(struct led_classdev *ldev, unsigned long rules)
  118. +{
  119. + u32 offload_trigger = 0;
  120. +
  121. + return qca8k_parse_netdev(rules, &offload_trigger);
  122. +}
  123. +
  124. +static int
  125. +qca8k_cled_hw_control_set(struct led_classdev *ldev, unsigned long rules)
  126. +{
  127. + struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev);
  128. + struct qca8k_led_pattern_en reg_info;
  129. + struct qca8k_priv *priv = led->priv;
  130. + u32 offload_trigger = 0;
  131. + int ret;
  132. +
  133. + ret = qca8k_parse_netdev(rules, &offload_trigger);
  134. + if (ret)
  135. + return ret;
  136. +
  137. + ret = qca8k_cled_trigger_offload(ldev, true);
  138. + if (ret)
  139. + return ret;
  140. +
  141. + qca8k_get_control_led_reg(led->port_num, led->led_num, &reg_info);
  142. +
  143. + return regmap_update_bits(priv->regmap, reg_info.reg,
  144. + QCA8K_LED_RULE_MASK << reg_info.shift,
  145. + offload_trigger << reg_info.shift);
  146. +}
  147. +
  148. +static int
  149. +qca8k_cled_hw_control_get(struct led_classdev *ldev, unsigned long *rules)
  150. +{
  151. + struct qca8k_led *led = container_of(ldev, struct qca8k_led, cdev);
  152. + struct qca8k_led_pattern_en reg_info;
  153. + struct qca8k_priv *priv = led->priv;
  154. + u32 val;
  155. + int ret;
  156. +
  157. + /* With hw control not active return err */
  158. + if (!qca8k_cled_hw_control_status(ldev))
  159. + return -EINVAL;
  160. +
  161. + qca8k_get_control_led_reg(led->port_num, led->led_num, &reg_info);
  162. +
  163. + ret = regmap_read(priv->regmap, reg_info.reg, &val);
  164. + if (ret)
  165. + return ret;
  166. +
  167. + val >>= reg_info.shift;
  168. + val &= QCA8K_LED_RULE_MASK;
  169. +
  170. + /* Parsing specific to netdev trigger */
  171. + if (val & QCA8K_LED_TX_BLINK_MASK)
  172. + set_bit(TRIGGER_NETDEV_TX, rules);
  173. + if (val & QCA8K_LED_RX_BLINK_MASK)
  174. + set_bit(TRIGGER_NETDEV_RX, rules);
  175. +
  176. + return 0;
  177. +}
  178. +
  179. +static int
  180. qca8k_parse_port_leds(struct qca8k_priv *priv, struct fwnode_handle *port, int port_num)
  181. {
  182. struct fwnode_handle *led = NULL, *leds = NULL;
  183. @@ -224,6 +374,10 @@ qca8k_parse_port_leds(struct qca8k_priv
  184. port_led->cdev.max_brightness = 1;
  185. port_led->cdev.brightness_set_blocking = qca8k_cled_brightness_set_blocking;
  186. port_led->cdev.blink_set = qca8k_cled_blink_set;
  187. + port_led->cdev.hw_control_is_supported = qca8k_cled_hw_control_is_supported;
  188. + port_led->cdev.hw_control_set = qca8k_cled_hw_control_set;
  189. + port_led->cdev.hw_control_get = qca8k_cled_hw_control_get;
  190. + port_led->cdev.hw_control_trigger = "netdev";
  191. init_data.default_label = ":port";
  192. init_data.fwnode = led;
  193. init_data.devname_mandatory = true;