Commit f4b830a5 authored by Duje Mihanović's avatar Duje Mihanović Committed by Lee Jones
Browse files

leds: expresswire: Fix chip state breakage



It is possible to put the KTD2801 chip in an unknown/undefined state by
changing the brightness very rapidly (for example, with a brightness
slider). When this happens, the brightness is stuck on max and cannot be
changed until the chip is power cycled.

Fix this by disabling interrupts while talking to the chip. While at it,
make expresswire_power_off() use fsleep() and also unexport some
functions meant to be internal.

Fixes: 1368d06d ("leds: Introduce ExpressWire library")
Tested-by: default avatarKarel Balej <balejk@matfyz.cz>
Signed-off-by: default avatarDuje Mihanović <duje@dujemihanovic.xyz>
Link: https://patch.msgid.link/20251217-expresswire-fix-v2-1-4a02b10acd96@dujemihanovic.xyz


Signed-off-by: default avatarLee Jones <lee@kernel.org>
parent 393d56d4
Loading
Loading
Loading
Loading
+17 −7
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/gpio/consumer.h>
#include <linux/irqflags.h>
#include <linux/types.h>

#include <linux/leds-expresswire.h>
@@ -16,37 +17,41 @@
void expresswire_power_off(struct expresswire_common_props *props)
{
	gpiod_set_value_cansleep(props->ctrl_gpio, 0);
	usleep_range(props->timing.poweroff_us, props->timing.poweroff_us * 2);
	fsleep(props->timing.poweroff_us);
}
EXPORT_SYMBOL_NS_GPL(expresswire_power_off, "EXPRESSWIRE");

void expresswire_enable(struct expresswire_common_props *props)
{
	unsigned long flags;

	local_irq_save(flags);

	gpiod_set_value(props->ctrl_gpio, 1);
	udelay(props->timing.detect_delay_us);
	gpiod_set_value(props->ctrl_gpio, 0);
	udelay(props->timing.detect_us);
	gpiod_set_value(props->ctrl_gpio, 1);

	local_irq_restore(flags);
}
EXPORT_SYMBOL_NS_GPL(expresswire_enable, "EXPRESSWIRE");

void expresswire_start(struct expresswire_common_props *props)
static void expresswire_start(struct expresswire_common_props *props)
{
	gpiod_set_value(props->ctrl_gpio, 1);
	udelay(props->timing.data_start_us);
}
EXPORT_SYMBOL_NS_GPL(expresswire_start, "EXPRESSWIRE");

void expresswire_end(struct expresswire_common_props *props)
static void expresswire_end(struct expresswire_common_props *props)
{
	gpiod_set_value(props->ctrl_gpio, 0);
	udelay(props->timing.end_of_data_low_us);
	gpiod_set_value(props->ctrl_gpio, 1);
	udelay(props->timing.end_of_data_high_us);
}
EXPORT_SYMBOL_NS_GPL(expresswire_end, "EXPRESSWIRE");

void expresswire_set_bit(struct expresswire_common_props *props, bool bit)
static void expresswire_set_bit(struct expresswire_common_props *props, bool bit)
{
	if (bit) {
		gpiod_set_value(props->ctrl_gpio, 0);
@@ -60,13 +65,18 @@ void expresswire_set_bit(struct expresswire_common_props *props, bool bit)
		udelay(props->timing.short_bitset_us);
	}
}
EXPORT_SYMBOL_NS_GPL(expresswire_set_bit, "EXPRESSWIRE");

void expresswire_write_u8(struct expresswire_common_props *props, u8 val)
{
	unsigned long flags;

	local_irq_save(flags);

	expresswire_start(props);
	for (int i = 7; i >= 0; i--)
		expresswire_set_bit(props, val & BIT(i));
	expresswire_end(props);

	local_irq_restore(flags);
}
EXPORT_SYMBOL_NS_GPL(expresswire_write_u8, "EXPRESSWIRE");
+0 −3
Original line number Diff line number Diff line
@@ -30,9 +30,6 @@ struct expresswire_common_props {

void expresswire_power_off(struct expresswire_common_props *props);
void expresswire_enable(struct expresswire_common_props *props);
void expresswire_start(struct expresswire_common_props *props);
void expresswire_end(struct expresswire_common_props *props);
void expresswire_set_bit(struct expresswire_common_props *props, bool bit);
void expresswire_write_u8(struct expresswire_common_props *props, u8 val);

#endif /* _LEDS_EXPRESSWIRE_H */