mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 01:04:41 +01:00
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: 1368d06dd2 ("leds: Introduce ExpressWire library")
Tested-by: Karel Balej <balejk@matfyz.cz>
Signed-off-by: Duje Mihanović <duje@dujemihanovic.xyz>
Link: https://patch.msgid.link/20251217-expresswire-fix-v2-1-4a02b10acd96@dujemihanovic.xyz
Signed-off-by: Lee Jones <lee@kernel.org>
82 lines
2.1 KiB
C
82 lines
2.1 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Shared library for Kinetic's ExpressWire protocol.
|
|
* This protocol works by pulsing the ExpressWire IC's control GPIO.
|
|
* ktd2692 and ktd2801 are known to use this protocol.
|
|
*/
|
|
|
|
#include <linux/bits.h>
|
|
#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>
|
|
|
|
void expresswire_power_off(struct expresswire_common_props *props)
|
|
{
|
|
gpiod_set_value_cansleep(props->ctrl_gpio, 0);
|
|
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");
|
|
|
|
static void expresswire_start(struct expresswire_common_props *props)
|
|
{
|
|
gpiod_set_value(props->ctrl_gpio, 1);
|
|
udelay(props->timing.data_start_us);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
static void expresswire_set_bit(struct expresswire_common_props *props, bool bit)
|
|
{
|
|
if (bit) {
|
|
gpiod_set_value(props->ctrl_gpio, 0);
|
|
udelay(props->timing.short_bitset_us);
|
|
gpiod_set_value(props->ctrl_gpio, 1);
|
|
udelay(props->timing.long_bitset_us);
|
|
} else {
|
|
gpiod_set_value(props->ctrl_gpio, 0);
|
|
udelay(props->timing.long_bitset_us);
|
|
gpiod_set_value(props->ctrl_gpio, 1);
|
|
udelay(props->timing.short_bitset_us);
|
|
}
|
|
}
|
|
|
|
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");
|