1 // SPDX-License-Identifier: GPL-2.0-only
3 #include <linux/delay.h>
5 #include <linux/init.h>
6 #include <linux/leds.h>
7 #include <linux/module.h>
8 #include <linux/mutex.h>
10 #include <linux/of_gpio.h>
11 #include <linux/gpio/consumer.h>
14 * Driver for the Ubiquiti RGB LED controller (LEDBAR).
15 * This Controller is based on a Holtek HT32F52241 and connected
18 * - The Controller needs an enable signal set to high when
19 * performing a transaction. On the U6-LR, this is located
22 * - The Pin is also printed when calling the "usetled" function
23 * contained in the ubntapp bootloader application.
26 #define UBNT_LEDBAR_MAX_BRIGHTNESS 0xff
28 #define UBNT_LEDBAR_TRANSACTION_LENGTH 8
29 #define UBNT_LEDBAR_TRANSACTION_SUCCESS (char) 0xaa
31 #define UBNT_LEDBAR_TRANSACTION_BLUE_IDX 2
32 #define UBNT_LEDBAR_TRANSACTION_GREEN_IDX 3
33 #define UBNT_LEDBAR_TRANSACTION_RED_IDX 4
37 struct i2c_client
*client
;
38 struct led_classdev led_red
;
39 struct led_classdev led_green
;
40 struct led_classdev led_blue
;
41 struct gpio_desc
*enable_gpio
;
42 struct gpio_desc
*reset_gpio
;
45 static void ubnt_ledbar_perform_transaction(struct ubnt_ledbar
*ledbar
,
46 const char *transaction
, int len
,
47 char *result
, int result_len
)
51 for (i
= 0; i
< len
; i
++)
52 i2c_smbus_write_byte(ledbar
->client
, transaction
[i
]);
54 for (i
= 0; i
< result_len
; i
++)
55 result
[i
] = i2c_smbus_read_byte(ledbar
->client
);
58 static int ubnt_ledbar_apply_state(struct ubnt_ledbar
*ledbar
)
60 char setup_msg
[UBNT_LEDBAR_TRANSACTION_LENGTH
] = {0x40, 0x10, 0x00, 0x00,
61 0x00, 0x00, 0x00, 0x11};
62 char led_msg
[UBNT_LEDBAR_TRANSACTION_LENGTH
] = {0x40, 0x00, 0x00, 0x00,
63 0x00, 0x00, 0x01, 0x00};
67 mutex_lock(&ledbar
->lock
);
69 led_msg
[UBNT_LEDBAR_TRANSACTION_BLUE_IDX
] = ledbar
->led_blue
.brightness
;
70 led_msg
[UBNT_LEDBAR_TRANSACTION_GREEN_IDX
] = ledbar
->led_green
.brightness
;
71 led_msg
[UBNT_LEDBAR_TRANSACTION_RED_IDX
] = ledbar
->led_red
.brightness
;
73 gpiod_set_value(ledbar
->enable_gpio
, 1);
77 ubnt_ledbar_perform_transaction(ledbar
, setup_msg
, sizeof(setup_msg
), &i2c_response
, sizeof(i2c_response
));
78 if (i2c_response
!= UBNT_LEDBAR_TRANSACTION_SUCCESS
) {
79 dev_err(&ledbar
->client
->dev
, "Error initializing LED transaction: %02hhx\n", i2c_response
);
84 ubnt_ledbar_perform_transaction(ledbar
, led_msg
, sizeof(led_msg
), &i2c_response
, sizeof(i2c_response
));
85 if (i2c_response
!= UBNT_LEDBAR_TRANSACTION_SUCCESS
) {
86 dev_err(&ledbar
->client
->dev
, "Failed LED transaction: %02hhx\n", i2c_response
);
93 gpiod_set_value(ledbar
->enable_gpio
, 0);
95 mutex_unlock(&ledbar
->lock
);
100 static void ubnt_ledbar_reset(struct ubnt_ledbar
*ledbar
)
102 static const char init_msg
[16] = {0x02, 0x81, 0xfd, 0x7e,
103 0x00, 0x00, 0x00, 0x00,
104 0x00, 0x00, 0x00, 0x00,
105 0x00, 0x00, 0x00, 0x00};
106 char init_response
[4];
108 if (!ledbar
->reset_gpio
)
111 mutex_lock(&ledbar
->lock
);
113 gpiod_set_value(ledbar
->reset_gpio
, 1);
115 gpiod_set_value(ledbar
->reset_gpio
, 0);
119 gpiod_set_value(ledbar
->enable_gpio
, 1);
121 ubnt_ledbar_perform_transaction(ledbar
, init_msg
, sizeof(init_msg
), init_response
, sizeof(init_response
));
123 gpiod_set_value(ledbar
->enable_gpio
, 0);
125 mutex_unlock(&ledbar
->lock
);
128 #define UBNT_LEDBAR_CONTROL_RGBS(name) \
129 static int ubnt_ledbar_set_##name##_brightness(struct led_classdev *led_cdev,\
130 enum led_brightness value) \
132 struct ubnt_ledbar *ledbar = \
133 container_of(led_cdev, struct ubnt_ledbar, led_##name); \
135 led_cdev->brightness = value; \
136 ret = ubnt_ledbar_apply_state(ledbar); \
140 UBNT_LEDBAR_CONTROL_RGBS(red
);
141 UBNT_LEDBAR_CONTROL_RGBS(green
);
142 UBNT_LEDBAR_CONTROL_RGBS(blue
);
145 static int ubnt_ledbar_init_led(struct device_node
*np
, struct ubnt_ledbar
*ledbar
,
146 struct led_classdev
*led_cdev
)
148 struct led_init_data init_data
= {};
154 init_data
.fwnode
= of_fwnode_handle(np
);
156 led_cdev
->max_brightness
= UBNT_LEDBAR_MAX_BRIGHTNESS
;
158 ret
= devm_led_classdev_register_ext(&ledbar
->client
->dev
, led_cdev
,
161 dev_err(&ledbar
->client
->dev
, "led register err: %d\n", ret
);
167 static int ubnt_ledbar_probe(struct i2c_client
*client
,
168 const struct i2c_device_id
*id
)
170 struct device_node
*np
= client
->dev
.of_node
;
171 struct ubnt_ledbar
*ledbar
;
174 ledbar
= devm_kzalloc(&client
->dev
, sizeof(*ledbar
), GFP_KERNEL
);
178 ledbar
->enable_gpio
= devm_gpiod_get(&client
->dev
, "enable", GPIOD_OUT_LOW
);
180 if (IS_ERR(ledbar
->enable_gpio
)) {
181 ret
= PTR_ERR(ledbar
->enable_gpio
);
182 dev_err(&client
->dev
, "Failed to get enable gpio: %d\n", ret
);
186 ledbar
->reset_gpio
= devm_gpiod_get_optional(&client
->dev
, "reset", GPIOD_OUT_LOW
);
188 if (IS_ERR(ledbar
->reset_gpio
)) {
189 ret
= PTR_ERR(ledbar
->reset_gpio
);
190 dev_err(&client
->dev
, "Failed to get reset gpio: %d\n", ret
);
194 ledbar
->client
= client
;
196 mutex_init(&ledbar
->lock
);
198 i2c_set_clientdata(client
, ledbar
);
200 // Reset and initialize the MCU
201 ubnt_ledbar_reset(ledbar
);
203 ledbar
->led_red
.brightness_set_blocking
= ubnt_ledbar_set_red_brightness
;
204 ubnt_ledbar_init_led(of_get_child_by_name(np
, "red"), ledbar
, &ledbar
->led_red
);
206 ledbar
->led_green
.brightness_set_blocking
= ubnt_ledbar_set_green_brightness
;
207 ubnt_ledbar_init_led(of_get_child_by_name(np
, "green"), ledbar
, &ledbar
->led_green
);
209 ledbar
->led_blue
.brightness_set_blocking
= ubnt_ledbar_set_blue_brightness
;
210 ubnt_ledbar_init_led(of_get_child_by_name(np
, "blue"), ledbar
, &ledbar
->led_blue
);
212 return ubnt_ledbar_apply_state(ledbar
);
215 static int ubnt_ledbar_remove(struct i2c_client
*client
)
217 struct ubnt_ledbar
*ledbar
= i2c_get_clientdata(client
);
219 mutex_destroy(&ledbar
->lock
);
224 static const struct i2c_device_id ubnt_ledbar_id
[] = {
225 { "ubnt-ledbar", 0 },
228 MODULE_DEVICE_TABLE(i2c
, ubnt_ledbar_id
);
230 static const struct of_device_id of_ubnt_ledbar_match
[] = {
231 { .compatible
= "ubnt,ledbar", },
234 MODULE_DEVICE_TABLE(of
, of_ubnt_ledbar_match
);
236 static struct i2c_driver ubnt_ledbar_driver
= {
238 .name
= "ubnt-ledbar",
239 .of_match_table
= of_ubnt_ledbar_match
,
241 .probe
= ubnt_ledbar_probe
,
242 .remove
= ubnt_ledbar_remove
,
243 .id_table
= ubnt_ledbar_id
,
245 module_i2c_driver(ubnt_ledbar_driver
);
247 MODULE_DESCRIPTION("Ubiquiti LEDBAR driver");
248 MODULE_AUTHOR("David Bauer <mail@david-bauer.net>");
249 MODULE_LICENSE("GPL v2");