901-v6.13-net-dsa-mv88e6xxx-Support-LED-control.patch 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250
  1. From 7b590490e3aa6bfa38bf6e2069a529017fd3c1d2 Mon Sep 17 00:00:00 2001
  2. From: Linus Walleij <[email protected]>
  3. Date: Fri, 13 Oct 2023 00:08:35 +0200
  4. Subject: [PATCH] net: dsa: mv88e6xxx: Support LED control
  5. This adds control over the hardware LEDs in the Marvell
  6. MV88E6xxx DSA switch and enables it for MV88E6352.
  7. This fixes an imminent problem on the Inteno XG6846 which
  8. has a WAN LED that simply do not work with hardware
  9. defaults: driver amendment is necessary.
  10. The patch is modeled after Christian Marangis LED support
  11. code for the QCA8k DSA switch, I got help with the register
  12. definitions from Tim Harvey.
  13. After this patch it is possible to activate hardware link
  14. indication like this (or with a similar script):
  15. cd /sys/class/leds/Marvell\ 88E6352:05:00:green:wan/
  16. echo netdev > trigger
  17. echo 1 > link
  18. This makes the green link indicator come up on any link
  19. speed. It is also possible to be more elaborate, like this:
  20. cd /sys/class/leds/Marvell\ 88E6352:05:00:green:wan/
  21. echo netdev > trigger
  22. echo 1 > link_1000
  23. cd /sys/class/leds/Marvell\ 88E6352:05:01:amber:wan/
  24. echo netdev > trigger
  25. echo 1 > link_100
  26. Making the green LED come on for a gigabit link and the
  27. amber LED come on for a 100 mbit link.
  28. Each port has 2 LED slots (the hardware may use just one or
  29. none) and the hardware triggers are specified in four bits per
  30. LED, and some of the hardware triggers are only available on the
  31. SFP (fiber) uplink. The restrictions are described in the
  32. port.h header file where the registers are described. For
  33. example, selector 1 set for LED 1 on port 5 or 6 will indicate
  34. Fiber 1000 (gigabit) and activity with a blinking LED, but
  35. ONLY for an SFP connection. If port 5/6 is used with something
  36. not SFP, this selector is a noop: something else need to be
  37. selected.
  38. After the previous series rewriting the MV88E6xxx DT
  39. bindings to use YAML a "leds" subnode is already valid
  40. for each port, in my scratch device tree it looks like
  41. this:
  42. leds {
  43. #address-cells = <1>;
  44. #size-cells = <0>;
  45. led@0 {
  46. reg = <0>;
  47. color = <LED_COLOR_ID_GREEN>;
  48. function = LED_FUNCTION_LAN;
  49. default-state = "off";
  50. linux,default-trigger = "netdev";
  51. };
  52. led@1 {
  53. reg = <1>;
  54. color = <LED_COLOR_ID_AMBER>;
  55. function = LED_FUNCTION_LAN;
  56. default-state = "off";
  57. };
  58. };
  59. This DT config is not yet configuring everything: when the netdev
  60. default trigger is assigned the hw acceleration callbacks are
  61. not called, and there is no way to set the netdev sub-trigger
  62. type (such as link_1000) from the device tree, such as if you want
  63. a gigabit link indicator. This has to be done from userspace at
  64. this point.
  65. We add LED operations to all switches in the 6352 family:
  66. 6172, 6176, 6240 and 6352.
  67. Signed-off-by: Linus Walleij <[email protected]>
  68. ---
  69. drivers/net/dsa/mv88e6xxx/Kconfig | 10 +
  70. drivers/net/dsa/mv88e6xxx/Makefile | 1 +
  71. drivers/net/dsa/mv88e6xxx/chip.c | 38 +-
  72. drivers/net/dsa/mv88e6xxx/chip.h | 11 +
  73. drivers/net/dsa/mv88e6xxx/leds.c | 839 +++++++++++++++++++++++++++++
  74. drivers/net/dsa/mv88e6xxx/port.c | 1 +
  75. drivers/net/dsa/mv88e6xxx/port.h | 133 +++++
  76. 7 files changed, 1031 insertions(+), 2 deletions(-)
  77. create mode 100644 drivers/net/dsa/mv88e6xxx/leds.c
  78. --- a/drivers/net/dsa/mv88e6xxx/Kconfig
  79. +++ b/drivers/net/dsa/mv88e6xxx/Kconfig
  80. @@ -17,3 +17,13 @@ config NET_DSA_MV88E6XXX_PTP
  81. help
  82. Say Y to enable PTP hardware timestamping on Marvell 88E6xxx switch
  83. chips that support it.
  84. +
  85. +config NET_DSA_MV88E6XXX_LEDS
  86. + bool "LED support for Marvell 88E6xxx"
  87. + default y
  88. + depends on NET_DSA_MV88E6XXX
  89. + depends on LEDS_CLASS=y || LEDS_CLASS=NET_DSA_MV88E6XXX
  90. + depends on LEDS_TRIGGERS
  91. + help
  92. + This enabled support for controlling the LEDs attached to the
  93. + Marvell 88E6xxx switch chips.
  94. --- a/drivers/net/dsa/mv88e6xxx/Makefile
  95. +++ b/drivers/net/dsa/mv88e6xxx/Makefile
  96. @@ -9,6 +9,7 @@ mv88e6xxx-objs += global2.o
  97. mv88e6xxx-objs += global2_avb.o
  98. mv88e6xxx-objs += global2_scratch.o
  99. mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += hwtstamp.o
  100. +mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_LEDS) += leds.o
  101. mv88e6xxx-objs += pcs-6185.o
  102. mv88e6xxx-objs += pcs-6352.o
  103. mv88e6xxx-objs += pcs-639x.o
  104. --- a/drivers/net/dsa/mv88e6xxx/chip.c
  105. +++ b/drivers/net/dsa/mv88e6xxx/chip.c
  106. @@ -27,6 +27,7 @@
  107. #include <linux/of_irq.h>
  108. #include <linux/of_mdio.h>
  109. #include <linux/platform_data/mv88e6xxx.h>
  110. +#include <linux/property.h>
  111. #include <linux/netdevice.h>
  112. #include <linux/gpio/consumer.h>
  113. #include <linux/phylink.h>
  114. @@ -3235,14 +3236,43 @@ static int mv88e6xxx_setup_upstream_port
  115. static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
  116. {
  117. struct device_node *phy_handle = NULL;
  118. + struct fwnode_handle *ports_fwnode;
  119. + struct fwnode_handle *port_fwnode;
  120. struct dsa_switch *ds = chip->ds;
  121. + struct mv88e6xxx_port *p;
  122. struct dsa_port *dp;
  123. int tx_amp;
  124. int err;
  125. u16 reg;
  126. + u32 val;
  127. - chip->ports[port].chip = chip;
  128. - chip->ports[port].port = port;
  129. + p = &chip->ports[port];
  130. + p->chip = chip;
  131. + p->port = port;
  132. +
  133. + /* Look up corresponding fwnode if any */
  134. + ports_fwnode = device_get_named_child_node(chip->dev, "ethernet-ports");
  135. + if (!ports_fwnode)
  136. + ports_fwnode = device_get_named_child_node(chip->dev, "ports");
  137. + if (ports_fwnode) {
  138. + fwnode_for_each_child_node(ports_fwnode, port_fwnode) {
  139. + if (fwnode_property_read_u32(port_fwnode, "reg", &val))
  140. + continue;
  141. + if (val == port) {
  142. + p->fwnode = port_fwnode;
  143. + p->fiber = fwnode_property_present(port_fwnode, "sfp");
  144. + break;
  145. + }
  146. + }
  147. + } else {
  148. + dev_dbg(chip->dev, "no ethernet ports node defined for the device\n");
  149. + }
  150. +
  151. + if (chip->info->ops->port_setup_leds) {
  152. + err = chip->info->ops->port_setup_leds(chip, port);
  153. + if (err && err != -EOPNOTSUPP)
  154. + return err;
  155. + }
  156. err = mv88e6xxx_port_setup_mac(chip, port, LINK_UNFORCED,
  157. SPEED_UNFORCED, DUPLEX_UNFORCED,
  158. @@ -4461,6 +4491,7 @@ static const struct mv88e6xxx_ops mv88e6
  159. .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
  160. .port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
  161. .port_get_cmode = mv88e6352_port_get_cmode,
  162. + .port_setup_leds = mv88e6xxx_port_setup_leds,
  163. .port_setup_message_port = mv88e6xxx_setup_message_port,
  164. .stats_snapshot = mv88e6320_g1_stats_snapshot,
  165. .stats_set_histogram = mv88e6095_g1_stats_set_histogram,
  166. @@ -4563,6 +4594,7 @@ static const struct mv88e6xxx_ops mv88e6
  167. .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
  168. .port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
  169. .port_get_cmode = mv88e6352_port_get_cmode,
  170. + .port_setup_leds = mv88e6xxx_port_setup_leds,
  171. .port_setup_message_port = mv88e6xxx_setup_message_port,
  172. .stats_snapshot = mv88e6320_g1_stats_snapshot,
  173. .stats_set_histogram = mv88e6095_g1_stats_set_histogram,
  174. @@ -4838,6 +4870,7 @@ static const struct mv88e6xxx_ops mv88e6
  175. .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
  176. .port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
  177. .port_get_cmode = mv88e6352_port_get_cmode,
  178. + .port_setup_leds = mv88e6xxx_port_setup_leds,
  179. .port_setup_message_port = mv88e6xxx_setup_message_port,
  180. .stats_snapshot = mv88e6320_g1_stats_snapshot,
  181. .stats_set_histogram = mv88e6095_g1_stats_set_histogram,
  182. @@ -5260,6 +5293,7 @@ static const struct mv88e6xxx_ops mv88e6
  183. .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
  184. .port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
  185. .port_get_cmode = mv88e6352_port_get_cmode,
  186. + .port_setup_leds = mv88e6xxx_port_setup_leds,
  187. .port_setup_message_port = mv88e6xxx_setup_message_port,
  188. .stats_snapshot = mv88e6320_g1_stats_snapshot,
  189. .stats_set_histogram = mv88e6095_g1_stats_set_histogram,
  190. --- a/drivers/net/dsa/mv88e6xxx/chip.h
  191. +++ b/drivers/net/dsa/mv88e6xxx/chip.h
  192. @@ -13,7 +13,9 @@
  193. #include <linux/irq.h>
  194. #include <linux/gpio/consumer.h>
  195. #include <linux/kthread.h>
  196. +#include <linux/leds.h>
  197. #include <linux/phy.h>
  198. +#include <linux/property.h>
  199. #include <linux/ptp_clock_kernel.h>
  200. #include <linux/timecounter.h>
  201. #include <net/dsa.h>
  202. @@ -275,6 +277,7 @@ struct mv88e6xxx_vlan {
  203. struct mv88e6xxx_port {
  204. struct mv88e6xxx_chip *chip;
  205. int port;
  206. + struct fwnode_handle *fwnode;
  207. struct mv88e6xxx_vlan bridge_pvid;
  208. u64 serdes_stats[2];
  209. u64 atu_member_violation;
  210. @@ -289,6 +292,11 @@ struct mv88e6xxx_port {
  211. struct devlink_region *region;
  212. void *pcs_private;
  213. + /* LED related information */
  214. + bool fiber;
  215. + struct led_classdev led0;
  216. + struct led_classdev led1;
  217. +
  218. /* MacAuth Bypass control flag */
  219. bool mab;
  220. };
  221. @@ -561,6 +569,9 @@ struct mv88e6xxx_ops {
  222. phy_interface_t mode);
  223. int (*port_get_cmode)(struct mv88e6xxx_chip *chip, int port, u8 *cmode);
  224. + /* LED control */
  225. + int (*port_setup_leds)(struct mv88e6xxx_chip *chip, int port);
  226. +
  227. /* Some devices have a per port register indicating what is
  228. * the upstream port this port should forward to.
  229. */
  230. --- /dev/null
  231. +++ b/drivers/net/dsa/mv88e6xxx/leds.c
  232. @@ -0,0 +1,839 @@
  233. +// SPDX-License-Identifier: GPL-2.0-or-later
  234. +#include <linux/bitfield.h>
  235. +#include <linux/leds.h>
  236. +#include <linux/property.h>
  237. +
  238. +#include "chip.h"
  239. +#include "global2.h"
  240. +#include "port.h"
  241. +
  242. +/* Offset 0x16: LED control */
  243. +
  244. +static int mv88e6xxx_port_led_write(struct mv88e6xxx_chip *chip, int port, u16 reg)
  245. +{
  246. + reg |= MV88E6XXX_PORT_LED_CONTROL_UPDATE;
  247. +
  248. + return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_LED_CONTROL, reg);
  249. +}
  250. +
  251. +static int mv88e6xxx_port_led_read(struct mv88e6xxx_chip *chip, int port,
  252. + u16 ptr, u16 *val)
  253. +{
  254. + int err;
  255. +
  256. + err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_LED_CONTROL, ptr);
  257. + if (err)
  258. + return err;
  259. +
  260. + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_LED_CONTROL, val);
  261. + *val &= 0x3ff;
  262. +
  263. + return err;
  264. +}
  265. +
  266. +static int mv88e6xxx_led_brightness_set(struct mv88e6xxx_port *p, int led,
  267. + int brightness)
  268. +{
  269. + u16 reg;
  270. + int err;
  271. +
  272. + err = mv88e6xxx_port_led_read(p->chip, p->port,
  273. + MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL,
  274. + &reg);
  275. + if (err)
  276. + return err;
  277. +
  278. + if (led == 1)
  279. + reg &= ~MV88E6XXX_PORT_LED_CONTROL_LED1_SEL_MASK;
  280. + else
  281. + reg &= ~MV88E6XXX_PORT_LED_CONTROL_LED0_SEL_MASK;
  282. +
  283. + if (brightness) {
  284. + /* Selector 0x0f == Force LED ON */
  285. + if (led == 1)
  286. + reg |= MV88E6XXX_PORT_LED_CONTROL_LED1_SELF;
  287. + else
  288. + reg |= MV88E6XXX_PORT_LED_CONTROL_LED0_SELF;
  289. + } else {
  290. + /* Selector 0x0e == Force LED OFF */
  291. + if (led == 1)
  292. + reg |= MV88E6XXX_PORT_LED_CONTROL_LED1_SELE;
  293. + else
  294. + reg |= MV88E6XXX_PORT_LED_CONTROL_LED0_SELE;
  295. + }
  296. +
  297. + reg |= MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL;
  298. +
  299. + return mv88e6xxx_port_led_write(p->chip, p->port, reg);
  300. +}
  301. +
  302. +static int mv88e6xxx_led0_brightness_set_blocking(struct led_classdev *ldev,
  303. + enum led_brightness brightness)
  304. +{
  305. + struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led0);
  306. + int err;
  307. +
  308. + mv88e6xxx_reg_lock(p->chip);
  309. + err = mv88e6xxx_led_brightness_set(p, 0, brightness);
  310. + mv88e6xxx_reg_unlock(p->chip);
  311. +
  312. + return err;
  313. +}
  314. +
  315. +static int mv88e6xxx_led1_brightness_set_blocking(struct led_classdev *ldev,
  316. + enum led_brightness brightness)
  317. +{
  318. + struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led1);
  319. + int err;
  320. +
  321. + mv88e6xxx_reg_lock(p->chip);
  322. + err = mv88e6xxx_led_brightness_set(p, 1, brightness);
  323. + mv88e6xxx_reg_unlock(p->chip);
  324. +
  325. + return err;
  326. +}
  327. +
  328. +struct mv88e6xxx_led_hwconfig {
  329. + int led;
  330. + u8 portmask;
  331. + unsigned long rules;
  332. + bool fiber;
  333. + bool blink_activity;
  334. + u16 selector;
  335. +};
  336. +
  337. +/* The following is a lookup table to check what rules we can support on a
  338. + * certain LED given restrictions such as that some rules only work with fiber
  339. + * (SFP) connections and some blink on activity by default.
  340. + */
  341. +#define MV88E6XXX_PORTS_0_3 (BIT(0) | BIT(1) | BIT(2) | BIT(3))
  342. +#define MV88E6XXX_PORTS_4_5 (BIT(4) | BIT(5))
  343. +#define MV88E6XXX_PORT_4 BIT(4)
  344. +#define MV88E6XXX_PORT_5 BIT(5)
  345. +
  346. +/* Entries are listed in selector order.
  347. + *
  348. + * These configurations vary across different switch families, list
  349. + * different tables per-family here.
  350. + */
  351. +static const struct mv88e6xxx_led_hwconfig mv88e6352_led_hwconfigs[] = {
  352. + {
  353. + .led = 0,
  354. + .portmask = MV88E6XXX_PORT_4,
  355. + .rules = BIT(TRIGGER_NETDEV_LINK),
  356. + .blink_activity = true,
  357. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL0,
  358. + },
  359. + {
  360. + .led = 1,
  361. + .portmask = MV88E6XXX_PORT_5,
  362. + .rules = BIT(TRIGGER_NETDEV_LINK_1000),
  363. + .blink_activity = true,
  364. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL0,
  365. + },
  366. + {
  367. + .led = 0,
  368. + .portmask = MV88E6XXX_PORTS_0_3,
  369. + .rules = BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK_1000),
  370. + .blink_activity = true,
  371. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL1,
  372. + },
  373. + {
  374. + .led = 1,
  375. + .portmask = MV88E6XXX_PORTS_0_3,
  376. + .rules = BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK_100),
  377. + .blink_activity = true,
  378. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL1,
  379. + },
  380. + {
  381. + .led = 0,
  382. + .portmask = MV88E6XXX_PORTS_4_5,
  383. + .rules = BIT(TRIGGER_NETDEV_LINK_100),
  384. + .blink_activity = true,
  385. + .fiber = true,
  386. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL1,
  387. + },
  388. + {
  389. + .led = 1,
  390. + .portmask = MV88E6XXX_PORTS_4_5,
  391. + .rules = BIT(TRIGGER_NETDEV_LINK_1000),
  392. + .blink_activity = true,
  393. + .fiber = true,
  394. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL1,
  395. + },
  396. + {
  397. + .led = 0,
  398. + .portmask = MV88E6XXX_PORTS_0_3,
  399. + .rules = BIT(TRIGGER_NETDEV_LINK_1000),
  400. + .blink_activity = true,
  401. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL2,
  402. + },
  403. + {
  404. + .led = 1,
  405. + .portmask = MV88E6XXX_PORTS_0_3,
  406. + .rules = BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK_100),
  407. + .blink_activity = true,
  408. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL2,
  409. + },
  410. + {
  411. + .led = 0,
  412. + .portmask = MV88E6XXX_PORTS_4_5,
  413. + .rules = BIT(TRIGGER_NETDEV_LINK_1000),
  414. + .blink_activity = true,
  415. + .fiber = true,
  416. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL2,
  417. + },
  418. + {
  419. + .led = 1,
  420. + .portmask = MV88E6XXX_PORTS_4_5,
  421. + .rules = BIT(TRIGGER_NETDEV_LINK_100),
  422. + .blink_activity = true,
  423. + .fiber = true,
  424. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL2,
  425. + },
  426. + {
  427. + .led = 0,
  428. + .portmask = MV88E6XXX_PORTS_0_3,
  429. + .rules = BIT(TRIGGER_NETDEV_LINK),
  430. + .blink_activity = true,
  431. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL3,
  432. + },
  433. + {
  434. + .led = 1,
  435. + .portmask = MV88E6XXX_PORTS_0_3,
  436. + .rules = BIT(TRIGGER_NETDEV_LINK_1000),
  437. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL3,
  438. + },
  439. + {
  440. + .led = 1,
  441. + .portmask = MV88E6XXX_PORTS_4_5,
  442. + .rules = BIT(TRIGGER_NETDEV_LINK),
  443. + .fiber = true,
  444. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL3,
  445. + },
  446. + {
  447. + .led = 1,
  448. + .portmask = MV88E6XXX_PORT_4,
  449. + .rules = BIT(TRIGGER_NETDEV_LINK),
  450. + .blink_activity = true,
  451. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL4,
  452. + },
  453. + {
  454. + .led = 1,
  455. + .portmask = MV88E6XXX_PORT_5,
  456. + .rules = BIT(TRIGGER_NETDEV_LINK),
  457. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL5,
  458. + },
  459. + {
  460. + .led = 0,
  461. + .portmask = MV88E6XXX_PORTS_0_3,
  462. + .rules = BIT(TRIGGER_NETDEV_FULL_DUPLEX),
  463. + .blink_activity = true,
  464. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL6,
  465. + },
  466. + {
  467. + .led = 1,
  468. + .portmask = MV88E6XXX_PORTS_0_3,
  469. + .rules = BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK_1000),
  470. + .blink_activity = true,
  471. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL6,
  472. + },
  473. + {
  474. + .led = 0,
  475. + .portmask = MV88E6XXX_PORT_4,
  476. + .rules = BIT(TRIGGER_NETDEV_FULL_DUPLEX),
  477. + .blink_activity = true,
  478. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL6,
  479. + },
  480. + {
  481. + .led = 1,
  482. + .portmask = MV88E6XXX_PORT_5,
  483. + .rules = BIT(TRIGGER_NETDEV_FULL_DUPLEX),
  484. + .blink_activity = true,
  485. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL6,
  486. + },
  487. + {
  488. + .led = 0,
  489. + .portmask = MV88E6XXX_PORTS_0_3,
  490. + .rules = BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK_1000),
  491. + .blink_activity = true,
  492. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL7,
  493. + },
  494. + {
  495. + .led = 1,
  496. + .portmask = MV88E6XXX_PORTS_0_3,
  497. + .rules = BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK_1000),
  498. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL7,
  499. + },
  500. + {
  501. + .led = 0,
  502. + .portmask = MV88E6XXX_PORTS_0_3,
  503. + .rules = BIT(TRIGGER_NETDEV_LINK),
  504. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL8,
  505. + },
  506. + {
  507. + .led = 1,
  508. + .portmask = MV88E6XXX_PORTS_0_3,
  509. + .rules = BIT(TRIGGER_NETDEV_LINK),
  510. + .blink_activity = true,
  511. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL8,
  512. + },
  513. + {
  514. + .led = 0,
  515. + .portmask = MV88E6XXX_PORT_5,
  516. + .rules = BIT(TRIGGER_NETDEV_LINK),
  517. + .blink_activity = true,
  518. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL8,
  519. + },
  520. + {
  521. + .led = 0,
  522. + .portmask = MV88E6XXX_PORTS_0_3,
  523. + .rules = BIT(TRIGGER_NETDEV_LINK_10),
  524. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SEL9,
  525. + },
  526. + {
  527. + .led = 1,
  528. + .portmask = MV88E6XXX_PORTS_0_3,
  529. + .rules = BIT(TRIGGER_NETDEV_LINK_100),
  530. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SEL9,
  531. + },
  532. + {
  533. + .led = 0,
  534. + .portmask = MV88E6XXX_PORTS_0_3,
  535. + .rules = BIT(TRIGGER_NETDEV_LINK_10),
  536. + .blink_activity = true,
  537. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SELA,
  538. + },
  539. + {
  540. + .led = 1,
  541. + .portmask = MV88E6XXX_PORTS_0_3,
  542. + .rules = BIT(TRIGGER_NETDEV_LINK_100),
  543. + .blink_activity = true,
  544. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SELA,
  545. + },
  546. + {
  547. + .led = 0,
  548. + .portmask = MV88E6XXX_PORTS_0_3,
  549. + .rules = BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK_1000),
  550. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED0_SELB,
  551. + },
  552. + {
  553. + .led = 1,
  554. + .portmask = MV88E6XXX_PORTS_0_3,
  555. + .rules = BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK_1000),
  556. + .blink_activity = true,
  557. + .selector = MV88E6XXX_PORT_LED_CONTROL_LED1_SELB,
  558. + },
  559. +};
  560. +
  561. +/* mv88e6xxx_led_match_selector() - look up the appropriate LED mode selector
  562. + * @p: port state container
  563. + * @led: LED number, 0 or 1
  564. + * @blink_activity: blink the LED (usually blink on indicated activity)
  565. + * @fiber: the link is connected to fiber such as SFP
  566. + * @rules: LED status flags from the LED classdev core
  567. + * @selector: fill in the selector in this parameter with an OR operation
  568. + */
  569. +static int mv88e6xxx_led_match_selector(struct mv88e6xxx_port *p, int led, bool blink_activity,
  570. + bool fiber, unsigned long rules, u16 *selector)
  571. +{
  572. + const struct mv88e6xxx_led_hwconfig *conf;
  573. + int i;
  574. +
  575. + /* No rules means we turn the LED off */
  576. + if (!rules) {
  577. + if (led == 1)
  578. + *selector |= MV88E6XXX_PORT_LED_CONTROL_LED1_SELE;
  579. + else
  580. + *selector |= MV88E6XXX_PORT_LED_CONTROL_LED0_SELE;
  581. + return 0;
  582. + }
  583. +
  584. + /* TODO: these rules are for MV88E6352, when adding other families,
  585. + * think about making sure you select the table that match the
  586. + * specific switch family.
  587. + */
  588. + for (i = 0; i < ARRAY_SIZE(mv88e6352_led_hwconfigs); i++) {
  589. + conf = &mv88e6352_led_hwconfigs[i];
  590. +
  591. + if (conf->led != led)
  592. + continue;
  593. +
  594. + if (!(conf->portmask & BIT(p->port)))
  595. + continue;
  596. +
  597. + if (conf->blink_activity != blink_activity)
  598. + continue;
  599. +
  600. + if (conf->fiber != fiber)
  601. + continue;
  602. +
  603. + if (conf->rules == rules) {
  604. + dev_dbg(p->chip->dev, "port%d LED %d set selector %04x for rules %08lx\n",
  605. + p->port, led, conf->selector, rules);
  606. + *selector |= conf->selector;
  607. + return 0;
  608. + }
  609. + }
  610. +
  611. + return -EOPNOTSUPP;
  612. +}
  613. +
  614. +/* mv88e6xxx_led_match_selector() - find Linux netdev rules from a selector value
  615. + * @p: port state container
  616. + * @selector: the selector value from the LED actity register
  617. + * @led: LED number, 0 or 1
  618. + * @rules: Linux netdev activity rules found from selector
  619. + */
  620. +static int
  621. +mv88e6xxx_led_match_rule(struct mv88e6xxx_port *p, u16 selector, int led, unsigned long *rules)
  622. +{
  623. + const struct mv88e6xxx_led_hwconfig *conf;
  624. + int i;
  625. +
  626. + /* Find the selector in the table, we just look for the right selector
  627. + * and ignore if the activity has special properties such as blinking
  628. + * or is fiber-only.
  629. + */
  630. + for (i = 0; i < ARRAY_SIZE(mv88e6352_led_hwconfigs); i++) {
  631. + conf = &mv88e6352_led_hwconfigs[i];
  632. +
  633. + if (conf->led != led)
  634. + continue;
  635. +
  636. + if (!(conf->portmask & BIT(p->port)))
  637. + continue;
  638. +
  639. + if (conf->selector == selector) {
  640. + dev_dbg(p->chip->dev, "port%d LED %d has selector %04x, rules %08lx\n",
  641. + p->port, led, selector, conf->rules);
  642. + *rules = conf->rules;
  643. + return 0;
  644. + }
  645. + }
  646. +
  647. + return -EINVAL;
  648. +}
  649. +
  650. +/* mv88e6xxx_led_get_selector() - get the appropriate LED mode selector
  651. + * @p: port state container
  652. + * @led: LED number, 0 or 1
  653. + * @fiber: the link is connected to fiber such as SFP
  654. + * @rules: LED status flags from the LED classdev core
  655. + * @selector: fill in the selector in this parameter with an OR operation
  656. + */
  657. +static int mv88e6xxx_led_get_selector(struct mv88e6xxx_port *p, int led,
  658. + bool fiber, unsigned long rules, u16 *selector)
  659. +{
  660. + int err;
  661. +
  662. + /* What happens here is that we first try to locate a trigger with solid
  663. + * indicator (such as LED is on for a 1000 link) else we try a second
  664. + * sweep to find something suitable with a trigger that will blink on
  665. + * activity.
  666. + */
  667. + err = mv88e6xxx_led_match_selector(p, led, false, fiber, rules, selector);
  668. + if (err)
  669. + return mv88e6xxx_led_match_selector(p, led, true, fiber, rules, selector);
  670. +
  671. + return 0;
  672. +}
  673. +
  674. +/* Sets up the hardware blinking period */
  675. +static int mv88e6xxx_led_set_blinking_period(struct mv88e6xxx_port *p, int led,
  676. + unsigned long delay_on, unsigned long delay_off)
  677. +{
  678. + unsigned long period;
  679. + u16 reg;
  680. +
  681. + period = delay_on + delay_off;
  682. +
  683. + reg = 0;
  684. +
  685. + switch (period) {
  686. + case 21:
  687. + reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_21MS;
  688. + break;
  689. + case 42:
  690. + reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_42MS;
  691. + break;
  692. + case 84:
  693. + reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_84MS;
  694. + break;
  695. + case 168:
  696. + reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_168MS;
  697. + break;
  698. + case 336:
  699. + reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_336MS;
  700. + break;
  701. + case 672:
  702. + reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_672MS;
  703. + break;
  704. + default:
  705. + /* Fall back to software blinking */
  706. + return -EINVAL;
  707. + }
  708. +
  709. + /* This is essentially PWM duty cycle: how long time of the period
  710. + * will the LED be on. Zero isn't great in most cases.
  711. + */
  712. + switch (delay_on) {
  713. + case 0:
  714. + /* This is usually pretty useless and will make the LED look OFF */
  715. + reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_NONE;
  716. + break;
  717. + case 21:
  718. + reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_21MS;
  719. + break;
  720. + case 42:
  721. + reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_42MS;
  722. + break;
  723. + case 84:
  724. + reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_84MS;
  725. + break;
  726. + case 168:
  727. + reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_168MS;
  728. + break;
  729. + default:
  730. + /* Just use something non-zero */
  731. + reg |= MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_21MS;
  732. + break;
  733. + }
  734. +
  735. + /* Set up blink rate */
  736. + reg |= MV88E6XXX_PORT_LED_CONTROL_POINTER_STRETCH_BLINK;
  737. +
  738. + return mv88e6xxx_port_led_write(p->chip, p->port, reg);
  739. +}
  740. +
  741. +static int mv88e6xxx_led_blink_set(struct mv88e6xxx_port *p, int led,
  742. + unsigned long *delay_on, unsigned long *delay_off)
  743. +{
  744. + u16 reg;
  745. + int err;
  746. +
  747. + /* Choose a sensible default 336 ms (~3 Hz) */
  748. + if ((*delay_on == 0) && (*delay_off == 0)) {
  749. + *delay_on = 168;
  750. + *delay_off = 168;
  751. + }
  752. +
  753. + /* No off delay is just on */
  754. + if (*delay_off == 0)
  755. + return mv88e6xxx_led_brightness_set(p, led, 1);
  756. +
  757. + err = mv88e6xxx_led_set_blinking_period(p, led, *delay_on, *delay_off);
  758. + if (err)
  759. + return err;
  760. +
  761. + err = mv88e6xxx_port_led_read(p->chip, p->port,
  762. + MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL,
  763. + &reg);
  764. + if (err)
  765. + return err;
  766. +
  767. + if (led == 1)
  768. + reg &= ~MV88E6XXX_PORT_LED_CONTROL_LED1_SEL_MASK;
  769. + else
  770. + reg &= ~MV88E6XXX_PORT_LED_CONTROL_LED0_SEL_MASK;
  771. +
  772. + /* This will select the forced blinking status */
  773. + if (led == 1)
  774. + reg |= MV88E6XXX_PORT_LED_CONTROL_LED1_SELD;
  775. + else
  776. + reg |= MV88E6XXX_PORT_LED_CONTROL_LED0_SELD;
  777. +
  778. + reg |= MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL;
  779. +
  780. + return mv88e6xxx_port_led_write(p->chip, p->port, reg);
  781. +}
  782. +
  783. +static int mv88e6xxx_led0_blink_set(struct led_classdev *ldev,
  784. + unsigned long *delay_on,
  785. + unsigned long *delay_off)
  786. +{
  787. + struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led0);
  788. + int err;
  789. +
  790. + mv88e6xxx_reg_lock(p->chip);
  791. + err = mv88e6xxx_led_blink_set(p, 0, delay_on, delay_off);
  792. + mv88e6xxx_reg_unlock(p->chip);
  793. +
  794. + return err;
  795. +}
  796. +
  797. +static int mv88e6xxx_led1_blink_set(struct led_classdev *ldev,
  798. + unsigned long *delay_on,
  799. + unsigned long *delay_off)
  800. +{
  801. + struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led1);
  802. + int err;
  803. +
  804. + mv88e6xxx_reg_lock(p->chip);
  805. + err = mv88e6xxx_led_blink_set(p, 1, delay_on, delay_off);
  806. + mv88e6xxx_reg_unlock(p->chip);
  807. +
  808. + return err;
  809. +}
  810. +
  811. +static int
  812. +mv88e6xxx_led0_hw_control_is_supported(struct led_classdev *ldev, unsigned long rules)
  813. +{
  814. + struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led0);
  815. + u16 selector = 0;
  816. +
  817. + return mv88e6xxx_led_get_selector(p, 0, p->fiber, rules, &selector);
  818. +}
  819. +
  820. +static int
  821. +mv88e6xxx_led1_hw_control_is_supported(struct led_classdev *ldev, unsigned long rules)
  822. +{
  823. + struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led1);
  824. + u16 selector = 0;
  825. +
  826. + return mv88e6xxx_led_get_selector(p, 1, p->fiber, rules, &selector);
  827. +}
  828. +
  829. +static int mv88e6xxx_led_hw_control_set(struct mv88e6xxx_port *p,
  830. + int led, unsigned long rules)
  831. +{
  832. + u16 reg;
  833. + int err;
  834. +
  835. + err = mv88e6xxx_port_led_read(p->chip, p->port,
  836. + MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL,
  837. + &reg);
  838. + if (err)
  839. + return err;
  840. +
  841. + if (led == 1)
  842. + reg &= ~MV88E6XXX_PORT_LED_CONTROL_LED1_SEL_MASK;
  843. + else
  844. + reg &= ~MV88E6XXX_PORT_LED_CONTROL_LED0_SEL_MASK;
  845. +
  846. + err = mv88e6xxx_led_get_selector(p, led, p->fiber, rules, &reg);
  847. + if (err)
  848. + return err;
  849. +
  850. + reg |= MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL;
  851. +
  852. + if (led == 0)
  853. + dev_dbg(p->chip->dev, "LED 0 hw control on port %d trigger selector 0x%02x\n",
  854. + p->port,
  855. + (unsigned int)(reg & MV88E6XXX_PORT_LED_CONTROL_LED0_SEL_MASK));
  856. + else
  857. + dev_dbg(p->chip->dev, "LED 1 hw control on port %d trigger selector 0x%02x\n",
  858. + p->port,
  859. + (unsigned int)(reg & MV88E6XXX_PORT_LED_CONTROL_LED1_SEL_MASK) >> 4);
  860. +
  861. + return mv88e6xxx_port_led_write(p->chip, p->port, reg);
  862. +}
  863. +
  864. +static int
  865. +mv88e6xxx_led_hw_control_get(struct mv88e6xxx_port *p, int led, unsigned long *rules)
  866. +{
  867. + u16 val;
  868. + int err;
  869. +
  870. + mv88e6xxx_reg_lock(p->chip);
  871. + err = mv88e6xxx_port_led_read(p->chip, p->port,
  872. + MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL, &val);
  873. + mv88e6xxx_reg_unlock(p->chip);
  874. + if (err)
  875. + return err;
  876. +
  877. + /* Mask out the selector bits for this port */
  878. + if (led == 1) {
  879. + val &= MV88E6XXX_PORT_LED_CONTROL_LED1_SEL_MASK;
  880. + /* It's forced blinking/OFF/ON */
  881. + if (val == MV88E6XXX_PORT_LED_CONTROL_LED1_SELD ||
  882. + val == MV88E6XXX_PORT_LED_CONTROL_LED1_SELE ||
  883. + val == MV88E6XXX_PORT_LED_CONTROL_LED1_SELF) {
  884. + *rules = 0;
  885. + return 0;
  886. + }
  887. + } else {
  888. + val &= MV88E6XXX_PORT_LED_CONTROL_LED0_SEL_MASK;
  889. + /* It's forced blinking/OFF/ON */
  890. + if (val == MV88E6XXX_PORT_LED_CONTROL_LED0_SELD ||
  891. + val == MV88E6XXX_PORT_LED_CONTROL_LED0_SELE ||
  892. + val == MV88E6XXX_PORT_LED_CONTROL_LED0_SELF) {
  893. + *rules = 0;
  894. + return 0;
  895. + }
  896. + }
  897. +
  898. + err = mv88e6xxx_led_match_rule(p, val, led, rules);
  899. + if (!err)
  900. + return 0;
  901. +
  902. + dev_dbg(p->chip->dev, "couldn't find matching selector for %04x\n", val);
  903. + *rules = 0;
  904. + return 0;
  905. +}
  906. +
  907. +static int
  908. +mv88e6xxx_led0_hw_control_set(struct led_classdev *ldev, unsigned long rules)
  909. +{
  910. + struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led0);
  911. + int err;
  912. +
  913. + mv88e6xxx_reg_lock(p->chip);
  914. + err = mv88e6xxx_led_hw_control_set(p, 0, rules);
  915. + mv88e6xxx_reg_unlock(p->chip);
  916. +
  917. + return err;
  918. +}
  919. +
  920. +static int
  921. +mv88e6xxx_led1_hw_control_set(struct led_classdev *ldev, unsigned long rules)
  922. +{
  923. + struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led1);
  924. + int err;
  925. +
  926. + mv88e6xxx_reg_lock(p->chip);
  927. + err = mv88e6xxx_led_hw_control_set(p, 1, rules);
  928. + mv88e6xxx_reg_unlock(p->chip);
  929. +
  930. + return err;
  931. +}
  932. +
  933. +static int
  934. +mv88e6xxx_led0_hw_control_get(struct led_classdev *ldev, unsigned long *rules)
  935. +{
  936. + struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led0);
  937. +
  938. + return mv88e6xxx_led_hw_control_get(p, 0, rules);
  939. +}
  940. +
  941. +static int
  942. +mv88e6xxx_led1_hw_control_get(struct led_classdev *ldev, unsigned long *rules)
  943. +{
  944. + struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led1);
  945. +
  946. + return mv88e6xxx_led_hw_control_get(p, 1, rules);
  947. +}
  948. +
  949. +static struct device *mv88e6xxx_led_hw_control_get_device(struct mv88e6xxx_port *p)
  950. +{
  951. + struct dsa_port *dp;
  952. +
  953. + dp = dsa_to_port(p->chip->ds, p->port);
  954. + if (!dp)
  955. + return NULL;
  956. + if (dp->slave)
  957. + return &dp->slave->dev;
  958. + return NULL;
  959. +}
  960. +
  961. +static struct device *
  962. +mv88e6xxx_led0_hw_control_get_device(struct led_classdev *ldev)
  963. +{
  964. + struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led0);
  965. +
  966. + return mv88e6xxx_led_hw_control_get_device(p);
  967. +}
  968. +
  969. +static struct device *
  970. +mv88e6xxx_led1_hw_control_get_device(struct led_classdev *ldev)
  971. +{
  972. + struct mv88e6xxx_port *p = container_of(ldev, struct mv88e6xxx_port, led1);
  973. +
  974. + return mv88e6xxx_led_hw_control_get_device(p);
  975. +}
  976. +
  977. +int mv88e6xxx_port_setup_leds(struct mv88e6xxx_chip *chip, int port)
  978. +{
  979. + struct fwnode_handle *led = NULL, *leds = NULL;
  980. + struct led_init_data init_data = { };
  981. + enum led_default_state state;
  982. + struct mv88e6xxx_port *p;
  983. + struct led_classdev *l;
  984. + struct device *dev;
  985. + u32 led_num;
  986. + int ret;
  987. +
  988. + /* LEDs are on ports 1,2,3,4, 5 and 6 (index 0..5), no more */
  989. + if (port > 5)
  990. + return -EOPNOTSUPP;
  991. +
  992. + p = &chip->ports[port];
  993. + if (!p->fwnode)
  994. + return 0;
  995. +
  996. + dev = chip->dev;
  997. +
  998. + leds = fwnode_get_named_child_node(p->fwnode, "leds");
  999. + if (!leds) {
  1000. + dev_dbg(dev, "No Leds node specified in device tree for port %d!\n",
  1001. + port);
  1002. + return 0;
  1003. + }
  1004. +
  1005. + fwnode_for_each_child_node(leds, led) {
  1006. + /* Reg represent the led number of the port, max 2
  1007. + * LEDs can be connected to each port, in some designs
  1008. + * only one LED is connected.
  1009. + */
  1010. + if (fwnode_property_read_u32(led, "reg", &led_num))
  1011. + continue;
  1012. + if (led_num > 1) {
  1013. + dev_err(dev, "invalid LED specified port %d\n", port);
  1014. + return -EINVAL;
  1015. + }
  1016. +
  1017. + if (led_num == 0)
  1018. + l = &p->led0;
  1019. + else
  1020. + l = &p->led1;
  1021. +
  1022. + state = led_init_default_state_get(led);
  1023. + switch (state) {
  1024. + case LEDS_DEFSTATE_ON:
  1025. + l->brightness = 1;
  1026. + mv88e6xxx_led_brightness_set(p, led_num, 1);
  1027. + break;
  1028. + case LEDS_DEFSTATE_KEEP:
  1029. + break;
  1030. + default:
  1031. + l->brightness = 0;
  1032. + mv88e6xxx_led_brightness_set(p, led_num, 0);
  1033. + }
  1034. +
  1035. + l->max_brightness = 1;
  1036. + if (led_num == 0) {
  1037. + l->brightness_set_blocking = mv88e6xxx_led0_brightness_set_blocking;
  1038. + l->blink_set = mv88e6xxx_led0_blink_set;
  1039. + l->hw_control_is_supported = mv88e6xxx_led0_hw_control_is_supported;
  1040. + l->hw_control_set = mv88e6xxx_led0_hw_control_set;
  1041. + l->hw_control_get = mv88e6xxx_led0_hw_control_get;
  1042. + l->hw_control_get_device = mv88e6xxx_led0_hw_control_get_device;
  1043. + } else {
  1044. + l->brightness_set_blocking = mv88e6xxx_led1_brightness_set_blocking;
  1045. + l->blink_set = mv88e6xxx_led1_blink_set;
  1046. + l->hw_control_is_supported = mv88e6xxx_led1_hw_control_is_supported;
  1047. + l->hw_control_set = mv88e6xxx_led1_hw_control_set;
  1048. + l->hw_control_get = mv88e6xxx_led1_hw_control_get;
  1049. + l->hw_control_get_device = mv88e6xxx_led1_hw_control_get_device;
  1050. + }
  1051. + l->hw_control_trigger = "netdev";
  1052. +
  1053. + init_data.default_label = ":port";
  1054. + init_data.fwnode = led;
  1055. + init_data.devname_mandatory = true;
  1056. + init_data.devicename = kasprintf(GFP_KERNEL, "%s:0%d:0%d", chip->info->name,
  1057. + port, led_num);
  1058. + if (!init_data.devicename)
  1059. + return -ENOMEM;
  1060. +
  1061. + ret = devm_led_classdev_register_ext(dev, l, &init_data);
  1062. + kfree(init_data.devicename);
  1063. +
  1064. + if (ret) {
  1065. + dev_err(dev, "Failed to init LED %d for port %d", led_num, port);
  1066. + return ret;
  1067. + }
  1068. + }
  1069. +
  1070. + return 0;
  1071. +}
  1072. --- a/drivers/net/dsa/mv88e6xxx/port.c
  1073. +++ b/drivers/net/dsa/mv88e6xxx/port.c
  1074. @@ -12,6 +12,7 @@
  1075. #include <linux/if_bridge.h>
  1076. #include <linux/phy.h>
  1077. #include <linux/phylink.h>
  1078. +#include <linux/property.h>
  1079. #include "chip.h"
  1080. #include "global2.h"
  1081. --- a/drivers/net/dsa/mv88e6xxx/port.h
  1082. +++ b/drivers/net/dsa/mv88e6xxx/port.h
  1083. @@ -309,6 +309,130 @@
  1084. /* Offset 0x13: OutFiltered Counter */
  1085. #define MV88E6XXX_PORT_OUT_FILTERED 0x13
  1086. +/* Offset 0x16: LED Control */
  1087. +#define MV88E6XXX_PORT_LED_CONTROL 0x16
  1088. +#define MV88E6XXX_PORT_LED_CONTROL_UPDATE BIT(15)
  1089. +#define MV88E6XXX_PORT_LED_CONTROL_POINTER_MASK GENMASK(14, 12)
  1090. +#define MV88E6XXX_PORT_LED_CONTROL_POINTER_LED01_CTRL (0x00 << 12) /* Control for LED 0 and 1 */
  1091. +#define MV88E6XXX_PORT_LED_CONTROL_POINTER_STRETCH_BLINK (0x06 << 12) /* Stetch and Blink Rate */
  1092. +#define MV88E6XXX_PORT_LED_CONTROL_POINTER_CNTL_SPECIAL (0x07 << 12) /* Control for the Port's Special LED */
  1093. +#define MV88E6XXX_PORT_LED_CONTROL_DATA_MASK GENMASK(10, 0)
  1094. +/* Selection masks valid for either port 1,2,3,4 or 5 */
  1095. +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL_MASK GENMASK(3, 0)
  1096. +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL_MASK GENMASK(7, 4)
  1097. +/* Selection control for LED 0 and 1, ports 5 and 6 only has LED 0
  1098. + * Bits Function
  1099. + * 0..3 LED 0 control selector on ports 1-5
  1100. + * 4..7 LED 1 control selector on ports 1-4 on port 5 this controls LED 0 of port 6
  1101. + *
  1102. + * Sel Port LED Function for the 6352 family:
  1103. + * 0 1-4 0 Link/Act/Speed by Blink Rate (off=no link, on=link, blink=activity, blink speed=link speed)
  1104. + * 1-4 1 Port 2's Special LED
  1105. + * 5-6 0 Port 5 Link/Act (off=no link, on=link, blink=activity)
  1106. + * 5-6 1 Port 6 Link/Act (off=no link, on=link 1000, blink=activity)
  1107. + * 1 1-4 0 100/1000 Link/Act (off=no link, on=100 or 1000 link, blink=activity)
  1108. + * 1-4 1 10/100 Link Act (off=no link, on=10 or 100 link, blink=activity)
  1109. + * 5-6 0 Fiber 100 Link/Act (off=no link, on=link 100, blink=activity)
  1110. + * 5-6 1 Fiber 1000 Link/Act (off=no link, on=link 1000, blink=activity)
  1111. + * 2 1-4 0 1000 Link/Act (off=no link, on=link 1000, blink=activity)
  1112. + * 1-4 1 10/100 Link/Act (off=no link, on=10 or 100 link, blink=activity)
  1113. + * 5-6 0 Fiber 1000 Link/Act (off=no link, on=link 1000, blink=activity)
  1114. + * 5-6 1 Fiber 100 Link/Act (off=no link, on=link 100, blink=activity)
  1115. + * 3 1-4 0 Link/Act (off=no link, on=link, blink=activity)
  1116. + * 1-4 1 1000 Link (off=no link, on=1000 link)
  1117. + * 5-6 0 Port 0's Special LED
  1118. + * 5-6 1 Fiber Link (off=no link, on=link)
  1119. + * 4 1-4 0 Port 0's Special LED
  1120. + * 1-4 1 Port 1's Special LED
  1121. + * 5-6 0 Port 1's Special LED
  1122. + * 5-6 1 Port 5 Link/Act (off=no link, on=link, blink=activity)
  1123. + * 5 1-4 0 Reserved
  1124. + * 1-4 1 Reserved
  1125. + * 5-6 0 Port 2's Special LED
  1126. + * 5-6 1 Port 6 Link (off=no link, on=link)
  1127. + * 6 1-4 0 Duplex/Collision (off=half-duplex,on=full-duplex,blink=collision)
  1128. + * 1-4 1 10/1000 Link/Act (off=no link, on=10 or 1000 link, blink=activity)
  1129. + * 5-6 0 Port 5 Duplex/Collision (off=half-duplex, on=full-duplex, blink=col)
  1130. + * 5-6 1 Port 6 Duplex/Collision (off=half-duplex, on=full-duplex, blink=col)
  1131. + * 7 1-4 0 10/1000 Link/Act (off=no link, on=10 or 1000 link, blink=activity)
  1132. + * 1-4 1 10/1000 Link (off=no link, on=10 or 1000 link)
  1133. + * 5-6 0 Port 5 Link/Act/Speed by Blink rate (off=no link, on=link, blink=activity, blink speed=link speed)
  1134. + * 5-6 1 Port 6 Link/Act/Speed by Blink rate (off=no link, on=link, blink=activity, blink speed=link speed)
  1135. + * 8 1-4 0 Link (off=no link, on=link)
  1136. + * 1-4 1 Activity (off=no link, blink on=activity)
  1137. + * 5-6 0 Port 6 Link/Act (off=no link, on=link, blink=activity)
  1138. + * 5-6 1 Port 0's Special LED
  1139. + * 9 1-4 0 10 Link (off=no link, on=10 link)
  1140. + * 1-4 1 100 Link (off=no link, on=100 link)
  1141. + * 5-6 0 Reserved
  1142. + * 5-6 1 Port 1's Special LED
  1143. + * a 1-4 0 10 Link/Act (off=no link, on=10 link, blink=activity)
  1144. + * 1-4 1 100 Link/Act (off=no link, on=100 link, blink=activity)
  1145. + * 5-6 0 Reserved
  1146. + * 5-6 1 Port 2's Special LED
  1147. + * b 1-4 0 100/1000 Link (off=no link, on=100 or 1000 link)
  1148. + * 1-4 1 10/100 Link (off=no link, on=100 link, blink=activity)
  1149. + * 5-6 0 Reserved
  1150. + * 5-6 1 Reserved
  1151. + * c * * PTP Act (blink on=PTP activity)
  1152. + * d * * Force Blink
  1153. + * e * * Force Off
  1154. + * f * * Force On
  1155. + */
  1156. +/* Select LED0 output */
  1157. +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL0 0x0
  1158. +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL1 0x1
  1159. +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL2 0x2
  1160. +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL3 0x3
  1161. +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL4 0x4
  1162. +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL5 0x5
  1163. +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL6 0x6
  1164. +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL7 0x7
  1165. +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL8 0x8
  1166. +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SEL9 0x9
  1167. +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SELA 0xa
  1168. +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SELB 0xb
  1169. +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SELC 0xc
  1170. +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SELD 0xd
  1171. +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SELE 0xe
  1172. +#define MV88E6XXX_PORT_LED_CONTROL_LED0_SELF 0xf
  1173. +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL0 (0x0 << 4)
  1174. +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL1 (0x1 << 4)
  1175. +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL2 (0x2 << 4)
  1176. +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL3 (0x3 << 4)
  1177. +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL4 (0x4 << 4)
  1178. +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL5 (0x5 << 4)
  1179. +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL6 (0x6 << 4)
  1180. +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL7 (0x7 << 4)
  1181. +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL8 (0x8 << 4)
  1182. +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SEL9 (0x9 << 4)
  1183. +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SELA (0xa << 4)
  1184. +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SELB (0xb << 4)
  1185. +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SELC (0xc << 4)
  1186. +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SELD (0xd << 4)
  1187. +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SELE (0xe << 4)
  1188. +#define MV88E6XXX_PORT_LED_CONTROL_LED1_SELF (0xf << 4)
  1189. +/* Stretch and Blink Rate Control (Index 0x06 of LED Control) */
  1190. +/* Pulse Stretch Selection for all LED's on this port */
  1191. +#define MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_NONE (0 << 4)
  1192. +#define MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_21MS (1 << 4)
  1193. +#define MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_42MS (2 << 4)
  1194. +#define MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_84MS (3 << 4)
  1195. +#define MV88E6XXX_PORT_LED_CONTROL_0x06_PULSE_STRETCH_168MS (4 << 4)
  1196. +/* Blink Rate Selection for all LEDs on this port */
  1197. +#define MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_21MS 0
  1198. +#define MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_42MS 1
  1199. +#define MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_84MS 2
  1200. +#define MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_168MS 3
  1201. +#define MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_336MS 4
  1202. +#define MV88E6XXX_PORT_LED_CONTROL_0x06_BLINK_RATE_672MS 5
  1203. + /* Control for Special LED (Index 0x7 of LED Control on Port0) */
  1204. +#define MV88E6XXX_PORT_LED_CONTROL_0x07_P0_LAN_LINKACT_SHIFT 0 /* bits 6:0 LAN Link Activity LED */
  1205. +/* Control for Special LED (Index 0x7 of LED Control on Port 1) */
  1206. +#define MV88E6XXX_PORT_LED_CONTROL_0x07_P1_WAN_LINKACT_SHIFT 0 /* bits 6:0 WAN Link Activity LED */
  1207. +/* Control for Special LED (Index 0x7 of LED Control on Port 2) */
  1208. +#define MV88E6XXX_PORT_LED_CONTROL_0x07_P2_PTP_ACT 0 /* bits 6:0 PTP Activity */
  1209. +
  1210. /* Offset 0x18: IEEE Priority Mapping Table */
  1211. #define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE 0x18
  1212. #define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_UPDATE 0x8000
  1213. @@ -457,6 +581,15 @@ int mv88e6393x_port_set_cmode(struct mv8
  1214. phy_interface_t mode);
  1215. int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode);
  1216. int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode);
  1217. +#ifdef CONFIG_NET_DSA_MV88E6XXX_LEDS
  1218. +int mv88e6xxx_port_setup_leds(struct mv88e6xxx_chip *chip, int port);
  1219. +#else
  1220. +static inline int mv88e6xxx_port_setup_leds(struct mv88e6xxx_chip *chip,
  1221. + int port)
  1222. +{
  1223. + return 0;
  1224. +}
  1225. +#endif
  1226. int mv88e6xxx_port_drop_untagged(struct mv88e6xxx_chip *chip, int port,
  1227. bool drop_untagged);
  1228. int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port, bool map);