1d910631fSJohnson Tsai // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2d910631fSJohnson Tsai /* Copyright(c) 2026 Realtek Corporation
3d910631fSJohnson Tsai */
4d910631fSJohnson Tsai
5d910631fSJohnson Tsai #include "core.h"
6d910631fSJohnson Tsai #include "debug.h"
7d910631fSJohnson Tsai #include "led.h"
8d910631fSJohnson Tsai #include "reg.h"
9d910631fSJohnson Tsai
10d910631fSJohnson Tsai static const struct rtw89_led_gpio_entry rtw89_common_led_gpios[] = {{
11d910631fSJohnson Tsai .pin = 8,
12d910631fSJohnson Tsai .pinmux = {.addr = R_AX_GPIO8_15_FUNC_SEL, .mask = GENMASK(3, 0), .data = 0xf},
13d910631fSJohnson Tsai .mode = {.addr = R_AX_GPIO_EXT_CTRL + 2, .data = BIT(0) | BIT(8)},
14d910631fSJohnson Tsai .out = {.addr = R_AX_GPIO_EXT_CTRL + 1, .data = BIT(0)},
15d910631fSJohnson Tsai }};
16d910631fSJohnson Tsai
17d910631fSJohnson Tsai static const struct rtw89_led_desc rtw89_common_led_desc = {
18d910631fSJohnson Tsai .gpios = rtw89_common_led_gpios,
19d910631fSJohnson Tsai .n_gpio = ARRAY_SIZE(rtw89_common_led_gpios),
20d910631fSJohnson Tsai };
21d910631fSJohnson Tsai
rtw89_led_gpio_config(struct rtw89_dev * rtwdev,const struct rtw89_led_gpio_entry * e)22d910631fSJohnson Tsai void rtw89_led_gpio_config(struct rtw89_dev *rtwdev,
23d910631fSJohnson Tsai const struct rtw89_led_gpio_entry *e)
24d910631fSJohnson Tsai {
25d910631fSJohnson Tsai rtw89_write32_mask(rtwdev, e->pinmux.addr, e->pinmux.mask, e->pinmux.data);
26d910631fSJohnson Tsai rtw89_write16_clr(rtwdev, e->mode.addr, e->mode.data);
27d910631fSJohnson Tsai }
28d910631fSJohnson Tsai
rtw89_led_gpio_set(struct rtw89_dev * rtwdev,const struct rtw89_led_gpio_entry * e,enum led_brightness brightness)29d910631fSJohnson Tsai void rtw89_led_gpio_set(struct rtw89_dev *rtwdev,
30d910631fSJohnson Tsai const struct rtw89_led_gpio_entry *e,
31d910631fSJohnson Tsai enum led_brightness brightness)
32d910631fSJohnson Tsai {
33d910631fSJohnson Tsai if (brightness == LED_OFF) {
34d910631fSJohnson Tsai rtw89_write16_clr(rtwdev, e->mode.addr, e->mode.data);
35d910631fSJohnson Tsai } else {
36d910631fSJohnson Tsai rtw89_write16_set(rtwdev, e->mode.addr, e->mode.data);
37d910631fSJohnson Tsai rtw89_write8_clr(rtwdev, e->out.addr, e->out.data);
38d910631fSJohnson Tsai }
39d910631fSJohnson Tsai }
40d910631fSJohnson Tsai
rtw89_led_set(struct led_classdev * led,enum led_brightness brightness)41d910631fSJohnson Tsai static int rtw89_led_set(struct led_classdev *led, enum led_brightness brightness)
42d910631fSJohnson Tsai {
43d910631fSJohnson Tsai struct rtw89_led *rtw_led = container_of(led, struct rtw89_led, led);
44d910631fSJohnson Tsai struct rtw89_dev *rtwdev = container_of(rtw_led, struct rtw89_dev, led);
45d910631fSJohnson Tsai const struct rtw89_led_desc *desc = rtw_led->desc;
46d910631fSJohnson Tsai const struct rtw89_led_gpio_entry *e = &desc->gpios[0];
47d910631fSJohnson Tsai
48*721d90c8SJohnson Tsai if (rtw_led->brightness_cache[0] == brightness) {
49d910631fSJohnson Tsai rtw89_debug(rtwdev, RTW89_DBG_LED, "led_set: pin=%u skip (no change)\n",
50d910631fSJohnson Tsai e->pin);
51d910631fSJohnson Tsai return 0;
52d910631fSJohnson Tsai }
53d910631fSJohnson Tsai
54d910631fSJohnson Tsai wiphy_lock(rtwdev->hw->wiphy);
55d910631fSJohnson Tsai
56d910631fSJohnson Tsai rtw89_debug(rtwdev, RTW89_DBG_LED, "led_set: pin=%u brightness=%u\n",
57d910631fSJohnson Tsai e->pin, brightness);
58d910631fSJohnson Tsai rtw89_led_gpio_set(rtwdev, e, brightness);
59*721d90c8SJohnson Tsai rtw_led->brightness_cache[0] = brightness;
60d910631fSJohnson Tsai
61d910631fSJohnson Tsai wiphy_unlock(rtwdev->hw->wiphy);
62d910631fSJohnson Tsai
63d910631fSJohnson Tsai return 0;
64d910631fSJohnson Tsai }
65d910631fSJohnson Tsai
rtw89_led_sc_init(struct rtw89_dev * rtwdev,const struct rtw89_led_desc * desc)66d910631fSJohnson Tsai static int rtw89_led_sc_init(struct rtw89_dev *rtwdev, const struct rtw89_led_desc *desc)
67d910631fSJohnson Tsai {
68d910631fSJohnson Tsai struct rtw89_led *rtw_led = &rtwdev->led;
69d910631fSJohnson Tsai int ret;
70d910631fSJohnson Tsai
71d910631fSJohnson Tsai snprintf(rtw_led->name, sizeof(rtw_led->name), "rtw89-%s",
72d910631fSJohnson Tsai wiphy_name(rtwdev->hw->wiphy));
73d910631fSJohnson Tsai rtw_led->led.name = rtw_led->name;
74d910631fSJohnson Tsai rtw_led->led.brightness_set_blocking = rtw89_led_set;
75d910631fSJohnson Tsai rtw_led->led.max_brightness = LED_ON;
76d910631fSJohnson Tsai rtw_led->led.default_trigger = ieee80211_get_assoc_led_name(rtwdev->hw);
77d910631fSJohnson Tsai
78d910631fSJohnson Tsai ret = led_classdev_register(rtwdev->dev, &rtw_led->led);
79d910631fSJohnson Tsai if (ret) {
80d910631fSJohnson Tsai rtw89_warn(rtwdev, "failed to register LED, ret=%d\n", ret);
81d910631fSJohnson Tsai return ret;
82d910631fSJohnson Tsai }
83d910631fSJohnson Tsai
84d910631fSJohnson Tsai rtw89_led_gpio_config(rtwdev, &desc->gpios[0]);
85d910631fSJohnson Tsai
86d910631fSJohnson Tsai return 0;
87d910631fSJohnson Tsai }
88d910631fSJohnson Tsai
rtw89_led_sc_deinit(struct rtw89_dev * rtwdev)89d910631fSJohnson Tsai static void rtw89_led_sc_deinit(struct rtw89_dev *rtwdev)
90d910631fSJohnson Tsai {
91d910631fSJohnson Tsai struct rtw89_led *rtw_led = &rtwdev->led;
92d910631fSJohnson Tsai
93d910631fSJohnson Tsai led_classdev_unregister(&rtw_led->led);
94d910631fSJohnson Tsai }
95d910631fSJohnson Tsai
rtw89_led_init(struct rtw89_dev * rtwdev)96d910631fSJohnson Tsai void rtw89_led_init(struct rtw89_dev *rtwdev)
97d910631fSJohnson Tsai {
98d910631fSJohnson Tsai const struct rtw89_led_desc *desc = &rtw89_common_led_desc;
99d910631fSJohnson Tsai struct rtw89_led *rtw_led = &rtwdev->led;
100*721d90c8SJohnson Tsai const struct rtw89_board_variant *board = rtwdev->board;
101d910631fSJohnson Tsai int ret;
102*721d90c8SJohnson Tsai int i;
103d910631fSJohnson Tsai
104d910631fSJohnson Tsai /* single-GPIO monochrome LED is the only supported layout */
105d910631fSJohnson Tsai BUILD_BUG_ON(ARRAY_SIZE(rtw89_common_led_gpios) != 1);
106d910631fSJohnson Tsai
107*721d90c8SJohnson Tsai if (board)
108*721d90c8SJohnson Tsai desc = board->led_desc;
109*721d90c8SJohnson Tsai if (!desc->n_gpio || desc->n_gpio > RTW89_LED_MAX_NUM)
110*721d90c8SJohnson Tsai return;
111d910631fSJohnson Tsai
112*721d90c8SJohnson Tsai rtw_led->desc = desc;
113*721d90c8SJohnson Tsai for (i = 0; i < ARRAY_SIZE(rtw_led->brightness_cache); i++)
114*721d90c8SJohnson Tsai rtw_led->brightness_cache[i] = LED_OFF;
115*721d90c8SJohnson Tsai
116*721d90c8SJohnson Tsai if (desc->n_gpio == 1)
117d910631fSJohnson Tsai ret = rtw89_led_sc_init(rtwdev, desc);
118*721d90c8SJohnson Tsai else
119*721d90c8SJohnson Tsai ret = rtw89_led_mc_init(rtwdev, desc);
120*721d90c8SJohnson Tsai
121d910631fSJohnson Tsai if (ret)
122d910631fSJohnson Tsai return;
123d910631fSJohnson Tsai
124d910631fSJohnson Tsai rtw_led->registered = true;
125d910631fSJohnson Tsai }
126d910631fSJohnson Tsai
rtw89_led_deinit(struct rtw89_dev * rtwdev)127d910631fSJohnson Tsai void rtw89_led_deinit(struct rtw89_dev *rtwdev)
128d910631fSJohnson Tsai {
129d910631fSJohnson Tsai struct rtw89_led *rtw_led = &rtwdev->led;
130*721d90c8SJohnson Tsai const struct rtw89_led_desc *desc = rtw_led->desc;
131d910631fSJohnson Tsai
132d910631fSJohnson Tsai if (!rtw_led->registered)
133d910631fSJohnson Tsai return;
134d910631fSJohnson Tsai
135*721d90c8SJohnson Tsai if (desc->n_gpio == 1)
136d910631fSJohnson Tsai rtw89_led_sc_deinit(rtwdev);
137*721d90c8SJohnson Tsai else
138*721d90c8SJohnson Tsai rtw89_led_mc_deinit(rtwdev);
139d910631fSJohnson Tsai }
140